| /* | |
| 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: ParseFile.cpp | |
| * Author: alain | |
| * | |
| * Created on February 24, 2017, 10:03 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 "SourceCompile/ParseFile.h" | |
| #include "SourceCompile/AntlrParserHandler.h" | |
| #include <cstdlib> | |
| #include <iostream> | |
| #include "antlr4-runtime.h" | |
| #include "atn/ParserATNSimulator.h" | |
| using namespace std; | |
| using namespace antlr4; | |
| using namespace SURELOG; | |
| #include "parser/SV3_1aLexer.h" | |
| #include "parser/SV3_1aParser.h" | |
| #include "parser/SV3_1aParserBaseListener.h" | |
| #include "SourceCompile/SV3_1aTreeShapeListener.h" | |
| #include "API/SV3_1aPythonListener.h" | |
| using namespace antlr4; | |
| #include "Utils/ParseUtils.h" | |
| #include "Utils/FileUtils.h" | |
| #include "Cache/ParseCache.h" | |
| #include "SourceCompile/AntlrParserErrorListener.h" | |
| #include "Package/Precompiled.h" | |
| #include "Utils/StringUtils.h" | |
| #include "Utils/Timer.h" | |
| ParseFile::ParseFile(SymbolId fileId, SymbolTable* symbolTable, | |
| ErrorContainer* errors) | |
| : m_fileId(fileId), | |
| m_ppFileId(0), | |
| m_compileSourceFile(NULL), | |
| m_compilationUnit(NULL), | |
| m_library(NULL), | |
| m_antlrParserHandler(NULL), | |
| m_listener(NULL), | |
| m_usingCachedVersion(false), | |
| m_keepParserHandler(false), | |
| m_fileContent(NULL), | |
| debug_AstModel(false), | |
| m_parent(NULL), | |
| m_offsetLine(0), | |
| m_symbolTable(symbolTable), | |
| m_errors(errors) { | |
| debug_AstModel = true; | |
| } | |
| ParseFile::ParseFile(SymbolId fileId, CompileSourceFile* csf, | |
| CompilationUnit* compilationUnit, Library* library, | |
| SymbolId ppFileId, bool keepParserHandler) | |
| : m_fileId(fileId), | |
| m_ppFileId(ppFileId), | |
| m_compileSourceFile(csf), | |
| m_compilationUnit(compilationUnit), | |
| m_library(library), | |
| m_antlrParserHandler(NULL), | |
| m_listener(NULL), | |
| m_usingCachedVersion(false), | |
| m_keepParserHandler(keepParserHandler), | |
| m_fileContent(NULL), | |
| debug_AstModel(false), | |
| m_parent(NULL), | |
| m_offsetLine(0), | |
| m_symbolTable(NULL), | |
| m_errors(NULL) { | |
| debug_AstModel = | |
| m_compileSourceFile->getCommandLineParser()->getDebugAstModel(); | |
| } | |
| ParseFile::ParseFile(CompileSourceFile* compileSourceFile, ParseFile* parent, | |
| SymbolId chunkFileId, unsigned int offsetLine) | |
| : m_fileId(parent->m_fileId), | |
| m_ppFileId(chunkFileId), | |
| m_compileSourceFile(compileSourceFile), | |
| m_compilationUnit(parent->m_compilationUnit), | |
| m_library(parent->m_library), | |
| m_antlrParserHandler(NULL), | |
| m_listener(NULL), | |
| m_usingCachedVersion(false), | |
| m_keepParserHandler(parent->m_keepParserHandler), | |
| m_fileContent(parent->m_fileContent), | |
| debug_AstModel(false), | |
| m_parent(parent), | |
| m_offsetLine(offsetLine), | |
| m_symbolTable(NULL), | |
| m_errors(NULL) { | |
| parent->m_children.push_back(this); | |
| } | |
| ParseFile::~ParseFile() { | |
| if (!m_keepParserHandler) delete m_antlrParserHandler; | |
| } | |
| SymbolTable* ParseFile::getSymbolTable() { | |
| return m_symbolTable ? m_symbolTable : m_compileSourceFile->getSymbolTable(); | |
| } | |
| ErrorContainer* ParseFile::getErrorContainer() { | |
| return m_errors ? m_errors : m_compileSourceFile->getErrorContainer(); | |
| } | |
| SymbolId ParseFile::registerSymbol(const std::string symbol) { | |
| return getCompileSourceFile()->getSymbolTable()->registerSymbol(symbol); | |
| } | |
| SymbolId ParseFile::getId(const std::string symbol) { | |
| return getCompileSourceFile()->getSymbolTable()->getId(symbol); | |
| } | |
| const std::string ParseFile::getSymbol(SymbolId id) { | |
| return getCompileSourceFile()->getSymbolTable()->getSymbol(id); | |
| } | |
| const std::string ParseFile::getFileName(unsigned int line) { | |
| return getSymbol(getFileId(line)); | |
| } | |
| void ParseFile::addError(Error& error) { | |
| getCompileSourceFile()->getErrorContainer()->addError(error); | |
| } | |
| SymbolId ParseFile::getFileId(unsigned int line) { | |
| if (!getCompileSourceFile()) return m_fileId; | |
| PreprocessFile* pp = getCompileSourceFile()->getPreprocessor(); | |
| auto& infos = pp->getIncludeFileInfo(); | |
| if (line == 0) return m_fileId; | |
| if (infos.size()) { | |
| bool inRange = false; | |
| unsigned int indexOpeningRange = 0; | |
| for (unsigned int i = infos.size() - 1; i >= 0; i--) { | |
| // if (!inRange) | |
| // { | |
| if ((line >= infos[i].m_originalLine) && (infos[i].m_type == 2)) { | |
| SymbolId fileId = getSymbolTable()->registerSymbol( | |
| pp->getSymbol(infos[i].m_sectionFile)); | |
| return (fileId); | |
| } | |
| // } | |
| if (infos[i].m_type == 2) { | |
| if (!inRange) { | |
| inRange = true; | |
| indexOpeningRange = infos[i].m_indexOpening; | |
| } | |
| } else { | |
| if (inRange) { | |
| if (i == indexOpeningRange) inRange = false; | |
| } | |
| } | |
| if ((line >= infos[i].m_originalLine) && (infos[i].m_type == 1) && | |
| (line < infos[infos[i].m_indexClosing].m_originalLine)) { | |
| SymbolId fileId = getSymbolTable()->registerSymbol( | |
| pp->getSymbol(infos[i].m_sectionFile)); | |
| return (fileId); | |
| } | |
| if (i == 0) break; | |
| } | |
| return m_fileId; | |
| } else { | |
| return m_fileId; | |
| } | |
| } | |
| unsigned int ParseFile::getLineNb(unsigned int line) { | |
| if (!getCompileSourceFile()) return line; | |
| PreprocessFile* pp = getCompileSourceFile()->getPreprocessor(); | |
| auto& infos = pp->getIncludeFileInfo(); | |
| if (line == 0) return 1; | |
| if (infos.size()) { | |
| bool inRange = false; | |
| unsigned int indexOpeningRange = 0; | |
| for (unsigned int i = infos.size() - 1; i >= 0; i--) { | |
| // if (!inRange) | |
| // { | |
| if ((line >= infos[i].m_originalLine) && (infos[i].m_type == 2)) { | |
| return (infos[i].m_sectionStartLine + (line - infos[i].m_originalLine)); | |
| } | |
| // } | |
| if (infos[i].m_type == 2) { | |
| if (!inRange) { | |
| inRange = true; | |
| indexOpeningRange = infos[i].m_indexOpening; | |
| } | |
| } else { | |
| if (inRange) { | |
| if (i == indexOpeningRange) inRange = false; | |
| } | |
| } | |
| if ((line >= infos[i].m_originalLine) && (infos[i].m_type == 1) && | |
| (line < infos[infos[i].m_indexClosing].m_originalLine)) { | |
| return (infos[i].m_sectionStartLine + (line - infos[i].m_originalLine)); | |
| } | |
| if (i == 0) break; | |
| } | |
| return line; | |
| } else { | |
| return line; | |
| } | |
| } | |
| bool ParseFile::parseOneFile_(std::string fileName, unsigned int lineOffset) { | |
| Timer tmr; | |
| AntlrParserHandler* antlrParserHandler = new AntlrParserHandler(); | |
| m_antlrParserHandler = antlrParserHandler; | |
| std::ifstream stream; | |
| stream.open(fileName); | |
| if (!stream.good()) { | |
| SymbolId fileId = registerSymbol(fileName); | |
| Location ppfile(fileId); | |
| Error err(ErrorDefinition::PA_CANNOT_OPEN_FILE, ppfile); | |
| addError(err); | |
| return false; | |
| } | |
| antlrParserHandler->m_inputStream = new ANTLRInputStream(stream); | |
| antlrParserHandler->m_errorListener = | |
| new AntlrParserErrorListener(this, false, lineOffset, fileName); | |
| antlrParserHandler->m_lexer = | |
| new SV3_1aLexer(antlrParserHandler->m_inputStream); | |
| antlrParserHandler->m_lexer->removeErrorListeners(); | |
| antlrParserHandler->m_lexer->addErrorListener( | |
| antlrParserHandler->m_errorListener); | |
| antlrParserHandler->m_tokens = | |
| new CommonTokenStream(antlrParserHandler->m_lexer); | |
| antlrParserHandler->m_tokens->fill(); | |
| if (getCompileSourceFile()->getCommandLineParser()->profile()) { | |
| // m_profileInfo += "Tokenizer: " + std::to_string (tmr.elapsed_rounded ()) | |
| // + " " + fileName + "\n"; | |
| tmr.reset(); | |
| } | |
| antlrParserHandler->m_parser = new SV3_1aParser(antlrParserHandler->m_tokens); | |
| m_antlrParserHandler->m_parser->getInterpreter<atn::ParserATNSimulator>() | |
| ->setPredictionMode(atn::PredictionMode::SLL); | |
| m_antlrParserHandler->m_parser->setErrorHandler( | |
| std::make_shared<BailErrorStrategy>()); | |
| m_antlrParserHandler->m_parser->removeErrorListeners(); | |
| try { | |
| m_antlrParserHandler->m_tree = | |
| m_antlrParserHandler->m_parser->top_level_rule(); | |
| if (getCompileSourceFile()->getCommandLineParser()->profile()) { | |
| m_profileInfo += | |
| "SSL Parsing: " + StringUtils::to_string(tmr.elapsed_rounded()) + | |
| " " + fileName + "\n"; | |
| tmr.reset(); | |
| } | |
| } catch (ParseCancellationException& pex) { | |
| m_antlrParserHandler->m_tokens->reset(); | |
| m_antlrParserHandler->m_parser->reset(); | |
| m_antlrParserHandler->m_parser->setErrorHandler( | |
| std::make_shared<DefaultErrorStrategy>()); | |
| antlrParserHandler->m_parser->removeErrorListeners(); | |
| antlrParserHandler->m_parser->addErrorListener( | |
| antlrParserHandler->m_errorListener); | |
| antlrParserHandler->m_parser->getInterpreter<atn::ParserATNSimulator>() | |
| ->setPredictionMode(atn::PredictionMode::LL); | |
| antlrParserHandler->m_tree = antlrParserHandler->m_parser->top_level_rule(); | |
| if (getCompileSourceFile()->getCommandLineParser()->profile()) { | |
| m_profileInfo += | |
| "LL Parsing: " + StringUtils::to_string(tmr.elapsed_rounded()) + | |
| " " + fileName + "\n"; | |
| tmr.reset(); | |
| } | |
| } | |
| stream.close(); | |
| return true; | |
| } | |
| std::string ParseFile::getProfileInfo() { | |
| std::string profile; | |
| profile = m_profileInfo; | |
| for (unsigned int i = 0; i < m_children.size(); i++) | |
| profile += m_children[i]->m_profileInfo; | |
| return profile; | |
| } | |
| bool ParseFile::parse() { | |
| Precompiled* prec = Precompiled::getSingleton(); | |
| std::string root = this->getPpFileName(); | |
| root = StringUtils::getRootFileName(root); | |
| bool precompiled = false; | |
| if (prec->isFilePrecompiled(root)) precompiled = true; | |
| if (m_children.size() == 0) { | |
| ParseCache cache(this); | |
| if (cache.restore()) { | |
| m_usingCachedVersion = true; | |
| if (debug_AstModel && !precompiled) | |
| std::cout << m_fileContent->printObjects(); | |
| return true; | |
| } | |
| } else { | |
| bool ok = true; | |
| for (unsigned int i = 0; i < m_children.size(); i++) { | |
| ParseCache cache(m_children[i]); | |
| if (cache.restore()) { | |
| m_children[i]->m_fileContent->setParent(m_fileContent); | |
| m_usingCachedVersion = true; | |
| if (debug_AstModel && !precompiled) | |
| std::cout << m_children[i]->m_fileContent->printObjects(); | |
| } else { | |
| ok = false; | |
| } | |
| } | |
| if (ok) { | |
| return true; | |
| } | |
| } | |
| // This is not a parent Parser object | |
| if (m_children.size() == 0) { | |
| // std::cout << std::endl << "Parsing " << getSymbol(m_ppFileId) << " Line: | |
| // " << m_offsetLine << std::endl << std::flush; | |
| parseOneFile_(getSymbol(m_ppFileId), m_offsetLine); | |
| // m_listener = new SV3_1aTreeShapeListener (this, | |
| // m_antlrParserHandler->m_tokens, m_offsetLine); | |
| // tree::ParseTreeWalker::DEFAULT.walk (m_listener, | |
| // m_antlrParserHandler->m_tree); std::cout << std::endl << "End Parsing " << | |
| // getSymbol(m_ppFileId) << " Line: " << m_offsetLine << std::endl << | |
| // std::flush; | |
| } | |
| // This is either a parent Parser object of this Parser object has no parent | |
| if ((m_children.size() != 0) || (m_parent == NULL)) { | |
| if ((m_parent == NULL) && (m_children.size() == 0)) { | |
| Timer tmr; | |
| m_listener = new SV3_1aTreeShapeListener( | |
| this, m_antlrParserHandler->m_tokens, m_offsetLine); | |
| tree::ParseTreeWalker::DEFAULT.walk(m_listener, | |
| m_antlrParserHandler->m_tree); | |
| if (debug_AstModel && !precompiled) | |
| std::cout << m_fileContent->printObjects(); | |
| if (getCompileSourceFile()->getCommandLineParser()->profile()) { | |
| // m_profileInfo += "AST Walking: " + std::to_string | |
| // (tmr.elapsed_rounded ()) + "\n"; | |
| tmr.reset(); | |
| } | |
| ParseCache cache(this); | |
| if (!cache.save()) { | |
| return false; | |
| } | |
| } | |
| if (m_children.size() != 0) { | |
| for (unsigned int i = 0; i < m_children.size(); i++) { | |
| if (m_children[i]->m_antlrParserHandler) { | |
| // Only visit the chunks that got re-parsed | |
| // TODO: Incrementally regenerate the FileContent | |
| m_children[i]->m_fileContent->setParent(m_fileContent); | |
| m_children[i]->m_listener = new SV3_1aTreeShapeListener( | |
| m_children[i], m_children[i]->m_antlrParserHandler->m_tokens, | |
| m_children[i]->m_offsetLine); | |
| Timer tmr; | |
| tree::ParseTreeWalker::DEFAULT.walk( | |
| m_children[i]->m_listener, | |
| m_children[i]->m_antlrParserHandler->m_tree); | |
| if (getCompileSourceFile()->getCommandLineParser()->profile()) { | |
| // m_profileInfo += "For file " + getSymbol | |
| // (m_children[i]->m_ppFileId) + ", AST Walking took" + | |
| // std::to_string (tmr.elapsed_rounded ()) + "\n"; | |
| tmr.reset(); | |
| } | |
| if (debug_AstModel && !precompiled) | |
| std::cout << m_children[i]->m_fileContent->printObjects(); | |
| ParseCache cache(m_children[i]); | |
| if (!cache.save()) { | |
| return false; | |
| } | |
| } | |
| } | |
| } | |
| } | |
| return true; | |
| } |