blob: 75a4f0d4669e84e0d3067b129fc6b11a6751768c [file] [log] [blame]
/*
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 "SourceCompile/SymbolTable.h"
#include "CommandLine/CommandLineParser.h"
#include "ErrorReporting/ErrorContainer.h"
#include "SourceCompile/CompilationUnit.h"
#include "SourceCompile/PreprocessFile.h"
#include "SourceCompile/CompileSourceFile.h"
#include "SourceCompile/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"
#include "SourceCompile/SV3_1aPpTreeShapeListener.h"
#include "Utils/Timer.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";
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 = true;
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() {
if (getCompileSourceFile()->getCommandLineParser()->parseOnly())
return true;
Timer tmr;
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;
}
// Remove ^M (DOS) from text file
std::string text;
char c = stream.get();
while (stream.good()) {
if (c != 0x0D)
text+=c;
c = stream.get();
}
stream.close();
try {
m_antlrParserHandler->m_inputStream = new ANTLRInputStream(text);
} 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 (getCompileSourceFile()->getCommandLineParser()->profile()) {
// m_profileInfo += "Tokenizer: " + std::to_string (tmr.elapsed_rounded ())
// + " " + fileName + "\n";
tmr.reset();
}
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();
if (getCompileSourceFile()->getCommandLineParser()->profile()) {
m_profileInfo +=
"PP SSL Parsing: " + StringUtils::to_string(tmr.elapsed_rounded()) +
" " + fileName + "\n";
tmr.reset();
}
} 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 (getCompileSourceFile()->getCommandLineParser()->profile()) {
m_profileInfo +=
"PP LL Parsing: " + StringUtils::to_string(tmr.elapsed_rounded()) +
" " + fileName + "\n";
tmr.reset();
}
}
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 (instructions.m_evaluate == SpecialInstructions::Evaluate) {
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("``"), "");
}
} else {
if (info) {
found = true;
result = "";
}
}
}
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 (getCompileSourceFile()->getCommandLineParser()->parseOnly())
return;
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();
}
}