2015-05-07 18:10:22 -07:00
|
|
|
#include "CPreprocessor.hpp"
|
|
|
|
|
#include <stdio.h>
|
|
|
|
|
#include <sstream>
|
|
|
|
|
#include <iostream>
|
2015-05-09 16:45:41 -07:00
|
|
|
#include <algorithm>
|
2015-05-07 18:10:22 -07:00
|
|
|
|
|
|
|
|
static std::string removeQuotes(const std::string& in)
|
|
|
|
|
{
|
|
|
|
|
return in.substr(1,in.size()-2);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static std::string addPaths(const std::string& first, const std::string& second)
|
|
|
|
|
{
|
|
|
|
|
std::string result;
|
|
|
|
|
size_t slash_pos = first.find_last_of('/');
|
|
|
|
|
if (slash_pos == 0 || slash_pos >= first.size()) return second;
|
|
|
|
|
result = first.substr(0,slash_pos+1);
|
|
|
|
|
result += second;
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void setLineMacro(CPreprocessor::DefineTable& defineTable, unsigned int line)
|
|
|
|
|
{
|
|
|
|
|
CPreprocessor::DefineEntry def;
|
|
|
|
|
CLexer::Token token;
|
|
|
|
|
token.type = CLexer::NUMBER;
|
|
|
|
|
std::stringstream sstr;
|
|
|
|
|
sstr << (line+1);
|
|
|
|
|
sstr >> token.value;
|
|
|
|
|
def.tokens.push_back(token);
|
|
|
|
|
defineTable["__LINE__"] = def;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void setFileMacro(CPreprocessor::DefineTable& defineTable, const std::string& file)
|
|
|
|
|
{
|
|
|
|
|
CPreprocessor::DefineEntry def;
|
|
|
|
|
CLexer::Token token;
|
|
|
|
|
token.type = CLexer::STRING;
|
|
|
|
|
token.value = std::string("\"")+file+"\"";
|
|
|
|
|
def.tokens.push_back(token);
|
|
|
|
|
defineTable["__FILE__"] = def;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CPreprocessor::CPreprocessor()
|
2015-05-09 16:45:41 -07:00
|
|
|
: m_errorCount(0)
|
2015-05-07 18:10:22 -07:00
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CPreprocessor::define(const std::string& def)
|
|
|
|
|
{
|
|
|
|
|
if (def.empty())
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
std::string data = "#define ";
|
|
|
|
|
data += def + "\n";
|
|
|
|
|
CLexer::TokenList tokens;
|
|
|
|
|
CLexer lexer;
|
|
|
|
|
lexer.lex(&data.front(), &data.back(), tokens);
|
|
|
|
|
|
|
|
|
|
_parseDefine(m_applicationDefined, tokens);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CPreprocessor::undefine(const std::string& def)
|
|
|
|
|
{
|
|
|
|
|
m_applicationDefined.erase(def);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CPreprocessor::registerPragma(const std::string& name, std::function<void (PragmaInstance)> cb)
|
|
|
|
|
{
|
|
|
|
|
PragmaIterator iter = m_registeredPragmas.find(name);
|
|
|
|
|
if (iter != m_registeredPragmas.end())
|
|
|
|
|
m_registeredPragmas.erase(iter);
|
|
|
|
|
|
|
|
|
|
m_registeredPragmas[name] = cb;
|
|
|
|
|
}
|
|
|
|
|
|
2015-05-09 16:45:41 -07:00
|
|
|
void CPreprocessor::registerHook(const std::string& name, std::function<void (CLexer::TokenList&,
|
|
|
|
|
CPreprocessor::DefineTable&,
|
|
|
|
|
PreprocessorState)> cb)
|
2015-05-07 19:20:06 -07:00
|
|
|
{
|
|
|
|
|
std::string pre = "#" + name;
|
|
|
|
|
HookIterator iter = m_registeredHooks.find(pre);
|
|
|
|
|
if (iter != m_registeredHooks.end())
|
|
|
|
|
m_registeredHooks.erase(iter);
|
|
|
|
|
|
|
|
|
|
m_registeredHooks[pre] = cb;
|
|
|
|
|
}
|
|
|
|
|
|
2015-05-07 18:10:22 -07:00
|
|
|
std::string CPreprocessor::finalizedSource()
|
|
|
|
|
{
|
|
|
|
|
std::string ret;
|
|
|
|
|
for (const CLexer::Token& token : m_tokens)
|
|
|
|
|
ret += token.value;
|
|
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
2015-05-09 16:45:41 -07:00
|
|
|
bool CPreprocessor::preprocessFile(const std::string& filename)
|
2015-05-07 18:10:22 -07:00
|
|
|
{
|
|
|
|
|
std::string code = _loadSource(filename);
|
|
|
|
|
if (code == std::string())
|
2015-05-09 16:45:41 -07:00
|
|
|
{
|
2015-05-07 18:10:22 -07:00
|
|
|
printErrorMessage(std::string("Empty source file specified: ") + filename);
|
2015-05-09 16:45:41 -07:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return preprocessCode(filename, code);;
|
2015-05-07 18:10:22 -07:00
|
|
|
}
|
|
|
|
|
|
2015-05-09 16:45:41 -07:00
|
|
|
bool CPreprocessor::preprocessCode(const std::string& filename, const std::string& code)
|
2015-05-07 18:10:22 -07:00
|
|
|
{
|
|
|
|
|
m_currentLine = 0;
|
|
|
|
|
m_errorCount = 0;
|
|
|
|
|
m_rootFile = filename;
|
|
|
|
|
DefineTable defineTable = m_applicationDefined;
|
|
|
|
|
m_lineTranslator.reset();
|
|
|
|
|
m_tokens.clear();
|
|
|
|
|
|
2015-05-09 16:45:41 -07:00
|
|
|
return preprocessRecursive(filename, code, m_tokens, defineTable);;
|
2015-05-07 18:10:22 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::string CPreprocessor::_loadSource(const std::string& filename)
|
|
|
|
|
{
|
|
|
|
|
std::string code;
|
|
|
|
|
FILE* file = fopen(filename.c_str(), "rb");
|
|
|
|
|
if (file)
|
|
|
|
|
{
|
|
|
|
|
fseek(file, 0, SEEK_END);
|
|
|
|
|
size_t length = ftell(file);
|
|
|
|
|
rewind(file);
|
|
|
|
|
code.resize(length);
|
|
|
|
|
fread(&code.front(), 1, length, file);
|
|
|
|
|
fclose(file);
|
|
|
|
|
|
|
|
|
|
if (code != std::string() && code.back() != '\n')
|
|
|
|
|
{
|
|
|
|
|
printWarningMessage(std::string("No new line at end of file: ") + filename);
|
|
|
|
|
code += '\n';
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return code;
|
|
|
|
|
}
|
|
|
|
|
|
2015-05-07 19:20:06 -07:00
|
|
|
void CPreprocessor::advanceList(CLexer::TokenList& tokens)
|
2015-05-07 18:10:22 -07:00
|
|
|
{
|
|
|
|
|
tokens.pop_front();
|
|
|
|
|
while(true)
|
|
|
|
|
{
|
|
|
|
|
if (tokens.front().type != CLexer::WHITESPACE)
|
|
|
|
|
break;
|
|
|
|
|
tokens.pop_front();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CPreprocessor::printErrorMessage(const std::string& errMsg)
|
|
|
|
|
{
|
|
|
|
|
std::cout << errMsg << std::endl;
|
|
|
|
|
m_errorCount++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CPreprocessor::printWarningMessage(const std::string& warnMesg)
|
|
|
|
|
{
|
|
|
|
|
std::cout << warnMesg << std::endl;
|
|
|
|
|
}
|
|
|
|
|
|
2015-05-09 16:45:41 -07:00
|
|
|
CLexer::TokenIterator CPreprocessor::_parseIdentifier(CLexer::TokenIterator begin, CLexer::TokenIterator end, CLexer::TokenList& tokens, DefineTable& defineTable)
|
|
|
|
|
{
|
|
|
|
|
std::string macroName = begin->value;
|
|
|
|
|
MacroIterator iter = std::find_if(m_macros.begin(), m_macros.end(), [¯oName](const Macro& m) -> bool { return m.name == macroName; });
|
|
|
|
|
if (iter != m_macros.end())
|
|
|
|
|
{
|
|
|
|
|
_expandMacro(begin, end, tokens, *iter);
|
|
|
|
|
--begin;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
begin = _expandDefine(begin, end, tokens, defineTable);
|
|
|
|
|
|
|
|
|
|
return begin;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool CPreprocessor::preprocessRecursive(const std::string& filename, const std::string& code, CLexer::TokenList& tokens, DefineTable& defineTable)
|
2015-05-07 18:10:22 -07:00
|
|
|
{
|
|
|
|
|
unsigned int startLine = m_currentLine;
|
|
|
|
|
m_currentFile = filename;
|
|
|
|
|
m_currentFileLines = 0;
|
|
|
|
|
setFileMacro(defineTable, m_currentFile);
|
|
|
|
|
setLineMacro(defineTable, m_currentFileLines);
|
|
|
|
|
|
|
|
|
|
CLexer lexer;
|
|
|
|
|
lexer.lex((char*)&code.front(), (char*)&code.back(), tokens);
|
|
|
|
|
|
|
|
|
|
CLexer::TokenIterator begin = tokens.begin();
|
|
|
|
|
CLexer::TokenIterator end = tokens.end();
|
|
|
|
|
|
|
|
|
|
while (begin != end)
|
|
|
|
|
{
|
|
|
|
|
while (begin->type == CLexer::WHITESPACE)
|
|
|
|
|
++begin;
|
|
|
|
|
|
|
|
|
|
if (begin->type == CLexer::NEWLINE)
|
|
|
|
|
{
|
|
|
|
|
m_currentLine++;
|
|
|
|
|
m_currentFileLines++;
|
|
|
|
|
++begin;
|
|
|
|
|
setLineMacro(defineTable, m_currentFileLines);
|
|
|
|
|
}
|
2015-05-09 16:45:41 -07:00
|
|
|
else if (begin->type == CLexer::MACRO)
|
|
|
|
|
{
|
|
|
|
|
CLexer::TokenIterator lineStart = begin;
|
|
|
|
|
CLexer::TokenIterator lineEnd = _findToken(begin, end, CLexer::NEWLINE);
|
|
|
|
|
CLexer::TokenList directive(lineStart, lineEnd);
|
|
|
|
|
begin = tokens.erase(lineStart, lineEnd);
|
|
|
|
|
advanceList(directive);
|
|
|
|
|
Macro macro;
|
|
|
|
|
macro.name = directive.begin()->value;
|
|
|
|
|
while (directive.begin()->type != CLexer::CLOSE && directive.begin()->value != ")")
|
|
|
|
|
{
|
|
|
|
|
advanceList(directive);
|
|
|
|
|
if (directive.empty())
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
if (directive.begin()->type == CLexer::IDENTIFIER || directive.begin()->type == CLexer::PREPROCESSOR)
|
|
|
|
|
macro.args.push_back(*directive.begin());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
advanceList(directive);
|
|
|
|
|
while(directive.begin()->value != "\n")
|
|
|
|
|
{
|
|
|
|
|
if (directive.empty())
|
|
|
|
|
break;
|
|
|
|
|
if(directive.begin()->value != "\\")
|
|
|
|
|
macro.code.push_back(*directive.begin());
|
|
|
|
|
|
|
|
|
|
directive.pop_front();
|
|
|
|
|
}
|
|
|
|
|
m_macros.push_back(macro);
|
|
|
|
|
}
|
2015-05-07 18:10:22 -07:00
|
|
|
else if (begin->type == CLexer::PREPROCESSOR)
|
|
|
|
|
{
|
|
|
|
|
CLexer::TokenIterator lineStart = begin;
|
|
|
|
|
CLexer::TokenIterator lineEnd = _findToken(begin, end, CLexer::NEWLINE);
|
2015-05-09 16:45:41 -07:00
|
|
|
|
2015-05-07 18:10:22 -07:00
|
|
|
CLexer::TokenList directive(lineStart, lineEnd);
|
|
|
|
|
begin = tokens.erase(lineStart, lineEnd);
|
|
|
|
|
|
|
|
|
|
std::string value = directive.begin()->value;
|
|
|
|
|
if (value == "#define")
|
|
|
|
|
_parseDefine(defineTable, directive);
|
|
|
|
|
else if (value == "#undef")
|
|
|
|
|
{
|
|
|
|
|
std::string defName;
|
|
|
|
|
_parseIf(directive, defName);
|
|
|
|
|
DefineIterator defineIter = defineTable.find(defName);
|
|
|
|
|
if (defineIter != defineTable.end())
|
|
|
|
|
defineTable.erase(defName);
|
|
|
|
|
}
|
|
|
|
|
else if (value == "#ifdef")
|
|
|
|
|
{
|
|
|
|
|
std::string defName;
|
|
|
|
|
_parseIf(directive, defName);
|
|
|
|
|
DefineIterator defineIter = defineTable.find(defName);
|
|
|
|
|
if (defineIter == defineTable.end())
|
|
|
|
|
{
|
|
|
|
|
CLexer::TokenIterator spliceTo = _parseIfDef(begin, end);
|
|
|
|
|
begin = tokens.erase(begin, spliceTo);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if (value == "#ifndef")
|
|
|
|
|
{
|
|
|
|
|
std::string defName;
|
|
|
|
|
_parseIf(directive, defName);
|
|
|
|
|
DefineIterator defineIter = defineTable.find(defName);
|
|
|
|
|
if (defineIter != defineTable.end())
|
|
|
|
|
{
|
|
|
|
|
CLexer::TokenIterator spliceTo = _parseIfDef(begin, end);
|
|
|
|
|
begin = tokens.erase(begin, spliceTo);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if (value == "#include")
|
|
|
|
|
{
|
|
|
|
|
m_lineTranslator.table().addLineRange(filename, startLine, m_currentLine - m_currentFileLines);
|
|
|
|
|
std::string includeFilename;
|
|
|
|
|
_parseIf(directive, includeFilename);
|
|
|
|
|
includeFilename = removeQuotes(includeFilename);
|
|
|
|
|
std::string newCode = _loadSource(includeFilename);
|
|
|
|
|
if (newCode != std::string())
|
|
|
|
|
{
|
|
|
|
|
unsigned int oldCurrentFileLines = m_currentFileLines;
|
|
|
|
|
CLexer::TokenList nextFile;
|
|
|
|
|
preprocessRecursive(addPaths(filename, includeFilename), newCode, nextFile, defineTable);
|
|
|
|
|
tokens.splice(begin, nextFile);
|
|
|
|
|
startLine = m_currentLine;
|
|
|
|
|
m_currentFileLines = oldCurrentFileLines;
|
|
|
|
|
m_currentFile = filename;
|
|
|
|
|
setFileMacro(defineTable, filename);
|
|
|
|
|
setLineMacro(defineTable, m_currentFileLines);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
printErrorMessage(std::string("Unable to find include file ") + includeFilename);
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
else if (value == "#pragma")
|
|
|
|
|
_parsePragma(directive);
|
|
|
|
|
else if (value == "#warning")
|
|
|
|
|
_parseWarning(directive, defineTable);
|
|
|
|
|
else if (value == "#error")
|
|
|
|
|
_parseError(directive, defineTable);
|
2015-05-07 19:20:06 -07:00
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
HookIterator iter = m_registeredHooks.find(value);
|
|
|
|
|
if (iter != m_registeredHooks.end() && m_registeredHooks[value])
|
2015-05-09 16:45:41 -07:00
|
|
|
{
|
|
|
|
|
PreprocessorState state;
|
|
|
|
|
state.currentFile = m_currentFile;
|
|
|
|
|
state.rootFile = m_rootFile;
|
|
|
|
|
state.currentLine = m_currentFileLines;
|
|
|
|
|
state.globalLine = m_currentLine;
|
|
|
|
|
m_registeredHooks[value](directive, defineTable, state);
|
|
|
|
|
}
|
2015-05-07 19:20:06 -07:00
|
|
|
}
|
2015-05-07 18:10:22 -07:00
|
|
|
}
|
|
|
|
|
else if (begin->type == CLexer::IDENTIFIER)
|
2015-05-09 16:45:41 -07:00
|
|
|
begin = _parseIdentifier(begin, end, tokens, defineTable);
|
|
|
|
|
else if (begin->degenerate)
|
2015-05-07 18:10:22 -07:00
|
|
|
{
|
2015-05-09 16:45:41 -07:00
|
|
|
switch(begin->type)
|
|
|
|
|
{
|
|
|
|
|
case CLexer::COMMENT:
|
|
|
|
|
{
|
|
|
|
|
std::stringstream ss;
|
|
|
|
|
ss << m_currentFile << ": Degenerate comment on line " << m_currentFileLines << std::endl;
|
|
|
|
|
printErrorMessage(ss.str());
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
default:
|
|
|
|
|
printErrorMessage(m_currentFile + ": Degenerate token: " + begin->value);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
++begin;
|
2015-05-07 18:10:22 -07:00
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
++begin;
|
|
|
|
|
}
|
2015-05-09 16:45:41 -07:00
|
|
|
|
|
|
|
|
return !(m_errorCount > 0);
|
2015-05-07 18:10:22 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CPreprocessor::callPragma(const std::string& name, const PragmaInstance& parms)
|
|
|
|
|
{
|
|
|
|
|
PragmaIterator iter = m_registeredPragmas.find(name);
|
|
|
|
|
if (iter == m_registeredPragmas.end())
|
|
|
|
|
{
|
|
|
|
|
printErrorMessage("Unknown pragma command.");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (iter->second)
|
|
|
|
|
iter->second(parms);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CLexer::TokenIterator CPreprocessor::_findToken(CLexer::TokenIterator begin, CLexer::TokenIterator end, CLexer::TokenType type)
|
|
|
|
|
{
|
|
|
|
|
while (begin != end && begin->type != type)
|
|
|
|
|
++begin;
|
|
|
|
|
|
|
|
|
|
return begin;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CLexer::TokenIterator CPreprocessor::_parseStatement(CLexer::TokenIterator begin, CLexer::TokenIterator end, CLexer::TokenList& dest)
|
|
|
|
|
{
|
|
|
|
|
int depth = 0;
|
|
|
|
|
while (begin != end)
|
|
|
|
|
{
|
|
|
|
|
if (begin->value == "," && depth == 0)
|
|
|
|
|
return begin;
|
|
|
|
|
if (begin->type == CLexer::CLOSE && depth == 0)
|
|
|
|
|
return begin;
|
|
|
|
|
if (begin->type == CLexer::SEMICOLON && depth == 0)
|
|
|
|
|
return begin;
|
|
|
|
|
dest.push_back(*begin);
|
|
|
|
|
if (begin->type == CLexer::OPEN)
|
|
|
|
|
depth++;
|
|
|
|
|
if (begin->type == CLexer::CLOSE)
|
|
|
|
|
{
|
|
|
|
|
if (depth == 0)
|
|
|
|
|
printErrorMessage("Mismatched braces while parsing statement.");
|
|
|
|
|
depth--;
|
|
|
|
|
}
|
|
|
|
|
++begin;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return begin;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CLexer::TokenIterator CPreprocessor::_parseDefineArguments(CLexer::TokenIterator begin, CLexer::TokenIterator end, CLexer::TokenList& tokens, std::vector<CLexer::TokenList>& args)
|
|
|
|
|
{
|
|
|
|
|
if (begin == end || begin->value == "(")
|
|
|
|
|
{
|
|
|
|
|
printErrorMessage("Expected argument list.");
|
|
|
|
|
return begin;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CLexer::TokenIterator beginErase = begin;
|
|
|
|
|
++begin;
|
|
|
|
|
|
|
|
|
|
while (begin != end)
|
|
|
|
|
{
|
|
|
|
|
CLexer::TokenList argument;
|
|
|
|
|
begin = _parseStatement(begin, end, argument);
|
|
|
|
|
args.push_back(argument);
|
|
|
|
|
|
|
|
|
|
if (begin == end)
|
|
|
|
|
{
|
|
|
|
|
printErrorMessage("Unexpected end of file");
|
|
|
|
|
return begin;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (begin->value == ",")
|
|
|
|
|
{
|
|
|
|
|
++begin;
|
|
|
|
|
if (begin == end)
|
|
|
|
|
{
|
|
|
|
|
printErrorMessage("Unexpected end of file.");
|
|
|
|
|
return begin;
|
|
|
|
|
}
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (begin->value == ")")
|
|
|
|
|
{
|
|
|
|
|
++begin;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return tokens.erase(beginErase, begin);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CLexer::TokenIterator CPreprocessor::_expandDefine(CLexer::TokenIterator begin, CLexer::TokenIterator end, CLexer::TokenList& tokens, CPreprocessor::DefineTable& defineTable)
|
|
|
|
|
{
|
|
|
|
|
DefineIterator defineEntry = defineTable.find(begin->value);
|
|
|
|
|
if (defineEntry == defineTable.end())
|
|
|
|
|
return ++begin;
|
|
|
|
|
begin = tokens.erase(begin);
|
|
|
|
|
|
|
|
|
|
if (defineEntry->second.arguments.size() == 0)
|
|
|
|
|
{
|
|
|
|
|
tokens.insert(begin, defineEntry->second.tokens.begin(), defineEntry->second.tokens.end());
|
|
|
|
|
return begin;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// We have arguments
|
|
|
|
|
std::vector<CLexer::TokenList> arguments;
|
|
|
|
|
begin = _parseDefineArguments(begin, end, tokens, arguments);
|
|
|
|
|
|
|
|
|
|
if (defineEntry->second.arguments.size() != arguments.size())
|
|
|
|
|
{
|
|
|
|
|
printErrorMessage("Didn't supply right number of arguments to define");
|
|
|
|
|
return begin;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CLexer::TokenList tmpList(defineEntry->second.tokens.begin(), defineEntry->second.tokens.end());
|
|
|
|
|
|
|
|
|
|
CLexer::TokenIterator tmpIter = tmpList.begin();
|
|
|
|
|
while (tmpIter != tmpList.end())
|
|
|
|
|
{
|
|
|
|
|
ArgSet::iterator arg = defineEntry->second.arguments.find(tmpIter->value);
|
|
|
|
|
if (arg == defineEntry->second.arguments.end())
|
|
|
|
|
{
|
|
|
|
|
++tmpIter;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
tmpIter = tmpList.erase(tmpIter);
|
|
|
|
|
tmpList.insert(begin, arguments[arg->second].begin(), arguments[arg->second].end());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
tokens.insert(begin, tmpList.begin(), tmpList.end());
|
|
|
|
|
|
|
|
|
|
return begin;
|
|
|
|
|
}
|
|
|
|
|
|
2015-05-09 16:45:41 -07:00
|
|
|
void CPreprocessor::_expandMacro(CLexer::TokenIterator begin, CLexer::TokenIterator end, CLexer::TokenList& tokens, const CPreprocessor::Macro& macro)
|
|
|
|
|
{
|
|
|
|
|
std::vector<CLexer::Token> macroArgs = macro.args;
|
|
|
|
|
std::vector<CLexer::Token> args;
|
|
|
|
|
std::vector<CLexer::Token> code = macro.code;
|
|
|
|
|
std::vector<CLexer::Token>::iterator codeIter = code.begin();
|
|
|
|
|
|
|
|
|
|
int depth = 0;
|
|
|
|
|
while (true)
|
|
|
|
|
{
|
|
|
|
|
begin = tokens.erase(begin);
|
|
|
|
|
if (begin == end)
|
|
|
|
|
break;
|
|
|
|
|
if (begin->type == CLexer::OPEN && begin->value == "(")
|
|
|
|
|
{
|
|
|
|
|
depth++;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (begin->type == CLexer::CLOSE && begin->value == ")")
|
|
|
|
|
{
|
|
|
|
|
depth--;
|
|
|
|
|
if (depth == 0)
|
|
|
|
|
{
|
|
|
|
|
begin = tokens.erase(begin);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (begin->type != CLexer::COMMA && begin->type != CLexer::WHITESPACE)
|
|
|
|
|
{
|
|
|
|
|
args.push_back(*begin);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (args.empty())
|
|
|
|
|
{
|
|
|
|
|
printErrorMessage("Expected args");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (args.size() != macroArgs.size())
|
|
|
|
|
{
|
|
|
|
|
printErrorMessage("Argument count mismatch");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
while (codeIter != code.end())
|
|
|
|
|
{
|
|
|
|
|
std::string value = codeIter->value;
|
|
|
|
|
bool stringify = (value[0] == '#');
|
|
|
|
|
if (stringify)
|
|
|
|
|
value.erase(value.begin());
|
|
|
|
|
|
|
|
|
|
auto it = std::find_if(macroArgs.begin(), macroArgs.end(), [&value](const CLexer::Token& t) -> bool { return t.value == value; });
|
|
|
|
|
if (it != macroArgs.end())
|
|
|
|
|
{
|
|
|
|
|
int index = it - macroArgs.begin();
|
|
|
|
|
code.erase(codeIter);
|
|
|
|
|
code.insert(codeIter, *(args.begin() + index));
|
|
|
|
|
if (stringify)
|
|
|
|
|
{
|
|
|
|
|
codeIter->type = CLexer::STRING;
|
|
|
|
|
codeIter->value = "\"" + codeIter->value + "\"";
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
++codeIter;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CLexer::TokenList newCode;
|
|
|
|
|
std::copy(code.begin(), code.end(), std::back_inserter(newCode));
|
|
|
|
|
tokens.splice(begin, newCode);
|
|
|
|
|
}
|
|
|
|
|
|
2015-05-07 18:10:22 -07:00
|
|
|
void CPreprocessor::_parseDefine(CPreprocessor::DefineTable& defineTable, CLexer::TokenList& tokens)
|
|
|
|
|
{
|
2015-05-07 19:20:06 -07:00
|
|
|
advanceList(tokens);
|
2015-05-07 18:10:22 -07:00
|
|
|
if (tokens.empty())
|
|
|
|
|
{
|
|
|
|
|
printErrorMessage("Define directive without arguments");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CLexer::Token name = *tokens.begin();
|
|
|
|
|
if (name.type != CLexer::IDENTIFIER)
|
|
|
|
|
{
|
|
|
|
|
printErrorMessage("Defines's name was not an identifier.");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
if (defineTable.find(name.value) != defineTable.end())
|
|
|
|
|
{
|
|
|
|
|
printErrorMessage(name.value + " already defined.");
|
|
|
|
|
return;
|
|
|
|
|
}
|
2015-05-07 19:20:06 -07:00
|
|
|
advanceList(tokens);
|
2015-05-07 18:10:22 -07:00
|
|
|
|
|
|
|
|
DefineEntry def;
|
|
|
|
|
|
|
|
|
|
if (!tokens.empty())
|
|
|
|
|
{
|
2015-05-09 16:45:41 -07:00
|
|
|
if (tokens.begin()->type == CLexer::PREPROCESSOR || tokens.begin()->type == CLexer::MACRO)
|
2015-05-07 18:10:22 -07:00
|
|
|
{
|
|
|
|
|
// macro has arguments
|
2015-05-07 19:20:06 -07:00
|
|
|
advanceList(tokens);
|
2015-05-07 18:10:22 -07:00
|
|
|
|
|
|
|
|
if (tokens.empty() || tokens.begin()->value != "(")
|
|
|
|
|
{
|
|
|
|
|
printErrorMessage("Expected arguments");
|
|
|
|
|
return;
|
|
|
|
|
}
|
2015-05-07 19:20:06 -07:00
|
|
|
advanceList(tokens);
|
2015-05-07 18:10:22 -07:00
|
|
|
|
|
|
|
|
int argCount = 0;
|
|
|
|
|
while (!tokens.empty() && tokens.begin()->value != ")")
|
|
|
|
|
{
|
|
|
|
|
if (tokens.begin()->type != CLexer::IDENTIFIER)
|
|
|
|
|
{
|
|
|
|
|
printErrorMessage("Expected identifier");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
def.arguments[tokens.begin()->value] = argCount;
|
2015-05-07 19:20:06 -07:00
|
|
|
advanceList(tokens);
|
2015-05-07 18:10:22 -07:00
|
|
|
if (!tokens.empty() && tokens.begin()->value == ",")
|
2015-05-07 19:20:06 -07:00
|
|
|
advanceList(tokens);
|
2015-05-07 18:10:22 -07:00
|
|
|
argCount++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!tokens.empty())
|
|
|
|
|
{
|
|
|
|
|
if (tokens.begin()->value != ")")
|
|
|
|
|
{
|
|
|
|
|
printErrorMessage("Expected closing parentheses");
|
|
|
|
|
return;
|
|
|
|
|
}
|
2015-05-07 19:20:06 -07:00
|
|
|
advanceList(tokens);
|
2015-05-07 18:10:22 -07:00
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
printErrorMessage("Unexpected end of file");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CLexer::TokenIterator iter = tokens.begin();
|
|
|
|
|
while (iter != tokens.end())
|
|
|
|
|
iter = _expandDefine(iter, tokens.end(), tokens, defineTable);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
def.tokens = tokens;
|
|
|
|
|
defineTable[name.value] = def;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CLexer::TokenIterator CPreprocessor::_parseIfDef(CLexer::TokenIterator begin, CLexer::TokenIterator end)
|
|
|
|
|
{
|
|
|
|
|
int depth = 0;
|
|
|
|
|
bool foundEnd = false;
|
|
|
|
|
while (begin != end)
|
|
|
|
|
{
|
|
|
|
|
CLexer::Token token = *begin;
|
|
|
|
|
if (token.type == CLexer::PREPROCESSOR)
|
|
|
|
|
{
|
|
|
|
|
if (token.value == "#endif" && depth == 0)
|
|
|
|
|
{
|
|
|
|
|
++begin;
|
|
|
|
|
foundEnd = true;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
if (token.value == "#ifdef" || token.value == "#ifndef")
|
|
|
|
|
depth++;
|
|
|
|
|
if (token.value == "#endif" && depth > 0)
|
|
|
|
|
depth--;
|
|
|
|
|
}
|
|
|
|
|
++begin;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (begin == end && !foundEnd)
|
|
|
|
|
{
|
|
|
|
|
printErrorMessage("Unexpected end of file");
|
|
|
|
|
return begin;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return begin;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CPreprocessor::_parseIf(CLexer::TokenList& directive, std::string& nameOut)
|
|
|
|
|
{
|
2015-05-07 19:20:06 -07:00
|
|
|
advanceList(directive);
|
2015-05-07 18:10:22 -07:00
|
|
|
if (directive.empty())
|
|
|
|
|
{
|
|
|
|
|
printErrorMessage("Expected argument.");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
nameOut = directive.begin()->value;
|
2015-05-07 19:20:06 -07:00
|
|
|
advanceList(directive);
|
2015-05-07 18:10:22 -07:00
|
|
|
if (!directive.empty())
|
2015-05-09 16:45:41 -07:00
|
|
|
printErrorMessage((m_lineTranslator.resolveOriginalFile(m_currentLine) + ": Too many arguments."));
|
2015-05-07 18:10:22 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CPreprocessor::_parsePragma(CLexer::TokenList& args)
|
|
|
|
|
{
|
2015-05-07 19:20:06 -07:00
|
|
|
advanceList(args);
|
2015-05-07 18:10:22 -07:00
|
|
|
if (args.empty())
|
|
|
|
|
{
|
|
|
|
|
printErrorMessage("Pragmas need arguments.");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
std::string pragmaName = args.begin()->value;
|
|
|
|
|
|
2015-05-07 19:20:06 -07:00
|
|
|
advanceList(args);
|
2015-05-07 18:10:22 -07:00
|
|
|
|
|
|
|
|
std::string pragmaArgs;
|
|
|
|
|
if (!args.empty())
|
|
|
|
|
{
|
|
|
|
|
if (args.begin()->type != CLexer::STRING)
|
|
|
|
|
printErrorMessage("Pragma parameter should be a string literal");
|
|
|
|
|
pragmaArgs = removeQuotes(args.begin()->value);
|
2015-05-07 19:20:06 -07:00
|
|
|
advanceList(args);
|
2015-05-07 18:10:22 -07:00
|
|
|
}
|
|
|
|
|
if (!args.empty())
|
2015-05-09 16:45:41 -07:00
|
|
|
printErrorMessage((m_lineTranslator.resolveOriginalFile(m_currentLine) + ": Too many paremeters for pragma."));
|
2015-05-07 18:10:22 -07:00
|
|
|
|
|
|
|
|
PragmaInstance pi;
|
|
|
|
|
pi.name = pragmaName;
|
|
|
|
|
pi.text = pragmaArgs;
|
2015-05-09 16:45:41 -07:00
|
|
|
pi.state.currentFile = m_currentFile;
|
|
|
|
|
pi.state.currentLine = m_currentFileLines;
|
|
|
|
|
pi.state.rootFile = m_rootFile;
|
|
|
|
|
pi.state.globalLine = m_currentLine;
|
2015-05-07 18:10:22 -07:00
|
|
|
callPragma(pragmaName, pi);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::string CPreprocessor::_expandMessage(DefineTable& defineTable, CLexer::TokenList& args)
|
|
|
|
|
{
|
|
|
|
|
std::string msg;
|
|
|
|
|
while (!args.empty())
|
|
|
|
|
{
|
|
|
|
|
DefineIterator defineEntry = defineTable.find(args.begin()->value);
|
|
|
|
|
if (defineEntry != defineTable.end())
|
|
|
|
|
{
|
|
|
|
|
CLexer::TokenList tmpList(defineEntry->second.tokens.begin(), defineEntry->second.tokens.end());
|
|
|
|
|
CLexer::TokenIterator tmpIter = tmpList.begin();
|
|
|
|
|
|
|
|
|
|
while (tmpIter != tmpList.end())
|
|
|
|
|
{
|
|
|
|
|
msg += tmpIter->value;
|
|
|
|
|
tmpIter = tmpList.erase(tmpIter);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if (args.begin()->type != CLexer::IGNORE)
|
|
|
|
|
msg += args.begin()->value;
|
|
|
|
|
args.pop_front();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return msg;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CPreprocessor::_parseWarning(CLexer::TokenList& args, DefineTable& defineTable)
|
|
|
|
|
{
|
2015-05-07 19:20:06 -07:00
|
|
|
advanceList(args);
|
2015-05-07 18:10:22 -07:00
|
|
|
if (args.empty())
|
|
|
|
|
{
|
|
|
|
|
printErrorMessage("Warnings need messages.");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::string msg = _expandMessage(defineTable, args);
|
2015-05-09 16:45:41 -07:00
|
|
|
printWarningMessage((m_lineTranslator.resolveOriginalFile(m_currentLine) + ": Warning ") + msg);
|
2015-05-07 18:10:22 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CPreprocessor::_parseError(CLexer::TokenList& args, CPreprocessor::DefineTable& defineTable)
|
|
|
|
|
{
|
2015-05-07 19:20:06 -07:00
|
|
|
advanceList(args);
|
2015-05-07 18:10:22 -07:00
|
|
|
if (args.empty())
|
|
|
|
|
{
|
|
|
|
|
printErrorMessage("Errors need messages.");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
std::string msg = _expandMessage(defineTable, args);
|
|
|
|
|
printErrorMessage(msg);
|
|
|
|
|
}
|
|
|
|
|
|