mirror of
https://github.com/eclipse-openvehicle-api/openvehicle-api.git
synced 2026-02-05 15:18:45 +00:00
404 lines
13 KiB
C++
404 lines
13 KiB
C++
|
|
#include "parser.h"
|
||
|
|
#include "exception.h"
|
||
|
|
|
||
|
|
CParser::CParser(const char* szCode, const CIdlCompilerEnvironment& renv /*= CIdlCompilerEnvironment()*/) :
|
||
|
|
CPreprocessor(*this), m_lexer(this, renv.CaseSensitiveTypeExtension()), m_environment(renv)
|
||
|
|
{
|
||
|
|
// Create soure code context object
|
||
|
|
CContextPtr ptrContext = std::make_shared<CContext>(szCode);
|
||
|
|
|
||
|
|
// Add additional keywords to the lexer.
|
||
|
|
if (renv.InterfaceTypeExtension())
|
||
|
|
{
|
||
|
|
m_lexer.AddKeyword("interface_id");
|
||
|
|
m_lexer.AddKeyword("interface_t");
|
||
|
|
m_lexer.AddKeyword("null");
|
||
|
|
}
|
||
|
|
if (renv.ExceptionTypeExtension())
|
||
|
|
{
|
||
|
|
m_lexer.AddKeyword("exception_id");
|
||
|
|
}
|
||
|
|
if (renv.PointerTypeExtension())
|
||
|
|
{
|
||
|
|
m_lexer.AddKeyword("pointer");
|
||
|
|
}
|
||
|
|
if (renv.UnicodeExtension())
|
||
|
|
{
|
||
|
|
m_lexer.AddKeyword("char16");
|
||
|
|
m_lexer.AddKeyword("char32");
|
||
|
|
m_lexer.AddKeyword("u8string");
|
||
|
|
m_lexer.AddKeyword("u16string");
|
||
|
|
m_lexer.AddKeyword("u32string");
|
||
|
|
}
|
||
|
|
|
||
|
|
// Parse the source
|
||
|
|
ParserPrepare(ptrContext);
|
||
|
|
}
|
||
|
|
|
||
|
|
CParser::CParser(const std::filesystem::path& rpath, const CIdlCompilerEnvironment& renv /*= CIdlCompilerEnvironment()*/) :
|
||
|
|
CPreprocessor(*this), m_lexer(this, renv.CaseSensitiveTypeExtension()), m_environment(renv), m_pathFile(rpath)
|
||
|
|
{
|
||
|
|
// Create soure code context object
|
||
|
|
CContextPtr ptrContext = std::make_shared<CContext>(rpath);
|
||
|
|
|
||
|
|
// Add additional keywords to the lexer.
|
||
|
|
if (renv.InterfaceTypeExtension())
|
||
|
|
{
|
||
|
|
m_lexer.AddKeyword("interface_id");
|
||
|
|
m_lexer.AddKeyword("interface_t");
|
||
|
|
m_lexer.AddKeyword("null");
|
||
|
|
}
|
||
|
|
if (renv.ExceptionTypeExtension())
|
||
|
|
{
|
||
|
|
m_lexer.AddKeyword("exception_id");
|
||
|
|
}
|
||
|
|
if (renv.PointerTypeExtension())
|
||
|
|
{
|
||
|
|
m_lexer.AddKeyword("pointer");
|
||
|
|
}
|
||
|
|
if (renv.UnicodeExtension())
|
||
|
|
{
|
||
|
|
m_lexer.AddKeyword("char16");
|
||
|
|
m_lexer.AddKeyword("char32");
|
||
|
|
m_lexer.AddKeyword("u8string");
|
||
|
|
m_lexer.AddKeyword("u16string");
|
||
|
|
m_lexer.AddKeyword("u32string");
|
||
|
|
}
|
||
|
|
|
||
|
|
// Parse the source
|
||
|
|
ParserPrepare(ptrContext);
|
||
|
|
}
|
||
|
|
|
||
|
|
sdv::interface_t CParser::GetInterface(sdv::interface_id idInterface)
|
||
|
|
{
|
||
|
|
// Own interfaces first
|
||
|
|
if (idInterface == sdv::GetInterfaceId<sdv::IInterfaceAccess>())
|
||
|
|
return static_cast<sdv::IInterfaceAccess*>(this);
|
||
|
|
if (idInterface == sdv::GetInterfaceId<sdv::idl::ICompilerInfo>())
|
||
|
|
return static_cast<sdv::idl::ICompilerInfo*>(this);
|
||
|
|
|
||
|
|
// Then ask the environment
|
||
|
|
sdv::interface_t ifc = m_environment.GetInterface(idInterface);
|
||
|
|
|
||
|
|
// Then as the root
|
||
|
|
if (ifc == nullptr && m_ptrRoot != nullptr)
|
||
|
|
ifc = m_ptrRoot->GetInterface(idInterface);
|
||
|
|
|
||
|
|
return ifc;
|
||
|
|
}
|
||
|
|
|
||
|
|
sdv::u8string CParser::GetFilePath() const
|
||
|
|
{
|
||
|
|
return m_pathFile.u8string();
|
||
|
|
}
|
||
|
|
|
||
|
|
sdv::u8string CParser::GetOutputDir() const
|
||
|
|
{
|
||
|
|
return m_environment.GetOutputDir().generic_u8string();
|
||
|
|
}
|
||
|
|
|
||
|
|
const CLexer& CParser::GetLexer() const
|
||
|
|
{
|
||
|
|
return m_lexer;
|
||
|
|
}
|
||
|
|
|
||
|
|
void CParser::IncludeFile(const std::filesystem::path& rpath, bool bLocal)
|
||
|
|
{
|
||
|
|
// Create soure code context object
|
||
|
|
CContextPtr ptrContext = std::make_shared<CContext>(rpath,
|
||
|
|
bLocal ? sdv::idl::IEntityContext::ELocation::local_include : sdv::idl::IEntityContext::ELocation::global_include);
|
||
|
|
|
||
|
|
// Prepare for parsing the file
|
||
|
|
ParserPrepare(ptrContext);
|
||
|
|
}
|
||
|
|
|
||
|
|
CIdlCompilerEnvironment& CParser::GetEnvironment()
|
||
|
|
{
|
||
|
|
return m_environment;
|
||
|
|
}
|
||
|
|
|
||
|
|
CToken CParser::GetToken()
|
||
|
|
{
|
||
|
|
// Check the token cache.
|
||
|
|
if (!m_lstCacheTokenList.empty())
|
||
|
|
{
|
||
|
|
CToken token = m_lstCacheTokenList.front();
|
||
|
|
m_lstCacheTokenList.pop_front();
|
||
|
|
return token;
|
||
|
|
}
|
||
|
|
|
||
|
|
// No token in the cache. Get the token from the lexer.
|
||
|
|
CToken token = GetTokenFromLexer(false);
|
||
|
|
|
||
|
|
return token;
|
||
|
|
}
|
||
|
|
|
||
|
|
CToken CParser::GetLastValidToken() const
|
||
|
|
{
|
||
|
|
return m_lexer.GetLastValidToken();
|
||
|
|
}
|
||
|
|
|
||
|
|
CToken CParser::PeekToken(size_t nIndex /*= 0*/)
|
||
|
|
{
|
||
|
|
CToken token;
|
||
|
|
|
||
|
|
// Check the token cache. If not large enough, fill it up.
|
||
|
|
while (m_lstCacheTokenList.size() <= nIndex)
|
||
|
|
{
|
||
|
|
token = GetTokenFromLexer(true);
|
||
|
|
if (!token) return token;
|
||
|
|
m_lstCacheTokenList.push_back(token);
|
||
|
|
}
|
||
|
|
|
||
|
|
CTokenList::iterator itPos = m_lstCacheTokenList.begin();
|
||
|
|
for (size_t nCnt = 0; nCnt < nIndex; ++nCnt) ++itPos;
|
||
|
|
token = *itPos;
|
||
|
|
return token;
|
||
|
|
}
|
||
|
|
|
||
|
|
CTokenList CParser::GetComments()
|
||
|
|
{
|
||
|
|
// If there are no comments, request the next token from the lexer (peeking only); this will request comments.
|
||
|
|
if (m_lstComments.empty())
|
||
|
|
PeekToken();
|
||
|
|
return m_lstComments;
|
||
|
|
}
|
||
|
|
|
||
|
|
void CParser::PrependToken(const CToken& rToken)
|
||
|
|
{
|
||
|
|
// Add the token at the front of to the cache list
|
||
|
|
if (!rToken) return;
|
||
|
|
m_lstCacheTokenList.push_front(rToken);
|
||
|
|
}
|
||
|
|
|
||
|
|
void CParser::SkipAdjacentComments()
|
||
|
|
{
|
||
|
|
PeekToken();
|
||
|
|
uint32_t uiLine = 0;
|
||
|
|
while (!m_lstComments.empty())
|
||
|
|
{
|
||
|
|
const CToken token = m_lstComments.front();
|
||
|
|
if (!uiLine || token.GetLine() <= uiLine + 1)
|
||
|
|
{
|
||
|
|
uiLine = token.GetLine();
|
||
|
|
m_lstComments.pop_front();
|
||
|
|
}
|
||
|
|
else
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
CParser& CParser::LexicalCheck()
|
||
|
|
{
|
||
|
|
while (GetToken());
|
||
|
|
return *this;
|
||
|
|
}
|
||
|
|
|
||
|
|
CParser& CParser::Parse()
|
||
|
|
{
|
||
|
|
// Stack is empty if there is no code available (or everything was parsed already). This is not an error.
|
||
|
|
if (m_stackCode.empty()) return *this;
|
||
|
|
|
||
|
|
// Create the root entity.
|
||
|
|
m_ptrRoot = std::make_shared<CRootEntity>(*this, m_stackCode.top());
|
||
|
|
CRootEntity* pRoot = m_ptrRoot->Get<CRootEntity>();
|
||
|
|
if (!pRoot) throw CCompileException("Internal error: cannot get access to the root object.");
|
||
|
|
|
||
|
|
try
|
||
|
|
{
|
||
|
|
// Parse the root entity.
|
||
|
|
pRoot->Process();
|
||
|
|
}
|
||
|
|
catch (sdv::idl::XCompileError& rexcept)
|
||
|
|
{
|
||
|
|
// Use the class for easy access.
|
||
|
|
CCompileException exception(rexcept);
|
||
|
|
|
||
|
|
// Add the line number if not existing
|
||
|
|
if (!exception.GetLineNo())
|
||
|
|
exception.SetLocation(m_lexer.GetLastValidToken());
|
||
|
|
|
||
|
|
// Add the path of the code causing the exception to the exception.
|
||
|
|
if (exception.GetPath().empty())
|
||
|
|
exception.SetPath(m_stackCode.empty() ? m_pathFile : m_stackCode.top()->Source().GetPathRef());
|
||
|
|
|
||
|
|
// Assign the changed values. Needed to keep the additional information that the derived class might have.
|
||
|
|
rexcept = exception;
|
||
|
|
|
||
|
|
// Rethrow the original derived class.
|
||
|
|
throw;
|
||
|
|
}
|
||
|
|
|
||
|
|
return *this;
|
||
|
|
}
|
||
|
|
|
||
|
|
const CRootEntity* CParser::Root() const
|
||
|
|
{
|
||
|
|
return m_ptrRoot ? m_ptrRoot->Get<CRootEntity>() : nullptr;
|
||
|
|
}
|
||
|
|
|
||
|
|
std::string CParser::GenerateAnonymousEntityName(const std::string& rssPrefix)
|
||
|
|
{
|
||
|
|
// Find the prefix in the map
|
||
|
|
auto itPrefix = m_mapAutoNameCount.find(rssPrefix);
|
||
|
|
if (itPrefix == m_mapAutoNameCount.end()) // Prefix not know; add a new prefix
|
||
|
|
itPrefix = m_mapAutoNameCount.emplace(rssPrefix, 0).first;
|
||
|
|
return rssPrefix + "_" + std::to_string(itPrefix->second++);
|
||
|
|
}
|
||
|
|
|
||
|
|
std::list<CParser::SMetaToken> CParser::GetAndRemoveMeta()
|
||
|
|
{
|
||
|
|
return std::move(m_lstMeta);
|
||
|
|
}
|
||
|
|
|
||
|
|
void CParser::ParserPrepare(CContextPtr& rptrContext)
|
||
|
|
{
|
||
|
|
// Check whether the file was already added to the parser (do not include the same file multipe times).
|
||
|
|
if (m_setProcessedFiles.find(rptrContext->Source().GetPathRef()) != m_setProcessedFiles.end()) return;
|
||
|
|
m_setProcessedFiles.insert(rptrContext->Source().GetPathRef());
|
||
|
|
|
||
|
|
// Check for maximum parsing depth.
|
||
|
|
if (m_stackCode.size() >= m_nMaxDepth)
|
||
|
|
throw CCompileException("Passed maximum amount of nested files - circular inclusion of files?");
|
||
|
|
|
||
|
|
// Add the source code context to the stack
|
||
|
|
m_stackCode.push(rptrContext);
|
||
|
|
|
||
|
|
// A new file will be processed. Enable preprocessing on the lexer.
|
||
|
|
m_lexer.EnablePreprocProcessing();
|
||
|
|
}
|
||
|
|
|
||
|
|
void CParser::InsertWhitespace(const CToken&)
|
||
|
|
{}
|
||
|
|
|
||
|
|
void CParser::InsertComment(const CToken& rtoken)
|
||
|
|
{
|
||
|
|
// Assign the context to the token (this prevents the context from unloading when the token is still in use).
|
||
|
|
CToken tokenComment = rtoken;
|
||
|
|
tokenComment.SetContext(m_stackCode.top());
|
||
|
|
|
||
|
|
// Comments can be invalid after the next token is read (in case the next token is in another file). Cache the comment
|
||
|
|
// text by requesting the string.
|
||
|
|
m_lstComments.push_back(std::move(tokenComment));
|
||
|
|
}
|
||
|
|
|
||
|
|
void CParser::ProcessPreprocDirective(CCodePos& rCode)
|
||
|
|
{
|
||
|
|
// Get the current parse context
|
||
|
|
CContextPtr ptrContext = m_stackCode.top();
|
||
|
|
|
||
|
|
// Callback inserting preprocessor directives
|
||
|
|
CToken tokenMeta = ProcessPreproc(rCode, ptrContext);
|
||
|
|
if (tokenMeta)
|
||
|
|
{
|
||
|
|
// Check for adjacent preceding comments
|
||
|
|
uint32_t uiLine = tokenMeta.GetLine();
|
||
|
|
CTokenList lstMetaComments;
|
||
|
|
while (m_lstComments.size())
|
||
|
|
{
|
||
|
|
uint32_t uiCommentLine = m_lstComments.back().GetEndLine();
|
||
|
|
if (uiCommentLine < uiLine - 1) break; // Not connecting to the meta data
|
||
|
|
if (uiCommentLine > uiLine) break; // Not preceding the meta data
|
||
|
|
uiLine = uiCommentLine;
|
||
|
|
lstMetaComments.push_front(m_lstComments.back());
|
||
|
|
m_lstComments.pop_back();
|
||
|
|
}
|
||
|
|
|
||
|
|
m_lstMeta.push_back(SMetaToken{ tokenMeta, lstMetaComments });
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
CToken CParser::GetTokenFromLexer(bool /*bPeekOnly*/)
|
||
|
|
{
|
||
|
|
// Clear the comments
|
||
|
|
m_lstComments.clear();
|
||
|
|
|
||
|
|
// Check for code
|
||
|
|
if (m_stackCode.empty())
|
||
|
|
return CToken();
|
||
|
|
|
||
|
|
// Get the token from the code
|
||
|
|
CToken token;
|
||
|
|
try
|
||
|
|
{
|
||
|
|
// Get a token as long as no token was found or the stack
|
||
|
|
while (!m_stackCode.empty())
|
||
|
|
{
|
||
|
|
CCodePos &rCode = m_stackCode.top()->Code();
|
||
|
|
|
||
|
|
// If the current position is not part of the macro expansion, reset the set of macros used in a expansion.
|
||
|
|
bool bInMacroExpansion = rCode.CurrentPositionInMacroExpansion();
|
||
|
|
|
||
|
|
// Get a token. This will automatically trigger preprocessing control.
|
||
|
|
// Assign the context to the token (this prevents the context from unloading when the token is still in use).
|
||
|
|
token = m_lexer.GetToken(rCode, m_stackCode.top());
|
||
|
|
|
||
|
|
// Should this code be processed. If not, skip a line and try again...
|
||
|
|
if (!CurrentSectionEnabled())
|
||
|
|
{
|
||
|
|
// Check for EOF
|
||
|
|
if (token) // Not EOF
|
||
|
|
m_lexer.SkipLine(rCode);
|
||
|
|
else // EOF, process the next file
|
||
|
|
{
|
||
|
|
// Check for EOF. If so, close the file.
|
||
|
|
// ATTENTION Do not use rCode, since the stack might have changed through a preprocessor include directive.
|
||
|
|
if (m_stackCode.top()->Code().HasEOF())
|
||
|
|
{
|
||
|
|
// Trigger final preproc processing (to check for correct conditional inclusion boundaries).
|
||
|
|
FinalProcessing(m_stackCode.top()->Code());
|
||
|
|
|
||
|
|
// Remove the top file from the stack
|
||
|
|
m_stackCode.pop();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
|
||
|
|
// Check whether the token is an identifier, if so, check for any macro
|
||
|
|
if (token.GetType() == ETokenType::token_identifier || token.GetType() == ETokenType::token_keyword)
|
||
|
|
{
|
||
|
|
if (GetEnvironment().TestAndExpand(static_cast<std::string>(token).c_str(), rCode, bInMacroExpansion))
|
||
|
|
continue; // macro was replaced, get a token again.
|
||
|
|
}
|
||
|
|
|
||
|
|
// Return the token if one was returned.
|
||
|
|
if (token)
|
||
|
|
return token;
|
||
|
|
|
||
|
|
// Check for EOF. If so, close the file.
|
||
|
|
// ATTENTION Do not use rCode, since the stack might have changed through a preprocessor include directive.
|
||
|
|
if (!m_stackCode.empty() && m_stackCode.top()->Code().HasEOF())
|
||
|
|
{
|
||
|
|
// Trigger final preproc processing (to check for correct conditional inclusion boundaries).
|
||
|
|
FinalProcessing(m_stackCode.top()->Code());
|
||
|
|
|
||
|
|
// Remove the top file from the stack
|
||
|
|
m_stackCode.pop();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
catch (sdv::idl::XCompileError& rexcept)
|
||
|
|
{
|
||
|
|
// Use the class for easy access.
|
||
|
|
CCompileException exception(rexcept);
|
||
|
|
|
||
|
|
// Add the token to the code if no token was assigned.
|
||
|
|
if (token) exception.SetLocation(token);
|
||
|
|
|
||
|
|
// Add the path of the code causing the exception to the exception.
|
||
|
|
exception.SetPath(m_stackCode.top()->Source().GetPathRef());
|
||
|
|
|
||
|
|
// Assign the changed values. Needed to keep the additional information that the derived class might have.
|
||
|
|
rexcept = exception;
|
||
|
|
|
||
|
|
// Rethrow the original derived class.
|
||
|
|
throw;
|
||
|
|
}
|
||
|
|
|
||
|
|
// At this point, all files were processed. There is no more token.
|
||
|
|
return CToken();
|
||
|
|
}
|
||
|
|
|