commit 4c93b47b2aeefc628b6b596d7bb295807fca12db Author: Phillip Stephens Date: Thu May 7 18:10:22 2015 -0700 Initial Commit diff --git a/AngelScriptPreprocessor.pro b/AngelScriptPreprocessor.pro new file mode 100644 index 0000000..c45cf23 --- /dev/null +++ b/AngelScriptPreprocessor.pro @@ -0,0 +1,15 @@ +TEMPLATE = app +CONFIG += console c++11 +CONFIG -= app_bundle +CONFIG -= qt + +SOURCES += main.cpp \ + CLexer.cpp \ + CPreprocessor.cpp \ + CLineTranslator.cpp + +HEADERS += \ + CLexer.hpp \ + CPreprocessor.hpp \ + CLineTranslator.hpp + diff --git a/CLexer.cpp b/CLexer.cpp new file mode 100644 index 0000000..35ed4f5 --- /dev/null +++ b/CLexer.cpp @@ -0,0 +1,300 @@ +#include "CLexer.hpp" +#include +#include + +const std::string numbers = "0123456789"; +const std::string identifierStart = "_abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; +const std::string identifierBody = "_abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; +const std::string hexnumbers = "0123456789abcdefABCDEF"; +const std::string trivials = ",;\n\r\t [{(]})"; + +const CLexer::TokenType TrivialTypes[] = +{ + CLexer::COMMA, + CLexer::SEMICOLON, + CLexer::NEWLINE, + CLexer::WHITESPACE, + CLexer::WHITESPACE, + CLexer::WHITESPACE, + CLexer::OPEN, + CLexer::OPEN, + CLexer::OPEN, + CLexer::CLOSE, + CLexer::CLOSE, + CLexer::CLOSE +}; + +int CLexer::lex(char* start, char* end, TokenList& tokens) +{ + assert(start != 0 && end != 0 && "start and end cannot be null"); + assert(start <= end && "degenerate lex detected: end < start"); + + while (true) + { + CLexer::Token currentToken; + start = _parseToken(start, end, currentToken); + if (currentToken.type != CLexer::COMMENT) + tokens.push_back(currentToken); + if (start == end) + return 0; + } +} + +bool CLexer::_searchString(const std::string& str, char in) const +{ + return (str.find_first_of(in) != std::string::npos); +} + +bool CLexer::_isTrivial(char in) const +{ + return _searchString(trivials, in); +} + +bool CLexer::_isIdentifierStart(char in) const +{ + return _searchString(identifierStart, in); +} + +bool CLexer::_isIdentifierBody(char in) const +{ + return _searchString(identifierBody, in); +} + +bool CLexer::_isNumber(char in) const +{ + return _searchString(numbers, in); +} + +bool CLexer::_isHex(char in) const +{ + return _searchString(hexnumbers, in); +} + +char* CLexer::_parseToken(char* start, char* end, CLexer::Token& out) +{ + if (start == end) + return start; + char curChar = *start; + if (_isTrivial(curChar)) + { + out.value += curChar; + out.type = TrivialTypes[trivials.find_first_of(curChar)]; + return ++start; + } + + if (_isIdentifierStart(curChar)) + return _parseIdentifier(start, end, out); + + if (curChar == '#') + { + start = _parseIdentifier(start, end, out); + out.type = CLexer::PREPROCESSOR; + return start; + } + + + if (_isNumber(curChar)) + return _parseNumber(start, end, out); + if (curChar == '\"') + return _parseStringLiteral(start, end, out); + if (curChar == '\'') + return _parseCharacterLiteral(start, end, out); + if (curChar == '/') + { + ++start; + // Is it a comment? + if (start == end) + return start; + if (*start == '*') + return _parseBlockComment(start, end, out); + if (*start == '/') + return _parseLineComment(start, end, out); + --start; + } + + // Nothing intradasting + out.value = curChar; + out.type = CLexer::IGNORE; + return ++start; +} + +char* CLexer::_parseLineComment(char* start, char* end, CLexer::Token& out) +{ + out.type = CLexer::COMMENT; + out.value += "//"; + + while (true) + { + ++start; + if (start == end) + return start; + out.value += start; + if (*start == '\n') + return start; + } +} + +char* CLexer::_parseBlockComment(char* start, char* end, CLexer::Token& out) +{ + out.type = CLexer::COMMENT; + out.value += "/*"; + + while (true) + { + ++start; + if (start == end) + break; + + out.value += *start; + if (*start == '*') + { + ++start; + if (start == end) + break; + if (*start == '/') + { + out.value += *start; + break; + } + else + --start; + } + } + + return start; +} + +char* CLexer::_parseNumber(char* start, char* end, CLexer::Token& out) +{ + out.type = CLexer::NUMBER; + out.value += *start; + while (true) + { + ++start; + if (start == end) + return start; + if (_isNumber(*start)) + out.value += *start; + else if (*start == '.') + return _parseFloatingPoint(start, end, out); + else if (*start == 'x') + return _parseFloatingPoint(start, end, out); + else + return start; + } +} + +char* CLexer::_parseHexConstant(char* start, char* end, CLexer::Token& out) +{ + out.value += *start; + while (true) + { + ++start; + if (start == end) + return start; + if (_isHex(*start)) + out.value += *start; + else + return start; + } +} + +char* CLexer::_parseFloatingPoint(char* start, char* end, CLexer::Token& out) +{ + out.value += *start; + while (true) + { + ++start; + if (start == end) + return start; + if (!_isNumber(*start)) + { + if (*start == 'f' || *start == 'F') + { + out.value += *start; + ++start; + return start; + } + return start; + } + else + out.value += *start; + } +} + +char* CLexer::_parseCharacterLiteral(char* start, char* end, CLexer::Token& out) +{ + ++start; + if (start == end) + return start; + out.type = CLexer::NUMBER; + if (*start == '\\') + { + ++start; + if (start == end) + return start; + if (*start == 'n') + out.value = '\n'; + if (*start == 't') + out.value = '\t'; + if (*start == 'r') + out.value = '\r'; + ++start; + if (start == end) + return start; + ++start; + } + else + { + out.value = *start; + ++start; + if (start == end) + return start; + ++start; + } + return start; +} + +char* CLexer::_parseStringLiteral(char* start, char* end, CLexer::Token& out) +{ + out.type = CLexer::STRING; + out.value += *start; + + while (true) + { + ++start; + if (start == end) + return start; + out.value += *start; + + // Are we at the end of the string? + if (*start == '\"') + return ++start; + // Is it an escape sequence? + if (*start == '\\') + { + ++start; + if (start == end) + return start; + out.value += *start; + } + } +} + +char* CLexer::_parseIdentifier(char* start, char* end, CLexer::Token& out) +{ + out.type = CLexer::IDENTIFIER; + out.value += *start; + while (true) + { + ++start; + if (start == end) + return start; + if (_isIdentifierBody(*start)) + out.value += *start; + else + return start; + } +} + + diff --git a/CLexer.hpp b/CLexer.hpp new file mode 100644 index 0000000..934bbd1 --- /dev/null +++ b/CLexer.hpp @@ -0,0 +1,60 @@ +#ifndef CLEXER_HPP +#define CLEXER_HPP + + +#include +#include + +class CLexer +{ +public: + enum TokenType + { + IDENTIFIER, //Names which can be expanded. + COMMA, //, + SEMICOLON, + OPEN, //{[( + CLOSE, //}]) + PREPROCESSOR, //Begins with # + NEWLINE, + WHITESPACE, + IGNORE, + COMMENT, + STRING, + NUMBER + }; + + struct Token + { + std::string value; + TokenType type; + }; + + typedef std::list TokenList; + typedef TokenList::iterator TokenIterator; + + CLexer() + { + } + + int lex(char* start, char* end, TokenList& tokens); +private: + bool _searchString(const std::string& str, char in) const; + bool _isTrivial(char in) const; + bool _isIdentifierStart(char in) const; + bool _isIdentifierBody(char in) const; + bool _isNumber(char in) const; + bool _isHex(char in) const; + char* _parseToken(char* start, char* end, Token& out); + char* _parseLiteral(char* start, char* end, Token& out); + char* _parseLineComment(char* start, char* end, Token& out); + char* _parseBlockComment(char* start, char* end, Token& out); + char* _parseNumber(char* start, char* end, Token& out); + char* _parseHexConstant(char* start, char* end, Token& out); + char* _parseFloatingPoint(char* start, char* end, Token& out); + char* _parseCharacterLiteral(char* start, char* end, Token& out); + char* _parseStringLiteral(char* start, char* end, Token& out); + char* _parseIdentifier(char* start, char* end, Token& out); +}; + +#endif // CLEXER_HPP diff --git a/CLineTranslator.cpp b/CLineTranslator.cpp new file mode 100644 index 0000000..8c73f38 --- /dev/null +++ b/CLineTranslator.cpp @@ -0,0 +1,42 @@ +#include "CLineTranslator.hpp" + + +CLineTranslator::Table::Entry& CLineTranslator::Table::search(unsigned int line) +{ + for (size_t i = 1; i < lines.size(); ++i) + { + if (line < lines[i].startLine) + return lines[i-1]; + } + + return lines[lines.size() - 1]; +} + +void CLineTranslator::Table::addLineRange(const std::string& file, unsigned int startLine, unsigned int offset) +{ + Entry e; + e.file = file; + e.startLine = startLine; + e.offset = offset; + lines.push_back(e); +} + + +CLineTranslator::CLineTranslator() +{ +} + +std::string CLineTranslator::resolveOriginalFile(unsigned int lineNumber) +{ + return m_table.search(lineNumber).file; +} + +unsigned int CLineTranslator::resolveOriginalLine(unsigned int lineNumber) +{ + return lineNumber - m_table.search(lineNumber).offset; +} + +void CLineTranslator::reset() +{ + m_table.lines.clear(); +} diff --git a/CLineTranslator.hpp b/CLineTranslator.hpp new file mode 100644 index 0000000..69b836f --- /dev/null +++ b/CLineTranslator.hpp @@ -0,0 +1,36 @@ +#ifndef CLINENUMBERTRANSLATOR_HPP +#define CLINENUMBERTRANSLATOR_HPP + +#include +#include + +class CLineTranslator +{ +public: + struct Table + { + struct Entry + { + std::string file; + unsigned int startLine; + unsigned int offset; + }; + + std::vector lines; + + Entry& search(unsigned int line); + + void addLineRange(const std::string& file, unsigned int startLine, unsigned int offset); + }; + + CLineTranslator(); + std::string resolveOriginalFile(unsigned int lineNumber); + unsigned int resolveOriginalLine(unsigned int lineNumber); + inline void setTable(Table table) { m_table = table; } + inline Table& table() { return m_table; } + void reset(); +private: + Table m_table; +}; + +#endif // CLINENUMBERTRANSLATOR_HPP diff --git a/CPreprocessor.cpp b/CPreprocessor.cpp new file mode 100644 index 0000000..0a72991 --- /dev/null +++ b/CPreprocessor.cpp @@ -0,0 +1,598 @@ +#include "CPreprocessor.hpp" +#include +#include +#include + +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() +{ +} + +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 cb) +{ + PragmaIterator iter = m_registeredPragmas.find(name); + if (iter != m_registeredPragmas.end()) + m_registeredPragmas.erase(iter); + + m_registeredPragmas[name] = cb; +} + +std::string CPreprocessor::finalizedSource() +{ + std::string ret; + for (const CLexer::Token& token : m_tokens) + ret += token.value; + + return ret; +} + +int CPreprocessor::preprocessFile(const std::string& filename) +{ + std::string code = _loadSource(filename); + if (code == std::string()) + printErrorMessage(std::string("Empty source file specified: ") + filename); + else + preprocessCode(filename, code); + return m_errorCount; +} + +int CPreprocessor::preprocessCode(const std::string& filename, const std::string& code) +{ + m_currentLine = 0; + m_errorCount = 0; + m_rootFile = filename; + DefineTable defineTable = m_applicationDefined; + m_lineTranslator.reset(); + m_tokens.clear(); + preprocessRecursive(filename, code, m_tokens, defineTable); + + return m_errorCount; +} + +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; +} + +void CPreprocessor::_advanceList(CLexer::TokenList& tokens) +{ + 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; +} + +void CPreprocessor::preprocessRecursive(const std::string& filename, const std::string& code, CLexer::TokenList& tokens, DefineTable& defineTable) +{ + 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); + } + else if (begin->type == CLexer::PREPROCESSOR) + { + CLexer::TokenIterator lineStart = begin; + CLexer::TokenIterator lineEnd = _findToken(begin, end, CLexer::NEWLINE); + 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); + } + else if (begin->type == CLexer::IDENTIFIER) + { + begin = _expandDefine(begin, end, tokens, defineTable); + } + else + ++begin; + } +} + +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& 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 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; +} + +void CPreprocessor::_parseDefine(CPreprocessor::DefineTable& defineTable, CLexer::TokenList& tokens) +{ + _advanceList(tokens); + 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; + } + _advanceList(tokens); + + DefineEntry def; + + if (!tokens.empty()) + { + if (tokens.begin()->type == CLexer::PREPROCESSOR && tokens.begin()->value == "#") + { + // macro has arguments + _advanceList(tokens); + + if (tokens.empty() || tokens.begin()->value != "(") + { + printErrorMessage("Expected arguments"); + return; + } + _advanceList(tokens); + + 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; + _advanceList(tokens); + if (!tokens.empty() && tokens.begin()->value == ",") + _advanceList(tokens); + argCount++; + } + + if (!tokens.empty()) + { + if (tokens.begin()->value != ")") + { + printErrorMessage("Expected closing parentheses"); + return; + } + _advanceList(tokens); + } + 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) +{ + _advanceList(directive); + if (directive.empty()) + { + printErrorMessage("Expected argument."); + return; + } + + nameOut = directive.begin()->value; + _advanceList(directive); + if (!directive.empty()) + printErrorMessage("Too many arguments."); +} + +void CPreprocessor::_parsePragma(CLexer::TokenList& args) +{ + _advanceList(args); + if (args.empty()) + { + printErrorMessage("Pragmas need arguments."); + return; + } + std::string pragmaName = args.begin()->value; + + _advanceList(args); + + 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); + _advanceList(args); + } + if (!args.empty()) + printErrorMessage("Too many paremeters for pragma."); + + PragmaInstance pi; + pi.name = pragmaName; + pi.text = pragmaArgs; + pi.currentFile = m_currentFile; + pi.currentLine = m_currentFileLines; + pi.rootFile = m_rootFile; + pi.globalLine = m_currentLine; + 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) +{ + _advanceList(args); + if (args.empty()) + { + printErrorMessage("Warnings need messages."); + return; + } + + std::string msg = _expandMessage(defineTable, args); + printWarningMessage(msg); +} + +void CPreprocessor::_parseError(CLexer::TokenList& args, CPreprocessor::DefineTable& defineTable) +{ + _advanceList(args); + if (args.empty()) + { + printErrorMessage("Errors need messages."); + return; + } + std::string msg = _expandMessage(defineTable, args); + printErrorMessage(msg); +} + diff --git a/CPreprocessor.hpp b/CPreprocessor.hpp new file mode 100644 index 0000000..801c28d --- /dev/null +++ b/CPreprocessor.hpp @@ -0,0 +1,76 @@ +#ifndef CPREPROCESSOR_HPP +#define CPREPROCESSOR_HPP + +#include +#include +#include +#include "CLexer.hpp" +#include "CLineTranslator.hpp" + +class CPreprocessor +{ +public: + struct PragmaInstance + { + std::string name; + std::string text; + std::string currentFile; + unsigned int currentLine; + std::string rootFile; + std::string globalLine; + }; + + CPreprocessor(); + typedef std::map ArgSet; + struct DefineEntry + { + CLexer::TokenList tokens; + ArgSet arguments; + }; + + typedef std::map DefineTable; + typedef DefineTable::iterator DefineIterator; + typedef std::map > PragmaMap; + typedef PragmaMap::iterator PragmaIterator; + + void define(const std::string& def); + void undefine(const std::string& def); + void registerPragma(const std::string& name, std::function cb); + + std::string finalizedSource(); + int preprocessFile(const std::string& filename); + int preprocessCode(const std::string& filename, const std::string& code); +private: + std::string _loadSource(const std::string& filename); + void printErrorMessage(const std::string& errMsg); + void printWarningMessage(const std::string& warnMesg); + + void preprocessRecursive(const std::string& filename, const std::string& code, CLexer::TokenList& tokens, DefineTable& defineTable); + + void callPragma(const std::string& name, const PragmaInstance& parms); + void _advanceList(CLexer::TokenList& tokens); + CLexer::TokenIterator _findToken(CLexer::TokenIterator begin, CLexer::TokenIterator end, CLexer::TokenType type); + CLexer::TokenIterator _parseStatement(CLexer::TokenIterator begin, CLexer::TokenIterator end, CLexer::TokenList& dest); + CLexer::TokenIterator _parseDefineArguments(CLexer::TokenIterator begin, CLexer::TokenIterator end, CLexer::TokenList& tokens, std::vector& args); + CLexer::TokenIterator _expandDefine(CLexer::TokenIterator begin, CLexer::TokenIterator end, CLexer::TokenList& tokens, DefineTable& defineTable); + void _parseDefine(DefineTable& defineTable, CLexer::TokenList& tokens); + CLexer::TokenIterator _parseIfDef(CLexer::TokenIterator begin, CLexer::TokenIterator end); + void _parseIf(CLexer::TokenList& directive, std::string& nameOut); + void _parsePragma(CLexer::TokenList& args); + std::string _expandMessage(DefineTable& defineTable, CLexer::TokenList& args); + void _parseWarning(CLexer::TokenList& args, DefineTable& defineTable); + void _parseError(CLexer::TokenList& args, DefineTable& defineTable); + + DefineTable m_applicationDefined; + PragmaMap m_registeredPragmas; + CLineTranslator m_lineTranslator; + CLexer::TokenList m_tokens; + + std::string m_rootFile; + std::string m_currentFile; + unsigned int m_currentLine; + unsigned int m_currentFileLines; + unsigned int m_errorCount; +}; + +#endif // CPREPROCESSOR_HPP diff --git a/main.cpp b/main.cpp new file mode 100644 index 0000000..1ff764f --- /dev/null +++ b/main.cpp @@ -0,0 +1,22 @@ +#include +#include "CLexer.hpp" +#include "CPreprocessor.hpp" +#include +#include +#include + +void printTokenList(CLexer::TokenList& out) +{ + for (const CLexer::Token& token : out) + std::cout << token.value; + std::cout << std::endl; +} + +int main() +{ + CPreprocessor preprocessor; + preprocessor.preprocessFile("main.as"); + std::cout << preprocessor.finalizedSource() << std::endl; + return 0; +} +