| /* |
| Copyright 2019 Alain Dargelas |
| |
| Licensed under the Apache License, Version 2.0 (the "License"); |
| you may not use this file except in compliance with the License. |
| You may obtain a copy of the License at |
| |
| http://www.apache.org/licenses/LICENSE-2.0 |
| |
| Unless required by applicable law or agreed to in writing, software |
| distributed under the License is distributed on an "AS IS" BASIS, |
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| See the License for the specific language governing permissions and |
| limitations under the License. |
| */ |
| |
| /* |
| * File: PreprocessFile.cpp |
| * Author: alain |
| * |
| * Created on February 24, 2017, 9:38 PM |
| */ |
| #include "SymbolTable.h" |
| #include "../CommandLine/CommandLineParser.hpp" |
| #include "../ErrorReporting/ErrorContainer.h" |
| #include "CompilationUnit.h" |
| #include "PreprocessFile.h" |
| #include "CompileSourceFile.h" |
| #include "Compiler.h" |
| #include "../Utils/StringUtils.h" |
| #include "../Cache/PPCache.h" |
| #include "../ErrorReporting/Waiver.h" |
| #include <cstdlib> |
| #include <iostream> |
| #include <regex> |
| #include <algorithm> |
| |
| using namespace std; |
| using namespace SURELOG; |
| |
| #include "../parser/SV3_1aPpLexer.h" |
| #include "../parser/SV3_1aPpParser.h" |
| #include "../parser/SV3_1aPpParserBaseListener.h" |
| using namespace antlr4; |
| #include "../Utils/ParseUtils.h" |
| #include "../Utils/FileUtils.h" |
| #include "antlr4-runtime.h" |
| #include "atn/ParserATNSimulator.h" |
| #include "Parser.h" |
| std::string PreprocessFile::MacroNotDefined = "SURELOG_MACRO_NOT_DEFINED"; |
| std::string PreprocessFile::PP__Line__Marking = "SURELOG__LINE__MARKING"; |
| std::string PreprocessFile::PP__File__Marking = "SURELOG__FILE__MARKING"; |
| |
| #include "SV3_1aPpTreeShapeListener.h" |
| |
| void |
| PreprocessFile::setDebug (int level) |
| { |
| switch (level) |
| { |
| case 0: |
| m_debugPP = false; |
| m_debugPPResult = false; |
| m_debugPPTokens = false; |
| m_debugPPTree = false; |
| m_debugMacro = false; |
| break; |
| case 1: |
| m_debugPP = true; |
| m_debugPPResult = false; |
| m_debugPPTokens = false; |
| m_debugPPTree = false; |
| m_debugMacro = false; |
| break; |
| case 2: |
| m_debugPP = true; |
| m_debugPPResult = false; |
| m_debugPPTokens = true; |
| m_debugPPTree = true; |
| m_debugMacro = false; |
| break; |
| case 3: |
| m_debugPP = true; |
| m_debugPPResult = true; |
| m_debugPPTokens = false; |
| m_debugPPTree = false; |
| m_debugMacro = true; |
| break; |
| case 4: |
| m_debugPP = true; |
| m_debugPPResult = true; |
| m_debugPPTokens = true; |
| m_debugPPTree = true; |
| m_debugMacro = true; |
| break; |
| default: |
| break; |
| } |
| } |
| class PreprocessFile::DescriptiveErrorListener : public ANTLRErrorListener |
| { |
| public: |
| DescriptiveErrorListener(PreprocessFile* pp, std::string filename) : m_pp(pp), m_fileName(filename) {} |
| |
| |
| void syntaxError(Recognizer *recognizer, Token *offendingSymbol, size_t line, |
| size_t charPositionInLine, const std::string &msg, std::exception_ptr e); |
| |
| void reportAmbiguity (Parser *recognizer, const dfa::DFA &dfa, size_t startIndex, size_t stopIndex, bool exact, |
| const antlrcpp::BitSet &ambigAlts, atn::ATNConfigSet *configs); |
| |
| void reportAttemptingFullContext (Parser *recognizer, const dfa::DFA &dfa, size_t startIndex, size_t stopIndex, |
| const antlrcpp::BitSet &conflictingAlts, atn::ATNConfigSet *configs); |
| |
| void reportContextSensitivity (Parser *recognizer, const dfa::DFA &dfa, size_t startIndex, size_t stopIndex, |
| size_t prediction, atn::ATNConfigSet *configs); |
| |
| PreprocessFile* m_pp; |
| std::string m_fileName; |
| std::string m_fileContent; |
| }; |
| |
| |
| void PreprocessFile::DescriptiveErrorListener::syntaxError(Recognizer *recognizer, Token *offendingSymbol, size_t line, |
| size_t charPositionInLine, const std::string &msg, std::exception_ptr e) { |
| SymbolId msgId = m_pp->registerSymbol (msg); |
| |
| |
| if (m_pp->m_macroInfo) |
| { |
| |
| std::string lineText = m_pp->getMacroBody (); |
| for (unsigned int i = 0; i < charPositionInLine; i++) |
| lineText += " "; |
| lineText += "^-- " + m_fileName + ":" + std::to_string (line) + " col:" + std::to_string (charPositionInLine); |
| msgId = m_pp->registerSymbol (msg + "," + lineText); |
| Location loc (m_pp->getMacroInfo()->m_file, m_pp->getMacroInfo()->m_line + line -1, charPositionInLine, msgId); |
| Location extraLoc (m_pp->getIncluderFileId (m_pp->getIncluderLine ()), m_pp->getIncluderLine (), 0, 0); |
| Error err (ErrorDefinition::PP_MACRO_SYNTAX_ERROR, loc, extraLoc); |
| m_pp->addError (err); |
| } |
| else |
| { |
| |
| if (m_fileContent == "") |
| { |
| m_fileContent = FileUtils::getFileContent (m_fileName); |
| } |
| |
| std::string lineText; |
| if (m_fileContent != "") |
| { |
| lineText = StringUtils::getLineInString (m_fileContent, line); |
| if (lineText != "") |
| { |
| if (!strstr (lineText.c_str (), "\n")) |
| { |
| lineText += "\n"; |
| } |
| for (unsigned int i = 0; i < charPositionInLine; i++) |
| lineText += " "; |
| lineText += "^-- " + m_fileName + ":" + std::to_string (line) + " col:" + std::to_string (charPositionInLine); |
| } |
| } |
| |
| Location loc2 (0, 0, 0, m_pp->registerSymbol (lineText)); |
| |
| Location loc (m_pp->getFileId (line), line, charPositionInLine, msgId); |
| Error err (ErrorDefinition::PP_SYNTAX_ERROR, loc, loc2); |
| m_pp->addError (err); |
| } |
| } |
| |
| void |
| PreprocessFile::DescriptiveErrorListener::reportAmbiguity (Parser *recognizer, const dfa::DFA &dfa, size_t startIndex, size_t stopIndex, bool exact, |
| const antlrcpp::BitSet &ambigAlts, atn::ATNConfigSet *configs) { } |
| |
| void |
| PreprocessFile::DescriptiveErrorListener::reportAttemptingFullContext (Parser *recognizer, const dfa::DFA &dfa, size_t startIndex, size_t stopIndex, |
| const antlrcpp::BitSet &conflictingAlts, atn::ATNConfigSet *configs) { } |
| |
| void |
| PreprocessFile::DescriptiveErrorListener::reportContextSensitivity (Parser *recognizer, const dfa::DFA &dfa, size_t startIndex, size_t stopIndex, |
| size_t prediction, atn::ATNConfigSet *configs) { } |
| |
| |
| PreprocessFile::PreprocessFile(SymbolId fileId, CompileSourceFile* csf, SpecialInstructions& instructions, CompilationUnit* comp_unit, Library* library) : |
| m_fileId(fileId), m_library(library), m_result(""), m_macroBody(""), m_includer(NULL), m_includerLine(0), m_compileSourceFile(csf), m_lineCount(0), m_listener(NULL), |
| m_instructions(instructions), m_antlrParserHandler(NULL), m_macroInfo(NULL), m_compilationUnit(comp_unit), m_pauseAppend(false), |
| m_usingCachedVersion(false), m_embeddedMacroCallLine(0), m_embeddedMacroCallFile(0) { |
| setDebug(m_compileSourceFile->m_commandLineParser->getDebugLevel ()); |
| IncludeFileInfo info (1, m_fileId, 1,1); |
| info.m_indexClosing = 0; |
| info.m_indexOpening = 0; |
| getIncludeFileInfo ().push_back (info); |
| } |
| |
| PreprocessFile::PreprocessFile(SymbolId fileId, PreprocessFile* includedIn, unsigned int includerLine, CompileSourceFile* csf, |
| SpecialInstructions& instructions, CompilationUnit* comp_unit, Library* library, |
| std::string macroBody, MacroInfo* macroInfo, unsigned int embeddedMacroCallLine, SymbolId embeddedMacroCallFile): |
| m_fileId(fileId), m_library(library), m_result(""), m_macroBody(macroBody) , m_compileSourceFile(csf), m_lineCount(0), m_listener(NULL), |
| m_instructions(instructions), m_antlrParserHandler(NULL), m_macroInfo(macroInfo), m_compilationUnit(comp_unit), m_pauseAppend(false), |
| m_usingCachedVersion(false), m_embeddedMacroCallLine(embeddedMacroCallLine), m_embeddedMacroCallFile(embeddedMacroCallFile) { |
| setDebug(m_compileSourceFile->m_commandLineParser->getDebugLevel ()); |
| m_includer = includedIn; |
| m_includerLine = includerLine; |
| if (includedIn) |
| { |
| includedIn->m_includes.push_back (this); |
| } |
| } |
| |
| void |
| PreprocessFile::addError (Error& error) |
| { |
| if (!m_instructions.m_mute) |
| getCompileSourceFile()->getErrorContainer ()->addError (error); |
| |
| } |
| |
| const std::string PreprocessFile::getSymbol(SymbolId id) { |
| return getCompileSourceFile()->getSymbolTable()->getSymbol(id); |
| } |
| |
| const std::string PreprocessFile::getFileName(unsigned int line) { |
| return getSymbol(getFileId(line)); |
| } |
| |
| SymbolId PreprocessFile::getMacroSignature() |
| { |
| std::string macroSignature = getSymbol(m_fileId); |
| if (m_macroInfo) |
| { |
| macroSignature += "|" + getSymbol (m_macroInfo->m_file); |
| macroSignature += "|" + std::to_string (m_macroInfo->m_line); |
| } |
| macroSignature += "|" + m_macroBody; |
| SymbolId sigId = registerSymbol (macroSignature); |
| return sigId; |
| } |
| |
| |
| PreprocessFile::PreprocessFile (const PreprocessFile& orig) { } |
| |
| PreprocessFile::~PreprocessFile () { |
| if (m_listener) |
| delete m_listener; |
| } |
| |
| PreprocessFile::AntlrParserHandler::~AntlrParserHandler () |
| { |
| delete m_errorListener; |
| //delete m_pptree; // INVALID MEMORY READ can be seen in AdvancedDebug |
| delete m_ppparser; |
| delete m_pptokens; |
| delete m_pplexer; |
| delete m_inputStream; |
| } |
| |
| bool |
| PreprocessFile::preprocess () |
| { |
| PPCache cache (this); |
| if (cache.restore ()) |
| { |
| m_usingCachedVersion = true; |
| getCompilationUnit()->setCurrentTimeInfo(getFileId(0)); |
| return true; |
| } |
| |
| m_result = ""; |
| std::string fileName = getSymbol (m_fileId); |
| |
| m_antlrParserHandler = getCompileSourceFile()->getAntlrPpHandlerForId ((m_macroBody == "") ? m_fileId : getMacroSignature()); |
| |
| if (m_antlrParserHandler == NULL) |
| { |
| m_antlrParserHandler = new AntlrParserHandler (); |
| if (m_macroBody != "") |
| { |
| if (m_debugPP) |
| { |
| std::cout << "PP PREPROCESS MACRO: " << m_macroBody << endl; |
| } |
| m_antlrParserHandler->m_inputStream = new ANTLRInputStream (m_macroBody); |
| } |
| else |
| { |
| |
| if (m_debugPP) |
| std::cout << "PP PREPROCESS FILE: " << fileName << endl; |
| std::ifstream stream; |
| stream.open (fileName); |
| if (!stream.good ()) |
| { |
| if (m_includer == NULL) |
| { |
| Location loc (m_fileId); |
| Error err (ErrorDefinition::PP_CANNOT_OPEN_FILE, loc); |
| addError (err); |
| } |
| else |
| { |
| Location includeFile (m_includer->m_fileId, m_includerLine, 0, m_fileId); |
| Error err (ErrorDefinition::PP_CANNOT_OPEN_INCLUDE_FILE, includeFile); |
| addError (err); |
| } |
| return false; |
| } |
| try { |
| m_antlrParserHandler->m_inputStream = new ANTLRInputStream (stream); |
| stream.close(); |
| } catch (...) { |
| Location loc(0); |
| if (m_includer == NULL) |
| { |
| Location file (m_fileId); |
| loc = file; |
| } |
| else |
| { |
| Location includeFile (m_includer->m_fileId, m_includerLine, 0, m_fileId); |
| loc = includeFile; |
| } |
| Error err (ErrorDefinition::PP_CANNOT_READ_FILE_CONTENT, loc); |
| addError (err); |
| return false; |
| } |
| |
| } |
| m_antlrParserHandler->m_errorListener = new PreprocessFile::DescriptiveErrorListener(this, (m_macroBody == "") ? fileName : "in macro " + fileName); |
| m_antlrParserHandler->m_pplexer = new SV3_1aPpLexer (m_antlrParserHandler->m_inputStream); |
| m_antlrParserHandler->m_pplexer->removeErrorListeners(); |
| m_antlrParserHandler->m_pplexer->addErrorListener(m_antlrParserHandler->m_errorListener); |
| m_antlrParserHandler->m_pptokens = new CommonTokenStream (m_antlrParserHandler->m_pplexer); |
| m_antlrParserHandler->m_pptokens->fill (); |
| |
| if (m_debugPPTokens) |
| { |
| std::cout << "PP TOKENS: " << std::endl; |
| for (auto token : m_antlrParserHandler->m_pptokens->getTokens ()) |
| { |
| std::cout << token->toString () << std::endl; |
| } |
| } |
| |
| m_antlrParserHandler->m_ppparser = new SV3_1aPpParser (m_antlrParserHandler->m_pptokens); |
| m_antlrParserHandler->m_ppparser->getInterpreter<atn::ParserATNSimulator>()->setPredictionMode (atn::PredictionMode::SLL); |
| m_antlrParserHandler->m_ppparser->removeErrorListeners(); |
| m_antlrParserHandler->m_ppparser->setErrorHandler(std::make_shared<BailErrorStrategy>()); |
| try |
| { |
| m_antlrParserHandler->m_pptree = m_antlrParserHandler->m_ppparser->source_text (); |
| } |
| catch (ParseCancellationException& pex) |
| { |
| m_antlrParserHandler->m_pptokens->reset(); |
| m_antlrParserHandler->m_ppparser->reset(); |
| m_antlrParserHandler->m_ppparser->addErrorListener (m_antlrParserHandler->m_errorListener); |
| m_antlrParserHandler->m_ppparser->setErrorHandler (std::make_shared<DefaultErrorStrategy>()); |
| m_antlrParserHandler->m_ppparser->getInterpreter<atn::ParserATNSimulator>()->setPredictionMode (atn::PredictionMode::LL); |
| m_antlrParserHandler->m_pptree = m_antlrParserHandler->m_ppparser->source_text (); |
| } |
| |
| |
| if (m_debugPPTree) |
| std::cout << "PP TREE: " << m_antlrParserHandler->m_pptree->toStringTree (m_antlrParserHandler->m_ppparser) << std::endl << std::endl; |
| |
| getCompileSourceFile ()->registerAntlrPpHandlerForId ((m_macroBody == "") ? m_fileId : getMacroSignature(), m_antlrParserHandler); |
| } |
| if (m_listener == NULL) |
| m_listener = new SV3_1aPpTreeShapeListener (this,m_instructions); |
| tree::ParseTreeWalker::DEFAULT.walk (m_listener, m_antlrParserHandler->m_pptree); |
| |
| return true; |
| } |
| |
| unsigned int PreprocessFile::getSumLineCount() { |
| unsigned int total = m_lineCount; |
| if (m_includer) |
| total += m_includer->getSumLineCount(); |
| return total; |
| } |
| |
| static unsigned int |
| LinesCount (const std::string& s) |
| { |
| return std::count (s.begin (), s.end (), '\n'); |
| } |
| |
| void PreprocessFile::append(const std::string& s) { |
| if (!m_pauseAppend) |
| { |
| m_lineCount += LinesCount(s); |
| m_result += s; |
| } |
| } |
| |
| void PreprocessFile::recordMacro(const std::string name, unsigned int line, unsigned short int column, const std::string arguments, const std::vector<std::string> tokens) |
| { |
| // *** Argument processing |
| std::string arguments_short = arguments; |
| // Remove ( |
| size_t p = arguments_short.find('('); |
| if (p != string::npos) |
| { |
| arguments_short.erase(p,1); |
| } |
| // Remove ) |
| p = arguments_short.find(')'); |
| if (p != string::npos) |
| { |
| arguments_short.erase(p,1); |
| } |
| // Tokenize args |
| std::vector<std::string> args; |
| StringUtils::tokenize(arguments_short, ",", args); |
| |
| if (m_debugMacro) |
| { |
| std::string body; |
| for (auto token : tokens) |
| { |
| body += token; |
| } |
| std::cout << "PP RECORDING MACRO: " << name << ": | " << body << " | " << endl; |
| } |
| |
| //std::cout << "PP RECORDING MACRO: " << name << ", FILE: " << getSymbol(getFileId(line)) << "" << endl; |
| |
| MacroInfo* macroInfo = new MacroInfo(name, arguments.size() ? MacroInfo::WITH_ARGS : MacroInfo::NO_ARGS, getFileId(line), line, column, args, tokens); |
| m_macros.insert(std::make_pair(name, macroInfo)); |
| m_compilationUnit->registerMacroInfo (name, macroInfo); |
| checkMacroArguments_(name, line, column, args, tokens); |
| } |
| |
| |
| std::string PreprocessFile::reportIncludeInfo() { |
| std::string report; |
| for (auto info : m_includeFileInfo) |
| { |
| std::string type = (info.m_type == 1) ? "in" : "out"; |
| report += std::to_string(info.m_originalLine) + " " + getSymbol(info.m_sectionFile) + " " + std::to_string(info.m_sectionStartLine) + " " + type + "\n"; |
| } |
| |
| return report; |
| } |
| |
| void PreprocessFile::recordMacro(const std::string name, unsigned int line, unsigned short int column, const std::vector<std::string> arguments, const std::vector<std::string> tokens) |
| { |
| MacroInfo* macroInfo = new MacroInfo(name, arguments.size() ? MacroInfo::WITH_ARGS : MacroInfo::NO_ARGS, getFileId(line), line, column, arguments, tokens); |
| m_macros.insert(std::make_pair(name, macroInfo)); |
| m_compilationUnit->registerMacroInfo (name, macroInfo); |
| } |
| |
| void PreprocessFile::checkMacroArguments_(const std::string& name, unsigned int line, unsigned short column, |
| const std::vector<std::string>& arguments, const std::vector<std::string>& tokens) { |
| std::set<std::string> argSet; |
| std::set<std::string> tokenSet; |
| for (auto s : arguments) { argSet.insert(StringUtils::trim(StringUtils::rtrimEqual(StringUtils::trim(s)))); } |
| for (auto s : tokens) { tokenSet.insert(StringUtils::trim(s)); } |
| for (auto s : argSet) |
| { |
| if (tokenSet.find (s) == tokenSet.end () && tokenSet.find ("``" + s + "``") == tokenSet.end () |
| && tokenSet.find (s + "``") == tokenSet.end ()) |
| { |
| Location loc (m_fileId, line, column, registerSymbol (s)); |
| Error err (ErrorDefinition::PP_MACRO_UNUSED_ARGUMENT, loc); |
| addError (err); |
| } |
| } |
| for (unsigned int i = 0; i < tokens.size(); i++) |
| { |
| std::string s1 = tokens[i]; |
| std::string s2 = tokens[i]; |
| bool check = false; |
| if ((s1.find ("``") != std::string::npos) && (s1 != "``")) // ``a`` |
| { |
| s1 = std::regex_replace (s1, std::regex ("``"), ""); |
| s2 = s1; |
| check = true; |
| } |
| else if (s1 == "``") |
| { |
| if (i > 0) |
| s1 = tokens[i - 1]; |
| s1 = StringUtils::trim (s1); |
| s2 = tokens[i + 1]; |
| s2 = StringUtils::trim (s2); |
| check = true; |
| } |
| if (check) |
| { |
| if ((argSet.find (s1) == argSet.end ()) && (argSet.find (s2) == argSet.end ())) |
| { |
| for (auto s :{s1, s2}) |
| { |
| if (argSet.find (s) == argSet.end ()) |
| { |
| if (s.find ("`") != std::string::npos) |
| continue; |
| if (s.find ("//") != std::string::npos) |
| continue; |
| if (!std::isalpha (s[0])) |
| continue; |
| Location loc (m_fileId, line, column, registerSymbol (s)); |
| Error err (ErrorDefinition::PP_MACRO_UNDEFINED_ARGUMENT, loc); |
| addError (err); |
| } |
| } |
| } |
| } |
| } |
| } |
| |
| |
| SymbolId PreprocessFile::getIncluderFileId(unsigned int line) { |
| PreprocessFile* tmp = this; |
| while (tmp->m_includer != NULL) { |
| tmp = tmp->m_includer; |
| } |
| return tmp->getFileId(line); |
| } |
| |
| |
| PreprocessFile* PreprocessFile::getSourceFile() { |
| PreprocessFile* tmp = this; |
| while (tmp->m_includer != NULL) { |
| tmp = tmp->m_includer; |
| } |
| return tmp; |
| } |
| |
| void PreprocessFile::forgetPreprocessor_(PreprocessFile* inc, PreprocessFile* pp) |
| { |
| for (std::vector<PreprocessFile*>::iterator itr = inc->m_includes.begin (); itr != inc->m_includes.end (); itr++) |
| { |
| if ((*itr) == pp) |
| { |
| inc->m_includes.erase (itr); |
| break; |
| } |
| } |
| } |
| |
| SymbolId PreprocessFile::registerSymbol(const std::string symbol) { |
| return getCompileSourceFile()->getSymbolTable()->registerSymbol(symbol); |
| } |
| |
| SymbolId PreprocessFile::getId(const std::string symbol) { |
| return getCompileSourceFile()->getSymbolTable()->getId(symbol); |
| } |
| |
| std::string PreprocessFile::evaluateMacroInstance(const std::string macro_instance, PreprocessFile* callingFile, |
| unsigned int callingLine, |
| SpecialInstructions::CheckLoopInstr checkMacroLoop, |
| SpecialInstructions::AsIsUndefinedMacroInstr asisUndefMacro) |
| { |
| std::string result; |
| //SymbolId macroArgs = registerSymbol (macro_instance); |
| SpecialInstructions instructions (SpecialInstructions::Mute, SpecialInstructions::Mark, SpecialInstructions::Filter, checkMacroLoop, asisUndefMacro); |
| PreprocessFile* pp = new PreprocessFile (0, /*macroArgs,*/ NULL, 0 /*m_includer ? m_includer : callingFile, callingLine*/, m_compileSourceFile, |
| instructions, m_includer ? m_includer->m_compilationUnit : callingFile->m_compilationUnit, callingFile->m_library, macro_instance); |
| if (!pp->preprocess ()) |
| { |
| result = MacroNotDefined; |
| } |
| else |
| { |
| result = pp->getPreProcessedFileContent (); |
| } |
| forgetPreprocessor_ (m_includer ? m_includer : callingFile, pp); |
| delete pp; |
| return result; |
| } |
| |
| |
| std::pair<bool, std::string> PreprocessFile::evaluateMacro_ (const std::string name, std::vector<std::string>& actual_args, PreprocessFile* callingFile, |
| unsigned int callingLine, |
| LoopCheck& loopChecker, |
| MacroInfo* macroInfo, SpecialInstructions& instructions, |
| unsigned int embeddedMacroCallLine, SymbolId embeddedMacroCallFile) |
| { |
| std::string result; |
| bool found = false; |
| std::vector<std::string>& formal_args = macroInfo->m_arguments; |
| // Don't modify the actual tokens of the macro, make a copy... |
| std::vector<std::string> body_tokens = macroInfo->m_tokens; |
| |
| if (instructions.m_check_macro_loop) |
| { |
| bool loop = loopChecker.addEdge (callingFile->m_fileId, getId (name)); |
| if (loop) |
| { |
| std::vector<SymbolId> loop = loopChecker.reportLoop (); |
| for (auto id : loop) |
| { |
| MacroInfo* macroInfo2 = m_compilationUnit->getMacroInfo(getSymbol(id)); |
| if (macroInfo2) |
| { |
| Location loc (macroInfo2->m_file, macroInfo2->m_line, 0, id); |
| Location exloc (macroInfo->m_file, macroInfo->m_line, 0, getId (name)); |
| Error err (ErrorDefinition::PP_RECURSIVE_MACRO_DEFINITION, loc, exloc); |
| addError (err); |
| return std::make_pair (false, getCompileSourceFile ()->getCompiler ()->getSymbolTable ()->getBadSymbol ()); |
| } |
| } |
| } |
| } |
| |
| StringUtils::replaceInTokenVector (body_tokens, "`\"", "\""); |
| StringUtils::replaceInTokenVector (body_tokens, "`\\`\"", "\\\""); |
| |
| // argument substitution |
| |
| for (unsigned int i = 0; i < actual_args.size (); i++) |
| { |
| if (actual_args[i].find ('`') != std::string::npos) |
| { |
| actual_args[i] = evaluateMacroInstance(actual_args[i], callingFile, callingLine, |
| SpecialInstructions::CheckLoop, |
| SpecialInstructions::AsIsUndefinedMacroInstr::ComplainUndefinedMacro); |
| } |
| } |
| |
| if ((actual_args.size() > formal_args.size() && (!m_instructions.m_mute))) |
| { |
| if (formal_args.size() == 0 && (StringUtils::getFirstNonEmptyToken(body_tokens) == "(")) |
| { |
| Location loc (macroInfo->m_file, macroInfo->m_line, macroInfo->m_column + name.size() + 1 , getId (name)); |
| Error err (ErrorDefinition::PP_MACRO_HAS_SPACE_BEFORE_ARGS, loc); |
| addError (err); |
| } |
| else |
| { |
| if (!Waiver::macroArgCheck (name)) |
| { |
| Location loc (callingFile->getFileId (callingLine), callingFile->getLineNb (callingLine), 0, getId (name)); |
| Location arg (0, 0, 0, registerSymbol (std::to_string (actual_args.size ()))); |
| Location def (macroInfo->m_file, macroInfo->m_line, 0, registerSymbol (std::to_string (formal_args.size ()))); |
| std::vector<Location> locs = {arg, def}; |
| Error err (ErrorDefinition::PP_TOO_MANY_ARGS_MACRO, loc, &locs); |
| addError (err); |
| } |
| } |
| } |
| |
| for (unsigned int i = 0; i < formal_args.size (); i++) |
| { |
| std::vector<std::string> formal_arg_default; |
| StringUtils::tokenize (formal_args[i], "=", formal_arg_default); |
| std::string formal = std::regex_replace (formal_arg_default[0], std::regex ("[ \t]*"), ""); |
| bool empty_actual = true; |
| if (i < actual_args.size ()) |
| { |
| for (unsigned int ii = 0; ii < actual_args[i].size (); ii++) |
| { |
| if (actual_args[i][ii] != ' ') |
| { |
| empty_actual = false; |
| break; |
| } |
| } |
| } |
| if (!empty_actual) |
| { |
| if (actual_args[i] == SymbolTable::getEmptyMacroMarker ()) |
| { |
| actual_args[i] = ""; |
| } |
| |
| StringUtils::replaceInTokenVector (body_tokens, { "``", formal , "``" }, actual_args[i]); |
| StringUtils::replaceInTokenVector (body_tokens, "``" + formal + "``", actual_args[i]); |
| StringUtils::replaceInTokenVector (body_tokens, {formal, "``"}, actual_args[i]); |
| StringUtils::replaceInTokenVector (body_tokens, {"``", formal}, actual_args[i]); |
| StringUtils::replaceInTokenVector (body_tokens, {formal, " " , "``"}, actual_args[i]); |
| StringUtils::replaceInTokenVector (body_tokens, formal + "``", actual_args[i]); |
| StringUtils::replaceInTokenVector (body_tokens, formal, actual_args[i]); |
| } |
| else |
| { |
| if (formal_arg_default.size () == 2) |
| { |
| std::string default_val = std::regex_replace (formal_arg_default[1], std::regex ("[ \t]*"), ""); |
| StringUtils::replaceInTokenVector (body_tokens, {"``", formal, "``" }, default_val); |
| StringUtils::replaceInTokenVector (body_tokens, "``" + formal + "``", default_val); |
| StringUtils::replaceInTokenVector (body_tokens, {formal, "``"}, default_val); |
| StringUtils::replaceInTokenVector (body_tokens, {"``", formal}, default_val); |
| StringUtils::replaceInTokenVector (body_tokens, {formal, " ", "``"}, default_val); |
| StringUtils::replaceInTokenVector (body_tokens, formal + "``", default_val); |
| StringUtils::replaceInTokenVector (body_tokens, formal, default_val); |
| } |
| else |
| { |
| StringUtils::replaceInTokenVector (body_tokens,{"``", formal, "``"}, ""); |
| StringUtils::replaceInTokenVector (body_tokens, "``" + formal + "``", ""); |
| StringUtils::replaceInTokenVector (body_tokens,{formal, "``"}, ""); |
| StringUtils::replaceInTokenVector (body_tokens,{"``", formal}, ""); |
| StringUtils::replaceInTokenVector (body_tokens,{formal, " ", "``"}, ""); |
| StringUtils::replaceInTokenVector (body_tokens, formal + "``", ""); |
| StringUtils::replaceInTokenVector (body_tokens, formal, ""); |
| |
| if ((int)i > (int)(((int)actual_args.size()) -1)) |
| { |
| if (!instructions.m_mute) |
| { |
| Location loc (callingFile->getFileId (callingLine), callingFile->getLineNb(callingLine), 0, getId (name)); |
| SymbolId id = registerSymbol (std::to_string (i + 1) + " (" + formal + ")"); |
| Location arg (0, 0, 0, id); |
| Location def (macroInfo->m_file, macroInfo->m_line, 0, id); |
| std::vector<Location> locs = {arg, def}; |
| Error err (ErrorDefinition::PP_MACRO_NO_DEFAULT_VALUE, loc, &locs); |
| addError (err); |
| } |
| } |
| } |
| } |
| } |
| |
| std::string body; |
| for (auto token : body_tokens) |
| { |
| body += token; |
| } |
| |
| // *** Body processing |
| std::string body_short = body; |
| // Replace \\n by \n |
| |
| for (string::iterator itr = body_short.end (); itr != body_short.begin (); itr--) |
| { |
| if ((*itr) == '\n') |
| { |
| if ((itr != body_short.begin ()) && (*(itr - 1) == '\\')) |
| { |
| body_short.erase (itr - 1); |
| } |
| } |
| } |
| |
| // Truncate trailing carriage returns (up to 2) |
| |
| for (int i = 0; i < 2; i++) |
| { |
| if (body_short.size ()) |
| { |
| if (body_short.at (body_short.size () - 1) == '\n') |
| { |
| body_short.erase (body_short.size () - 1); |
| } |
| else |
| { |
| break; |
| } |
| } |
| } |
| |
| // If it is a Multiline macro, insert a \n at the end |
| |
| if (body_short.find ('\n') != string::npos) |
| { |
| body_short.push_back ('\n'); |
| } |
| |
| if (body_short.find ('`') != std::string::npos) |
| { |
| // Recursively resolve macro instantiation within the macro |
| if (m_debugMacro) |
| { |
| const std::string fileName = getSymbol (m_fileId); |
| std::cout << "PP BODY EXPANSION FOR " << name << " in : " << fileName << std::endl; |
| for (auto arg : actual_args) |
| { |
| std::cout << "PP ARG: " << arg << "\n"; |
| } |
| } |
| SymbolId macroId = registerSymbol (name); |
| SpecialInstructions instructions (m_instructions.m_mute, |
| SpecialInstructions::DontMark, |
| SpecialInstructions::Filter, |
| m_instructions.m_check_macro_loop, |
| m_instructions.m_as_is_undefined_macro); |
| PreprocessFile* pp = new PreprocessFile (macroId, callingFile ? callingFile : m_includer |
| , callingLine, m_compileSourceFile, |
| instructions, callingFile ? callingFile->m_compilationUnit : m_includer->m_compilationUnit, |
| callingFile ? callingFile->m_library : m_includer->m_library , |
| body_short, macroInfo, |
| embeddedMacroCallLine, embeddedMacroCallFile); |
| getCompileSourceFile()->registerPP(pp); |
| if (!pp->preprocess ()) |
| { |
| result = MacroNotDefined; |
| } |
| else |
| { |
| std::string pp_result = pp->getPreProcessedFileContent (); |
| |
| if (callingLine && callingFile && !callingFile->isMacroBody ()) |
| { |
| pp_result = std::regex_replace (pp_result, std::regex(PP__File__Marking), "\"" + FileUtils::getFullPath(callingFile->getFileName(callingLine)) + "\"" ); |
| pp_result = std::regex_replace (pp_result, std::regex(PP__Line__Marking), std::to_string (callingLine)); |
| } |
| result = pp_result; |
| found = true; |
| } |
| } |
| else |
| { |
| result = body_short; |
| found = true; |
| } |
| return std::make_pair(found, result); |
| } |
| |
| |
| MacroInfo* PreprocessFile::getMacro(const std::string name) { |
| registerSymbol(name); |
| return m_compilationUnit->getMacroInfo(name); |
| } |
| |
| bool PreprocessFile::deleteMacro (const std::string name, std::set<PreprocessFile*>& visited) { |
| /*SymbolId macroId = */ registerSymbol(name); |
| if (m_debugMacro) |
| std::cout << "PP CALL TO deleteMacro for " << name << std::endl; |
| bool found = false; |
| // Try CommandLine overrides |
| //const std::map<SymbolId,std::string>& defines = m_compileSourceFile->m_commandLineParser->getDefineList(); |
| //std::map<SymbolId,std::string>::const_iterator itMap = defines.find(macroId); |
| // if (itMap != defines.end()) |
| // { |
| // result = (*itMap).second; |
| // found = true; |
| // } |
| |
| // Try local file scope |
| if (found == false) |
| { |
| MacroStorage::iterator itr = m_macros.find (name); |
| if (itr != m_macros.end ()) |
| { |
| m_macros.erase(itr); |
| m_compilationUnit->deleteMacro (name); |
| found = true; |
| } |
| } |
| // Try in included files |
| if (found == false) |
| { |
| for (std::vector<PreprocessFile*>::iterator iitr = m_includes.begin (); iitr != m_includes.end (); iitr++) |
| { |
| PreprocessFile* pFile = *iitr; |
| if (visited.find (pFile) == visited.end ()) |
| { |
| visited.insert (pFile); |
| bool tmp = pFile->deleteMacro (name, visited); |
| if (tmp == true) |
| { |
| found = true; |
| break; |
| } |
| } |
| } |
| } |
| // Try in file that included this file |
| if (found == false) |
| { |
| if (m_includer) |
| { |
| if (visited.find (m_includer) == visited.end ()) |
| { |
| visited.insert (m_includer); |
| bool tmp = m_includer->deleteMacro (name, visited); |
| if (tmp == true) |
| { |
| found = true; |
| } |
| } |
| } |
| } |
| return found; |
| } |
| |
| void PreprocessFile::undefineAllMacros(std::set<PreprocessFile*>& visited) { |
| |
| if (m_debugMacro) |
| std::cout << "PP CALL TO undefineAllMacros" << std::endl; |
| m_macros.clear (); |
| m_compilationUnit->deleteAllMacros(); |
| |
| for (std::vector<PreprocessFile*>::iterator iitr = m_includes.begin (); iitr != m_includes.end (); iitr++) |
| { |
| PreprocessFile* pFile = *iitr; |
| if (visited.find (pFile) == visited.end ()) |
| { |
| visited.insert (pFile); |
| pFile->undefineAllMacros (visited); |
| } |
| } |
| |
| if (m_includer) |
| { |
| if (visited.find (m_includer) == visited.end ()) |
| { |
| visited.insert (m_includer); |
| m_includer->undefineAllMacros(visited); |
| } |
| } |
| } |
| |
| std::string |
| PreprocessFile::getMacro (const std::string name, std::vector<std::string>& arguments, PreprocessFile* callingFile, |
| unsigned int callingLine, LoopCheck& loopChecker, SpecialInstructions& instructions, |
| unsigned int embeddedMacroCallLine, SymbolId embeddedMacroCallFile) |
| { |
| SymbolId macroId = registerSymbol(name); |
| if (m_debugMacro) |
| { |
| std::cout << "PP CALL TO getMacro for " << name << "\n"; |
| for (auto arg : arguments) |
| { |
| std::cout << "PP ARG: " << arg << "\n"; |
| } |
| instructions.print(); |
| } |
| std::string result; |
| bool found = false; |
| // Try CommandLine overrides |
| const std::map<SymbolId,std::string>& defines = m_compileSourceFile->m_commandLineParser->getDefineList(); |
| std::map<SymbolId,std::string>::const_iterator itMap = defines.find(macroId); |
| if (itMap != defines.end()) |
| { |
| result = (*itMap).second; |
| found = true; |
| } |
| |
| // Try local file scope |
| if (found == false) |
| { |
| MacroInfo* info = m_compilationUnit->getMacroInfo(name); |
| if (info) |
| { |
| std::pair<bool, std::string> evalResult = evaluateMacro_(name, arguments, callingFile, callingLine, loopChecker, info, instructions, |
| embeddedMacroCallLine, embeddedMacroCallFile); |
| found = evalResult.first; |
| result = evalResult.second; |
| result = std::regex_replace (result, std::regex ("``"), ""); |
| } |
| } |
| if (found == false) |
| { |
| if (instructions.m_as_is_undefined_macro == SpecialInstructions::AsIsUndefinedMacro) |
| { |
| return "`" + name; |
| } |
| else |
| { |
| return MacroNotDefined; |
| } |
| } |
| else |
| { |
| return result; |
| } |
| |
| } |
| |
| SymbolId |
| PreprocessFile::getFileId (unsigned int line) |
| { |
| const unsigned int size = m_lineTranslationVec.size (); |
| if (isMacroBody() && m_macroInfo) |
| { |
| return m_macroInfo->m_file; |
| } |
| else |
| { |
| if (size) |
| { |
| if (size == 1) |
| { |
| if (line >= m_lineTranslationVec[0].m_originalLine) |
| { |
| return (m_lineTranslationVec[0].m_pretendFileId); |
| } |
| } |
| else |
| { |
| for (unsigned int i = size - 1; i >= 0; i--) |
| { |
| if (line >= m_lineTranslationVec[i].m_originalLine) |
| { |
| return (m_lineTranslationVec[i].m_pretendFileId); |
| } |
| } |
| } |
| return m_fileId; |
| } |
| else |
| { |
| return m_fileId; |
| } |
| } |
| } |
| |
| unsigned int |
| PreprocessFile::getLineNb (unsigned int line) |
| { |
| if (isMacroBody() && m_macroInfo) |
| { |
| return (m_macroInfo->m_line + line -1); |
| } |
| else |
| { |
| if (m_lineTranslationVec.size ()) |
| { |
| for (unsigned int i = m_lineTranslationVec.size () - 1; i >= 0; i--) |
| { |
| if (line >= m_lineTranslationVec[i].m_originalLine) |
| { |
| return (m_lineTranslationVec[i].m_pretendLine + (line - m_lineTranslationVec[i].m_originalLine)); |
| } |
| } |
| return line; |
| } |
| else |
| { |
| return line; |
| } |
| } |
| } |
| |
| |
| std::string PreprocessFile::getPreProcessedFileContent() |
| { |
| // If File is empty (Only CR) return an empty string |
| bool nonEmpty = false; |
| unsigned int pp_result_size = m_result.size (); |
| for (unsigned int i = 0; i < pp_result_size; i++) |
| { |
| if ((m_result[i] != '\n') && (m_result[i] != ' ')) |
| { |
| nonEmpty = true; |
| break; |
| } |
| } |
| if (!nonEmpty) |
| m_result = ""; |
| if (m_debugPPResult) |
| { |
| const std::string fileName = getSymbol(m_fileId); |
| std::string objName = (m_macroBody != "") ? "macro " + m_macroBody : "file " + fileName; |
| std::cout << "PP RESULT for " << objName |
| << " : \nvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv\n" |
| << m_result |
| << "\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n" |
| << std::endl; |
| } |
| return m_result; |
| } |
| |
| PreprocessFile::IfElseStack& PreprocessFile::getStack() { |
| PreprocessFile* tmp = this; |
| while (tmp->m_includer != NULL) { |
| tmp = tmp->m_includer; |
| } |
| //std::cout << "STACK FOR: " << tmp->m_fileName << std::endl; |
| return tmp->m_ifStack; |
| } |
| |
| void |
| PreprocessFile::collectIncludedFiles (std::set<PreprocessFile*>& included) |
| { |
| for (std::vector<PreprocessFile*>::iterator itr = m_includes.begin (); itr != m_includes.end (); itr++) |
| { |
| if (!(*itr)->isMacroBody ()) |
| { |
| included.insert (*itr); |
| } |
| (*itr)->collectIncludedFiles(included); |
| |
| } |
| } |
| |
| void PreprocessFile::saveCache() { |
| if (m_macroBody == "") |
| { |
| if (!m_usingCachedVersion) { |
| PPCache cache(this); |
| cache.save(); |
| } |
| } |
| for (std::vector<PreprocessFile*>::iterator itr = m_includes.begin (); itr != m_includes.end (); itr++) |
| { |
| (*itr)->saveCache(); |
| } |
| } |
| |
| |
| |
| |