/******************************************************************************** * Copyright (c) 2025-2026 ZF Friedrichshafen AG * * This program and the accompanying materials are made available under the * terms of the Apache License Version 2.0 which is available at * https://www.apache.org/licenses/LICENSE-2.0 * * SPDX-License-Identifier: Apache-2.0 * * Contributors: * Erik Verhoeven - initial API and implementation ********************************************************************************/ #include "preproc.h" #include "lexer.h" #include "parser.h" #include "exception.h" #include "source.h" #include #include "logger.h" CPreprocessor::CPreprocessor(CParser& rParser) : m_rParser(rParser) {} CToken CPreprocessor::ProcessPreproc(CCodePos& rCode, const CContextPtr& rptrContext) { // Use a local copy of the code (used when parsing preprocessor commands) CCodePos codePreproc(rCode); SLexerDummyCallback sCallback; CLexer lexerPreproc(&sCallback, m_rParser.GetEnvironment().CaseSensitiveTypeExtension(), CLexer::ELexingMode::lexing_preproc); CToken tokenMeta; // // Check for a preprocessor directive // CToken sPreprocLine = m_rParser.GetLexer().HasPreprocessor(rCode); // if (!sPreprocLine) return; // Expecting the number sign if (lexerPreproc.GetToken(codePreproc, rptrContext) != "#") throw CCompileException(codePreproc.GetLocation(), "Expected preprocessor directive sign."); // Get an identifier CToken sKeyword = lexerPreproc.GetToken(codePreproc, rptrContext); if (sKeyword == "define") { // Skip whitespace (space and tab) while (*rCode == ' ' || *rCode == '\t') ++rCode; tokenMeta = codePreproc.GetLocation(ETokenType::token_meta); tokenMeta.SetContext(rptrContext); ProcessDefine(lexerPreproc, codePreproc, rptrContext); codePreproc.UpdateLocation(tokenMeta, ETokenMetaType::token_meta_define); } else if (sKeyword == "if") { ProcessIf(lexerPreproc, codePreproc, rptrContext); } else if (sKeyword == "ifdef") { ProcessIfDef(lexerPreproc, codePreproc, rptrContext, false); } else if (sKeyword == "ifndef") { ProcessIfDef(lexerPreproc, codePreproc, rptrContext, true); } else if (sKeyword == "elif") { ProcessElif(lexerPreproc, codePreproc, rptrContext); } else if (sKeyword == "else") { ProcessElse(lexerPreproc, codePreproc, rptrContext); } else if (sKeyword == "endif") { ProcessEndif(lexerPreproc, codePreproc, rptrContext); } else if (sKeyword == "undef") { // Skip whitespace (space and tab) while (*rCode == ' ' || *rCode == '\t') ++rCode; tokenMeta = codePreproc.GetLocation(ETokenType::token_meta); tokenMeta.SetContext(rptrContext); ProcessUndef(lexerPreproc, codePreproc, rptrContext); codePreproc.UpdateLocation(tokenMeta, ETokenMetaType::token_meta_undef); } else if (sKeyword == "include") { bool bLocal = false; tokenMeta = codePreproc.GetLocation(ETokenType::token_meta); tokenMeta.SetContext(rptrContext); ProcessInclude(lexerPreproc, codePreproc, rptrContext, bLocal); codePreproc.UpdateLocation(tokenMeta, bLocal ? ETokenMetaType::token_meta_include_local : ETokenMetaType::token_meta_include_global); } else if (sKeyword == "verbatim") { // Skip whitespace (space and tab) while (*codePreproc == ' ' || *codePreproc == '\t') ++codePreproc; // The rest of the line is verbatim text. tokenMeta = codePreproc.GetLocation(ETokenType::token_meta); tokenMeta.SetContext(rptrContext); ProcessVerbatim(lexerPreproc, codePreproc, rptrContext); codePreproc.UpdateLocation(tokenMeta, ETokenMetaType::token_meta_verbatim); } else if (sKeyword == "verbatim_begin") { // Skip the rest of the line and the newline lexerPreproc.SkipLine(codePreproc); if (*codePreproc == '\r') ++codePreproc; if (*codePreproc == '\n') ++codePreproc; // The rest until the end of the block is verbatim. tokenMeta = codePreproc.GetLocation(ETokenType::token_meta); tokenMeta.SetContext(rptrContext); ProcessVerbatimBlock(lexerPreproc, codePreproc, rptrContext); codePreproc.UpdateLocation(tokenMeta, ETokenMetaType::token_meta_verbatim); // Skip the "#verbatim_end" directive. lexerPreproc.SkipLine(codePreproc); } else if (sKeyword == "verbatim_end") { // Check whether to ignore directive based on the current section. if (CurrentSectionEnabled()) throw CCompileException(sKeyword, "Preprocessor directive \"#verbatim_end\" without \"verbatim_begin\" found."); } else if (sKeyword == "pragma") { // TODO: Add line to code generating target // Check whether to ignore directive based on the current section. if (CurrentSectionEnabled()) throw CCompileException(sKeyword, "Unsupported preprocessor directive \"#pragma\" found."); } else { // Check whether to ignore directive based on the current section. if (CurrentSectionEnabled()) throw CCompileException(sKeyword, "Unknown preprocessor directive."); } // Skip the rest of the line lexerPreproc.SkipLine(codePreproc); // Store the new position rCode = codePreproc; // Return the meta token if available return CurrentSectionEnabled() ? tokenMeta : CToken(); } void CPreprocessor::FinalProcessing(const CCodePos& rCode) { if (!m_stackConditional.empty()) throw CCompileException(rCode.GetLocation(), "Missing '#endif' directive."); } bool CPreprocessor::CurrentSectionEnabled() const { return m_stackConditional.empty() || m_stackConditional.top().m_eProcessingControl == EConditionalProcessing::current; } // Incorrect cppcheck warning concerning using const reference for the rCode parameter. The code position is updated and therefore // the parameter cannot be a const reference. // cppcheck-suppress constParameterReference void CPreprocessor::ProcessDefine(CLexer& rLexer, CCodePos& rCode, const CContextPtr& rptrContext) { CLog log("Preprocess #define..."); // Check whether to ignore directive based on the current section. if (!CurrentSectionEnabled()) { log << "Not included in the current section..." << std::endl; return; } // Read the macro name CToken tokenName = rLexer.GetToken(rCode, rptrContext); if (!tokenName || tokenName.GetType() != ETokenType::token_identifier) throw CCompileException(rCode.GetLocation(), "Definition name missing."); // Check for the next character. If there is no space in between and the next token is a left parenthesis, there are // parameters. bool bHasParam = false; std::vector vecParams; CToken tokenSymbol; if (*rCode == '(') { // Get the left parenthesis as a token. tokenSymbol = rLexer.GetToken(rCode, rptrContext); // Parameter bracket has been provided bHasParam = true; if (tokenSymbol != "(") throw CCompileException(tokenSymbol, "Invalid character for macro definition; expecting '('."); // Read zero or more parameter bool bDone = false; bool bInitial = true; while (!bDone) { // Check for closing bracket tokenSymbol = rLexer.GetToken(rCode, rptrContext); if (bInitial && tokenSymbol == ")") bDone = true; else if (tokenSymbol.GetType() != ETokenType::token_identifier) throw CCompileException(tokenSymbol, "Expecting a parameter name."); bInitial = false; if (!bDone) { // The token should represent an identifier CToken sIdentifierToken = tokenSymbol; // Check for duplicates if (std::find(vecParams.begin(), vecParams.end(), static_cast(sIdentifierToken)) != vecParams.end()) throw CCompileException(tokenSymbol, "Duplicate parameter names for macro definition."); // Add the parameter to the list vecParams.push_back(sIdentifierToken); // Check for a comma or a right parenthesis tokenSymbol = rLexer.GetToken(rCode, rptrContext); if (tokenSymbol == ")") bDone = true; else if (tokenSymbol != ",") throw CCompileException(tokenSymbol, "Unexpected symbol in parameter list."); } } } // Create and store the macro structure CToken tokenValue = rCode.GetLocation(); rLexer.SkipLine(rCode); rCode.UpdateLocation(tokenValue); CMacro macro(static_cast(tokenName).c_str(), bHasParam ? &vecParams : nullptr, static_cast(tokenValue).c_str()); // Log information log << "Macro definition = " << static_cast(tokenName); if (bHasParam) { log << "("; bool bInitial = true; for (const std::string& rssParam : vecParams) { if (bInitial) log << ", "; log << rssParam; bInitial = false; } log << ")"; } log << " " << static_cast(tokenValue) << std::endl; // Add the macro GetEnvironment().AddDefinition(rCode.GetLocation(), macro); } // Incorrect cppcheck warning concerning using const reference for the rCode parameter. The code position is updated and therefore // the parameter cannot be a const reference. // cppcheck-suppress constParameterReference void CPreprocessor::ProcessUndef(CLexer& rLexer, CCodePos& rCode, const CContextPtr& rptrContext) { CLog log("Preprocess #undef..."); // Check whether to ignore directive based on the current section. if (!CurrentSectionEnabled()) { log << "Not included in the current section..." << std::endl; return; } // Read the macro name CToken tokenName = rLexer.GetToken(rCode, rptrContext); if (!tokenName || tokenName.GetType() != ETokenType::token_identifier) throw CCompileException(rCode.GetLocation(), "Definition name missing."); // Log information log << "Macro definition = " << static_cast(tokenName) << std::endl; GetEnvironment().RemoveDefinition(static_cast(tokenName).c_str()); } void CPreprocessor::ProcessInclude(CLexer& /*rLexer*/, CCodePos& rCode, const CContextPtr& rptrContext, bool& rbLocal) { CLog log("Preprocess #include..."); // Check whether to ignore directive based on the current section. if (!CurrentSectionEnabled()) { log << "Not included in the current section..." << std::endl; return; } // NOTE: The file name could contain characters that might be interpreted as escape character. Do not use the lexer to read // the path. // Skip whitespace (tab and space) while (*rCode == ' ' || *rCode == '\t') ++rCode; // Get the next symbol CToken tokenSymbol = rCode.GetLocation(); tokenSymbol.SetContext(rptrContext); switch (*rCode) { case '<': rbLocal = false; break; case '\"': rbLocal = true; break; default: throw CCompileException(tokenSymbol, "Invalid symbol."); } ++rCode; // Get the path CToken tokenPath = rCode.GetLocation(); tokenPath.SetContext(rptrContext); bool bDone = false; while (!bDone) { switch (*rCode) { case '\0': throw CCompileException(tokenPath, "Unexpected end of file."); case '\r': case '\n': throw CCompileException(tokenPath, "Unexpected end of line."); case '\"': if (rbLocal) bDone = true; else ++rCode; break; case '>': if (!rbLocal) bDone = true; else ++rCode; break; default: ++rCode; break; } } rCode.UpdateLocation(tokenPath); if (!tokenPath) throw CCompileException(tokenPath, "No filename supplied."); // Skipping closing symbol ++rCode; // Find the file std::filesystem::path pathConfirmedFile; // If a local filename has been provided, look locally first if (rbLocal) { // Determine the local path. std::filesystem::path pathSourceDir = tokenSymbol.GetContext()->Source().GetPathRef().parent_path(); std::filesystem::path pathIncludeFile = static_cast(tokenPath); std::filesystem::path pathLocalInclude = pathIncludeFile.is_absolute() ? pathIncludeFile : pathSourceDir / pathIncludeFile; if (std::filesystem::exists(pathLocalInclude)) pathConfirmedFile = pathLocalInclude; } // If no file yet, run through the include directories and try to find a fitting file. if (pathConfirmedFile.empty()) { std::vector vecIncludeDirs = GetEnvironment().GetIncludeDirs(); for (const std::filesystem::path &rpathDir : vecIncludeDirs) { std::filesystem::path pathFile = rpathDir / static_cast(tokenPath); if (std::filesystem::exists(pathFile)) { pathConfirmedFile = pathFile; break; } } } // File found? if (pathConfirmedFile.empty()) throw CCompileException(tokenSymbol, "Include file not found."); // Log information log << "Statement = " << (rbLocal ? "\"" : "<") << static_cast(tokenPath) << (rbLocal ? "\"" : ">") << std::endl; log << "Path = " << pathConfirmedFile.generic_u8string() << std::endl; // Parse the source file IncludeFile(pathConfirmedFile, rbLocal); } void CPreprocessor::ProcessIf(CLexer& rLexer, CCodePos& rCode, const CContextPtr& rptrContext) { CLog log("Preprocess #if..."); // Check whether to ignore directive based on the current section. EConditionalProcessing eProcessingControl = EConditionalProcessing::disabled; if (CurrentSectionEnabled()) { // Calculate the conditional value CToken token = rCode.GetLocation(); bool bResult = ConditionalCalc(rLexer, rCode, rptrContext) ? true : false; rCode.UpdateLocation(token); eProcessingControl = bResult ? EConditionalProcessing::current : EConditionalProcessing::future; log << "Condition = " << static_cast(token) << std::endl; log << "State = " << (bResult ? "currently processed" : "currently not processed"); } else log << "Not included in the current section..." << std::endl; // Create a new section control m_stackConditional.push(SConditionalControl{EConditionalInclusion::if_section, eProcessingControl}); } // Incorrect cppcheck warning concerning using const reference for the rCode parameter. The code position is updated and therefore // the parameter cannot be a const reference. // cppcheck-suppress constParameterReference void CPreprocessor::ProcessIfDef(CLexer& rLexer, CCodePos& rCode, const CContextPtr& rptrContext, bool bInverted) { CLog log("Preprocess #ifdef..."); // Check whether to ignore directive based on the current section. EConditionalProcessing eProcessingControl = EConditionalProcessing::disabled; if (CurrentSectionEnabled()) { // Check for the macro name CToken tokenName = rLexer.GetToken(rCode, rptrContext); if (!tokenName || tokenName.GetType() != ETokenType::token_identifier) throw CCompileException(rCode.GetLocation(), "Definition name missing."); bool bDefined = GetEnvironment().Defined(static_cast(tokenName).c_str()); eProcessingControl = (bInverted ? !bDefined : bDefined) ? EConditionalProcessing::current : EConditionalProcessing::future; log << "Definition name = " << static_cast(tokenName) << std::endl; log << "Definition " << (bDefined ? "exists" : "doesn't exist") << std::endl; } else log << "Not included in the current section..." << std::endl; // Create a new section control m_stackConditional.push(SConditionalControl{EConditionalInclusion::if_section, eProcessingControl}); } void CPreprocessor::ProcessElif(CLexer& rLexer, CCodePos& rCode, const CContextPtr& rptrContext) { CLog log("Preprocess #elif..."); // Currently in a conditional section? if (m_stackConditional.empty()) throw CCompileException(rCode.GetLocation(), "Unexpected '#elif' directive."); // Currently in an else-section? if (m_stackConditional.top().m_eInclusionControl == EConditionalInclusion::else_section) throw CCompileException(rCode.GetLocation(), "Duplicate '#elif' directive."); switch (m_stackConditional.top().m_eProcessingControl) { case EConditionalProcessing::future: { CToken token = rCode.GetLocation(); bool bResult = ConditionalCalc(rLexer, rCode, rptrContext) ? true : false; rCode.UpdateLocation(token); log << "Condition = " << static_cast(token) << std::endl; // Check whether the condition is true if (bResult) { m_stackConditional.top().m_eProcessingControl = EConditionalProcessing::current; log << "State = currently processed" << std::endl; } else { log << "State = currently not processed" << std::endl; } break; } case EConditionalProcessing::current: m_stackConditional.top().m_eProcessingControl = EConditionalProcessing::previous; log << "State = currently not processed" << std::endl; break; default: // Do nothing... disabled and previous are valid values. log << "Not included in the current section..." << std::endl; break; } } void CPreprocessor::ProcessElse(CLexer& /*rLexer*/, const CCodePos& rCode, const CContextPtr& /*rptrContext*/) { CLog log("Preprocess #else..."); // Currently in a conditional section? if (m_stackConditional.empty()) throw CCompileException(rCode.GetLocation(), "Unexpected '#else' directive."); // Currently in an else-section? if (m_stackConditional.top().m_eInclusionControl == EConditionalInclusion::else_section) throw CCompileException(rCode.GetLocation(), "Duplicate '#else' directive."); // Update the current state m_stackConditional.top().m_eInclusionControl = EConditionalInclusion::else_section; switch (m_stackConditional.top().m_eProcessingControl) { case EConditionalProcessing::future: m_stackConditional.top().m_eProcessingControl = EConditionalProcessing::current; log << "State = currently processed" << std::endl; break; case EConditionalProcessing::current: m_stackConditional.top().m_eProcessingControl = EConditionalProcessing::previous; log << "State = currently not processed" << std::endl; break; default: // Do nothing... disabled and previous are valid values. log << "Not included in the current section..." << std::endl; break; } } void CPreprocessor::ProcessEndif(CLexer& /*rLexer*/, const CCodePos& rCode, const CContextPtr& /*rptrContext*/) { CLog log("Preprocess #endif..."); // Currently in a conditional section? if (m_stackConditional.empty()) throw CCompileException(rCode.GetLocation(), "Unexpected '#endif' directive."); // End the section m_stackConditional.pop(); } // Incorrect cppcheck warning concerning using const reference for the rCode parameter. The code position is updated and therefore // the parameter cannot be a const reference. // Older version: const parameter for rLexer is also not possible. // cppcheck-suppress constParameterReference // cppcheck-suppress constParameter void CPreprocessor::ProcessVerbatim(CLexer& rLexer, CCodePos& rCode, const CContextPtr& /*rptrContext*/) { CLog log("Preprocess #verbatim..."); // Check whether to ignore directive based on the current section. if (!CurrentSectionEnabled()) { log << "Not included in the current section..." << std::endl; return; } rLexer.SkipLine(rCode); } // Incorrect cppcheck warning concerning using const reference for the rCode parameter. The code position is updated and therefore // the parameter cannot be a const reference. // cppcheck-suppress constParameterReference void CPreprocessor::ProcessVerbatimBlock(CLexer& rLexer, CCodePos& rCode, const CContextPtr& /*rptrContext*/) { CLog log("Preprocess #verbatim_begin - #verbatim_end..."); // Check whether to ignore directive based on the current section. if (!CurrentSectionEnabled()) { log << "Not included in the current section..." << std::endl; return; } // Check until end of code or end of file while (true) { // Check when done if (!static_cast(rCode)) throw CCompileException(rCode.GetLocation(), "Missing '#verbatim_end' directive."); if (!*rCode) throw CCompileException(rCode.GetLocation(), "Missing '#verbatim_end' directive."); // Store the current position at the beginning of the line CCodePos posBackup = rCode; // Check for #verbatim_end (whitespace could exist before and after the number sign '#'). while (*rCode == ' ' || *rCode == '\t') ++rCode; if (*rCode == '#') { ++rCode; while (*rCode == ' ' || *rCode == '\t') ++rCode; if (std::strncmp(rCode, "verbatim_end", 12) == 0) { rCode = posBackup; break; } } // Skip the current line rLexer.SkipLine(rCode); while (*rCode == '\r' || *rCode == '\n') ++rCode; } } int64_t CPreprocessor::ConditionalCalc(CLexer& rLexer, CCodePos& rCode, const CContextPtr& rptrContext, uint32_t uiPrecedence /*= 100*/) { // Read a token CToken token = rLexer.GetToken(rCode, rptrContext); // Token can be either a left parenthesis, a unary operator (precedence 0), an identifier (defined, macro or unknown), or a // number. int64_t iLValue = 0; if (token == "(") { // Calculate the content between the parenthesis. iLValue = ConditionalCalc(rLexer, rCode, rptrContext); // Expecting a closing parenthesis token = rLexer.GetToken(rCode, rptrContext); if (token != ")") throw CCompileException(token, "Expecting a right parenthesis."); } else if (token == "!") { // Calculate the next token an use logical NOT on this. iLValue = ConditionalCalc(rLexer, rCode, 0) ? 0 : 1; } else if (token == "~") { // Calculate the next token an use bitwise NOT on this. iLValue = ~ConditionalCalc(rLexer, rCode, 0); } else if (token == "defined") { // The 'defined' identifier is used in two ways // - function line: defined(macro) // - unary operator: defined macro token = rLexer.GetToken(rCode, rptrContext); if (token == "(") { // Get the macro name token = rLexer.GetToken(rCode, rptrContext); if (token.GetType() != ETokenType::token_identifier) throw CCompileException(token, "Expecting macro name between parenthesis of defined operator"); iLValue = GetEnvironment().Defined(static_cast(token).c_str()) ? 1 : 0; token = rLexer.GetToken(rCode, rptrContext); if (token != ")") throw CCompileException(token, "Expecting right parenthesis"); } else { if (token.GetType() != ETokenType::token_identifier) throw CCompileException(token, "Expecting macro name following defined operator"); iLValue = GetEnvironment().Defined(static_cast(token).c_str()) ? 1 : 0; } } else if (token == "true" || token == "TRUE") { iLValue = 1; } else if (token == "false" || token == "FALSE") { iLValue = 0; } else if (token.GetType() == ETokenType::token_identifier) { // Test and expand any macros if (GetEnvironment().TestAndExpand(token, rCode)) iLValue = ConditionalCalc(rLexer, rCode, rptrContext, uiPrecedence); else iLValue = 0; } else if (token.GetType() == ETokenType::token_literal) { // Only integer and character literals can be interpreted. Additional interpretation characters are not supported. // Anything other will result in the value 0. if (token.c_str()[0] == '\'') { if (token.c_str()[1] == '\\') { switch (token.c_str()[2]) { case '\"': iLValue = static_cast('\"'); break; case '\'': iLValue = static_cast('\''); break; case '?': iLValue = static_cast('?'); break; case '\\': iLValue = static_cast('\\'); break; case 'a': iLValue = static_cast('a'); break; case 'b': iLValue = static_cast('b'); break; case 'f': iLValue = static_cast('f'); break; case 'n': iLValue = static_cast('n'); break; case 'r': iLValue = static_cast('r'); break; case 't': iLValue = static_cast('t'); break; case 'v': iLValue = static_cast('v'); break; default: iLValue = 0; break; } } else iLValue = static_cast(token.c_str()[1]); } else iLValue = static_cast(std::atoll(token.c_str())); } else throw CCompileException(token, "Unexpected token '", static_cast(token), "'."); // Run until all operations at this or any higher precedence have been executed bool bDone = false; while (!bDone) { // Peek for the next operator and decide on precedence... token = rLexer.GetToken(rCode, rptrContext); // If no token, we're done. if (!token) return iLValue; // If right parenthesis, replace the parenthesis and we're done. if (token == ")") { rCode.PrependCode(token); break; } // Conditionally calculate the result based on the supplied operator function and the precedence level. auto fnCalculate = [&](std::function fnCalculation, uint32_t uiOperatorPrecedence, bool bCheckDiv = false) { // If operator has höhere oder gleiche precedence, replace operator and we're done. if (uiOperatorPrecedence >= uiPrecedence) { rCode.PrependCode(token); bDone = true; return; } // If operator has niedriger precedence, get next token and calculate result. CToken tokenTemp = token; int64_t iRValue = ConditionalCalc(rLexer, rCode, rptrContext, uiOperatorPrecedence); // Check for division by zero. if (bCheckDiv && !iRValue) throw CCompileException(tokenTemp, "Division by zero."); // Calculate the result. iLValue = fnCalculation(iLValue, iRValue); }; // Check for the operator and calculate the result if (token == "*") { fnCalculate([](int64_t i1, int64_t i2){ return i1 * i2; }, 1); continue; } if (token == "/") { fnCalculate([](int64_t i1, int64_t i2){ return i1 / i2; }, 1, true); continue; } if (token == "%") { fnCalculate([](int64_t i1, int64_t i2){ return i1 % i2; }, 1, true); continue; } if (token == "+") { fnCalculate([](int64_t i1, int64_t i2){ return i1 + i2; }, 2); continue; } if (token == "-") { fnCalculate([](int64_t i1, int64_t i2){ return i1 - i2; }, 2); continue; } if (token == "<<") { fnCalculate([](int64_t i1, int64_t i2){ return i1 << i2; }, 3); continue; } if (token == ">>") { fnCalculate([](int64_t i1, int64_t i2){ return i1 >> i2; }, 3); continue; } if (token == "<") { fnCalculate([](int64_t i1, int64_t i2){ return i1 < i2; }, 4); continue; } if (token == "<=") { fnCalculate([](int64_t i1, int64_t i2){ return i1 <= i2; }, 4); continue; } if (token == ">") { fnCalculate([](int64_t i1, int64_t i2){ return i1 > i2; }, 4); continue; } if (token == ">=") { fnCalculate([](int64_t i1, int64_t i2){ return i1 >= i2; }, 4); continue; } if (token == "==") { fnCalculate([](int64_t i1, int64_t i2){ return i1 == i2; }, 5); continue; } if (token == "!=") { fnCalculate([](int64_t i1, int64_t i2){ return i1 != i2; }, 5); continue; } if (token == "&") { fnCalculate([](int64_t i1, int64_t i2){ return i1 & i2; }, 6); continue; } if (token == "^") { fnCalculate([](int64_t i1, int64_t i2){ return i1 ^ i2; }, 7); continue; } if (token == "|") { fnCalculate([](int64_t i1, int64_t i2){ return i1 | i2; }, 8); continue; } if (token == "&&") { fnCalculate([](int64_t i1, int64_t i2){ return i1 && i2; }, 9); continue; } if (token == "||") { fnCalculate([](int64_t i1, int64_t i2){ return i1 || i2; }, 10); continue; } // If not an operator, this is an error throw CCompileException(token, "Expecting operator"); }; return iLValue; }