cppcheck/externals/simplecpp/simplecpp.cpp

2878 lines
105 KiB
C++
Raw Normal View History

2016-07-20 12:21:00 +02:00
/*
* simplecpp - A simple and high-fidelity C/C++ preprocessor library
* Copyright (C) 2016 Daniel Marjamäki.
*
* This library is free software: you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation, either
* version 3 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
2017-06-21 12:09:50 +02:00
#if defined(_WIN32) || defined(__CYGWIN__) || defined(__MINGW32__)
2018-01-13 18:08:23 +01:00
#define SIMPLECPP_WINDOWS
2017-06-21 12:09:50 +02:00
#define NOMINMAX
#endif
2016-07-20 12:21:00 +02:00
#include "simplecpp.h"
#include <algorithm>
#include <cstdlib>
2016-07-20 12:21:00 +02:00
#include <cstring>
2017-05-28 15:23:15 +02:00
#include <exception>
2016-07-20 12:21:00 +02:00
#include <fstream>
#include <iostream>
2017-05-28 15:23:15 +02:00
#include <limits>
#include <sstream>
2016-07-20 12:21:00 +02:00
#include <stack>
2017-05-28 15:23:15 +02:00
#include <stdexcept>
#include <utility>
2016-07-20 12:21:00 +02:00
2018-01-13 18:08:23 +01:00
#ifdef SIMPLECPP_WINDOWS
2016-08-21 17:08:40 +02:00
#include <windows.h>
#undef ERROR
#undef TRUE
#endif
2017-05-26 16:45:07 +02:00
static bool isHex(const std::string &s)
{
2017-05-18 21:40:47 +02:00
return s.size()>2 && (s.compare(0,2,"0x")==0 || s.compare(0,2,"0X")==0);
}
2019-09-25 20:19:26 +02:00
static bool isOct(const std::string &s)
{
return s.size()>1 && (s[0]=='0') && (s[1] >= '0') && (s[1] < '8');
}
2016-07-20 12:21:00 +02:00
2017-05-26 16:45:07 +02:00
static const simplecpp::TokenString DEFINE("define");
static const simplecpp::TokenString UNDEF("undef");
2016-07-20 12:21:00 +02:00
2017-05-26 16:45:07 +02:00
static const simplecpp::TokenString INCLUDE("include");
2016-07-20 12:21:00 +02:00
2017-05-26 16:45:07 +02:00
static const simplecpp::TokenString ERROR("error");
static const simplecpp::TokenString WARNING("warning");
2016-07-20 12:21:00 +02:00
2017-05-26 16:45:07 +02:00
static const simplecpp::TokenString IF("if");
static const simplecpp::TokenString IFDEF("ifdef");
static const simplecpp::TokenString IFNDEF("ifndef");
static const simplecpp::TokenString DEFINED("defined");
static const simplecpp::TokenString ELSE("else");
static const simplecpp::TokenString ELIF("elif");
static const simplecpp::TokenString ENDIF("endif");
2017-05-26 16:45:07 +02:00
static const simplecpp::TokenString PRAGMA("pragma");
static const simplecpp::TokenString ONCE("once");
2016-07-20 20:03:27 +02:00
2017-05-26 16:45:07 +02:00
template<class T> static std::string toString(T t)
{
std::ostringstream ostr;
ostr << t;
return ostr.str();
}
2016-07-21 15:59:59 +02:00
2017-05-26 16:45:07 +02:00
static long long stringToLL(const std::string &s)
{
long long ret;
const bool hex = isHex(s);
2019-09-25 20:19:26 +02:00
const bool oct = isOct(s);
std::istringstream istr(hex ? s.substr(2) : oct ? s.substr(1) : s);
2017-05-26 16:45:07 +02:00
if (hex)
istr >> std::hex;
2019-09-25 20:19:26 +02:00
else if (oct)
istr >> std::oct;
2017-05-26 16:45:07 +02:00
istr >> ret;
return ret;
}
2016-07-21 15:59:59 +02:00
2017-05-26 16:45:07 +02:00
static unsigned long long stringToULL(const std::string &s)
{
unsigned long long ret;
const bool hex = isHex(s);
2019-09-25 20:19:26 +02:00
const bool oct = isOct(s);
std::istringstream istr(hex ? s.substr(2) : oct ? s.substr(1) : s);
2017-05-26 16:45:07 +02:00
if (hex)
istr >> std::hex;
2019-09-25 20:19:26 +02:00
else if (oct)
istr >> std::oct;
2017-05-26 16:45:07 +02:00
istr >> ret;
return ret;
}
2017-05-26 16:45:07 +02:00
static bool startsWith(const std::string &str, const std::string &s)
{
return (str.size() >= s.size() && str.compare(0, s.size(), s) == 0);
}
2016-07-21 15:59:59 +02:00
2017-05-26 16:45:07 +02:00
static bool endsWith(const std::string &s, const std::string &e)
{
return (s.size() >= e.size() && s.compare(s.size() - e.size(), e.size(), e) == 0);
}
2016-07-31 20:48:55 +02:00
2017-05-26 16:45:07 +02:00
static bool sameline(const simplecpp::Token *tok1, const simplecpp::Token *tok2)
{
return tok1 && tok2 && tok1->location.sameline(tok2->location);
}
2016-07-31 20:48:55 +02:00
2017-05-26 16:45:07 +02:00
static bool isAlternativeBinaryOp(const simplecpp::Token *tok, const std::string &alt)
{
return (tok->name &&
2018-05-14 13:00:22 +02:00
tok->str() == alt &&
2017-05-26 16:45:07 +02:00
tok->previous &&
tok->next &&
(tok->previous->number || tok->previous->name || tok->previous->op == ')') &&
(tok->next->number || tok->next->name || tok->next->op == '('));
}
2016-07-31 20:48:55 +02:00
2017-05-26 16:45:07 +02:00
static bool isAlternativeUnaryOp(const simplecpp::Token *tok, const std::string &alt)
{
2018-05-14 13:00:22 +02:00
return ((tok->name && tok->str() == alt) &&
2017-05-26 16:45:07 +02:00
(!tok->previous || tok->previous->op == '(') &&
(tok->next && (tok->next->name || tok->next->number)));
2016-07-20 12:21:00 +02:00
}
2018-05-14 13:00:22 +02:00
const std::string simplecpp::Location::emptyFileName;
void simplecpp::Location::adjust(const std::string &str)
{
2016-07-20 12:21:00 +02:00
if (str.find_first_of("\r\n") == std::string::npos) {
2016-07-30 10:37:55 +02:00
col += str.size();
2016-07-20 12:21:00 +02:00
return;
}
2016-07-29 13:05:35 +02:00
for (std::size_t i = 0U; i < str.size(); ++i) {
2016-07-20 12:21:00 +02:00
col++;
if (str[i] == '\n' || str[i] == '\r') {
2017-06-21 12:09:50 +02:00
col = 1;
2016-07-20 12:21:00 +02:00
line++;
if (str[i] == '\r' && (i+1)<str.size() && str[i+1]=='\n')
++i;
}
}
}
bool simplecpp::Token::isOneOf(const char ops[]) const
{
2019-05-07 19:15:31 +02:00
return (op != '\0') && (std::strchr(ops, op) != NULL);
2016-07-20 12:21:00 +02:00
}
bool simplecpp::Token::startsWithOneOf(const char c[]) const
{
2019-05-07 19:15:31 +02:00
return std::strchr(c, string[0]) != NULL;
2016-07-20 12:21:00 +02:00
}
bool simplecpp::Token::endsWithOneOf(const char c[]) const
{
2019-05-07 19:15:31 +02:00
return std::strchr(c, string[string.size() - 1U]) != NULL;
2016-07-20 12:21:00 +02:00
}
void simplecpp::Token::printAll() const
{
2016-07-25 09:13:59 +02:00
const Token *tok = this;
while (tok->previous)
tok = tok->previous;
for (; tok; tok = tok->next) {
2016-07-25 09:13:59 +02:00
if (tok->previous) {
std::cout << (sameline(tok, tok->previous) ? ' ' : '\n');
}
2018-05-14 13:00:22 +02:00
std::cout << tok->str();
2016-07-25 09:13:59 +02:00
}
std::cout << std::endl;
}
void simplecpp::Token::printOut() const
{
2016-07-25 09:13:59 +02:00
for (const Token *tok = this; tok; tok = tok->next) {
if (tok != this) {
std::cout << (sameline(tok, tok->previous) ? ' ' : '\n');
}
2018-05-14 13:00:22 +02:00
std::cout << tok->str();
2016-07-25 09:13:59 +02:00
}
std::cout << std::endl;
}
2016-07-29 08:46:38 +02:00
simplecpp::TokenList::TokenList(std::vector<std::string> &filenames) : frontToken(NULL), backToken(NULL), files(filenames) {}
2016-07-20 12:21:00 +02:00
simplecpp::TokenList::TokenList(std::istream &istr, std::vector<std::string> &filenames, const std::string &filename, OutputList *outputList)
: frontToken(NULL), backToken(NULL), files(filenames)
{
2016-07-20 12:21:00 +02:00
readfile(istr,filename,outputList);
}
simplecpp::TokenList::TokenList(const TokenList &other) : frontToken(NULL), backToken(NULL), files(other.files)
{
2016-07-20 12:21:00 +02:00
*this = other;
}
2019-05-03 18:49:48 +02:00
#if __cplusplus >= 201103L
simplecpp::TokenList::TokenList(TokenList &&other) : frontToken(NULL), backToken(NULL), files(other.files)
{
*this = std::move(other);
}
#endif
simplecpp::TokenList::~TokenList()
{
2016-07-20 12:21:00 +02:00
clear();
}
2017-08-13 13:59:09 +02:00
simplecpp::TokenList &simplecpp::TokenList::operator=(const TokenList &other)
{
2017-08-13 13:59:09 +02:00
if (this != &other) {
clear();
for (const Token *tok = other.cfront(); tok; tok = tok->next)
push_back(new Token(*tok));
sizeOfType = other.sizeOfType;
}
return *this;
2016-07-20 12:21:00 +02:00
}
2019-05-03 18:49:48 +02:00
#if __cplusplus >= 201103L
simplecpp::TokenList &simplecpp::TokenList::operator=(TokenList &&other)
{
if (this != &other) {
clear();
backToken = other.backToken;
other.backToken = NULL;
frontToken = other.frontToken;
other.frontToken = NULL;
sizeOfType = std::move(other.sizeOfType);
}
return *this;
}
#endif
void simplecpp::TokenList::clear()
{
2016-07-29 08:46:38 +02:00
backToken = NULL;
while (frontToken) {
Token *next = frontToken->next;
delete frontToken;
frontToken = next;
2016-07-20 12:21:00 +02:00
}
2016-07-20 15:04:03 +02:00
sizeOfType.clear();
2016-07-20 12:21:00 +02:00
}
void simplecpp::TokenList::push_back(Token *tok)
{
2016-07-29 08:46:38 +02:00
if (!frontToken)
frontToken = tok;
2016-07-20 12:21:00 +02:00
else
2016-07-29 08:46:38 +02:00
backToken->next = tok;
tok->previous = backToken;
backToken = tok;
2016-07-20 12:21:00 +02:00
}
void simplecpp::TokenList::dump() const
{
2016-07-23 09:26:06 +02:00
std::cout << stringify() << std::endl;
2016-07-20 12:21:00 +02:00
}
std::string simplecpp::TokenList::stringify() const
{
2016-07-20 12:21:00 +02:00
std::ostringstream ret;
Location loc(files);
2016-07-29 08:46:38 +02:00
for (const Token *tok = cfront(); tok; tok = tok->next) {
2016-08-11 19:00:35 +02:00
if (tok->location.line < loc.line || tok->location.fileIndex != loc.fileIndex) {
ret << "\n#line " << tok->location.line << " \"" << tok->location.file() << "\"\n";
loc = tok->location;
}
2016-07-20 12:21:00 +02:00
while (tok->location.line > loc.line) {
ret << '\n';
loc.line++;
}
if (sameline(tok->previous, tok))
ret << ' ';
2018-05-14 13:00:22 +02:00
ret << tok->str();
2016-07-20 12:21:00 +02:00
2018-05-14 13:00:22 +02:00
loc.adjust(tok->str());
2016-07-20 12:21:00 +02:00
}
return ret.str();
}
static unsigned char readChar(std::istream &istr, unsigned int bom)
{
unsigned char ch = (unsigned char)istr.get();
// For UTF-16 encoded files the BOM is 0xfeff/0xfffe. If the
// character is non-ASCII character then replace it with 0xff
if (bom == 0xfeff || bom == 0xfffe) {
const unsigned char ch2 = (unsigned char)istr.get();
const int ch16 = (bom == 0xfeff) ? (ch<<8 | ch2) : (ch2<<8 | ch);
ch = (unsigned char)((ch16 >= 0x80) ? 0xff : ch16);
}
// Handling of newlines..
if (ch == '\r') {
ch = '\n';
if (bom == 0 && (char)istr.peek() == '\n')
(void)istr.get();
else if (bom == 0xfeff || bom == 0xfffe) {
int c1 = istr.get();
int c2 = istr.get();
int ch16 = (bom == 0xfeff) ? (c1<<8 | c2) : (c2<<8 | c1);
if (ch16 != '\n') {
istr.unget();
istr.unget();
}
}
}
return ch;
}
static unsigned char peekChar(std::istream &istr, unsigned int bom)
{
2016-07-20 12:21:00 +02:00
unsigned char ch = (unsigned char)istr.peek();
// For UTF-16 encoded files the BOM is 0xfeff/0xfffe. If the
// character is non-ASCII character then replace it with 0xff
if (bom == 0xfeff || bom == 0xfffe) {
2016-08-06 15:50:40 +02:00
(void)istr.get();
2016-07-20 12:21:00 +02:00
const unsigned char ch2 = (unsigned char)istr.peek();
2016-08-06 15:50:40 +02:00
istr.unget();
2016-07-20 12:21:00 +02:00
const int ch16 = (bom == 0xfeff) ? (ch<<8 | ch2) : (ch2<<8 | ch);
ch = (unsigned char)((ch16 >= 0x80) ? 0xff : ch16);
}
// Handling of newlines..
2016-08-06 15:50:40 +02:00
if (ch == '\r')
2016-07-20 12:21:00 +02:00
ch = '\n';
return ch;
}
static void ungetChar(std::istream &istr, unsigned int bom)
{
2016-07-28 09:26:23 +02:00
istr.unget();
2016-10-05 21:35:40 +02:00
if (bom == 0xfeff || bom == 0xfffe)
2016-07-28 09:26:23 +02:00
istr.unget();
}
static unsigned short getAndSkipBOM(std::istream &istr)
{
2019-02-23 08:35:10 +01:00
const int ch1 = istr.peek();
2016-07-20 12:21:00 +02:00
// The UTF-16 BOM is 0xfffe or 0xfeff.
if (ch1 >= 0xfe) {
unsigned short bom = ((unsigned char)istr.get() << 8);
if (istr.peek() >= 0xfe)
return bom | (unsigned char)istr.get();
2019-02-23 08:35:10 +01:00
istr.unget();
2016-07-20 12:21:00 +02:00
return 0;
}
// Skip UTF-8 BOM 0xefbbbf
if (ch1 == 0xef) {
2018-01-13 18:08:23 +01:00
(void)istr.get();
if (istr.get() == 0xbb && istr.peek() == 0xbf) {
(void)istr.get();
} else {
istr.unget();
istr.unget();
}
2016-07-20 12:21:00 +02:00
}
return 0;
}
2017-05-26 16:45:07 +02:00
static bool isNameChar(unsigned char ch)
{
2016-07-29 13:05:35 +02:00
return std::isalnum(ch) || ch == '_' || ch == '$';
2016-07-29 20:54:11 +02:00
}
2016-07-29 13:05:35 +02:00
static std::string escapeString(const std::string &str)
{
2016-08-11 19:00:35 +02:00
std::ostringstream ostr;
ostr << '\"';
for (std::size_t i = 1U; i < str.size() - 1; ++i) {
char c = str[i];
if (c == '\\' || c == '\"' || c == '\'')
ostr << '\\';
ostr << c;
}
ostr << '\"';
return ostr.str();
}
static void portabilityBackslash(simplecpp::OutputList *outputList, const std::vector<std::string> &files, const simplecpp::Location &location)
{
if (!outputList)
return;
simplecpp::Output err(files);
err.type = simplecpp::Output::PORTABILITY_BACKSLASH;
err.location = location;
err.msg = "Combination 'backslash space newline' is not portable.";
outputList->push_back(err);
}
2019-03-10 08:47:27 +01:00
static bool isStringLiteralPrefix(const std::string &str)
2018-01-14 15:18:21 +01:00
{
2019-03-10 08:47:27 +01:00
return str == "u" || str == "U" || str == "L" || str == "u8" ||
str == "R" || str == "uR" || str == "UR" || str == "LR" || str == "u8R";
2018-01-14 15:18:21 +01:00
}
2019-05-03 18:49:48 +02:00
void simplecpp::TokenList::lineDirective(unsigned int fileIndex, unsigned int line, Location *location)
{
if (fileIndex != location->fileIndex || line >= location->line) {
location->fileIndex = fileIndex;
location->line = line;
return;
}
if (line + 2 >= location->line) {
location->line = line;
while (cback()->op != '#')
deleteToken(back());
deleteToken(back());
return;
}
}
2016-07-20 12:21:00 +02:00
void simplecpp::TokenList::readfile(std::istream &istr, const std::string &filename, OutputList *outputList)
{
std::stack<simplecpp::Location> loc;
unsigned int multiline = 0U;
2016-07-21 15:05:29 +02:00
const Token *oldLastToken = NULL;
2016-07-20 12:21:00 +02:00
const unsigned short bom = getAndSkipBOM(istr);
Location location(files);
location.fileIndex = fileIndex(filename);
location.line = 1U;
2016-07-30 10:37:55 +02:00
location.col = 1U;
2016-07-20 12:21:00 +02:00
while (istr.good()) {
unsigned char ch = readChar(istr,bom);
if (!istr.good())
break;
2017-05-19 10:01:12 +02:00
if (ch < ' ' && ch != '\t' && ch != '\n' && ch != '\r')
ch = ' ';
2016-07-20 12:21:00 +02:00
2017-09-12 22:42:10 +02:00
if (ch >= 0x80) {
if (outputList) {
simplecpp::Output err(files);
err.type = simplecpp::Output::UNHANDLED_CHAR_ERROR;
err.location = location;
std::ostringstream s;
s << (int)ch;
err.msg = "The code contains unhandled character(s) (character code=" + s.str() + "). Neither unicode nor extended ascii is supported.";
outputList->push_back(err);
}
clear();
return;
}
2016-07-20 12:21:00 +02:00
if (ch == '\n') {
2016-07-29 08:46:38 +02:00
if (cback() && cback()->op == '\\') {
if (location.col > cback()->location.col + 1U)
portabilityBackslash(outputList, files, cback()->location);
2016-07-20 12:21:00 +02:00
++multiline;
2016-07-29 08:46:38 +02:00
deleteToken(back());
2016-07-20 12:21:00 +02:00
} else {
location.line += multiline + 1;
multiline = 0U;
}
if (!multiline)
location.col = 1;
2016-07-20 12:21:00 +02:00
2016-07-29 08:46:38 +02:00
if (oldLastToken != cback()) {
oldLastToken = cback();
2016-07-20 12:21:00 +02:00
const std::string lastline(lastLine());
if (lastline == "# file %str%") {
loc.push(location);
2018-05-14 13:00:22 +02:00
location.fileIndex = fileIndex(cback()->str().substr(1U, cback()->str().size() - 2U));
2016-07-20 12:21:00 +02:00
location.line = 1U;
} else if (lastline == "# line %num%") {
2019-05-03 18:49:48 +02:00
lineDirective(location.fileIndex, std::atol(cback()->str().c_str()), &location);
} else if (lastline == "# %num% %str%" || lastline == "# line %num% %str%") {
lineDirective(fileIndex(cback()->str().substr(1U, cback()->str().size() - 2U)),
std::atol(cback()->previous->str().c_str()), &location);
2016-07-20 12:21:00 +02:00
}
// #endfile
else if (lastline == "# endfile" && !loc.empty()) {
location = loc.top();
loc.pop();
}
}
continue;
}
2016-07-30 10:37:55 +02:00
if (std::isspace(ch)) {
location.col++;
2016-07-20 12:21:00 +02:00
continue;
2016-07-30 10:37:55 +02:00
}
2016-07-20 12:21:00 +02:00
TokenString currentToken;
2018-02-14 22:14:42 +01:00
if (cback() && cback()->location.line == location.line && cback()->previous && cback()->previous->op == '#' && (lastLine() == "# error" || lastLine() == "# warning")) {
2019-02-06 16:26:59 +01:00
char prev = ' ';
while (istr.good() && (prev == '\\' || (ch != '\r' && ch != '\n'))) {
2018-02-11 15:48:37 +01:00
currentToken += ch;
2019-02-06 16:26:59 +01:00
prev = ch;
2018-02-11 15:48:37 +01:00
ch = readChar(istr, bom);
}
2019-03-10 08:47:27 +01:00
ungetChar(istr, bom);
2018-02-11 15:48:37 +01:00
push_back(new Token(currentToken, location));
2019-02-06 16:26:59 +01:00
location.adjust(currentToken);
2018-02-11 15:48:37 +01:00
continue;
}
2016-07-20 12:21:00 +02:00
// number or name
2016-07-29 13:05:35 +02:00
if (isNameChar(ch)) {
2017-09-08 23:20:39 +02:00
const bool num = std::isdigit(ch);
2016-07-29 13:05:35 +02:00
while (istr.good() && isNameChar(ch)) {
2016-07-20 12:21:00 +02:00
currentToken += ch;
ch = readChar(istr,bom);
2017-09-08 23:20:39 +02:00
if (num && ch=='\'' && isNameChar(peekChar(istr,bom)))
ch = readChar(istr,bom);
2016-07-20 12:21:00 +02:00
}
2016-07-28 09:26:23 +02:00
ungetChar(istr,bom);
2016-07-20 12:21:00 +02:00
}
// comment
else if (ch == '/' && peekChar(istr,bom) == '/') {
while (istr.good() && ch != '\r' && ch != '\n') {
currentToken += ch;
ch = readChar(istr, bom);
}
const std::string::size_type pos = currentToken.find_last_not_of(" \t");
if (pos < currentToken.size() - 1U && currentToken[pos] == '\\')
portabilityBackslash(outputList, files, location);
2016-07-20 12:21:00 +02:00
if (currentToken[currentToken.size() - 1U] == '\\') {
2016-07-30 10:37:55 +02:00
++multiline;
currentToken.erase(currentToken.size() - 1U);
2016-07-20 12:21:00 +02:00
} else {
2019-03-10 08:47:27 +01:00
ungetChar(istr, bom);
2016-07-20 12:21:00 +02:00
}
}
// comment
else if (ch == '/' && peekChar(istr,bom) == '*') {
currentToken = "/*";
(void)readChar(istr,bom);
ch = readChar(istr,bom);
while (istr.good()) {
currentToken += ch;
2016-07-30 10:37:55 +02:00
if (currentToken.size() >= 4U && endsWith(currentToken, "*/"))
2016-07-20 12:21:00 +02:00
break;
ch = readChar(istr,bom);
}
2016-07-30 10:37:55 +02:00
// multiline..
2016-07-30 10:37:55 +02:00
std::string::size_type pos = 0;
while ((pos = currentToken.find("\\\n",pos)) != std::string::npos) {
currentToken.erase(pos,2);
++multiline;
}
if (multiline || startsWith(lastLine(10),"# ")) {
pos = 0;
while ((pos = currentToken.find('\n',pos)) != std::string::npos) {
currentToken.erase(pos,1);
++multiline;
}
}
2016-07-20 12:21:00 +02:00
}
// string / char literal
else if (ch == '\"' || ch == '\'') {
2019-03-11 18:18:00 +01:00
std::string prefix;
2019-03-31 07:49:43 +02:00
if (cback() && cback()->name && isStringLiteralPrefix(cback()->str()) &&
2019-05-03 18:49:48 +02:00
((cback()->location.col + cback()->str().size()) == location.col) &&
(cback()->location.line == location.line)) {
2019-03-11 18:18:00 +01:00
prefix = cback()->str();
}
// C++11 raw string literal
if (ch == '\"' && !prefix.empty() && *cback()->str().rbegin() == 'R') {
2016-07-28 09:26:23 +02:00
std::string delim;
2019-03-10 08:47:27 +01:00
currentToken = ch;
2019-03-11 18:18:00 +01:00
prefix.resize(prefix.size() - 1);
ch = readChar(istr,bom);
while (istr.good() && ch != '(' && ch != '\n') {
delim += ch;
2016-07-28 09:26:23 +02:00
ch = readChar(istr,bom);
2019-03-11 18:18:00 +01:00
}
if (!istr.good() || ch == '\n') {
if (outputList) {
Output err(files);
err.type = Output::SYNTAX_ERROR;
err.location = location;
err.msg = "Invalid newline in raw string delimiter.";
outputList->push_back(err);
2019-03-10 08:47:27 +01:00
}
2019-03-11 18:18:00 +01:00
return;
2016-07-28 09:26:23 +02:00
}
2019-03-11 18:18:00 +01:00
const std::string endOfRawString(')' + delim + currentToken);
2019-03-10 08:47:27 +01:00
while (istr.good() && !(endsWith(currentToken, endOfRawString) && currentToken.size() > 1))
2016-07-28 09:26:23 +02:00
currentToken += readChar(istr,bom);
2019-03-11 18:18:00 +01:00
if (!endsWith(currentToken, endOfRawString)) {
if (outputList) {
Output err(files);
err.type = Output::SYNTAX_ERROR;
err.location = location;
err.msg = "Raw string missing terminating delimiter.";
outputList->push_back(err);
}
2016-07-28 09:26:23 +02:00
return;
2019-03-11 18:18:00 +01:00
}
2016-07-28 09:26:23 +02:00
currentToken.erase(currentToken.size() - endOfRawString.size(), endOfRawString.size() - 1U);
2019-03-11 18:18:00 +01:00
currentToken = escapeString(currentToken);
2019-03-10 08:47:27 +01:00
currentToken.insert(0, prefix);
back()->setstr(currentToken);
2017-06-21 12:09:50 +02:00
location.adjust(currentToken);
if (currentToken.find_first_of("\r\n") == std::string::npos)
location.col += 2 + 2 * delim.size();
else
location.col += 1 + delim.size();
2019-03-11 18:18:00 +01:00
2016-07-28 09:26:23 +02:00
continue;
}
2019-03-10 08:47:27 +01:00
currentToken = readUntil(istr,location,ch,ch,outputList,bom);
2016-07-20 12:21:00 +02:00
if (currentToken.size() < 2U)
2019-03-11 18:18:00 +01:00
// Error is reported by readUntil()
2016-07-20 12:21:00 +02:00
return;
2017-05-28 15:23:15 +02:00
std::string s = currentToken;
std::string::size_type pos;
2018-02-14 22:14:42 +01:00
int newlines = 0;
2017-05-28 15:23:15 +02:00
while ((pos = s.find_first_of("\r\n")) != std::string::npos) {
s.erase(pos,1);
2018-02-14 22:14:42 +01:00
newlines++;
2017-05-28 15:23:15 +02:00
}
2019-03-11 18:18:00 +01:00
if (prefix.empty())
push_back(new Token(s, location)); // push string without newlines
else
back()->setstr(prefix + s);
2017-05-28 15:23:15 +02:00
2018-02-14 22:14:42 +01:00
if (newlines > 0 && lastLine().compare(0,9,"# define ") == 0) {
multiline += newlines;
location.adjust(s);
} else {
location.adjust(currentToken);
}
2017-05-28 15:23:15 +02:00
continue;
2016-07-20 12:21:00 +02:00
}
else {
currentToken += ch;
}
if (currentToken == "<" && lastLine() == "# include") {
2019-03-10 08:47:27 +01:00
currentToken = readUntil(istr, location, '<', '>', outputList, bom);
2016-07-20 12:21:00 +02:00
if (currentToken.size() < 2U)
return;
}
push_back(new Token(currentToken, location));
if (multiline)
location.col += currentToken.size();
else
location.adjust(currentToken);
2016-07-20 12:21:00 +02:00
}
combineOperators();
}
void simplecpp::TokenList::constFold()
{
2016-07-29 08:46:38 +02:00
while (cfront()) {
2016-07-20 12:21:00 +02:00
// goto last '('
2016-07-29 08:46:38 +02:00
Token *tok = back();
2016-07-20 12:21:00 +02:00
while (tok && tok->op != '(')
tok = tok->previous;
// no '(', goto first token
if (!tok)
2016-07-29 08:46:38 +02:00
tok = front();
2016-07-20 12:21:00 +02:00
// Constant fold expression
constFoldUnaryNotPosNeg(tok);
constFoldMulDivRem(tok);
constFoldAddSub(tok);
2017-09-08 23:20:39 +02:00
constFoldShift(tok);
2016-07-20 12:21:00 +02:00
constFoldComparison(tok);
constFoldBitwise(tok);
constFoldLogicalOp(tok);
constFoldQuestionOp(&tok);
// If there is no '(' we are done with the constant folding
if (tok->op != '(')
break;
if (!tok->next || !tok->next->next || tok->next->next->op != ')')
break;
tok = tok->next;
deleteToken(tok->previous);
deleteToken(tok->next);
}
}
2017-05-26 16:45:07 +02:00
static bool isFloatSuffix(const simplecpp::Token *tok)
{
2018-05-14 13:00:22 +02:00
if (!tok || tok->str().size() != 1U)
2017-05-18 21:40:47 +02:00
return false;
2018-05-14 13:00:22 +02:00
const char c = std::tolower(tok->str()[0]);
2017-05-26 16:45:07 +02:00
return c == 'f' || c == 'l';
2017-05-18 21:40:47 +02:00
}
void simplecpp::TokenList::combineOperators()
{
2018-04-01 09:06:13 +02:00
std::stack<bool> executableScope;
executableScope.push(false);
2016-07-29 08:46:38 +02:00
for (Token *tok = front(); tok; tok = tok->next) {
2018-04-01 09:06:13 +02:00
if (tok->op == '{') {
if (executableScope.top()) {
executableScope.push(true);
continue;
}
const Token *prev = tok->previous;
while (prev && prev->isOneOf(";{}()"))
prev = prev->previous;
executableScope.push(prev && prev->op == ')');
continue;
}
if (tok->op == '}') {
if (executableScope.size() > 1)
executableScope.pop();
continue;
}
2016-07-20 12:21:00 +02:00
if (tok->op == '.') {
// ellipsis ...
if (tok->next && tok->next->op == '.' && tok->next->location.col == (tok->location.col + 1) &&
tok->next->next && tok->next->next->op == '.' && tok->next->next->location.col == (tok->location.col + 2)) {
tok->setstr("...");
deleteToken(tok->next);
deleteToken(tok->next);
2017-05-18 21:40:47 +02:00
continue;
}
2016-07-20 12:21:00 +02:00
// float literals..
if (tok->previous && tok->previous->number) {
2018-05-14 13:00:22 +02:00
tok->setstr(tok->previous->str() + '.');
2016-07-20 12:21:00 +02:00
deleteToken(tok->previous);
2017-05-18 21:40:47 +02:00
if (isFloatSuffix(tok->next) || (tok->next && tok->next->startsWithOneOf("Ee"))) {
2018-05-14 13:00:22 +02:00
tok->setstr(tok->str() + tok->next->str());
2016-07-20 12:21:00 +02:00
deleteToken(tok->next);
}
}
if (tok->next && tok->next->number) {
2018-05-14 13:00:22 +02:00
tok->setstr(tok->str() + tok->next->str());
2016-07-20 12:21:00 +02:00
deleteToken(tok->next);
}
}
// match: [0-9.]+E [+-] [0-9]+
2018-05-14 13:00:22 +02:00
const char lastChar = tok->str()[tok->str().size() - 1];
2019-09-25 20:19:26 +02:00
if (tok->number && !isHex(tok->str()) && !isOct(tok->str()) && (lastChar == 'E' || lastChar == 'e') && tok->next && tok->next->isOneOf("+-") && tok->next->next && tok->next->next->number) {
2018-05-14 13:00:22 +02:00
tok->setstr(tok->str() + tok->next->op + tok->next->next->str());
2016-07-20 12:21:00 +02:00
deleteToken(tok->next);
deleteToken(tok->next);
}
if (tok->op == '\0' || !tok->next || tok->next->op == '\0')
continue;
2016-08-11 19:00:35 +02:00
if (!sameline(tok,tok->next))
continue;
if (tok->location.col + 1U != tok->next->location.col)
continue;
2016-07-20 12:21:00 +02:00
if (tok->next->op == '=' && tok->isOneOf("=!<>+-*/%&|^")) {
2018-04-01 09:06:13 +02:00
if (tok->op == '&' && !executableScope.top()) {
// don't combine &= if it is a anonymous reference parameter with default value:
// void f(x&=2)
int indentlevel = 0;
const Token *start = tok;
while (indentlevel >= 0 && start) {
if (start->op == ')')
++indentlevel;
else if (start->op == '(')
--indentlevel;
else if (start->isOneOf(";{}"))
break;
start = start->previous;
}
if (indentlevel == -1 && start) {
const Token *ftok = start;
bool isFuncDecl = ftok->name;
while (isFuncDecl) {
2018-05-14 13:00:22 +02:00
if (!start->name && start->str() != "::" && start->op != '*' && start->op != '&')
2018-04-01 09:06:13 +02:00
isFuncDecl = false;
if (!start->previous)
break;
if (start->previous->isOneOf(";{}:"))
break;
start = start->previous;
}
isFuncDecl &= start != ftok && start->name;
if (isFuncDecl) {
// TODO: we could loop through the parameters here and check if they are correct.
continue;
}
}
}
2018-05-14 13:00:22 +02:00
tok->setstr(tok->str() + "=");
2016-07-20 12:21:00 +02:00
deleteToken(tok->next);
} else if ((tok->op == '|' || tok->op == '&') && tok->op == tok->next->op) {
2018-05-14 13:00:22 +02:00
tok->setstr(tok->str() + tok->next->str());
2016-07-20 12:21:00 +02:00
deleteToken(tok->next);
} else if (tok->op == ':' && tok->next->op == ':') {
2018-05-14 13:00:22 +02:00
tok->setstr(tok->str() + tok->next->str());
2016-07-20 12:21:00 +02:00
deleteToken(tok->next);
} else if (tok->op == '-' && tok->next->op == '>') {
2018-05-14 13:00:22 +02:00
tok->setstr(tok->str() + tok->next->str());
2016-07-20 12:21:00 +02:00
deleteToken(tok->next);
} else if ((tok->op == '<' || tok->op == '>') && tok->op == tok->next->op) {
2018-05-14 13:00:22 +02:00
tok->setstr(tok->str() + tok->next->str());
2016-07-20 12:21:00 +02:00
deleteToken(tok->next);
2019-09-25 20:19:26 +02:00
if (tok->next && tok->next->op == '=' && tok->next->next && tok->next->next->op != '=') {
2018-05-14 13:00:22 +02:00
tok->setstr(tok->str() + tok->next->str());
2016-07-20 12:21:00 +02:00
deleteToken(tok->next);
}
} else if ((tok->op == '+' || tok->op == '-') && tok->op == tok->next->op) {
if (tok->location.col + 1U != tok->next->location.col)
continue;
if (tok->previous && tok->previous->number)
continue;
if (tok->next->next && tok->next->next->number)
continue;
2018-05-14 13:00:22 +02:00
tok->setstr(tok->str() + tok->next->str());
2016-07-20 12:21:00 +02:00
deleteToken(tok->next);
}
}
}
2018-01-13 18:08:23 +01:00
static const std::string COMPL("compl");
static const std::string NOT("not");
void simplecpp::TokenList::constFoldUnaryNotPosNeg(simplecpp::Token *tok)
{
2016-07-20 12:21:00 +02:00
for (; tok && tok->op != ')'; tok = tok->next) {
2016-07-31 20:48:55 +02:00
// "not" might be !
if (isAlternativeUnaryOp(tok, NOT))
tok->op = '!';
2018-01-13 18:08:23 +01:00
// "compl" might be ~
else if (isAlternativeUnaryOp(tok, COMPL))
tok->op = '~';
2016-07-31 20:48:55 +02:00
2016-07-20 12:21:00 +02:00
if (tok->op == '!' && tok->next && tok->next->number) {
2018-05-14 13:00:22 +02:00
tok->setstr(tok->next->str() == "0" ? "1" : "0");
2016-07-20 12:21:00 +02:00
deleteToken(tok->next);
2018-01-13 18:08:23 +01:00
} else if (tok->op == '~' && tok->next && tok->next->number) {
2018-05-14 13:00:22 +02:00
tok->setstr(toString(~stringToLL(tok->next->str())));
2018-01-13 18:08:23 +01:00
deleteToken(tok->next);
} else {
2016-07-20 12:21:00 +02:00
if (tok->previous && (tok->previous->number || tok->previous->name))
continue;
if (!tok->next || !tok->next->number)
continue;
switch (tok->op) {
case '+':
2018-05-14 13:00:22 +02:00
tok->setstr(tok->next->str());
2016-07-20 12:21:00 +02:00
deleteToken(tok->next);
break;
case '-':
2018-05-14 13:00:22 +02:00
tok->setstr(tok->op + tok->next->str());
2016-07-20 12:21:00 +02:00
deleteToken(tok->next);
break;
}
}
}
}
void simplecpp::TokenList::constFoldMulDivRem(Token *tok)
{
2016-07-20 12:21:00 +02:00
for (; tok && tok->op != ')'; tok = tok->next) {
if (!tok->previous || !tok->previous->number)
continue;
if (!tok->next || !tok->next->number)
continue;
long long result;
if (tok->op == '*')
2018-05-14 13:00:22 +02:00
result = (stringToLL(tok->previous->str()) * stringToLL(tok->next->str()));
2016-07-20 12:21:00 +02:00
else if (tok->op == '/' || tok->op == '%') {
2018-05-14 13:00:22 +02:00
long long rhs = stringToLL(tok->next->str());
2016-07-20 12:21:00 +02:00
if (rhs == 0)
throw std::overflow_error("division/modulo by zero");
2018-05-14 13:00:22 +02:00
long long lhs = stringToLL(tok->previous->str());
2016-07-20 20:40:16 +02:00
if (rhs == -1 && lhs == std::numeric_limits<long long>::min())
throw std::overflow_error("division overflow");
2016-07-20 12:21:00 +02:00
if (tok->op == '/')
2016-07-20 20:40:16 +02:00
result = (lhs / rhs);
2016-07-20 12:21:00 +02:00
else
2016-07-20 20:40:16 +02:00
result = (lhs % rhs);
} else
2016-07-20 12:21:00 +02:00
continue;
tok = tok->previous;
2016-07-20 20:03:27 +02:00
tok->setstr(toString(result));
2016-07-20 12:21:00 +02:00
deleteToken(tok->next);
deleteToken(tok->next);
}
}
void simplecpp::TokenList::constFoldAddSub(Token *tok)
{
2016-07-20 12:21:00 +02:00
for (; tok && tok->op != ')'; tok = tok->next) {
if (!tok->previous || !tok->previous->number)
continue;
if (!tok->next || !tok->next->number)
continue;
long long result;
if (tok->op == '+')
2018-05-14 13:00:22 +02:00
result = stringToLL(tok->previous->str()) + stringToLL(tok->next->str());
2016-07-20 12:21:00 +02:00
else if (tok->op == '-')
2018-05-14 13:00:22 +02:00
result = stringToLL(tok->previous->str()) - stringToLL(tok->next->str());
2016-07-20 12:21:00 +02:00
else
continue;
tok = tok->previous;
2016-07-20 20:03:27 +02:00
tok->setstr(toString(result));
2016-07-20 12:21:00 +02:00
deleteToken(tok->next);
deleteToken(tok->next);
}
}
2017-09-08 23:20:39 +02:00
void simplecpp::TokenList::constFoldShift(Token *tok)
{
for (; tok && tok->op != ')'; tok = tok->next) {
if (!tok->previous || !tok->previous->number)
continue;
if (!tok->next || !tok->next->number)
continue;
long long result;
2018-05-14 13:00:22 +02:00
if (tok->str() == "<<")
result = stringToLL(tok->previous->str()) << stringToLL(tok->next->str());
else if (tok->str() == ">>")
result = stringToLL(tok->previous->str()) >> stringToLL(tok->next->str());
2017-09-08 23:20:39 +02:00
else
continue;
tok = tok->previous;
tok->setstr(toString(result));
deleteToken(tok->next);
deleteToken(tok->next);
}
}
static const std::string NOTEQ("not_eq");
void simplecpp::TokenList::constFoldComparison(Token *tok)
{
2016-07-20 12:21:00 +02:00
for (; tok && tok->op != ')'; tok = tok->next) {
2016-07-31 20:48:55 +02:00
if (isAlternativeBinaryOp(tok,NOTEQ))
tok->setstr("!=");
2016-07-20 12:21:00 +02:00
if (!tok->startsWithOneOf("<>=!"))
continue;
if (!tok->previous || !tok->previous->number)
continue;
if (!tok->next || !tok->next->number)
continue;
int result;
2018-05-14 13:00:22 +02:00
if (tok->str() == "==")
result = (stringToLL(tok->previous->str()) == stringToLL(tok->next->str()));
else if (tok->str() == "!=")
result = (stringToLL(tok->previous->str()) != stringToLL(tok->next->str()));
else if (tok->str() == ">")
result = (stringToLL(tok->previous->str()) > stringToLL(tok->next->str()));
else if (tok->str() == ">=")
result = (stringToLL(tok->previous->str()) >= stringToLL(tok->next->str()));
else if (tok->str() == "<")
result = (stringToLL(tok->previous->str()) < stringToLL(tok->next->str()));
else if (tok->str() == "<=")
result = (stringToLL(tok->previous->str()) <= stringToLL(tok->next->str()));
2016-07-20 12:21:00 +02:00
else
continue;
tok = tok->previous;
2016-07-20 20:03:27 +02:00
tok->setstr(toString(result));
2016-07-20 12:21:00 +02:00
deleteToken(tok->next);
deleteToken(tok->next);
}
}
static const std::string BITAND("bitand");
static const std::string BITOR("bitor");
static const std::string XOR("xor");
2016-07-20 12:21:00 +02:00
void simplecpp::TokenList::constFoldBitwise(Token *tok)
{
Token * const tok1 = tok;
for (const char *op = "&^|"; *op; op++) {
const std::string* altop;
2016-07-23 09:26:06 +02:00
if (*op == '&')
altop = &BITAND;
2016-07-23 09:26:06 +02:00
else if (*op == '|')
altop = &BITOR;
2016-07-23 09:26:06 +02:00
else
altop = &XOR;
2016-07-20 12:21:00 +02:00
for (tok = tok1; tok && tok->op != ')'; tok = tok->next) {
if (tok->op != *op && !isAlternativeBinaryOp(tok, *altop))
2016-07-20 12:21:00 +02:00
continue;
if (!tok->previous || !tok->previous->number)
continue;
if (!tok->next || !tok->next->number)
continue;
long long result;
2016-07-23 09:26:06 +02:00
if (*op == '&')
2018-05-14 13:00:22 +02:00
result = (stringToLL(tok->previous->str()) & stringToLL(tok->next->str()));
2016-07-23 09:26:06 +02:00
else if (*op == '^')
2018-05-14 13:00:22 +02:00
result = (stringToLL(tok->previous->str()) ^ stringToLL(tok->next->str()));
2016-07-23 09:26:06 +02:00
else /*if (*op == '|')*/
2018-05-14 13:00:22 +02:00
result = (stringToLL(tok->previous->str()) | stringToLL(tok->next->str()));
2016-07-20 12:21:00 +02:00
tok = tok->previous;
2016-07-20 20:03:27 +02:00
tok->setstr(toString(result));
2016-07-20 12:21:00 +02:00
deleteToken(tok->next);
deleteToken(tok->next);
}
}
}
static const std::string AND("and");
static const std::string OR("or");
void simplecpp::TokenList::constFoldLogicalOp(Token *tok)
{
2016-07-20 12:21:00 +02:00
for (; tok && tok->op != ')'; tok = tok->next) {
2016-07-31 20:48:55 +02:00
if (tok->name) {
if (isAlternativeBinaryOp(tok,AND))
tok->setstr("&&");
else if (isAlternativeBinaryOp(tok,OR))
tok->setstr("||");
}
2018-05-14 13:00:22 +02:00
if (tok->str() != "&&" && tok->str() != "||")
2016-07-20 12:21:00 +02:00
continue;
if (!tok->previous || !tok->previous->number)
continue;
if (!tok->next || !tok->next->number)
continue;
int result;
2018-05-14 13:00:22 +02:00
if (tok->str() == "||")
result = (stringToLL(tok->previous->str()) || stringToLL(tok->next->str()));
else /*if (tok->str() == "&&")*/
result = (stringToLL(tok->previous->str()) && stringToLL(tok->next->str()));
2016-07-20 12:21:00 +02:00
tok = tok->previous;
2016-07-20 20:03:27 +02:00
tok->setstr(toString(result));
2016-07-20 12:21:00 +02:00
deleteToken(tok->next);
deleteToken(tok->next);
}
}
void simplecpp::TokenList::constFoldQuestionOp(Token **tok1)
{
2016-07-20 12:21:00 +02:00
bool gotoTok1 = false;
for (Token *tok = *tok1; tok && tok->op != ')'; tok = gotoTok1 ? *tok1 : tok->next) {
gotoTok1 = false;
2018-05-14 13:00:22 +02:00
if (tok->str() != "?")
2016-07-20 12:21:00 +02:00
continue;
if (!tok->previous || !tok->next || !tok->next->next)
throw std::runtime_error("invalid expression");
if (!tok->previous->number)
2016-07-20 12:21:00 +02:00
continue;
if (tok->next->next->op != ':')
2016-07-20 12:21:00 +02:00
continue;
Token * const condTok = tok->previous;
Token * const trueTok = tok->next;
Token * const falseTok = trueTok->next->next;
if (!falseTok)
throw std::runtime_error("invalid expression");
2016-07-20 12:21:00 +02:00
if (condTok == *tok1)
2018-05-14 13:00:22 +02:00
*tok1 = (condTok->str() != "0" ? trueTok : falseTok);
2016-07-20 12:21:00 +02:00
deleteToken(condTok->next); // ?
deleteToken(trueTok->next); // :
2018-05-14 13:00:22 +02:00
deleteToken(condTok->str() == "0" ? trueTok : falseTok);
2016-07-20 12:21:00 +02:00
deleteToken(condTok);
gotoTok1 = true;
}
}
void simplecpp::TokenList::removeComments()
{
2016-07-29 08:46:38 +02:00
Token *tok = frontToken;
2016-07-20 12:21:00 +02:00
while (tok) {
Token *tok1 = tok;
tok = tok->next;
if (tok1->comment)
deleteToken(tok1);
}
}
2019-03-10 08:47:27 +01:00
std::string simplecpp::TokenList::readUntil(std::istream &istr, const Location &location, const char start, const char end, OutputList *outputList, unsigned int bom)
{
2016-07-20 12:21:00 +02:00
std::string ret;
ret += start;
2017-05-28 15:23:15 +02:00
bool backslash = false;
2016-07-20 12:21:00 +02:00
char ch = 0;
while (ch != end && ch != '\r' && ch != '\n' && istr.good()) {
2019-03-10 08:47:27 +01:00
ch = readChar(istr, bom);
2017-05-28 15:23:15 +02:00
if (backslash && ch == '\n') {
ch = 0;
backslash = false;
continue;
}
backslash = false;
2016-07-20 12:21:00 +02:00
ret += ch;
2017-05-28 15:23:15 +02:00
if (ch == '\\') {
2019-03-10 08:47:27 +01:00
const char next = readChar(istr, bom);
2017-05-28 15:23:15 +02:00
if (next == '\r' || next == '\n') {
ret.erase(ret.size()-1U);
backslash = (next == '\r');
}
ret += next;
}
2016-07-20 12:21:00 +02:00
}
if (!istr.good() || ch != end) {
clear();
if (outputList) {
Output err(files);
err.type = Output::SYNTAX_ERROR;
2016-07-20 12:21:00 +02:00
err.location = location;
err.msg = std::string("No pair for character (") + start + "). Can't process file. File is either invalid or unicode, which is currently not supported.";
outputList->push_back(err);
}
return "";
}
return ret;
}
std::string simplecpp::TokenList::lastLine(int maxsize) const
{
2016-07-20 12:21:00 +02:00
std::string ret;
2016-07-28 09:26:23 +02:00
int count = 0;
2016-07-29 08:46:38 +02:00
for (const Token *tok = cback(); sameline(tok,cback()); tok = tok->previous) {
2016-07-20 12:21:00 +02:00
if (tok->comment)
continue;
if (!ret.empty())
2019-05-03 18:49:48 +02:00
ret.insert(0, 1, ' ');
ret.insert(0, tok->str()[0] == '\"' ? std::string("%str%")
: tok->number ? std::string("%num%") : tok->str());
2016-07-28 09:26:23 +02:00
if (++count > maxsize)
return "";
2016-07-20 12:21:00 +02:00
}
return ret;
}
unsigned int simplecpp::TokenList::fileIndex(const std::string &filename)
{
2016-07-20 12:21:00 +02:00
for (unsigned int i = 0; i < files.size(); ++i) {
if (files[i] == filename)
return i;
}
files.push_back(filename);
return files.size() - 1U;
}
namespace simplecpp {
class Macro {
public:
explicit Macro(std::vector<std::string> &f) : nameTokDef(NULL), variadic(false), valueToken(NULL), endToken(NULL), files(f), tokenListDefine(f), valueDefinedInCode_(false) {}
Macro(const Token *tok, std::vector<std::string> &f) : nameTokDef(NULL), files(f), tokenListDefine(f), valueDefinedInCode_(true) {
if (sameline(tok->previous, tok))
throw std::runtime_error("bad macro syntax");
if (tok->op != '#')
throw std::runtime_error("bad macro syntax");
const Token * const hashtok = tok;
tok = tok->next;
2018-05-14 13:00:22 +02:00
if (!tok || tok->str() != DEFINE)
throw std::runtime_error("bad macro syntax");
tok = tok->next;
if (!tok || !tok->name || !sameline(hashtok,tok))
throw std::runtime_error("bad macro syntax");
if (!parseDefine(tok))
throw std::runtime_error("bad macro syntax");
}
2016-07-20 12:21:00 +02:00
Macro(const std::string &name, const std::string &value, std::vector<std::string> &f) : nameTokDef(NULL), files(f), tokenListDefine(f), valueDefinedInCode_(false) {
const std::string def(name + ' ' + value);
std::istringstream istr(def);
tokenListDefine.readfile(istr);
if (!parseDefine(tokenListDefine.cfront()))
2019-02-06 16:26:59 +01:00
throw std::runtime_error("bad macro syntax. macroname=" + name + " value=" + value);
}
2016-07-20 12:21:00 +02:00
Macro(const Macro &macro) : nameTokDef(NULL), files(macro.files), tokenListDefine(macro.files), valueDefinedInCode_(macro.valueDefinedInCode_) {
*this = macro;
}
2016-07-20 12:21:00 +02:00
void operator=(const Macro &macro) {
if (this != &macro) {
valueDefinedInCode_ = macro.valueDefinedInCode_;
if (macro.tokenListDefine.empty())
parseDefine(macro.nameTokDef);
else {
tokenListDefine = macro.tokenListDefine;
parseDefine(tokenListDefine.cfront());
}
2016-07-20 12:21:00 +02:00
}
}
bool valueDefinedInCode() const {
return valueDefinedInCode_;
}
/**
* Expand macro. This will recursively expand inner macros.
2017-08-13 13:59:09 +02:00
* @param output destination tokenlist
* @param rawtok macro token
* @param macros list of macros
* @param inputFiles the input files
* @return token after macro
* @throw Can throw wrongNumberOfParameters or invalidHashHash
*/
const Token * expand(TokenList * const output,
const Token * rawtok,
const std::map<TokenString,Macro> &macros,
2017-08-13 13:59:09 +02:00
std::vector<std::string> &inputFiles) const {
std::set<TokenString> expandedmacros;
2017-08-13 13:59:09 +02:00
TokenList output2(inputFiles);
if (functionLike() && rawtok->next && rawtok->next->op == '(') {
// Copy macro call to a new tokenlist with no linebreaks
const Token * const rawtok1 = rawtok;
2017-08-13 13:59:09 +02:00
TokenList rawtokens2(inputFiles);
2018-05-14 13:00:22 +02:00
rawtokens2.push_back(new Token(rawtok->str(), rawtok1->location));
rawtok = rawtok->next;
2018-05-14 13:00:22 +02:00
rawtokens2.push_back(new Token(rawtok->str(), rawtok1->location));
rawtok = rawtok->next;
int par = 1;
while (rawtok && par > 0) {
if (rawtok->op == '(')
++par;
else if (rawtok->op == ')')
--par;
2018-04-17 08:12:15 +02:00
else if (rawtok->op == '#' && !sameline(rawtok->previous, rawtok))
throw Error(rawtok->location, "it is invalid to use a preprocessor directive as macro parameter");
2018-05-14 13:00:22 +02:00
rawtokens2.push_back(new Token(rawtok->str(), rawtok1->location));
rawtok = rawtok->next;
2016-07-24 08:22:49 +02:00
}
if (expand(&output2, rawtok1->location, rawtokens2.cfront(), macros, expandedmacros))
rawtok = rawtok1->next;
} else {
rawtok = expand(&output2, rawtok->location, rawtok, macros, expandedmacros);
2016-07-23 16:26:11 +02:00
}
while (output2.cback() && rawtok) {
unsigned int par = 0;
Token* macro2tok = output2.back();
while (macro2tok) {
if (macro2tok->op == '(') {
if (par==0)
break;
--par;
} else if (macro2tok->op == ')')
++par;
macro2tok = macro2tok->previous;
}
if (macro2tok) { // macro2tok->op == '('
macro2tok = macro2tok->previous;
expandedmacros.insert(name());
} else if (rawtok->op == '(')
macro2tok = output2.back();
if (!macro2tok || !macro2tok->name)
break;
2018-05-14 13:00:22 +02:00
if (output2.cfront() != output2.cback() && macro2tok->str() == this->name())
break;
2018-05-14 13:00:22 +02:00
const std::map<TokenString,Macro>::const_iterator macro = macros.find(macro2tok->str());
if (macro == macros.end() || !macro->second.functionLike())
break;
2017-08-13 13:59:09 +02:00
TokenList rawtokens2(inputFiles);
const Location loc(macro2tok->location);
while (macro2tok) {
Token *next = macro2tok->next;
2018-05-14 13:00:22 +02:00
rawtokens2.push_back(new Token(macro2tok->str(), loc));
output2.deleteToken(macro2tok);
macro2tok = next;
}
par = (rawtokens2.cfront() != rawtokens2.cback()) ? 1U : 0U;
const Token *rawtok2 = rawtok;
for (; rawtok2; rawtok2 = rawtok2->next) {
2018-05-14 13:00:22 +02:00
rawtokens2.push_back(new Token(rawtok2->str(), loc));
if (rawtok2->op == '(')
++par;
else if (rawtok2->op == ')') {
if (par <= 1U)
break;
--par;
}
2016-07-23 16:26:11 +02:00
}
if (!rawtok2 || par != 1U)
break;
if (macro->second.expand(&output2, rawtok->location, rawtokens2.cfront(), macros, expandedmacros) != NULL)
break;
rawtok = rawtok2->next;
2016-07-23 16:26:11 +02:00
}
output->takeTokens(output2);
return rawtok;
2016-07-23 16:26:11 +02:00
}
2016-07-20 12:21:00 +02:00
/** macro name */
const TokenString &name() const {
2018-05-14 13:00:22 +02:00
return nameTokDef->str();
}
2016-07-20 12:21:00 +02:00
/** location for macro definition */
const Location &defineLocation() const {
return nameTokDef->location;
}
2016-07-20 12:21:00 +02:00
/** how has this macro been used so far */
const std::list<Location> &usage() const {
return usageList;
}
2016-07-23 16:26:11 +02:00
/** is this a function like macro */
bool functionLike() const {
return nameTokDef->next &&
nameTokDef->next->op == '(' &&
sameline(nameTokDef, nameTokDef->next) &&
2018-05-14 13:00:22 +02:00
nameTokDef->next->location.col == nameTokDef->location.col + nameTokDef->str().size();
}
2016-07-20 12:21:00 +02:00
/** base class for errors */
struct Error {
Error(const Location &loc, const std::string &s) : location(loc), what(s) {}
Location location;
std::string what;
};
2016-07-20 12:21:00 +02:00
/** Struct that is thrown when macro is expanded with wrong number of parameters */
struct wrongNumberOfParameters : public Error {
wrongNumberOfParameters(const Location &loc, const std::string &macroName) : Error(loc, "Wrong number of parameters for macro \'" + macroName + "\'.") {}
};
2016-07-20 12:21:00 +02:00
/** Struct that is thrown when there is invalid ## usage */
struct invalidHashHash : public Error {
invalidHashHash(const Location &loc, const std::string &macroName) : Error(loc, "Invalid ## usage when expanding \'" + macroName + "\'.") {}
};
private:
/** Create new token where Token::macro is set for replaced tokens */
Token *newMacroToken(const TokenString &str, const Location &loc, bool replaced) const {
Token *tok = new Token(str,loc);
if (replaced)
2018-05-14 13:00:22 +02:00
tok->macro = nameTokDef->str();
return tok;
2016-07-20 12:21:00 +02:00
}
bool parseDefine(const Token *nametoken) {
nameTokDef = nametoken;
variadic = false;
if (!nameTokDef) {
valueToken = endToken = NULL;
args.clear();
return false;
}
// function like macro..
if (functionLike()) {
args.clear();
const Token *argtok = nameTokDef->next->next;
while (sameline(nametoken, argtok) && argtok->op != ')') {
if (argtok->str() == "..." &&
argtok->next && argtok->next->op == ')') {
variadic = true;
if (!argtok->previous->name)
args.push_back("__VA_ARGS__");
argtok = argtok->next; // goto ')'
break;
}
if (argtok->op != ',')
2018-05-14 13:00:22 +02:00
args.push_back(argtok->str());
argtok = argtok->next;
2016-07-20 12:21:00 +02:00
}
if (!sameline(nametoken, argtok)) {
2017-09-05 23:19:56 +02:00
endToken = argtok ? argtok->previous : argtok;
2017-09-08 23:20:39 +02:00
valueToken = NULL;
return false;
}
valueToken = argtok ? argtok->next : NULL;
} else {
args.clear();
valueToken = nameTokDef->next;
}
2016-07-20 12:21:00 +02:00
if (!sameline(valueToken, nameTokDef))
valueToken = NULL;
endToken = valueToken;
while (sameline(endToken, nameTokDef))
endToken = endToken->next;
return true;
2016-07-20 12:21:00 +02:00
}
unsigned int getArgNum(const TokenString &str) const {
unsigned int par = 0;
while (par < args.size()) {
if (str == args[par])
return par;
par++;
2016-07-20 12:21:00 +02:00
}
return ~0U;
2016-07-20 12:21:00 +02:00
}
std::vector<const Token *> getMacroParameters(const Token *nameTokInst, bool calledInDefine) const {
if (!nameTokInst->next || nameTokInst->next->op != '(' || !functionLike())
return std::vector<const Token *>();
2016-08-24 20:27:29 +02:00
std::vector<const Token *> parametertokens;
parametertokens.push_back(nameTokInst->next);
unsigned int par = 0U;
for (const Token *tok = nameTokInst->next->next; calledInDefine ? sameline(tok, nameTokInst) : (tok != NULL); tok = tok->next) {
2016-08-04 22:53:10 +02:00
if (tok->op == '(')
++par;
else if (tok->op == ')') {
if (par == 0U) {
parametertokens.push_back(tok);
2016-08-04 22:53:10 +02:00
break;
}
--par;
} else if (par == 0U && tok->op == ',' && (!variadic || parametertokens.size() < args.size()))
parametertokens.push_back(tok);
2016-07-23 09:26:06 +02:00
}
return parametertokens;
2016-07-23 09:26:06 +02:00
}
const Token *appendTokens(TokenList *tokens,
const Token *lpar,
const std::map<TokenString,Macro> &macros,
const std::set<TokenString> &expandedmacros,
const std::vector<const Token*> &parametertokens) const {
if (!lpar || lpar->op != '(')
return NULL;
unsigned int par = 0;
const Token *tok = lpar;
while (sameline(lpar, tok)) {
if (tok->op == '#' && sameline(tok,tok->next) && tok->next->op == '#' && sameline(tok,tok->next->next)) {
// A##B => AB
tok = expandHashHash(tokens, tok->location, tok, macros, expandedmacros, parametertokens);
} else if (tok->op == '#' && sameline(tok, tok->next) && tok->next->op != '#') {
tok = expandHash(tokens, tok->location, tok, macros, expandedmacros, parametertokens);
} else {
if (!expandArg(tokens, tok, tok->location, macros, expandedmacros, parametertokens)) {
bool expanded = false;
2018-05-14 13:00:22 +02:00
const std::map<TokenString, Macro>::const_iterator it = macros.find(tok->str());
if (it != macros.end() && expandedmacros.find(tok->str()) == expandedmacros.end()) {
const Macro &m = it->second;
if (!m.functionLike()) {
2018-02-14 22:14:42 +01:00
m.expand(tokens, tok->location, tok, macros, expandedmacros);
expanded = true;
}
}
if (!expanded)
tokens->push_back(new Token(*tok));
}
2016-07-29 08:46:38 +02:00
if (tok->op == '(')
++par;
else if (tok->op == ')') {
--par;
if (par == 0U)
break;
}
tok = tok->next;
}
}
return sameline(lpar,tok) ? tok : NULL;
2016-07-29 08:46:38 +02:00
}
const Token * expand(TokenList * const output, const Location &loc, const Token * const nameTokInst, const std::map<TokenString,Macro> &macros, std::set<TokenString> expandedmacros) const {
2018-05-14 13:00:22 +02:00
expandedmacros.insert(nameTokInst->str());
usageList.push_back(loc);
2016-07-29 08:46:38 +02:00
2018-05-14 13:00:22 +02:00
if (nameTokInst->str() == "__FILE__") {
output->push_back(new Token('\"'+loc.file()+'\"', loc));
return nameTokInst->next;
2016-07-29 08:46:38 +02:00
}
2018-05-14 13:00:22 +02:00
if (nameTokInst->str() == "__LINE__") {
output->push_back(new Token(toString(loc.line), loc));
return nameTokInst->next;
}
2018-05-14 13:00:22 +02:00
if (nameTokInst->str() == "__COUNTER__") {
output->push_back(new Token(toString(usageList.size()-1U), loc));
return nameTokInst->next;
}
const bool calledInDefine = (loc.fileIndex != nameTokInst->location.fileIndex ||
loc.line < nameTokInst->location.line);
2016-07-29 08:46:38 +02:00
std::vector<const Token*> parametertokens1(getMacroParameters(nameTokInst, calledInDefine));
if (functionLike()) {
// No arguments => not macro expansion
if (nameTokInst->next && nameTokInst->next->op != '(') {
2018-05-14 13:00:22 +02:00
output->push_back(new Token(nameTokInst->str(), loc));
return nameTokInst->next;
2016-07-29 08:46:38 +02:00
}
// Parse macro-call
if (variadic) {
if (parametertokens1.size() < args.size()) {
throw wrongNumberOfParameters(nameTokInst->location, name());
}
} else {
if (parametertokens1.size() != args.size() + (args.empty() ? 2U : 1U))
throw wrongNumberOfParameters(nameTokInst->location, name());
2016-07-29 20:54:11 +02:00
}
}
// If macro call uses __COUNTER__ then expand that first
TokenList tokensparams(files);
std::vector<const Token *> parametertokens2;
if (!parametertokens1.empty()) {
bool counter = false;
for (const Token *tok = parametertokens1[0]; tok != parametertokens1.back(); tok = tok->next) {
2018-05-14 13:00:22 +02:00
if (tok->str() == "__COUNTER__") {
counter = true;
break;
}
}
const std::map<TokenString,Macro>::const_iterator m = macros.find("__COUNTER__");
if (!counter || m == macros.end())
parametertokens2.swap(parametertokens1);
else {
const Macro &counterMacro = m->second;
unsigned int par = 0;
for (const Token *tok = parametertokens1[0]; tok && par < parametertokens1.size(); tok = tok->next) {
2018-05-14 13:00:22 +02:00
if (tok->str() == "__COUNTER__") {
tokensparams.push_back(new Token(toString(counterMacro.usageList.size()), tok->location));
counterMacro.usageList.push_back(tok->location);
} else {
tokensparams.push_back(new Token(*tok));
if (tok == parametertokens1[par]) {
parametertokens2.push_back(tokensparams.cback());
par++;
}
2016-07-29 20:54:11 +02:00
}
}
}
}
Token * const output_end_1 = output->back();
2016-07-29 20:54:11 +02:00
// expand
for (const Token *tok = valueToken; tok != endToken;) {
if (tok->op != '#') {
// A##B => AB
2019-02-06 16:26:59 +01:00
if (sameline(tok, tok->next) && tok->next && tok->next->op == '#' && tok->next->next && tok->next->next->op == '#') {
if (!sameline(tok, tok->next->next->next))
throw invalidHashHash(tok->location, name());
output->push_back(newMacroToken(expandArgStr(tok, parametertokens2), loc, isReplaced(expandedmacros)));
tok = tok->next;
} else {
tok = expandToken(output, loc, tok, macros, expandedmacros, parametertokens2);
}
continue;
}
int numberOfHash = 1;
const Token *hashToken = tok->next;
while (sameline(tok,hashToken) && hashToken->op == '#') {
hashToken = hashToken->next;
++numberOfHash;
}
if (numberOfHash == 4) {
// # ## # => ##
output->push_back(newMacroToken("##", loc, isReplaced(expandedmacros)));
tok = hashToken;
continue;
}
tok = tok->next;
if (tok == endToken) {
output->push_back(new Token(*tok->previous));
break;
}
if (tok->op == '#') {
// A##B => AB
tok = expandHashHash(output, loc, tok->previous, macros, expandedmacros, parametertokens2);
2016-07-29 08:46:38 +02:00
} else {
// #123 => "123"
tok = expandHash(output, loc, tok->previous, macros, expandedmacros, parametertokens2);
2016-07-29 08:46:38 +02:00
}
}
if (!functionLike()) {
for (Token *tok = output_end_1 ? output_end_1->next : output->front(); tok; tok = tok->next) {
2018-05-14 13:00:22 +02:00
tok->macro = nameTokInst->str();
}
}
if (!parametertokens1.empty())
parametertokens1.swap(parametertokens2);
return functionLike() ? parametertokens2.back()->next : nameTokInst->next;
2016-07-29 08:46:38 +02:00
}
const Token *expandToken(TokenList *output, const Location &loc, const Token *tok, const std::map<TokenString,Macro> &macros, const std::set<TokenString> &expandedmacros, const std::vector<const Token*> &parametertokens) const {
// Not name..
if (!tok->name) {
2018-05-14 13:00:22 +02:00
output->push_back(newMacroToken(tok->str(), loc, true));
return tok->next;
2016-08-06 08:41:19 +02:00
}
2016-07-29 08:46:38 +02:00
// Macro parameter..
{
TokenList temp(files);
if (expandArg(&temp, tok, loc, macros, expandedmacros, parametertokens)) {
if (!(temp.cback() && temp.cback()->name && tok->next && tok->next->op == '(')) {
output->takeTokens(temp);
return tok->next;
}
2016-07-29 20:54:11 +02:00
2018-02-14 22:14:42 +01:00
if (!sameline(tok, tok->next)) {
output->takeTokens(temp);
return tok->next;
}
2018-05-14 13:00:22 +02:00
const std::map<TokenString, Macro>::const_iterator it = macros.find(temp.cback()->str());
if (it == macros.end() || expandedmacros.find(temp.cback()->str()) != expandedmacros.end()) {
output->takeTokens(temp);
return tok->next;
}
2016-07-23 09:26:06 +02:00
const Macro &calledMacro = it->second;
if (!calledMacro.functionLike()) {
output->takeTokens(temp);
return tok->next;
}
2016-07-20 12:21:00 +02:00
TokenList temp2(files);
2018-05-14 13:00:22 +02:00
temp2.push_back(new Token(temp.cback()->str(), tok->location));
const Token *tok2 = appendTokens(&temp2, tok->next, macros, expandedmacros, parametertokens);
if (!tok2)
return tok->next;
2016-07-23 09:26:06 +02:00
output->takeTokens(temp);
output->deleteToken(output->back());
calledMacro.expand(output, loc, temp2.cfront(), macros, expandedmacros);
return tok2->next;
2016-07-23 09:26:06 +02:00
}
}
2016-07-23 09:26:06 +02:00
// Macro..
2018-05-14 13:00:22 +02:00
const std::map<TokenString, Macro>::const_iterator it = macros.find(tok->str());
if (it != macros.end() && expandedmacros.find(tok->str()) == expandedmacros.end()) {
2016-07-23 09:26:06 +02:00
const Macro &calledMacro = it->second;
if (!calledMacro.functionLike())
return calledMacro.expand(output, loc, tok, macros, expandedmacros);
if (!sameline(tok, tok->next) || tok->next->op != '(') {
2018-05-14 13:00:22 +02:00
output->push_back(newMacroToken(tok->str(), loc, true));
2016-07-23 09:26:06 +02:00
return tok->next;
}
TokenList tokens(files);
tokens.push_back(new Token(*tok));
const Token *tok2 = appendTokens(&tokens, tok->next, macros, expandedmacros, parametertokens);
if (!tok2) {
2018-05-14 13:00:22 +02:00
output->push_back(newMacroToken(tok->str(), loc, true));
2016-07-23 09:26:06 +02:00
return tok->next;
}
calledMacro.expand(output, loc, tokens.cfront(), macros, expandedmacros);
2016-07-23 09:26:06 +02:00
return tok2->next;
}
2016-07-20 12:21:00 +02:00
2018-05-14 13:00:22 +02:00
else if (tok->str() == DEFINED) {
2017-06-08 15:55:12 +02:00
const Token *tok2 = tok->next;
const Token *tok3 = tok2 ? tok2->next : NULL;
const Token *tok4 = tok3 ? tok3->next : NULL;
const Token *defToken = NULL;
const Token *lastToken = NULL;
if (sameline(tok, tok4) && tok2->op == '(' && tok3->name && tok4->op == ')') {
defToken = tok3;
lastToken = tok4;
} else if (sameline(tok,tok2) && tok2->name) {
defToken = lastToken = tok2;
}
if (defToken) {
2018-05-14 13:00:22 +02:00
const bool def = (macros.find(defToken->str()) != macros.end());
2017-06-08 15:55:12 +02:00
output->push_back(newMacroToken(def ? "1" : "0", loc, true));
return lastToken->next;
}
}
2018-05-14 13:00:22 +02:00
output->push_back(newMacroToken(tok->str(), loc, true));
return tok->next;
2016-07-20 12:21:00 +02:00
}
bool expandArg(TokenList *output, const Token *tok, const std::vector<const Token*> &parametertokens) const {
if (!tok->name)
return false;
2016-07-20 12:21:00 +02:00
2018-05-14 13:00:22 +02:00
const unsigned int argnr = getArgNum(tok->str());
if (argnr >= args.size())
return false;
2016-07-20 12:21:00 +02:00
// empty variadic parameter
if (variadic && argnr + 1U >= parametertokens.size())
return true;
2016-07-24 08:51:35 +02:00
for (const Token *partok = parametertokens[argnr]->next; partok != parametertokens[argnr + 1U]; partok = partok->next)
output->push_back(new Token(*partok));
2016-07-20 12:21:00 +02:00
2016-07-24 08:51:35 +02:00
return true;
2016-07-20 12:21:00 +02:00
}
bool expandArg(TokenList *output, const Token *tok, const Location &loc, const std::map<TokenString, Macro> &macros, const std::set<TokenString> &expandedmacros, const std::vector<const Token*> &parametertokens) const {
if (!tok->name)
return false;
2018-05-14 13:00:22 +02:00
const unsigned int argnr = getArgNum(tok->str());
if (argnr >= args.size())
return false;
if (variadic && argnr + 1U >= parametertokens.size()) // empty variadic parameter
return true;
for (const Token *partok = parametertokens[argnr]->next; partok != parametertokens[argnr + 1U];) {
2018-05-14 13:00:22 +02:00
const std::map<TokenString, Macro>::const_iterator it = macros.find(partok->str());
if (it != macros.end() && (partok->str() == name() || expandedmacros.find(partok->str()) == expandedmacros.end()))
partok = it->second.expand(output, loc, partok, macros, expandedmacros);
else {
2018-05-14 13:00:22 +02:00
output->push_back(newMacroToken(partok->str(), loc, isReplaced(expandedmacros)));
partok = partok->next;
}
}
return true;
2016-07-20 12:21:00 +02:00
}
/**
* Get string for token. If token is argument, the expanded string is returned.
* @param tok The token
* @param parametertokens parameters given when expanding this macro
* @return string
*/
std::string expandArgStr(const Token *tok, const std::vector<const Token *> &parametertokens) const {
TokenList tokens(files);
if (expandArg(&tokens, tok, parametertokens)) {
std::string s;
for (const Token *tok2 = tokens.cfront(); tok2; tok2 = tok2->next)
2018-05-14 13:00:22 +02:00
s += tok2->str();
return s;
}
2018-05-14 13:00:22 +02:00
return tok->str();
}
/**
* Expand #X => "X"
* @param output destination tokenlist
* @param loc location for expanded token
* @param tok The # token
* @param macros all macros
* @param expandedmacros set with expanded macros, with this macro
* @param parametertokens parameters given when expanding this macro
* @return token after the X
*/
const Token *expandHash(TokenList *output, const Location &loc, const Token *tok, const std::map<TokenString, Macro> &macros, const std::set<TokenString> &expandedmacros, const std::vector<const Token*> &parametertokens) const {
TokenList tokenListHash(files);
tok = expandToken(&tokenListHash, loc, tok->next, macros, expandedmacros, parametertokens);
std::ostringstream ostr;
ostr << '\"';
for (const Token *hashtok = tokenListHash.cfront(); hashtok; hashtok = hashtok->next)
2018-05-14 13:00:22 +02:00
ostr << hashtok->str();
ostr << '\"';
output->push_back(newMacroToken(escapeString(ostr.str()), loc, isReplaced(expandedmacros)));
return tok;
}
/**
* Expand A##B => AB
* The A should already be expanded. Call this when you reach the first # token
* @param output destination tokenlist
* @param loc location for expanded token
* @param tok first # token
* @param macros all macros
* @param expandedmacros set with expanded macros, with this macro
* @param parametertokens parameters given when expanding this macro
* @return token after B
*/
const Token *expandHashHash(TokenList *output, const Location &loc, const Token *tok, const std::map<TokenString, Macro> &macros, const std::set<TokenString> &expandedmacros, const std::vector<const Token*> &parametertokens) const {
Token *A = output->back();
if (!A)
throw invalidHashHash(tok->location, name());
if (!sameline(tok, tok->next) || !sameline(tok, tok->next->next))
throw invalidHashHash(tok->location, name());
2019-02-23 08:35:10 +01:00
bool canBeConcatenatedWithEqual = A->isOneOf("+-*/%&|^") || A->str() == "<<" || A->str() == ">>";
if (!A->name && !A->number && A->op != ',' && !A->str().empty() && !canBeConcatenatedWithEqual)
2018-03-15 21:34:58 +01:00
throw invalidHashHash(tok->location, name());
Token *B = tok->next->next;
2019-02-23 08:35:10 +01:00
if (!B->name && !B->number && B->op && !B->isOneOf("#="))
throw invalidHashHash(tok->location, name());
if ((canBeConcatenatedWithEqual && B->op != '=') ||
(!canBeConcatenatedWithEqual && B->op == '='))
2018-03-15 21:34:58 +01:00
throw invalidHashHash(tok->location, name());
std::string strAB;
2018-05-14 13:00:22 +02:00
const bool varargs = variadic && args.size() >= 1U && B->str() == args[args.size()-1U];
TokenList tokensB(files);
if (expandArg(&tokensB, B, parametertokens)) {
if (tokensB.empty())
2018-05-14 13:00:22 +02:00
strAB = A->str();
else if (varargs && A->op == ',') {
strAB = ",";
} else {
2018-05-14 13:00:22 +02:00
strAB = A->str() + tokensB.cfront()->str();
tokensB.deleteToken(tokensB.front());
}
2016-08-27 15:09:18 +02:00
} else {
2018-05-14 13:00:22 +02:00
strAB = A->str() + B->str();
2016-08-24 20:27:29 +02:00
}
2016-08-06 08:41:19 +02:00
const Token *nextTok = B->next;
2016-08-06 08:41:19 +02:00
2018-05-14 13:00:22 +02:00
if (varargs && tokensB.empty() && tok->previous->str() == ",")
output->deleteToken(A);
2018-01-13 18:08:23 +01:00
else if (strAB != "," && macros.find(strAB) == macros.end()) {
A->setstr(strAB);
for (Token *b = tokensB.front(); b; b = b->next)
b->location = loc;
output->takeTokens(tokensB);
} else {
output->deleteToken(A);
TokenList tokens(files);
tokens.push_back(new Token(strAB, tok->location));
// for function like macros, push the (...)
if (tokensB.empty() && sameline(B,B->next) && B->next->op=='(') {
const std::map<TokenString,Macro>::const_iterator it = macros.find(strAB);
if (it != macros.end() && expandedmacros.find(strAB) == expandedmacros.end() && it->second.functionLike()) {
const Token *tok2 = appendTokens(&tokens, B->next, macros, expandedmacros, parametertokens);
if (tok2)
nextTok = tok2->next;
}
}
expandToken(output, loc, tokens.cfront(), macros, expandedmacros, parametertokens);
for (Token *b = tokensB.front(); b; b = b->next)
b->location = loc;
output->takeTokens(tokensB);
}
2016-08-06 08:41:19 +02:00
return nextTok;
}
2016-08-06 08:41:19 +02:00
2017-08-13 13:59:09 +02:00
static bool isReplaced(const std::set<std::string> &expandedmacros) {
// return true if size > 1
std::set<std::string>::const_iterator it = expandedmacros.begin();
if (it == expandedmacros.end())
return false;
++it;
return (it != expandedmacros.end());
}
2016-07-20 12:21:00 +02:00
/** name token in definition */
const Token *nameTokDef;
2016-08-06 08:41:19 +02:00
/** arguments for macro */
std::vector<TokenString> args;
2016-08-06 08:41:19 +02:00
/** is macro variadic? */
bool variadic;
2016-08-06 08:41:19 +02:00
/** first token in replacement string */
const Token *valueToken;
2016-08-06 08:41:19 +02:00
/** token after replacement string */
const Token *endToken;
2016-08-06 08:41:19 +02:00
/** files */
std::vector<std::string> &files;
2016-08-06 08:41:19 +02:00
/** this is used for -D where the definition is not seen anywhere in code */
TokenList tokenListDefine;
2016-08-06 08:41:19 +02:00
/** usage of this macro */
mutable std::list<Location> usageList;
/** was the value of this macro actually defined in the code? */
bool valueDefinedInCode_;
};
2016-07-20 12:21:00 +02:00
}
2019-03-10 08:47:27 +01:00
namespace simplecpp {
std::string convertCygwinToWindowsPath(const std::string &cygwinPath)
{
std::string windowsPath;
std::string::size_type pos = 0;
if (cygwinPath.size() >= 11 && startsWith(cygwinPath, "/cygdrive/")) {
unsigned char driveLetter = cygwinPath[10];
if (std::isalpha(driveLetter)) {
if (cygwinPath.size() == 11) {
windowsPath = toupper(driveLetter);
windowsPath += ":\\"; // volume root directory
pos = 11;
} else if (cygwinPath[11] == '/') {
windowsPath = toupper(driveLetter);
windowsPath += ":";
pos = 11;
}
}
}
for (; pos < cygwinPath.size(); ++pos) {
unsigned char c = cygwinPath[pos];
if (c == '/')
c = '\\';
windowsPath += c;
}
return windowsPath;
}
}
2016-08-21 17:08:40 +02:00
#ifdef SIMPLECPP_WINDOWS
2019-02-06 16:26:59 +01:00
2019-02-23 08:35:10 +01:00
class ScopedLock {
2019-02-06 16:26:59 +01:00
public:
2019-02-23 08:35:10 +01:00
explicit ScopedLock(CRITICAL_SECTION& criticalSection)
: m_criticalSection(criticalSection) {
EnterCriticalSection(&m_criticalSection);
2019-02-06 16:26:59 +01:00
}
2019-02-23 08:35:10 +01:00
~ScopedLock() {
LeaveCriticalSection(&m_criticalSection);
2019-02-06 16:26:59 +01:00
}
private:
ScopedLock& operator=(const ScopedLock&);
ScopedLock(const ScopedLock&);
2019-02-23 08:35:10 +01:00
CRITICAL_SECTION& m_criticalSection;
2019-02-06 16:26:59 +01:00
};
2019-02-23 08:35:10 +01:00
class RealFileNameMap {
2019-02-06 16:26:59 +01:00
public:
2019-02-23 08:35:10 +01:00
RealFileNameMap() {
InitializeCriticalSection(&m_criticalSection);
2019-02-06 16:26:59 +01:00
}
2019-02-23 08:35:10 +01:00
~RealFileNameMap() {
DeleteCriticalSection(&m_criticalSection);
2019-02-06 16:26:59 +01:00
}
2019-03-10 08:47:27 +01:00
bool getCacheEntry(const std::string& path, std::string* returnPath) {
2019-02-23 08:35:10 +01:00
ScopedLock lock(m_criticalSection);
2019-02-06 16:26:59 +01:00
std::map<std::string, std::string>::iterator it = m_fileMap.find(path);
2019-02-23 08:35:10 +01:00
if (it != m_fileMap.end()) {
2019-02-06 16:26:59 +01:00
*returnPath = it->second;
return true;
}
return false;
}
2019-02-23 08:35:10 +01:00
void addToCache(const std::string& path, const std::string& actualPath) {
ScopedLock lock(m_criticalSection);
2019-02-06 16:26:59 +01:00
m_fileMap[path] = actualPath;
}
2019-02-23 08:35:10 +01:00
private:
2019-02-06 16:26:59 +01:00
std::map<std::string, std::string> m_fileMap;
2019-02-23 08:35:10 +01:00
CRITICAL_SECTION m_criticalSection;
2019-02-06 16:26:59 +01:00
};
static RealFileNameMap realFileNameMap;
2017-06-23 20:09:26 +02:00
static bool realFileName(const std::string &f, std::string *result)
{
// are there alpha characters in last subpath?
bool alpha = false;
2019-03-10 08:47:27 +01:00
for (std::string::size_type pos = 1; pos <= f.size(); ++pos) {
2017-06-23 20:09:26 +02:00
unsigned char c = f[f.size() - pos];
2019-03-10 08:47:27 +01:00
if (c == '/' || c == '\\')
2017-06-23 20:09:26 +02:00
break;
if (std::isalpha(c)) {
alpha = true;
break;
2017-06-21 12:09:50 +02:00
}
}
2017-06-23 20:09:26 +02:00
// do not convert this path if there are no alpha characters (either pointless or cause wrong results for . and ..)
if (!alpha)
return false;
2017-06-21 12:09:50 +02:00
2017-06-23 20:09:26 +02:00
// Lookup filename or foldername on file system
2019-03-10 08:47:27 +01:00
if (!realFileNameMap.getCacheEntry(f, result)) {
2019-02-06 16:26:59 +01:00
WIN32_FIND_DATAA FindFileData;
2019-03-10 08:47:27 +01:00
#ifdef __CYGWIN__
std::string fConverted = simplecpp::convertCygwinToWindowsPath(f);
HANDLE hFind = FindFirstFileExA(fConverted.c_str(), FindExInfoBasic, &FindFileData, FindExSearchNameMatch, NULL, 0);
#else
2019-02-06 16:26:59 +01:00
HANDLE hFind = FindFirstFileExA(f.c_str(), FindExInfoBasic, &FindFileData, FindExSearchNameMatch, NULL, 0);
2019-03-10 08:47:27 +01:00
#endif
2019-02-06 16:26:59 +01:00
if (INVALID_HANDLE_VALUE == hFind)
return false;
*result = FindFileData.cFileName;
realFileNameMap.addToCache(f, *result);
FindClose(hFind);
}
2017-06-23 20:09:26 +02:00
return true;
}
2017-06-21 12:09:50 +02:00
2019-03-10 08:47:27 +01:00
static RealFileNameMap realFilePathMap;
2017-06-23 20:09:26 +02:00
/** Change case in given path to match filesystem */
static std::string realFilename(const std::string &f)
{
std::string ret;
ret.reserve(f.size()); // this will be the final size
2019-03-10 08:47:27 +01:00
if (realFilePathMap.getCacheEntry(f, &ret))
2019-02-23 08:35:10 +01:00
return ret;
2017-06-21 12:09:50 +02:00
2017-06-23 20:09:26 +02:00
// Current subpath
std::string subpath;
2017-06-21 12:09:50 +02:00
2017-06-23 20:09:26 +02:00
for (std::string::size_type pos = 0; pos < f.size(); ++pos) {
unsigned char c = f[pos];
2017-06-21 12:09:50 +02:00
2017-06-23 20:09:26 +02:00
// Separator.. add subpath and separator
if (c == '/' || c == '\\') {
// if subpath is empty just add separator
if (subpath.empty()) {
2017-06-21 12:09:50 +02:00
ret += c;
2017-06-23 20:09:26 +02:00
continue;
2017-05-26 16:45:07 +02:00
}
2017-06-21 12:09:50 +02:00
2019-05-03 18:49:48 +02:00
bool isDriveSpecification =
2019-03-10 08:47:27 +01:00
(pos == 2 && subpath.size() == 2 && std::isalpha(subpath[0]) && subpath[1] == ':');
2017-06-23 20:09:26 +02:00
// Append real filename (proper case)
2017-06-21 12:09:50 +02:00
std::string f2;
2019-03-10 08:47:27 +01:00
if (!isDriveSpecification && realFileName(f.substr(0, pos), &f2))
2017-06-21 12:09:50 +02:00
ret += f2;
else
ret += subpath;
2017-06-23 20:09:26 +02:00
subpath.clear();
// Append separator
ret += c;
} else {
subpath += c;
}
2017-06-21 12:09:50 +02:00
}
2017-06-23 20:09:26 +02:00
if (!subpath.empty()) {
std::string f2;
if (realFileName(f,&f2))
ret += f2;
else
ret += subpath;
2016-08-21 17:08:40 +02:00
}
2017-06-21 12:09:50 +02:00
2019-03-10 08:47:27 +01:00
realFilePathMap.addToCache(f, ret);
2017-06-23 20:09:26 +02:00
return ret;
}
static bool isAbsolutePath(const std::string &path)
{
if (path.length() >= 3 && path[0] > 0 && std::isalpha(path[0]) && path[1] == ':' && (path[2] == '\\' || path[2] == '/'))
return true;
2017-08-13 13:59:09 +02:00
return path.length() > 1U && (path[0] == '/' || path[0] == '\\');
2017-06-23 20:09:26 +02:00
}
2016-08-21 17:08:40 +02:00
#else
#define realFilename(f) f
2017-06-21 12:09:50 +02:00
2017-06-23 20:09:26 +02:00
static bool isAbsolutePath(const std::string &path)
{
return path.length() > 1U && path[0] == '/';
}
2016-08-17 21:16:29 +02:00
#endif
2017-06-23 20:09:26 +02:00
namespace simplecpp {
/**
* perform path simplifications for . and ..
*/
std::string simplifyPath(std::string path)
{
2018-01-13 18:08:23 +01:00
if (path.empty())
return path;
std::string::size_type pos;
2016-07-25 09:13:59 +02:00
// replace backslash separators
std::replace(path.begin(), path.end(), '\\', '/');
2016-07-24 09:59:57 +02:00
2017-06-21 12:09:50 +02:00
const bool unc(path.compare(0,2,"//") == 0);
// replace "//" with "/"
pos = 0;
while ((pos = path.find("//",pos)) != std::string::npos) {
path.erase(pos,1);
}
2017-06-08 15:55:12 +02:00
// remove "./"
pos = 0;
2017-06-08 15:55:12 +02:00
while ((pos = path.find("./",pos)) != std::string::npos) {
if (pos == 0 || path[pos - 1U] == '/')
path.erase(pos,2);
else
pos += 2;
}
2016-07-24 09:59:57 +02:00
2017-06-21 12:09:50 +02:00
// remove trailing dot if path ends with "/."
if (endsWith(path,"/."))
path.erase(path.size()-1);
// simplify ".."
pos = 1; // don't simplify ".." if path starts with that
while ((pos = path.find("/..", pos)) != std::string::npos) {
// not end of path, then string must be "/../"
if (pos + 3 < path.size() && path[pos + 3] != '/') {
++pos;
continue;
}
// get previous subpath
const std::string::size_type pos1 = path.rfind('/', pos - 1U) + 1U;
const std::string previousSubPath = path.substr(pos1, pos-pos1);
if (previousSubPath == "..") {
// don't simplify
++pos;
2017-06-08 15:55:12 +02:00
} else {
2017-06-21 12:09:50 +02:00
// remove previous subpath and ".."
path.erase(pos1,pos-pos1+4);
if (path.empty())
path = ".";
// update pos
pos = (pos1 == 0) ? 1 : (pos1 - 1);
}
2016-07-24 11:36:02 +02:00
}
2016-07-24 09:59:57 +02:00
2017-06-21 12:09:50 +02:00
// Remove trailing '/'?
//if (path.size() > 1 && endsWith(path, "/"))
// path.erase(path.size()-1);
if (unc)
path = '/' + path;
2018-04-09 10:47:41 +02:00
return path.find_first_of("*?") == std::string::npos ? realFilename(path) : path;
}
2016-07-24 09:59:57 +02:00
}
2017-06-21 12:09:50 +02:00
/** Evaluate sizeof(type) */
static void simplifySizeof(simplecpp::TokenList &expr, const std::map<std::string, std::size_t> &sizeOfType)
{
for (simplecpp::Token *tok = expr.front(); tok; tok = tok->next) {
2018-05-14 13:00:22 +02:00
if (tok->str() != "sizeof")
2017-06-21 12:09:50 +02:00
continue;
simplecpp::Token *tok1 = tok->next;
if (!tok1) {
2017-08-21 18:43:42 +02:00
throw std::runtime_error("missing sizeof argument");
2017-06-21 12:09:50 +02:00
}
simplecpp::Token *tok2 = tok1->next;
if (!tok2) {
2017-08-21 18:43:42 +02:00
throw std::runtime_error("missing sizeof argument");
2017-06-21 12:09:50 +02:00
}
if (tok1->op == '(') {
tok1 = tok1->next;
2017-08-21 18:43:42 +02:00
while (tok2->op != ')') {
2017-06-21 12:09:50 +02:00
tok2 = tok2->next;
2017-08-21 18:43:42 +02:00
if (!tok2) {
throw std::runtime_error("invalid sizeof expression");
}
}
2017-06-21 12:09:50 +02:00
}
2017-06-21 12:09:50 +02:00
std::string type;
for (simplecpp::Token *typeToken = tok1; typeToken != tok2; typeToken = typeToken->next) {
2018-05-14 13:00:22 +02:00
if ((typeToken->str() == "unsigned" || typeToken->str() == "signed") && typeToken->next->name)
continue;
2018-05-14 13:00:22 +02:00
if (typeToken->str() == "*" && type.find('*') != std::string::npos)
2017-06-21 12:09:50 +02:00
continue;
if (!type.empty())
type += ' ';
2018-05-14 13:00:22 +02:00
type += typeToken->str();
}
2017-06-21 12:09:50 +02:00
const std::map<std::string, std::size_t>::const_iterator it = sizeOfType.find(type);
if (it != sizeOfType.end())
tok->setstr(toString(it->second));
else
continue;
tok2 = tok2->next;
while (tok->next != tok2)
expr.deleteToken(tok->next);
2016-07-20 12:21:00 +02:00
}
2017-06-21 12:09:50 +02:00
}
2016-07-20 12:21:00 +02:00
2018-01-13 18:08:23 +01:00
static const char * const altopData[] = {"and","or","bitand","bitor","compl","not","not_eq","xor"};
static const std::set<std::string> altop(&altopData[0], &altopData[8]);
2017-06-21 12:09:50 +02:00
static void simplifyName(simplecpp::TokenList &expr)
{
for (simplecpp::Token *tok = expr.front(); tok; tok = tok->next) {
if (tok->name) {
2018-05-14 13:00:22 +02:00
if (altop.find(tok->str()) != altop.end()) {
2017-06-21 12:09:50 +02:00
bool alt;
2018-05-14 13:00:22 +02:00
if (tok->str() == "not" || tok->str() == "compl") {
alt = isAlternativeUnaryOp(tok,tok->str());
2017-06-21 12:09:50 +02:00
} else {
2018-05-14 13:00:22 +02:00
alt = isAlternativeBinaryOp(tok,tok->str());
2016-07-31 20:48:55 +02:00
}
2017-06-21 12:09:50 +02:00
if (alt)
continue;
2016-07-23 09:26:06 +02:00
}
2017-06-21 12:09:50 +02:00
tok->setstr("0");
2016-07-23 09:26:06 +02:00
}
2016-07-20 12:21:00 +02:00
}
2017-06-21 12:09:50 +02:00
}
2016-07-20 12:21:00 +02:00
2017-06-21 12:09:50 +02:00
static void simplifyNumbers(simplecpp::TokenList &expr)
{
for (simplecpp::Token *tok = expr.front(); tok; tok = tok->next) {
2018-05-14 13:00:22 +02:00
if (tok->str().size() == 1U)
2017-06-21 12:09:50 +02:00
continue;
2018-05-14 13:00:22 +02:00
if (tok->str().compare(0,2,"0x") == 0)
tok->setstr(toString(stringToULL(tok->str())));
else if (tok->str()[0] == '\'')
tok->setstr(toString(tok->str()[1] & 0xffU));
2016-07-20 12:21:00 +02:00
}
2017-06-21 12:09:50 +02:00
}
2016-07-20 12:21:00 +02:00
2017-06-21 12:09:50 +02:00
static long long evaluate(simplecpp::TokenList &expr, const std::map<std::string, std::size_t> &sizeOfType)
{
simplifySizeof(expr, sizeOfType);
simplifyName(expr);
simplifyNumbers(expr);
expr.constFold();
// TODO: handle invalid expressions
2018-05-14 13:00:22 +02:00
return expr.cfront() && expr.cfront() == expr.cback() && expr.cfront()->number ? stringToLL(expr.cfront()->str()) : 0LL;
2017-06-21 12:09:50 +02:00
}
2016-07-20 12:21:00 +02:00
2017-06-21 12:09:50 +02:00
static const simplecpp::Token *gotoNextLine(const simplecpp::Token *tok)
{
const unsigned int line = tok->location.line;
const unsigned int file = tok->location.fileIndex;
while (tok && tok->location.line == line && tok->location.fileIndex == file)
tok = tok->next;
return tok;
}
2019-03-10 08:47:27 +01:00
#ifdef SIMPLECPP_WINDOWS
class NonExistingFilesCache {
public:
NonExistingFilesCache() {
InitializeCriticalSection(&m_criticalSection);
}
~NonExistingFilesCache() {
DeleteCriticalSection(&m_criticalSection);
}
bool contains(const std::string& path) {
ScopedLock lock(m_criticalSection);
return (m_pathSet.find(path) != m_pathSet.end());
}
void add(const std::string& path) {
ScopedLock lock(m_criticalSection);
m_pathSet.insert(path);
}
private:
std::set<std::string> m_pathSet;
CRITICAL_SECTION m_criticalSection;
};
static NonExistingFilesCache nonExistingFilesCache;
#endif
static std::string _openHeader(std::ifstream &f, const std::string &path)
{
#ifdef SIMPLECPP_WINDOWS
std::string simplePath = simplecpp::simplifyPath(path);
if (nonExistingFilesCache.contains(simplePath))
return ""; // file is known not to exist, skip expensive file open call
f.open(simplePath.c_str());
if (f.is_open())
return simplePath;
else {
nonExistingFilesCache.add(simplePath);
return "";
}
#else
f.open(path.c_str());
return f.is_open() ? simplecpp::simplifyPath(path) : "";
#endif
}
2019-05-07 19:15:31 +02:00
static std::string openHeader(std::ifstream &f, const simplecpp::DUI &dui, const std::string &sourcefile, const std::string &header, bool systemheader)
2017-06-21 12:09:50 +02:00
{
2017-06-23 20:09:26 +02:00
if (isAbsolutePath(header)) {
2019-03-10 08:47:27 +01:00
return _openHeader(f, header);
2017-06-21 12:09:50 +02:00
}
2016-07-20 12:21:00 +02:00
2019-05-07 19:15:31 +02:00
if (!systemheader) {
if (sourcefile.find_first_of("\\/") != std::string::npos) {
const std::string s = sourcefile.substr(0, sourcefile.find_last_of("\\/") + 1U) + header;
std::string simplePath = _openHeader(f, s);
if (!simplePath.empty())
return simplePath;
} else {
std::string simplePath = _openHeader(f, header);
if (!simplePath.empty())
return simplePath;
}
2017-06-21 12:09:50 +02:00
}
2016-07-20 12:21:00 +02:00
2017-06-21 12:09:50 +02:00
for (std::list<std::string>::const_iterator it = dui.includePaths.begin(); it != dui.includePaths.end(); ++it) {
std::string s = *it;
if (!s.empty() && s[s.size()-1U]!='/' && s[s.size()-1U]!='\\')
s += '/';
s += header;
2019-03-10 08:47:27 +01:00
std::string simplePath = _openHeader(f, s);
if (!simplePath.empty())
return simplePath;
2016-07-20 12:21:00 +02:00
}
2017-06-21 12:09:50 +02:00
return "";
}
static std::string getFileName(const std::map<std::string, simplecpp::TokenList *> &filedata, const std::string &sourcefile, const std::string &header, const simplecpp::DUI &dui, bool systemheader)
{
2018-01-13 18:08:23 +01:00
if (filedata.empty()) {
return "";
}
2017-06-23 20:09:26 +02:00
if (isAbsolutePath(header)) {
2017-06-21 12:09:50 +02:00
return (filedata.find(header) != filedata.end()) ? simplecpp::simplifyPath(header) : "";
}
2016-07-20 12:21:00 +02:00
2017-06-21 12:09:50 +02:00
if (!systemheader) {
if (sourcefile.find_first_of("\\/") != std::string::npos) {
const std::string s(simplecpp::simplifyPath(sourcefile.substr(0, sourcefile.find_last_of("\\/") + 1U) + header));
if (filedata.find(s) != filedata.end())
return s;
} else {
std::string s = simplecpp::simplifyPath(header);
2016-07-21 21:04:54 +02:00
if (filedata.find(s) != filedata.end())
2016-07-25 21:33:06 +02:00
return s;
2016-07-21 21:04:54 +02:00
}
2016-07-20 12:21:00 +02:00
}
2017-06-21 12:09:50 +02:00
for (std::list<std::string>::const_iterator it = dui.includePaths.begin(); it != dui.includePaths.end(); ++it) {
std::string s = *it;
if (!s.empty() && s[s.size()-1U]!='/' && s[s.size()-1U]!='\\')
s += '/';
s += header;
s = simplecpp::simplifyPath(s);
if (filedata.find(s) != filedata.end())
return s;
}
2017-06-21 12:09:50 +02:00
return "";
}
static bool hasFile(const std::map<std::string, simplecpp::TokenList *> &filedata, const std::string &sourcefile, const std::string &header, const simplecpp::DUI &dui, bool systemheader)
{
return !getFileName(filedata, sourcefile, header, dui, systemheader).empty();
2016-07-20 12:21:00 +02:00
}
2016-07-29 13:05:35 +02:00
std::map<std::string, simplecpp::TokenList*> simplecpp::load(const simplecpp::TokenList &rawtokens, std::vector<std::string> &fileNumbers, const simplecpp::DUI &dui, simplecpp::OutputList *outputList)
2016-07-20 12:21:00 +02:00
{
std::map<std::string, simplecpp::TokenList*> ret;
std::list<const Token *> filelist;
2016-08-06 08:41:19 +02:00
// -include files
for (std::list<std::string>::const_iterator it = dui.includes.begin(); it != dui.includes.end(); ++it) {
2016-08-21 17:08:40 +02:00
const std::string &filename = realFilename(*it);
2016-08-17 21:16:29 +02:00
if (ret.find(filename) != ret.end())
2016-08-06 08:41:19 +02:00
continue;
2016-08-17 21:16:29 +02:00
std::ifstream fin(filename.c_str());
2016-08-06 08:41:19 +02:00
if (!fin.is_open())
continue;
2016-08-17 21:16:29 +02:00
TokenList *tokenlist = new TokenList(fin, fileNumbers, filename, outputList);
2016-08-06 08:41:19 +02:00
if (!tokenlist->front()) {
delete tokenlist;
continue;
}
2016-08-17 21:16:29 +02:00
ret[filename] = tokenlist;
2016-08-06 08:41:19 +02:00
filelist.push_back(tokenlist->front());
}
2016-07-29 08:46:38 +02:00
for (const Token *rawtok = rawtokens.cfront(); rawtok || !filelist.empty(); rawtok = rawtok ? rawtok->next : NULL) {
2016-07-21 15:05:29 +02:00
if (rawtok == NULL) {
2016-07-20 12:21:00 +02:00
rawtok = filelist.back();
filelist.pop_back();
}
2016-07-21 18:58:11 +02:00
if (rawtok->op != '#' || sameline(rawtok->previousSkipComments(), rawtok))
2016-07-20 12:21:00 +02:00
continue;
2016-07-21 18:58:11 +02:00
rawtok = rawtok->nextSkipComments();
2018-05-14 13:00:22 +02:00
if (!rawtok || rawtok->str() != INCLUDE)
2016-07-20 12:21:00 +02:00
continue;
const std::string &sourcefile = rawtok->location.file();
2016-07-21 18:58:11 +02:00
const Token *htok = rawtok->nextSkipComments();
if (!sameline(rawtok, htok))
continue;
2018-05-14 13:00:22 +02:00
bool systemheader = (htok->str()[0] == '<');
2016-07-21 21:04:54 +02:00
2018-05-14 13:00:22 +02:00
const std::string header(realFilename(htok->str().substr(1U, htok->str().size() - 2U)));
2016-07-21 21:04:54 +02:00
if (hasFile(ret, sourcefile, header, dui, systemheader))
2016-07-20 12:21:00 +02:00
continue;
std::ifstream f;
2019-05-07 19:15:31 +02:00
const std::string header2 = openHeader(f,dui,sourcefile,header,systemheader);
2016-07-20 12:21:00 +02:00
if (!f.is_open())
continue;
2016-07-21 18:58:11 +02:00
TokenList *tokens = new TokenList(f, fileNumbers, header2, outputList);
2016-07-20 12:21:00 +02:00
ret[header2] = tokens;
2016-07-29 08:46:38 +02:00
if (tokens->front())
filelist.push_back(tokens->front());
2016-07-20 12:21:00 +02:00
}
return ret;
}
static bool preprocessToken(simplecpp::TokenList &output, const simplecpp::Token **tok1, std::map<std::string, simplecpp::Macro> &macros, std::vector<std::string> &files, simplecpp::OutputList *outputList)
{
const simplecpp::Token *tok = *tok1;
2018-05-14 13:00:22 +02:00
const std::map<std::string,simplecpp::Macro>::const_iterator it = macros.find(tok->str());
if (it != macros.end()) {
simplecpp::TokenList value(files);
try {
*tok1 = it->second.expand(&value, tok, macros, files);
} catch (simplecpp::Macro::Error &err) {
if (outputList) {
simplecpp::Output out(files);
out.type = simplecpp::Output::SYNTAX_ERROR;
out.location = err.location;
2018-05-14 13:00:22 +02:00
out.msg = "failed to expand \'" + tok->str() + "\', " + err.what;
outputList->push_back(out);
}
return false;
}
output.takeTokens(value);
} else {
if (!tok->comment)
output.push_back(new simplecpp::Token(*tok));
*tok1 = tok->next;
}
return true;
}
void simplecpp::preprocess(simplecpp::TokenList &output, const simplecpp::TokenList &rawtokens, std::vector<std::string> &files, std::map<std::string, simplecpp::TokenList *> &filedata, const simplecpp::DUI &dui, simplecpp::OutputList *outputList, std::list<simplecpp::MacroUsage> *macroUsage)
2016-07-20 12:21:00 +02:00
{
2016-07-20 20:07:16 +02:00
std::map<std::string, std::size_t> sizeOfType(rawtokens.sizeOfType);
2019-07-03 17:03:46 +02:00
sizeOfType.insert(std::make_pair("char", sizeof(char)));
sizeOfType.insert(std::make_pair("short", sizeof(short)));
sizeOfType.insert(std::make_pair("short int", sizeOfType["short"]));
sizeOfType.insert(std::make_pair("int", sizeof(int)));
sizeOfType.insert(std::make_pair("long", sizeof(long)));
sizeOfType.insert(std::make_pair("long int", sizeOfType["long"]));
sizeOfType.insert(std::make_pair("long long", sizeof(long long)));
sizeOfType.insert(std::make_pair("float", sizeof(float)));
sizeOfType.insert(std::make_pair("double", sizeof(double)));
sizeOfType.insert(std::make_pair("long double", sizeof(long double)));
sizeOfType.insert(std::make_pair("char *", sizeof(char *)));
sizeOfType.insert(std::make_pair("short *", sizeof(short *)));
sizeOfType.insert(std::make_pair("short int *", sizeOfType["short *"]));
sizeOfType.insert(std::make_pair("int *", sizeof(int *)));
sizeOfType.insert(std::make_pair("long *", sizeof(long *)));
sizeOfType.insert(std::make_pair("long int *", sizeOfType["long *"]));
sizeOfType.insert(std::make_pair("long long *", sizeof(long long *)));
sizeOfType.insert(std::make_pair("float *", sizeof(float *)));
sizeOfType.insert(std::make_pair("double *", sizeof(double *)));
sizeOfType.insert(std::make_pair("long double *", sizeof(long double *)));
2016-07-20 20:07:16 +02:00
2016-07-20 12:21:00 +02:00
std::map<TokenString, Macro> macros;
for (std::list<std::string>::const_iterator it = dui.defines.begin(); it != dui.defines.end(); ++it) {
const std::string &macrostr = *it;
const std::string::size_type eq = macrostr.find('=');
const std::string::size_type par = macrostr.find('(');
2016-07-20 12:21:00 +02:00
const std::string macroname = macrostr.substr(0, std::min(eq,par));
if (dui.undefined.find(macroname) != dui.undefined.end())
continue;
const std::string lhs(macrostr.substr(0,eq));
const std::string rhs(eq==std::string::npos ? std::string("1") : macrostr.substr(eq+1));
const Macro macro(lhs, rhs, files);
macros.insert(std::pair<TokenString,Macro>(macro.name(), macro));
}
2019-07-03 17:03:46 +02:00
macros.insert(std::make_pair("__FILE__", Macro("__FILE__", "__FILE__", files)));
macros.insert(std::make_pair("__LINE__", Macro("__LINE__", "__LINE__", files)));
macros.insert(std::make_pair("__COUNTER__", Macro("__COUNTER__", "__COUNTER__", files)));
2016-07-25 09:13:59 +02:00
2016-07-20 12:21:00 +02:00
// TRUE => code in current #if block should be kept
// ELSE_IS_TRUE => code in current #if block should be dropped. the code in the #else should be kept.
// ALWAYS_FALSE => drop all code in #if and #else
enum IfState { TRUE, ELSE_IS_TRUE, ALWAYS_FALSE };
2016-07-21 15:05:29 +02:00
std::stack<int> ifstates;
2016-07-20 12:21:00 +02:00
ifstates.push(TRUE);
std::stack<const Token *> includetokenstack;
std::set<std::string> pragmaOnce;
2016-08-06 08:41:19 +02:00
includetokenstack.push(rawtokens.cfront());
for (std::list<std::string>::const_iterator it = dui.includes.begin(); it != dui.includes.end(); ++it) {
const std::map<std::string, TokenList*>::const_iterator f = filedata.find(*it);
if (f != filedata.end())
includetokenstack.push(f->second->cfront());
}
for (const Token *rawtok = NULL; rawtok || !includetokenstack.empty();) {
2016-07-21 15:05:29 +02:00
if (rawtok == NULL) {
2016-07-20 12:21:00 +02:00
rawtok = includetokenstack.top();
includetokenstack.pop();
continue;
}
if (rawtok->op == '#' && !sameline(rawtok->previous, rawtok)) {
2018-04-17 08:12:15 +02:00
if (!sameline(rawtok, rawtok->next)) {
rawtok = rawtok->next;
continue;
}
2016-07-20 12:21:00 +02:00
rawtok = rawtok->next;
2018-04-17 08:12:15 +02:00
if (!rawtok->name) {
rawtok = gotoNextLine(rawtok);
2016-07-20 12:21:00 +02:00
continue;
2016-07-25 09:13:59 +02:00
}
2016-07-20 12:21:00 +02:00
2018-05-14 13:00:22 +02:00
if (ifstates.size() <= 1U && (rawtok->str() == ELIF || rawtok->str() == ELSE || rawtok->str() == ENDIF)) {
if (outputList) {
simplecpp::Output err(files);
err.type = Output::SYNTAX_ERROR;
err.location = rawtok->location;
2018-05-14 13:00:22 +02:00
err.msg = "#" + rawtok->str() + " without #if";
outputList->push_back(err);
}
output.clear();
return;
}
2018-05-14 13:00:22 +02:00
if (ifstates.top() == TRUE && (rawtok->str() == ERROR || rawtok->str() == WARNING)) {
2016-07-20 12:21:00 +02:00
if (outputList) {
simplecpp::Output err(rawtok->location.files);
2018-05-14 13:00:22 +02:00
err.type = rawtok->str() == ERROR ? Output::ERROR : Output::WARNING;
2016-07-20 12:21:00 +02:00
err.location = rawtok->location;
for (const Token *tok = rawtok->next; tok && sameline(rawtok,tok); tok = tok->next) {
2018-05-14 13:00:22 +02:00
if (!err.msg.empty() && isNameChar(tok->str()[0]))
2016-07-20 12:21:00 +02:00
err.msg += ' ';
2018-05-14 13:00:22 +02:00
err.msg += tok->str();
2016-07-20 12:21:00 +02:00
}
2018-05-14 13:00:22 +02:00
err.msg = '#' + rawtok->str() + ' ' + err.msg;
2016-07-20 12:21:00 +02:00
outputList->push_back(err);
}
2018-05-14 13:00:22 +02:00
if (rawtok->str() == ERROR) {
2016-08-11 19:00:35 +02:00
output.clear();
return;
}
2016-07-20 12:21:00 +02:00
}
2018-05-14 13:00:22 +02:00
if (rawtok->str() == DEFINE) {
2016-07-20 12:21:00 +02:00
if (ifstates.top() != TRUE)
continue;
try {
const Macro &macro = Macro(rawtok->previous, files);
if (dui.undefined.find(macro.name()) == dui.undefined.end()) {
std::map<TokenString, Macro>::iterator it = macros.find(macro.name());
if (it == macros.end())
macros.insert(std::pair<TokenString, Macro>(macro.name(), macro));
else
it->second = macro;
}
} catch (const std::runtime_error &) {
if (outputList) {
simplecpp::Output err(files);
err.type = Output::SYNTAX_ERROR;
err.location = rawtok->location;
err.msg = "Failed to parse #define";
outputList->push_back(err);
}
output.clear();
return;
2016-07-20 12:21:00 +02:00
}
2018-05-14 13:00:22 +02:00
} else if (ifstates.top() == TRUE && rawtok->str() == INCLUDE) {
TokenList inc1(files);
for (const Token *inctok = rawtok->next; sameline(rawtok,inctok); inctok = inctok->next) {
if (!inctok->comment)
inc1.push_back(new Token(*inctok));
}
TokenList inc2(files);
if (!inc1.empty() && inc1.cfront()->name) {
const Token *inctok = inc1.cfront();
if (!preprocessToken(inc2, &inctok, macros, files, outputList)) {
output.clear();
return;
}
} else {
inc2.takeTokens(inc1);
}
2018-02-11 15:48:37 +01:00
if (!inc2.empty() && inc2.cfront()->op == '<' && inc2.cback()->op == '>') {
TokenString hdr;
// TODO: Sometimes spaces must be added in the string
// Somehow preprocessToken etc must be told that the location should be source location not destination location
for (const Token *tok = inc2.cfront(); tok; tok = tok->next) {
2018-05-14 13:00:22 +02:00
hdr += tok->str();
2018-02-11 15:48:37 +01:00
}
inc2.clear();
inc2.push_back(new Token(hdr, inc1.cfront()->location));
2018-03-15 21:34:58 +01:00
inc2.front()->op = '<';
2018-02-11 15:48:37 +01:00
}
2018-05-14 13:00:22 +02:00
if (inc2.empty() || inc2.cfront()->str().size() <= 2U) {
if (outputList) {
simplecpp::Output err(files);
err.type = Output::SYNTAX_ERROR;
err.location = rawtok->location;
err.msg = "No header in #include";
outputList->push_back(err);
}
output.clear();
return;
}
const Token *inctok = inc2.cfront();
const bool systemheader = (inctok->op == '<');
2018-05-14 13:00:22 +02:00
const std::string header(realFilename(inctok->str().substr(1U, inctok->str().size() - 2U)));
std::string header2 = getFileName(filedata, rawtok->location.file(), header, dui, systemheader);
if (header2.empty()) {
// try to load file..
std::ifstream f;
2019-05-07 19:15:31 +02:00
header2 = openHeader(f, dui, rawtok->location.file(), header, systemheader);
if (f.is_open()) {
TokenList *tokens = new TokenList(f, files, header2, outputList);
filedata[header2] = tokens;
}
}
if (header2.empty()) {
if (outputList) {
simplecpp::Output out(files);
out.type = Output::MISSING_HEADER;
out.location = rawtok->location;
2018-05-14 13:00:22 +02:00
out.msg = "Header not found: " + inctok->str();
outputList->push_back(out);
}
} else if (includetokenstack.size() >= 400) {
if (outputList) {
simplecpp::Output out(files);
out.type = Output::INCLUDE_NESTED_TOO_DEEPLY;
out.location = rawtok->location;
out.msg = "#include nested too deeply";
outputList->push_back(out);
}
} else if (pragmaOnce.find(header2) == pragmaOnce.end()) {
includetokenstack.push(gotoNextLine(rawtok));
const TokenList *includetokens = filedata.find(header2)->second;
2019-05-07 19:15:31 +02:00
rawtok = includetokens ? includetokens->cfront() : NULL;
continue;
2016-07-20 12:21:00 +02:00
}
2018-05-14 13:00:22 +02:00
} else if (rawtok->str() == IF || rawtok->str() == IFDEF || rawtok->str() == IFNDEF || rawtok->str() == ELIF) {
if (!sameline(rawtok,rawtok->next)) {
if (outputList) {
simplecpp::Output out(files);
out.type = Output::SYNTAX_ERROR;
out.location = rawtok->location;
2018-05-14 13:00:22 +02:00
out.msg = "Syntax error in #" + rawtok->str();
outputList->push_back(out);
}
output.clear();
return;
}
2016-07-20 12:21:00 +02:00
bool conditionIsTrue;
2018-05-14 13:00:22 +02:00
if (ifstates.top() == ALWAYS_FALSE || (ifstates.top() == ELSE_IS_TRUE && rawtok->str() != ELIF))
2016-07-20 12:21:00 +02:00
conditionIsTrue = false;
2018-05-14 13:00:22 +02:00
else if (rawtok->str() == IFDEF)
conditionIsTrue = (macros.find(rawtok->next->str()) != macros.end());
else if (rawtok->str() == IFNDEF)
conditionIsTrue = (macros.find(rawtok->next->str()) == macros.end());
else { /*if (rawtok->str() == IF || rawtok->str() == ELIF)*/
2016-07-20 12:21:00 +02:00
TokenList expr(files);
for (const Token *tok = rawtok->next; tok && tok->location.sameline(rawtok->location); tok = tok->next) {
if (!tok->name) {
expr.push_back(new Token(*tok));
continue;
}
2018-05-14 13:00:22 +02:00
if (tok->str() == DEFINED) {
2016-07-20 12:21:00 +02:00
tok = tok->next;
const bool par = (tok && tok->op == '(');
if (par)
tok = tok->next;
if (tok) {
2018-05-14 13:00:22 +02:00
if (macros.find(tok->str()) != macros.end())
expr.push_back(new Token("1", tok->location));
else
expr.push_back(new Token("0", tok->location));
}
if (par)
tok = tok ? tok->next : NULL;
if (!tok || !sameline(rawtok,tok) || (par && tok->op != ')')) {
if (outputList) {
Output out(rawtok->location.files);
out.type = Output::SYNTAX_ERROR;
out.location = rawtok->location;
2018-05-14 13:00:22 +02:00
out.msg = "failed to evaluate " + std::string(rawtok->str() == IF ? "#if" : "#elif") + " condition";
outputList->push_back(out);
}
output.clear();
return;
}
2016-07-20 12:21:00 +02:00
continue;
}
const Token *tmp = tok;
if (!preprocessToken(expr, &tmp, macros, files, outputList)) {
output.clear();
return;
2016-07-20 12:21:00 +02:00
}
2019-07-03 17:03:46 +02:00
if (!tmp)
break;
tok = tmp->previous;
2016-07-20 12:21:00 +02:00
}
try {
2016-07-20 20:07:16 +02:00
conditionIsTrue = (evaluate(expr, sizeOfType) != 0);
2017-08-21 18:43:42 +02:00
} catch (const std::exception &e) {
if (outputList) {
Output out(rawtok->location.files);
out.type = Output::SYNTAX_ERROR;
out.location = rawtok->location;
2018-05-14 13:00:22 +02:00
out.msg = "failed to evaluate " + std::string(rawtok->str() == IF ? "#if" : "#elif") + " condition";
2017-08-21 18:43:42 +02:00
if (e.what() && *e.what())
out.msg += std::string(", ") + e.what();
2016-07-20 12:21:00 +02:00
outputList->push_back(out);
}
2016-07-21 19:42:26 +02:00
output.clear();
return;
2016-07-20 12:21:00 +02:00
}
}
2018-05-14 13:00:22 +02:00
if (rawtok->str() != ELIF) {
2016-07-20 12:21:00 +02:00
// push a new ifstate..
if (ifstates.top() != TRUE)
ifstates.push(ALWAYS_FALSE);
else
ifstates.push(conditionIsTrue ? TRUE : ELSE_IS_TRUE);
} else if (ifstates.top() == TRUE) {
ifstates.top() = ALWAYS_FALSE;
} else if (ifstates.top() == ELSE_IS_TRUE && conditionIsTrue) {
ifstates.top() = TRUE;
}
2018-05-14 13:00:22 +02:00
} else if (rawtok->str() == ELSE) {
2016-07-20 12:21:00 +02:00
ifstates.top() = (ifstates.top() == ELSE_IS_TRUE) ? TRUE : ALWAYS_FALSE;
2018-05-14 13:00:22 +02:00
} else if (rawtok->str() == ENDIF) {
ifstates.pop();
2018-05-14 13:00:22 +02:00
} else if (rawtok->str() == UNDEF) {
2016-07-20 12:21:00 +02:00
if (ifstates.top() == TRUE) {
const Token *tok = rawtok->next;
while (sameline(rawtok,tok) && tok->comment)
tok = tok->next;
if (sameline(rawtok, tok))
2018-05-14 13:00:22 +02:00
macros.erase(tok->str());
2016-07-20 12:21:00 +02:00
}
2018-05-14 13:00:22 +02:00
} else if (ifstates.top() == TRUE && rawtok->str() == PRAGMA && rawtok->next && rawtok->next->str() == ONCE && sameline(rawtok,rawtok->next)) {
pragmaOnce.insert(rawtok->location.file());
2016-07-20 12:21:00 +02:00
}
rawtok = gotoNextLine(rawtok);
continue;
}
if (ifstates.top() != TRUE) {
// drop code
rawtok = gotoNextLine(rawtok);
continue;
}
2016-07-25 09:13:59 +02:00
bool hash=false, hashhash=false;
if (rawtok->op == '#' && sameline(rawtok,rawtok->next)) {
if (rawtok->next->op != '#') {
hash = true;
rawtok = rawtok->next; // skip '#'
} else if (sameline(rawtok,rawtok->next->next)) {
hashhash = true;
rawtok = rawtok->next->next; // skip '#' '#'
}
}
const Location loc(rawtok->location);
TokenList tokens(files);
if (!preprocessToken(tokens, &rawtok, macros, files, outputList)) {
output.clear();
return;
2016-07-25 09:13:59 +02:00
}
if (hash || hashhash) {
std::string s;
2016-07-29 08:46:38 +02:00
for (const Token *hashtok = tokens.cfront(); hashtok; hashtok = hashtok->next)
2018-05-14 13:00:22 +02:00
s += hashtok->str();
2016-07-25 09:13:59 +02:00
if (hash)
output.push_back(new Token('\"' + s + '\"', loc));
2016-07-29 08:46:38 +02:00
else if (output.back())
2018-05-14 13:00:22 +02:00
output.back()->setstr(output.cback()->str() + s);
2016-07-25 09:13:59 +02:00
else
output.push_back(new Token(s, loc));
} else {
output.takeTokens(tokens);
}
2016-07-20 12:21:00 +02:00
}
if (macroUsage) {
for (std::map<TokenString, simplecpp::Macro>::const_iterator macroIt = macros.begin(); macroIt != macros.end(); ++macroIt) {
const Macro &macro = macroIt->second;
const std::list<Location> &usage = macro.usage();
for (std::list<Location>::const_iterator usageIt = usage.begin(); usageIt != usage.end(); ++usageIt) {
MacroUsage mu(usageIt->files, macro.valueDefinedInCode());
2016-07-20 12:21:00 +02:00
mu.macroName = macro.name();
mu.macroLocation = macro.defineLocation();
mu.useLocation = *usageIt;
macroUsage->push_back(mu);
}
}
}
}
2016-07-31 20:48:55 +02:00
void simplecpp::cleanup(std::map<std::string, TokenList*> &filedata)
{
2016-07-31 20:48:55 +02:00
for (std::map<std::string, TokenList*>::iterator it = filedata.begin(); it != filedata.end(); ++it)
delete it->second;
filedata.clear();
}