/*
* simplecpp - A simple and high-fidelity C/C++ preprocessor library
* Copyright (C) 2016 Daniel Marjamki.
*
* 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 .
*/
#if defined(_WIN32) || defined(__CYGWIN__) || defined(__MINGW32__)
#define SIMPLECPP_WINDOWS
#define NOMINMAX
#endif
#include "simplecpp.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#ifdef SIMPLECPP_WINDOWS
#include
#undef ERROR
#undef TRUE
#endif
static bool isHex(const std::string &s)
{
return s.size()>2 && (s.compare(0,2,"0x")==0 || s.compare(0,2,"0X")==0);
}
static const simplecpp::TokenString DEFINE("define");
static const simplecpp::TokenString UNDEF("undef");
static const simplecpp::TokenString INCLUDE("include");
static const simplecpp::TokenString ERROR("error");
static const simplecpp::TokenString WARNING("warning");
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");
static const simplecpp::TokenString PRAGMA("pragma");
static const simplecpp::TokenString ONCE("once");
template static std::string toString(T t)
{
std::ostringstream ostr;
ostr > std::hex;
istr >> ret;
return ret;
}
static unsigned long long stringToULL(const std::string &s)
{
unsigned long long ret;
const bool hex = isHex(s);
std::istringstream istr(hex ? s.substr(2) : s);
if (hex)
istr >> std::hex;
istr >> ret;
return ret;
}
static bool startsWith(const std::string &str, const std::string &s)
{
return (str.size() >= s.size() && str.compare(0, s.size(), s) == 0);
}
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);
}
static bool sameline(const simplecpp::Token *tok1, const simplecpp::Token *tok2)
{
return tok1 && tok2 && tok1->location.sameline(tok2->location);
}
static bool isAlternativeBinaryOp(const simplecpp::Token *tok, const std::string &alt)
{
return (tok->name &&
tok->str == alt &&
tok->previous &&
tok->next &&
(tok->previous->number || tok->previous->name || tok->previous->op == ')') &&
(tok->next->number || tok->next->name || tok->next->op == '('));
}
static bool isAlternativeUnaryOp(const simplecpp::Token *tok, const std::string &alt)
{
return ((tok->name && tok->str == alt) &&
(!tok->previous || tok->previous->op == '(') &&
(tok->next && (tok->next->name || tok->next->number)));
}
void simplecpp::Location::adjust(const std::string &str)
{
if (str.find_first_of("\r\n") == std::string::npos) {
col += str.size();
return;
}
for (std::size_t i = 0U; i < str.size(); ++i) {
col++;
if (str[i] == '\n' || str[i] == '\r') {
col = 1;
line++;
if (str[i] == '\r' && (i+1)previous)
tok = tok->previous;
for (; tok; tok = tok->next) {
if (tok->previous) {
std::cout previous) ? ' ' : '\n');
}
std::cout str;
}
std::cout next) {
if (tok != this) {
std::cout previous) ? ' ' : '\n');
}
std::cout str;
}
std::cout next)
push_back(new Token(*tok));
sizeOfType = other.sizeOfType;
}
return *this;
}
void simplecpp::TokenList::clear()
{
backToken = NULL;
while (frontToken) {
Token *next = frontToken->next;
delete frontToken;
frontToken = next;
}
sizeOfType.clear();
}
void simplecpp::TokenList::push_back(Token *tok)
{
if (!frontToken)
frontToken = tok;
else
backToken->next = tok;
tok->previous = backToken;
backToken = tok;
}
void simplecpp::TokenList::dump() const
{
std::cout location.line < loc.line || tok->location.fileIndex != loc.fileIndex) {
ret location;
}
while (tok->location.line > loc.line) {
ret previous, tok))
ret str);
}
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) ? (chop == 'R') {
std::string delim;
ch = readChar(istr,bom);
while (istr.good() && ch != '(' && ch != '\n') {
delim += ch;
ch = readChar(istr,bom);
}
if (!istr.good() || ch == '\n')
// TODO report
return;
currentToken = '\"';
const std::string endOfRawString(')' + delim + '\"');
while (istr.good() && !endsWith(currentToken, endOfRawString))
currentToken += readChar(istr,bom);
if (!endsWith(currentToken, endOfRawString))
// TODO report
return;
currentToken.erase(currentToken.size() - endOfRawString.size(), endOfRawString.size() - 1U);
back()->setstr(escapeString(currentToken));
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();
continue;
}
currentToken = readUntil(istr,location,ch,ch,outputList);
if (currentToken.size() < 2U)
// TODO report
return;
std::string s = currentToken;
std::string::size_type pos;
while ((pos = s.find_first_of("\r\n")) != std::string::npos) {
s.erase(pos,1);
}
push_back(new Token(s, location)); // push string without newlines
location.adjust(currentToken);
continue;
}
else {
currentToken += ch;
}
if (currentToken == ">")
result = stringToLL(tok->previous->str) >> stringToLL(tok->next->str);
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)
{
for (; tok && tok->op != ')'; tok = tok->next) {
if (isAlternativeBinaryOp(tok,NOTEQ))
tok->setstr("!=");
if (!tok->startsWithOneOf("=!"))
continue;
if (!tok->previous || !tok->previous->number)
continue;
if (!tok->next || !tok->next->number)
continue;
int result;
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 == "setstr(toString(tok->str[1] & 0xffU));
}
}
static long long evaluate(simplecpp::TokenList &expr, const std::map &sizeOfType)
{
simplifySizeof(expr, sizeOfType);
simplifyName(expr);
simplifyNumbers(expr);
expr.constFold();
// TODO: handle invalid expressions
return expr.cfront() && expr.cfront() == expr.cback() && expr.cfront()->number ? stringToLL(expr.cfront()->str) : 0LL;
}
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;
}
static std::string openHeader(std::ifstream &f, const simplecpp::DUI &dui, const std::string &sourcefile, const std::string &header, bool systemheader)
{
if (isAbsolutePath(header)) {
f.open(header.c_str());
return f.is_open() ? simplecpp::simplifyPath(header) : "";
}
if (!systemheader) {
if (sourcefile.find_first_of("\\/") != std::string::npos) {
const std::string s = sourcefile.substr(0, sourcefile.find_last_of("\\/") + 1U) + header;
f.open(s.c_str());
if (f.is_open())
return simplecpp::simplifyPath(s);
} else {
f.open(header.c_str());
if (f.is_open())
return simplecpp::simplifyPath(header);
}
}
for (std::list::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;
f.open(s.c_str());
if (f.is_open())
return simplecpp::simplifyPath(s);
}
return "";
}
static std::string getFileName(const std::map &filedata, const std::string &sourcefile, const std::string &header, const simplecpp::DUI &dui, bool systemheader)
{
if (filedata.empty()) {
return "";
}
if (isAbsolutePath(header)) {
return (filedata.find(header) != filedata.end()) ? simplecpp::simplifyPath(header) : "";
}
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);
if (filedata.find(s) != filedata.end())
return s;
}
}
for (std::list::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;
}
return "";
}
static bool hasFile(const std::map &filedata, const std::string &sourcefile, const std::string &header, const simplecpp::DUI &dui, bool systemheader)
{
return !getFileName(filedata, sourcefile, header, dui, systemheader).empty();
}
std::map simplecpp::load(const simplecpp::TokenList &rawtokens, std::vector &fileNumbers, const simplecpp::DUI &dui, simplecpp::OutputList *outputList)
{
std::map ret;
std::list filelist;
// -include files
for (std::list::const_iterator it = dui.includes.begin(); it != dui.includes.end(); ++it) {
const std::string &filename = realFilename(*it);
if (ret.find(filename) != ret.end())
continue;
std::ifstream fin(filename.c_str());
if (!fin.is_open())
continue;
TokenList *tokenlist = new TokenList(fin, fileNumbers, filename, outputList);
if (!tokenlist->front()) {
delete tokenlist;
continue;
}
ret[filename] = tokenlist;
filelist.push_back(tokenlist->front());
}
for (const Token *rawtok = rawtokens.cfront(); rawtok || !filelist.empty(); rawtok = rawtok ? rawtok->next : NULL) {
if (rawtok == NULL) {
rawtok = filelist.back();
filelist.pop_back();
}
if (rawtok->op != '#' || sameline(rawtok->previousSkipComments(), rawtok))
continue;
rawtok = rawtok->nextSkipComments();
if (!rawtok || rawtok->str != INCLUDE)
continue;
const std::string &sourcefile = rawtok->location.file();
const Token *htok = rawtok->nextSkipComments();
if (!sameline(rawtok, htok))
continue;
bool systemheader = (htok->str[0] == '