From 4c93b47b2aeefc628b6b596d7bb295807fca12db Mon Sep 17 00:00:00 2001 From: Phillip Stephens Date: Thu, 7 May 2015 18:10:22 -0700 Subject: [PATCH] Initial Commit --- AngelScriptPreprocessor.pro | 15 + CLexer.cpp | 300 ++++++++++++++++++ CLexer.hpp | 60 ++++ CLineTranslator.cpp | 42 +++ CLineTranslator.hpp | 36 +++ CPreprocessor.cpp | 598 ++++++++++++++++++++++++++++++++++++ CPreprocessor.hpp | 76 +++++ main.cpp | 22 ++ 8 files changed, 1149 insertions(+) create mode 100644 AngelScriptPreprocessor.pro create mode 100644 CLexer.cpp create mode 100644 CLexer.hpp create mode 100644 CLineTranslator.cpp create mode 100644 CLineTranslator.hpp create mode 100644 CPreprocessor.cpp create mode 100644 CPreprocessor.hpp create mode 100644 main.cpp 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; +} +