/*************************************************************************************************** ** ** Original work Copyright (C) 2013 Ingo Berg ** Modified work Copyright 2014 Roman Telezhynskyi ** ** Permission is hereby granted, free of charge, to any person obtaining a copy of this ** software and associated documentation files (the "Software"), to deal in the Software ** without restriction, including without limitation the rights to use, copy, modify, ** merge, publish, distribute, sublicense, and/or sell copies of the Software, and to ** permit persons to whom the Software is furnished to do so, subject to the following conditions: ** ** The above copyright notice and this permission notice shall be included in all copies or ** substantial portions of the Software. ** ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT ** NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND ** NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, ** DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ** ******************************************************************************************************/ #include "qmuparserbase.h" #include #include #ifdef QMUP_USE_OPENMP #include #endif #include "qmuparsererror.h" #include "qmuparsertokenreader.h" using namespace std; /** * @file * @brief This file contains the basic implementation of the muparser engine. */ namespace qmu { std::locale QmuParserBase::s_locale = std::locale(std::locale::classic(), new change_dec_sep('.')); bool QmuParserBase::g_DbgDumpCmdCode = false; bool QmuParserBase::g_DbgDumpStack = false; /** * @brief Identifiers for built in binary operators. * * When defining custom binary operators with #AddOprt(...) make sure not to choose * names conflicting with these definitions. */ const QStringList QmuParserBase::c_DefaultOprt = QStringList() << "<=" << ">=" << "!=" << "==" << "<" << ">" << "+" << "-" << "*" << "/" << "^" << "&&" << "||" << "=" << "(" << ")" << "?" << ":"; //--------------------------------------------------------------------------------------------------------------------- /** * @brief Constructor. * @param a_szFormula the formula to interpret. * @throw ParserException if a_szFormula is null. */ QmuParserBase::QmuParserBase() :m_pParseFormula(&QmuParserBase::ParseString), m_vRPN(), m_vStringBuf(), m_vStringVarBuf(), m_pTokenReader(), m_FunDef(), m_PostOprtDef(), m_InfixOprtDef(), m_OprtDef(), m_ConstDef(), m_StrVarDef(), m_VarDef(), m_bBuiltInOp(true), m_sNameChars(), m_sOprtChars(), m_sInfixOprtChars(), m_nIfElseCounter(0), m_vStackBuffer(), m_nFinalResultIdx(0) { InitTokenReader(); } //--------------------------------------------------------------------------------------------------------------------- /** * @brief Copy constructor. * * Tha parser can be safely copy constructed but the bytecode is reset during copy construction. */ QmuParserBase::QmuParserBase(const QmuParserBase &a_Parser) :m_pParseFormula(&QmuParserBase::ParseString), m_vRPN(), m_vStringBuf(), m_vStringVarBuf(), m_pTokenReader(), m_FunDef(), m_PostOprtDef(), m_InfixOprtDef(), m_OprtDef(), m_ConstDef(), m_StrVarDef(), m_VarDef(), m_bBuiltInOp(true), m_sNameChars(), m_sOprtChars(), m_sInfixOprtChars(), m_nIfElseCounter(0), m_vStackBuffer(), m_nFinalResultIdx(0) { m_pTokenReader.reset(new token_reader_type(this)); Assign(a_Parser); } //--------------------------------------------------------------------------------------------------------------------- QmuParserBase::~QmuParserBase() {} //--------------------------------------------------------------------------------------------------------------------- /** * @brief Assignement operator. * * Implemented by calling Assign(a_Parser). Self assignement is suppressed. * @param a_Parser Object to copy to this. * @return *this * @throw nothrow */ QmuParserBase& QmuParserBase::operator=(const QmuParserBase &a_Parser) Q_DECL_NOEXCEPT { Assign(a_Parser); return *this; } //--------------------------------------------------------------------------------------------------------------------- /** * @brief Copy state of a parser object to this. * * Clears Variables and Functions of this parser. * Copies the states of all internal variables. * Resets parse function to string parse mode. * * @param a_Parser the source object. */ void QmuParserBase::Assign(const QmuParserBase &a_Parser) { if (&a_Parser==this) { return; } // Don't copy bytecode instead cause the parser to create new bytecode // by resetting the parse function. ReInit(); m_ConstDef = a_Parser.m_ConstDef; // Copy user define constants m_VarDef = a_Parser.m_VarDef; // Copy user defined variables m_bBuiltInOp = a_Parser.m_bBuiltInOp; m_vStringBuf = a_Parser.m_vStringBuf; m_vStackBuffer = a_Parser.m_vStackBuffer; m_nFinalResultIdx = a_Parser.m_nFinalResultIdx; m_StrVarDef = a_Parser.m_StrVarDef; m_vStringVarBuf = a_Parser.m_vStringVarBuf; m_nIfElseCounter = a_Parser.m_nIfElseCounter; m_pTokenReader.reset(a_Parser.m_pTokenReader->Clone(this)); // Copy function and operator callbacks m_FunDef = a_Parser.m_FunDef; // Copy function definitions m_PostOprtDef = a_Parser.m_PostOprtDef; // post value unary operators m_InfixOprtDef = a_Parser.m_InfixOprtDef; // unary operators for infix notation m_OprtDef = a_Parser.m_OprtDef; // binary operators m_sNameChars = a_Parser.m_sNameChars; m_sOprtChars = a_Parser.m_sOprtChars; m_sInfixOprtChars = a_Parser.m_sInfixOprtChars; } //--------------------------------------------------------------------------------------------------------------------- /** * @brief Set the decimal separator. * @param cDecSep Decimal separator as a character value. * @sa SetThousandsSep * * By default muparser uses the "C" locale. The decimal separator of this * locale is overwritten by the one provided here. */ // cppcheck-suppress unusedFunction void QmuParserBase::SetDecSep(char_type cDecSep) { char_type cThousandsSep = std::use_facet< change_dec_sep >(s_locale).thousands_sep(); s_locale = std::locale(std::locale("C"), new change_dec_sep(cDecSep, cThousandsSep)); } //--------------------------------------------------------------------------------------------------------------------- /** * @brief Sets the thousands operator. * @param cThousandsSep The thousands separator as a character * @sa SetDecSep * * By default muparser uses the "C" locale. The thousands separator of this * locale is overwritten by the one provided here. */ // cppcheck-suppress unusedFunction void QmuParserBase::SetThousandsSep(char_type cThousandsSep) { char_type cDecSep = std::use_facet< change_dec_sep >(s_locale).decimal_point(); s_locale = std::locale(std::locale("C"), new change_dec_sep(cDecSep, cThousandsSep)); } //--------------------------------------------------------------------------------------------------------------------- /** * @brief Resets the locale. * * The default locale used "." as decimal separator, no thousands separator and "," as function argument separator. */ // cppcheck-suppress unusedFunction void QmuParserBase::ResetLocale() { s_locale = std::locale(std::locale("C"), new change_dec_sep('.')); SetArgSep(','); } //--------------------------------------------------------------------------------------------------------------------- /** * @brief Reset parser to string parsing mode and clear internal buffers. * * Clear bytecode, reset the token reader. * @throw nothrow */ void QmuParserBase::ReInit() const Q_DECL_NOEXCEPT { m_pParseFormula = &QmuParserBase::ParseString; m_vStringBuf.clear(); m_vRPN.clear(); m_pTokenReader->ReInit(); m_nIfElseCounter = 0; } //--------------------------------------------------------------------------------------------------------------------- void QmuParserBase::OnDetectVar(const QString &pExpr, int &nStart, int &nEnd) { Q_UNUSED(pExpr); Q_UNUSED(nStart); Q_UNUSED(nEnd); } //--------------------------------------------------------------------------------------------------------------------- /** * @brief Returns the version of muparser. * @param eInfo A flag indicating whether the full version info should be returned or not. * * Format is as follows: "MAJOR.MINOR (COMPILER_FLAGS)" The COMPILER_FLAGS are returned only if eInfo==pviFULL. */ // cppcheck-suppress unusedFunction QString QmuParserBase::GetVersion(EParserVersionInfo eInfo) { QString versionInfo; QTextStream ss(&versionInfo); ss << QMUP_VERSION; if (eInfo==pviFULL) { ss << " (" << QMUP_VERSION_DATE; ss << "; " << sizeof(void*)*8 << "BIT"; #ifdef _DEBUG ss << "; DEBUG"; #else ss << "; RELEASE"; #endif #ifdef _UNICODE ss << "; UNICODE"; #else #ifdef _MBCS ss << "; MBCS"; #else ss << "; ASCII"; #endif #endif #ifdef QMUP_USE_OPENMP ss << "; OPENMP"; //#else // ss << "; NO_OPENMP"; #endif #if defined(MUP_MATH_EXCEPTIONS) ss << "; MATHEXC"; //#else // ss << "; NO_MATHEXC"; #endif ss << ")"; } return versionInfo; } //--------------------------------------------------------------------------------------------------------------------- /** * @brief Add a function or operator callback to the parser. */ void QmuParserBase::AddCallback(const QString &a_strName, const QmuParserCallback &a_Callback, funmap_type &a_Storage, const QString &a_szCharSet ) { if (a_Callback.GetAddr()==0) { Error(ecINVALID_FUN_PTR); } const funmap_type *pFunMap = &a_Storage; // Check for conflicting operator or function names if ( pFunMap!=&m_FunDef && m_FunDef.find(a_strName)!=m_FunDef.end() ) { Error(ecNAME_CONFLICT, -1, a_strName); } if ( pFunMap!=&m_PostOprtDef && m_PostOprtDef.find(a_strName)!=m_PostOprtDef.end() ) { Error(ecNAME_CONFLICT, -1, a_strName); } if ( pFunMap!=&m_InfixOprtDef && pFunMap!=&m_OprtDef && m_InfixOprtDef.find(a_strName)!=m_InfixOprtDef.end() ) { Error(ecNAME_CONFLICT, -1, a_strName); } if ( pFunMap!=&m_InfixOprtDef && pFunMap!=&m_OprtDef && m_OprtDef.find(a_strName)!=m_OprtDef.end() ) { Error(ecNAME_CONFLICT, -1, a_strName); } CheckOprt(a_strName, a_Callback, a_szCharSet); a_Storage[a_strName] = a_Callback; ReInit(); } //--------------------------------------------------------------------------------------------------------------------- /** * @brief Check if a name contains invalid characters. * * @throw ParserException if the name contains invalid charakters. */ void QmuParserBase::CheckOprt(const QString &a_sName, const QmuParserCallback &a_Callback, const QString &a_szCharSet) const { #if defined(_UNICODE) const std::wstring a_sNameStd = a_sName.toStdWString(); const std::wstring a_szCharSetStd = a_szCharSet.toStdWString(); #else const std::string a_sNameStd = a_sName.toStdString(); const std::string a_szCharSetStd = a_szCharSet.toStdString(); #endif if ( a_sNameStd.length() == false || (a_sNameStd.find_first_not_of(a_szCharSetStd)!=string_type::npos) || (a_sNameStd.at(0)>='0' && a_sNameStd.at(0)<='9')) { switch (a_Callback.GetCode()) { case cmOPRT_POSTFIX: Error(ecINVALID_POSTFIX_IDENT, -1, a_sName); break; case cmOPRT_INFIX: Error(ecINVALID_INFIX_IDENT, -1, a_sName); break; case cmLE: case cmGE: case cmNEQ: case cmEQ: case cmLT: case cmGT: case cmADD: case cmSUB: case cmMUL: case cmDIV: case cmPOW: case cmLAND: case cmLOR: case cmASSIGN: case cmBO: case cmBC: case cmIF: case cmELSE: case cmENDIF: case cmARG_SEP: case cmVAR: case cmVAL: case cmVARPOW2: case cmVARPOW3: case cmVARPOW4: case cmVARMUL: case cmPOW2: case cmFUNC: case cmFUNC_STR: case cmFUNC_BULK: case cmSTRING: case cmOPRT_BIN: case cmEND: case cmUNKNOWN: default: Error(ecINVALID_NAME, -1, a_sName); break; } } } //--------------------------------------------------------------------------------------------------------------------- /** * @brief Check if a name contains invalid characters. * * @throw ParserException if the name contains invalid charakters. */ void QmuParserBase::CheckName(const QString &a_sName, const QString &a_szCharSet) const { #if defined(_UNICODE) std::wstring a_sNameStd = a_sName.toStdWString(); std::wstring a_szCharSetStd = a_szCharSet.toStdWString(); #else std::string a_sNameStd = a_sName.toStdString(); std::string a_szCharSetStd = a_szCharSet.toStdString(); #endif if ( a_sNameStd.length() == false || (a_sNameStd.find_first_not_of(a_szCharSetStd)!=string_type::npos) || (a_sNameStd[0]>='0' && a_sNameStd[0]<='9')) { Error(ecINVALID_NAME); } } //--------------------------------------------------------------------------------------------------------------------- /** * @brief Set the formula. * @param a_strFormula Formula as string_type * @throw ParserException in case of syntax errors. * * Triggers first time calculation thus the creation of the bytecode and scanning of used variables. */ void QmuParserBase::SetExpr(const QString &a_sExpr) { // Check locale compatibility std::locale loc; if (m_pTokenReader->GetArgSep()==std::use_facet >(loc).decimal_point()) { Error(ecLOCALE); } // 20060222: Bugfix for Borland-Kylix: // adding a space to the expression will keep Borlands KYLIX from going wild // when calling tellg on a stringstream created from the expression after // reading a value at the end of an expression. (qmu::QmuParser::IsVal function) // (tellg returns -1 otherwise causing the parser to ignore the value) QString sBuf(a_sExpr + " " ); m_pTokenReader->SetFormula(sBuf); ReInit(); } //--------------------------------------------------------------------------------------------------------------------- /** * @brief Virtual function that defines the characters allowed in name identifiers. * @sa #ValidOprtChars, #ValidPrefixOprtChars */ const QString& QmuParserBase::ValidNameChars() const { assert(m_sNameChars.size()); return m_sNameChars; } //--------------------------------------------------------------------------------------------------------------------- /** * @brief Virtual function that defines the characters allowed in operator definitions. * @sa #ValidNameChars, #ValidPrefixOprtChars */ const QString &QmuParserBase::ValidOprtChars() const { assert(m_sOprtChars.size()); return m_sOprtChars; } //--------------------------------------------------------------------------------------------------------------------- /** * @brief Virtual function that defines the characters allowed in infix operator definitions. * @sa #ValidNameChars, #ValidOprtChars */ const QString &QmuParserBase::ValidInfixOprtChars() const { assert(m_sInfixOprtChars.size()); return m_sInfixOprtChars; } //--------------------------------------------------------------------------------------------------------------------- /** * @brief Add a user defined operator. * @post Will reset the Parser to string parsing mode. */ void QmuParserBase::DefinePostfixOprt(const QString &a_sName, fun_type1 a_pFun, bool a_bAllowOpt) { AddCallback(a_sName, QmuParserCallback(a_pFun, a_bAllowOpt, prPOSTFIX, cmOPRT_POSTFIX), m_PostOprtDef, ValidOprtChars() ); } //--------------------------------------------------------------------------------------------------------------------- /** * @brief Initialize user defined functions. * * Calls the virtual functions InitFun(), InitConst() and InitOprt(). */ // cppcheck-suppress unusedFunction void QmuParserBase::Init() { InitCharSets(); InitFun(); InitConst(); InitOprt(); } //--------------------------------------------------------------------------------------------------------------------- /** * @brief Add a user defined operator. * @post Will reset the Parser to string parsing mode. * @param [in] a_sName operator Identifier * @param [in] a_pFun Operator callback function * @param [in] a_iPrec Operator Precedence (default=prSIGN) * @param [in] a_bAllowOpt True if operator is volatile (default=false) * @sa EPrec */ void QmuParserBase::DefineInfixOprt(const QString &a_sName, fun_type1 a_pFun, int a_iPrec, bool a_bAllowOpt) { AddCallback(a_sName, QmuParserCallback(a_pFun, a_bAllowOpt, a_iPrec, cmOPRT_INFIX), m_InfixOprtDef, ValidInfixOprtChars() ); } //--------------------------------------------------------------------------------------------------------------------- /** * @brief Define a binary operator. * @param [in] a_sName The identifier of the operator. * @param [in] a_pFun Pointer to the callback function. * @param [in] a_iPrec Precedence of the operator. * @param [in] a_eAssociativity The associativity of the operator. * @param [in] a_bAllowOpt If this is true the operator may be optimized away. * * Adds a new Binary operator the the parser instance. */ void QmuParserBase::DefineOprt( const QString &a_sName, fun_type2 a_pFun, unsigned a_iPrec, EOprtAssociativity a_eAssociativity, bool a_bAllowOpt ) { // Check for conflicts with built in operator names for (int i=0; m_bBuiltInOp && iIgnoreUndefVar(true); CreateRPN(); // try to create bytecode, but don't use it for any further calculations since it // may contain references to nonexisting variables. m_pParseFormula = &QmuParserBase::ParseString; m_pTokenReader->IgnoreUndefVar(false); } catch (const QmuParserError &e) { // Make sure to stay in string parse mode, dont call ReInit() // because it deletes the array with the used variables m_pParseFormula = &QmuParserBase::ParseString; m_pTokenReader->IgnoreUndefVar(false); throw; } return m_pTokenReader->GetUsedVar(); } //--------------------------------------------------------------------------------------------------------------------- /** * @brief Execute a function that takes a single string argument. * @param a_FunTok Function token. * @throw QmuParserError If the function token is not a string function */ QmuParserBase::token_type QmuParserBase::ApplyStrFunc(const token_type &a_FunTok, const QVector &a_vArg) const { if (a_vArg.back().GetCode()!=cmSTRING) { Error(ecSTRING_EXPECTED, m_pTokenReader->GetPos(), a_FunTok.GetAsString()); } token_type valTok; generic_fun_type pFunc = a_FunTok.GetFuncAddr(); assert(pFunc); try { // Check function arguments; write dummy value into valtok to represent the result switch (a_FunTok.GetArgCount()) { case 0: valTok.SetVal(1); a_vArg[0].GetAsString(); break; case 1: valTok.SetVal(1); a_vArg[1].GetAsString(); a_vArg[0].GetVal(); break; case 2: valTok.SetVal(1); a_vArg[2].GetAsString(); a_vArg[1].GetVal(); a_vArg[0].GetVal(); break; default: Error(ecINTERNAL_ERROR); break; } } catch (QmuParserError& ) { Error(ecVAL_EXPECTED, m_pTokenReader->GetPos(), a_FunTok.GetAsString()); } // string functions won't be optimized m_vRPN.AddStrFun(pFunc, a_FunTok.GetArgCount(), a_vArg.back().GetIdx()); // Push dummy value representing the function result to the stack return valTok; } //--------------------------------------------------------------------------------------------------------------------- /** * @brief Apply a function token. * @param iArgCount Number of Arguments actually gathered used only for multiarg functions. * @post The result is pushed to the value stack * @post The function token is removed from the stack * @throw QmuParserError if Argument count does not mach function requirements. */ void QmuParserBase::ApplyFunc( QStack &a_stOpt, QStack &a_stVal, int a_iArgCount) const { assert(m_pTokenReader.get()); // Operator stack empty or does not contain tokens with callback functions if (a_stOpt.empty() || a_stOpt.top().GetFuncAddr()==0 ) { return; } token_type funTok = a_stOpt.pop(); assert(funTok.GetFuncAddr()); // Binary operators must rely on their internal operator number // since counting of operators relies on commas for function arguments // binary operators do not have commas in their expression int iArgCount = (funTok.GetCode()==cmOPRT_BIN) ? funTok.GetArgCount() : a_iArgCount; // determine how many parameters the function needs. To remember iArgCount includes the // string parameter whilst GetArgCount() counts only numeric parameters. int iArgRequired = funTok.GetArgCount() + ((funTok.GetType()==tpSTR) ? 1 : 0); // Thats the number of numerical parameters int iArgNumerical = iArgCount - ((funTok.GetType()==tpSTR) ? 1 : 0); if (funTok.GetCode()==cmFUNC_STR && iArgCount-iArgNumerical>1) { Error(ecINTERNAL_ERROR); } if (funTok.GetArgCount()>=0 && iArgCount>iArgRequired) { Error(ecTOO_MANY_PARAMS, m_pTokenReader->GetPos()-1, funTok.GetAsString()); } if (funTok.GetCode()!=cmOPRT_BIN && iArgCountGetPos()-1, funTok.GetAsString()); } if (funTok.GetCode()==cmFUNC_STR && iArgCount>iArgRequired ) { Error(ecTOO_MANY_PARAMS, m_pTokenReader->GetPos()-1, funTok.GetAsString()); } // Collect the numeric function arguments from the value stack and store them // in a vector QVector stArg; for (int i=0; iGetPos(), funTok.GetAsString()); } } switch (funTok.GetCode()) { case cmFUNC_STR: stArg.push_back(a_stVal.pop()); if ( stArg.back().GetType()==tpSTR && funTok.GetType()!=tpSTR ) { Error(ecVAL_EXPECTED, m_pTokenReader->GetPos(), funTok.GetAsString()); } ApplyStrFunc(funTok, stArg); break; case cmFUNC_BULK: m_vRPN.AddBulkFun(funTok.GetFuncAddr(), stArg.size()); break; case cmOPRT_BIN: case cmOPRT_POSTFIX: case cmOPRT_INFIX: case cmFUNC: if (funTok.GetArgCount()==-1 && iArgCount==0) { Error(ecTOO_FEW_PARAMS, m_pTokenReader->GetPos(), funTok.GetAsString()); } m_vRPN.AddFun(funTok.GetFuncAddr(), (funTok.GetArgCount()==-1) ? -iArgNumerical : iArgNumerical); break; case cmLE: Q_UNREACHABLE(); break; case cmGE: Q_UNREACHABLE(); break; case cmNEQ: Q_UNREACHABLE(); break; case cmEQ: Q_UNREACHABLE(); break; case cmLT: Q_UNREACHABLE(); break; case cmGT: Q_UNREACHABLE(); break; case cmADD: Q_UNREACHABLE(); break; case cmSUB: Q_UNREACHABLE(); break; case cmMUL: Q_UNREACHABLE(); break; case cmDIV: Q_UNREACHABLE(); break; case cmPOW: Q_UNREACHABLE(); break; case cmLAND: Q_UNREACHABLE(); break; case cmLOR: Q_UNREACHABLE(); break; case cmASSIGN: Q_UNREACHABLE(); break; case cmBO: Q_UNREACHABLE(); break; case cmBC: Q_UNREACHABLE(); break; case cmIF: Q_UNREACHABLE(); break; case cmELSE: Q_UNREACHABLE(); break; case cmENDIF: Q_UNREACHABLE(); break; case cmARG_SEP: Q_UNREACHABLE(); break; case cmVAR: Q_UNREACHABLE(); break; case cmVAL: Q_UNREACHABLE(); break; case cmVARPOW2: Q_UNREACHABLE(); break; case cmVARPOW3: Q_UNREACHABLE(); break; case cmVARPOW4: Q_UNREACHABLE(); break; case cmVARMUL: Q_UNREACHABLE(); break; case cmPOW2: Q_UNREACHABLE(); break; case cmSTRING: Q_UNREACHABLE(); break; case cmEND: Q_UNREACHABLE(); break; case cmUNKNOWN: Q_UNREACHABLE(); break; default: break; } // Push dummy value representing the function result to the stack token_type token; token.SetVal(1); a_stVal.push(token); } //--------------------------------------------------------------------------------------------------------------------- void QmuParserBase::ApplyIfElse(QStack &a_stOpt, QStack &a_stVal) const { // Check if there is an if Else clause to be calculated while (a_stOpt.size() && a_stOpt.top().GetCode()==cmELSE) { token_type opElse = a_stOpt.pop(); Q_ASSERT(a_stOpt.size()>0); // Take the value associated with the else branch from the value stack token_type vVal2 = a_stVal.pop(); Q_ASSERT(a_stOpt.size()>0); Q_ASSERT(a_stVal.size()>=2); // it then else is a ternary operator Pop all three values from the value s // tack and just return the right value token_type vVal1 = a_stVal.pop(); token_type vExpr = a_stVal.pop(); a_stVal.push( (qFuzzyCompare(vExpr.GetVal()+1, 1+0)==false) ? vVal1 : vVal2); token_type opIf = a_stOpt.pop(); Q_ASSERT(opElse.GetCode()==cmELSE); Q_ASSERT(opIf.GetCode()==cmIF); m_vRPN.AddIfElse(cmENDIF); } // while pending if-else-clause found } //--------------------------------------------------------------------------------------------------------------------- /** * @brief Performs the necessary steps to write code for the execution of binary operators into the bytecode. */ void QmuParserBase::ApplyBinOprt(QStack &a_stOpt, QStack &a_stVal) const { // is it a user defined binary operator? if (a_stOpt.top().GetCode()==cmOPRT_BIN) { ApplyFunc(a_stOpt, a_stVal, 2); } else { Q_ASSERT(a_stVal.size()>=2); token_type valTok1 = a_stVal.pop(), valTok2 = a_stVal.pop(), optTok = a_stOpt.pop(), resTok; if ( valTok1.GetType()!=valTok2.GetType() || (valTok1.GetType()==tpSTR && valTok2.GetType()==tpSTR) ) { Error(ecOPRT_TYPE_CONFLICT, m_pTokenReader->GetPos(), optTok.GetAsString()); } if (optTok.GetCode()==cmASSIGN) { if (valTok2.GetCode()!=cmVAR) { Error(ecUNEXPECTED_OPERATOR, -1, "="); } m_vRPN.AddAssignOp(valTok2.GetVar()); } else { m_vRPN.AddOp(optTok.GetCode()); } resTok.SetVal(1); a_stVal.push(resTok); } } //--------------------------------------------------------------------------------------------------------------------- /** * @brief Apply a binary operator. * @param a_stOpt The operator stack * @param a_stVal The value stack */ void QmuParserBase::ApplyRemainingOprt(QStack &stOpt, QStack &stVal) const { while (stOpt.size() && stOpt.top().GetCode() != cmBO && stOpt.top().GetCode() != cmIF) { token_type tok = stOpt.top(); switch (tok.GetCode()) { case cmOPRT_INFIX: case cmOPRT_BIN: case cmLE: case cmGE: case cmNEQ: case cmEQ: case cmLT: case cmGT: case cmADD: case cmSUB: case cmMUL: case cmDIV: case cmPOW: case cmLAND: case cmLOR: case cmASSIGN: if (stOpt.top().GetCode()==cmOPRT_INFIX) { ApplyFunc(stOpt, stVal, 1); } else { ApplyBinOprt(stOpt, stVal); } break; case cmELSE: ApplyIfElse(stOpt, stVal); break; case cmBO: case cmBC: case cmIF: case cmENDIF: case cmARG_SEP: case cmVAR: case cmVAL: case cmVARPOW2: case cmVARPOW3: case cmVARPOW4: case cmVARMUL: case cmPOW2: case cmFUNC: case cmFUNC_STR: case cmFUNC_BULK: case cmSTRING: case cmOPRT_POSTFIX: case cmEND: case cmUNKNOWN: default: Error(ecINTERNAL_ERROR); break; } } } //--------------------------------------------------------------------------------------------------------------------- /** * @brief Parse the command code. * @sa ParseString(...) * * Command code contains precalculated stack positions of the values and the associated operators. The Stack is * filled beginning from index one the value at index zero is not used at all. */ qreal QmuParserBase::ParseCmdCode() const { return ParseCmdCodeBulk(0, 0); } //--------------------------------------------------------------------------------------------------------------------- /** * @brief Evaluate the RPN. * @param nOffset The offset added to variable addresses (for bulk mode) * @param nThreadID OpenMP Thread id of the calling thread */ qreal QmuParserBase::ParseCmdCodeBulk(int nOffset, int nThreadID) const { assert(nThreadID<=s_MaxNumOpenMPThreads); // Note: The check for nOffset==0 and nThreadID here is not necessary but // brings a minor performance gain when not in bulk mode. qreal *Stack = ((nOffset==0) && (nThreadID==0)) ? &m_vStackBuffer[0] : &m_vStackBuffer[nThreadID * (m_vStackBuffer.size() / s_MaxNumOpenMPThreads)]; qreal buf; int sidx(0); for (const SToken *pTok = m_vRPN.GetBase(); pTok->Cmd!=cmEND ; ++pTok) { switch (pTok->Cmd) { // built in binary operators case cmLE: --sidx; Stack[sidx] = Stack[sidx] <= Stack[sidx+1]; continue; case cmGE: --sidx; Stack[sidx] = Stack[sidx] >= Stack[sidx+1]; continue; case cmNEQ: --sidx; Stack[sidx] = (qFuzzyCompare(Stack[sidx], Stack[sidx+1])==false); continue; case cmEQ: --sidx; Stack[sidx] = qFuzzyCompare(Stack[sidx], Stack[sidx+1]); continue; case cmLT: --sidx; Stack[sidx] = Stack[sidx] < Stack[sidx+1]; continue; case cmGT: --sidx; Stack[sidx] = Stack[sidx] > Stack[sidx+1]; continue; case cmADD: --sidx; Stack[sidx] += Stack[1+sidx]; continue; case cmSUB: --sidx; Stack[sidx] -= Stack[1+sidx]; continue; case cmMUL: --sidx; Stack[sidx] *= Stack[1+sidx]; continue; case cmDIV: --sidx; #if defined(MUP_MATH_EXCEPTIONS) if (Stack[1+sidx]==0) { Error(ecDIV_BY_ZERO); } #endif Stack[sidx] /= Stack[1+sidx]; continue; case cmPOW: --sidx; Stack[sidx] = qPow(Stack[sidx], Stack[1+sidx]); continue; case cmLAND: --sidx; #ifdef Q_CC_GNU #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wfloat-equal" #endif Stack[sidx] = Stack[sidx] && Stack[sidx+1]; #ifdef Q_CC_GNU #pragma GCC diagnostic pop #endif continue; case cmLOR: --sidx; #ifdef Q_CC_GNU #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wfloat-equal" #endif Stack[sidx] = Stack[sidx] || Stack[sidx+1]; #ifdef Q_CC_GNU #pragma GCC diagnostic pop #endif continue; case cmASSIGN: --sidx; Stack[sidx] = *pTok->Oprt.ptr = Stack[sidx+1]; continue; case cmIF: if (qFuzzyCompare(Stack[sidx--]+1, 1+0)) { pTok += pTok->Oprt.offset; } continue; case cmELSE: pTok += pTok->Oprt.offset; continue; case cmENDIF: continue; // value and variable tokens case cmVAR: Stack[++sidx] = *(pTok->Val.ptr + nOffset); continue; case cmVAL: Stack[++sidx] = pTok->Val.data2; continue; case cmVARPOW2: buf = *(pTok->Val.ptr + nOffset); Stack[++sidx] = buf*buf; continue; case cmVARPOW3: buf = *(pTok->Val.ptr + nOffset); Stack[++sidx] = buf*buf*buf; continue; case cmVARPOW4: buf = *(pTok->Val.ptr + nOffset); Stack[++sidx] = buf*buf*buf*buf; continue; case cmVARMUL: Stack[++sidx] = *(pTok->Val.ptr + nOffset) * pTok->Val.data + pTok->Val.data2; continue; // Next is treatment of numeric functions case cmFUNC: { int iArgCount = pTok->Fun.argc; // switch according to argument count switch (iArgCount) { case 0: sidx += 1; Stack[sidx] = (*reinterpret_cast(pTok->Fun.ptr))(); continue; case 1: Stack[sidx] = (*reinterpret_cast(pTok->Fun.ptr))(Stack[sidx]); continue; case 2: sidx -= 1; Stack[sidx] = (*reinterpret_cast(pTok->Fun.ptr))(Stack[sidx], Stack[sidx+1]); continue; case 3: sidx -= 2; Stack[sidx] = (*reinterpret_cast(pTok->Fun.ptr))(Stack[sidx], Stack[sidx+1], Stack[sidx+2]); continue; case 4: sidx -= 3; Stack[sidx] = (*reinterpret_cast(pTok->Fun.ptr))(Stack[sidx], Stack[sidx+1], Stack[sidx+2], Stack[sidx+3]); continue; case 5: sidx -= 4; Stack[sidx] = (*reinterpret_cast(pTok->Fun.ptr))(Stack[sidx], Stack[sidx+1], Stack[sidx+2], Stack[sidx+3], Stack[sidx+4]); continue; case 6: sidx -= 5; Stack[sidx] = (*reinterpret_cast(pTok->Fun.ptr))(Stack[sidx], Stack[sidx+1], Stack[sidx+2], Stack[sidx+3], Stack[sidx+4], Stack[sidx+5]); continue; case 7: sidx -= 6; Stack[sidx] = (*reinterpret_cast(pTok->Fun.ptr))(Stack[sidx], Stack[sidx+1], Stack[sidx+2], Stack[sidx+3], Stack[sidx+4], Stack[sidx+5], Stack[sidx+6]); continue; case 8: sidx -= 7; Stack[sidx] = (*reinterpret_cast(pTok->Fun.ptr))(Stack[sidx], Stack[sidx+1], Stack[sidx+2], Stack[sidx+3], Stack[sidx+4], Stack[sidx+5], Stack[sidx+6], Stack[sidx+7]); continue; case 9: sidx -= 8; Stack[sidx] = (*reinterpret_cast(pTok->Fun.ptr))(Stack[sidx], Stack[sidx+1], Stack[sidx+2], Stack[sidx+3], Stack[sidx+4], Stack[sidx+5], Stack[sidx+6], Stack[sidx+7], Stack[sidx+8]); continue; case 10: sidx -= 9; Stack[sidx] = (*reinterpret_cast(pTok->Fun.ptr))(Stack[sidx], Stack[sidx+1], Stack[sidx+2], Stack[sidx+3], Stack[sidx+4], Stack[sidx+5], Stack[sidx+6], Stack[sidx+7], Stack[sidx+8], Stack[sidx+9]); continue; default: if (iArgCount>0) // function with variable arguments store the number as a negative value { Error(ecINTERNAL_ERROR, 1); } sidx -= -iArgCount - 1; Stack[sidx] =(*reinterpret_cast(pTok->Fun.ptr))(&Stack[sidx], -iArgCount); continue; } } // Next is treatment of string functions case cmFUNC_STR: { sidx -= pTok->Fun.argc -1; // The index of the string argument in the string table int iIdxStack = pTok->Fun.idx; Q_ASSERT( iIdxStack>=0 && iIdxStackFun.argc) // switch according to argument count { case 0: Stack[sidx] = (*reinterpret_cast(pTok->Fun.ptr))(m_vStringBuf.at(iIdxStack)); continue; case 1: Stack[sidx] = (*reinterpret_cast(pTok->Fun.ptr))(m_vStringBuf.at(iIdxStack), Stack[sidx]); continue; case 2: Stack[sidx] = (*reinterpret_cast(pTok->Fun.ptr))(m_vStringBuf.at(iIdxStack), Stack[sidx], Stack[sidx+1]); continue; default: break; } continue; } case cmFUNC_BULK: { int iArgCount = pTok->Fun.argc; // switch according to argument count switch (iArgCount) { case 0: sidx += 1; Stack[sidx] = (*reinterpret_cast(pTok->Fun.ptr))(nOffset, nThreadID); continue; case 1: Stack[sidx] = (*reinterpret_cast(pTok->Fun.ptr))(nOffset, nThreadID, Stack[sidx]); continue; case 2: sidx -= 1; Stack[sidx] = (*reinterpret_cast(pTok->Fun.ptr))(nOffset, nThreadID, Stack[sidx], Stack[sidx+1]); continue; case 3: sidx -= 2; Stack[sidx] = (*reinterpret_cast(pTok->Fun.ptr))(nOffset, nThreadID, Stack[sidx], Stack[sidx+1], Stack[sidx+2]); continue; case 4: sidx -= 3; Stack[sidx] = (*reinterpret_cast(pTok->Fun.ptr))(nOffset, nThreadID, Stack[sidx], Stack[sidx+1], Stack[sidx+2], Stack[sidx+3]); continue; case 5: sidx -= 4; Stack[sidx] = (*reinterpret_cast(pTok->Fun.ptr))(nOffset, nThreadID, Stack[sidx], Stack[sidx+1], Stack[sidx+2], Stack[sidx+3], Stack[sidx+4]); continue; case 6: sidx -= 5; Stack[sidx] = (*reinterpret_cast(pTok->Fun.ptr))(nOffset, nThreadID, Stack[sidx], Stack[sidx+1], Stack[sidx+2], Stack[sidx+3], Stack[sidx+4], Stack[sidx+5]); continue; case 7: sidx -= 6; Stack[sidx] = (*reinterpret_cast(pTok->Fun.ptr))(nOffset, nThreadID, Stack[sidx], Stack[sidx+1], Stack[sidx+2], Stack[sidx+3], Stack[sidx+4], Stack[sidx+5], Stack[sidx+6]); continue; case 8: sidx -= 7; Stack[sidx] = (*reinterpret_cast(pTok->Fun.ptr))(nOffset, nThreadID, Stack[sidx], Stack[sidx+1], Stack[sidx+2], Stack[sidx+3], Stack[sidx+4], Stack[sidx+5], Stack[sidx+6], Stack[sidx+7]); continue; case 9: sidx -= 8; Stack[sidx] = (*reinterpret_cast(pTok->Fun.ptr))(nOffset, nThreadID, Stack[sidx], Stack[sidx+1], Stack[sidx+2], Stack[sidx+3], Stack[sidx+4], Stack[sidx+5], Stack[sidx+6], Stack[sidx+7], Stack[sidx+8]); continue; case 10: sidx -= 9; Stack[sidx] = (*reinterpret_cast(pTok->Fun.ptr))(nOffset, nThreadID, Stack[sidx], Stack[sidx+1], Stack[sidx+2], Stack[sidx+3], Stack[sidx+4], Stack[sidx+5], Stack[sidx+6], Stack[sidx+7], Stack[sidx+8], Stack[sidx+9]); continue; default: Error(ecINTERNAL_ERROR, 2); continue; } } case cmSTRING: case cmOPRT_BIN: case cmOPRT_POSTFIX: case cmOPRT_INFIX: // Q_ASSERT(INVALID_CODE_IN_BYTECODE); // continue; case cmEND: // return Stack[m_nFinalResultIdx]; case cmPOW2: case cmUNKNOWN: case cmBO: // unused, listed for compiler optimization purposes case cmBC: // Q_ASSERT(INVALID_CODE_IN_BYTECODE); // continue; case cmARG_SEP: // Q_ASSERT(INVALID_CODE_IN_BYTECODE); // continue; default: Error(ecINTERNAL_ERROR, 3); return 0; } // switch CmdCode } // for all bytecode tokens return Stack[m_nFinalResultIdx]; } //--------------------------------------------------------------------------------------------------------------------- void QmuParserBase::CreateRPN() const { if (m_pTokenReader->GetExpr().length() == false) { Error(ecUNEXPECTED_EOF, 0); } QStack stOpt, stVal; QStack stArgCount; token_type opta, opt; // for storing operators //token_type val, tval; // for storing value //string_type strBuf; // buffer for string function arguments ReInit(); // The outermost counter counts the number of seperated items // such as in "a=10,b=20,c=c+a" stArgCount.push(1); for (;;) { opt = m_pTokenReader->ReadNextToken(); switch (opt.GetCode()) { // // Next three are different kind of value entries // case cmSTRING: opt.SetIdx(m_vStringBuf.size()); // Assign buffer index to token stVal.push(opt); m_vStringBuf.push_back(opt.GetAsString()); // Store string in internal buffer break; case cmVAR: stVal.push(opt); m_vRPN.AddVar( static_cast(opt.GetVar()) ); break; case cmVAL: stVal.push(opt); m_vRPN.AddVal( opt.GetVal() ); break; case cmELSE: m_nIfElseCounter--; if (m_nIfElseCounter<0) { Error(ecMISPLACED_COLON, m_pTokenReader->GetPos()); } ApplyRemainingOprt(stOpt, stVal); m_vRPN.AddIfElse(cmELSE); stOpt.push(opt); break; case cmARG_SEP: if (stArgCount.empty()) { Error(ecUNEXPECTED_ARG_SEP, m_pTokenReader->GetPos()); } ++stArgCount.top(); // fallthrough intentional (no break!) case cmEND: ApplyRemainingOprt(stOpt, stVal); break; case cmBC: { // The argument count for parameterless functions is zero // by default an opening bracket sets parameter count to 1 // in preparation of arguments to come. If the last token // was an opening bracket we know better... if (opta.GetCode()==cmBO) { --stArgCount.top(); } ApplyRemainingOprt(stOpt, stVal); // Check if the bracket content has been evaluated completely if (stOpt.size() && stOpt.top().GetCode()==cmBO) { // if opt is ")" and opta is "(" the bracket has been evaluated, now its time to check // if there is either a function or a sign pending // neither the opening nor the closing bracket will be pushed back to // the operator stack // Check if a function is standing in front of the opening bracket, // if yes evaluate it afterwards check for infix operators assert(stArgCount.size()); int iArgCount = stArgCount.pop(); stOpt.pop(); // Take opening bracket from stack if (iArgCount>1 && ( stOpt.size()==0 || (stOpt.top().GetCode()!=cmFUNC && stOpt.top().GetCode()!=cmFUNC_BULK && stOpt.top().GetCode()!=cmFUNC_STR) ) ) { Error(ecUNEXPECTED_ARG, m_pTokenReader->GetPos()); } // The opening bracket was popped from the stack now check if there // was a function before this bracket if (stOpt.size() && stOpt.top().GetCode()!=cmOPRT_INFIX && stOpt.top().GetCode()!=cmOPRT_BIN && stOpt.top().GetFuncAddr()!=0) { ApplyFunc(stOpt, stVal, iArgCount); } } } // if bracket content is evaluated break; // // Next are the binary operator entries // //case cmAND: // built in binary operators //case cmOR: //case cmXOR: case cmIF: m_nIfElseCounter++; // fallthrough intentional (no break!) case cmLAND: case cmLOR: case cmLT: case cmGT: case cmLE: case cmGE: case cmNEQ: case cmEQ: case cmADD: case cmSUB: case cmMUL: case cmDIV: case cmPOW: case cmASSIGN: case cmOPRT_BIN: // A binary operator (user defined or built in) has been found. while ( stOpt.size() && stOpt.top().GetCode() != cmBO && stOpt.top().GetCode() != cmELSE && stOpt.top().GetCode() != cmIF) { int nPrec1 = GetOprtPrecedence(stOpt.top()), nPrec2 = GetOprtPrecedence(opt); if (stOpt.top().GetCode()==opt.GetCode()) { // Deal with operator associativity EOprtAssociativity eOprtAsct = GetOprtAssociativity(opt); if ( (eOprtAsct==oaRIGHT && (nPrec1 <= nPrec2)) || (eOprtAsct==oaLEFT && (nPrec1 < nPrec2)) ) { break; } } else if (nPrec1 < nPrec2) { // In case the operators are not equal the precedence decides alone... break; } if (stOpt.top().GetCode()==cmOPRT_INFIX) { ApplyFunc(stOpt, stVal, 1); } else { ApplyBinOprt(stOpt, stVal); } } // while ( ... ) if (opt.GetCode()==cmIF) { m_vRPN.AddIfElse(opt.GetCode()); } // The operator can't be evaluated right now, push back to the operator stack stOpt.push(opt); break; // // Last section contains functions and operators implicitely mapped to functions // case cmBO: stArgCount.push(1); stOpt.push(opt); break; case cmOPRT_INFIX: case cmFUNC: case cmFUNC_BULK: case cmFUNC_STR: stOpt.push(opt); break; case cmOPRT_POSTFIX: stOpt.push(opt); ApplyFunc(stOpt, stVal, 1); // this is the postfix operator break; case cmENDIF: case cmVARPOW2: case cmVARPOW3: case cmVARPOW4: case cmVARMUL: case cmPOW2: case cmUNKNOWN: default: Error(ecINTERNAL_ERROR, 3); } // end of switch operator-token opta = opt; if ( opt.GetCode() == cmEND ) { m_vRPN.Finalize(); break; } if (QmuParserBase::g_DbgDumpStack) { StackDump(stVal, stOpt); m_vRPN.AsciiDump(); } } // while (true) if (QmuParserBase::g_DbgDumpCmdCode) { m_vRPN.AsciiDump(); } if (m_nIfElseCounter>0) { Error(ecMISSING_ELSE_CLAUSE); } // get the last value (= final result) from the stack Q_ASSERT(stArgCount.size()==1); m_nFinalResultIdx = stArgCount.top(); if (m_nFinalResultIdx==0) { Error(ecINTERNAL_ERROR, 9); } if (stVal.size()==0) { Error(ecEMPTY_EXPRESSION); } if (stVal.top().GetType()!=tpDBL) { Error(ecSTR_RESULT); } m_vStackBuffer.resize(m_vRPN.GetMaxStackSize() * s_MaxNumOpenMPThreads); } //--------------------------------------------------------------------------------------------------------------------- /** * @brief One of the two main parse functions. * @sa ParseCmdCode(...) * * Parse expression from input string. Perform syntax checking and create bytecode. After parsing the string and * creating the bytecode the function pointer #m_pParseFormula will be changed to the second parse routine the * uses bytecode instead of string parsing. */ qreal QmuParserBase::ParseString() const { try { CreateRPN(); m_pParseFormula = &QmuParserBase::ParseCmdCode; return (this->*m_pParseFormula)(); } catch (qmu::QmuParserError &exc) { exc.SetFormula(m_pTokenReader->GetExpr()); throw; } } //--------------------------------------------------------------------------------------------------------------------- /** * @brief Create an error containing the parse error position. * * This function will create an Parser Exception object containing the error text and its position. * * @param a_iErrc [in] The error code of type #EErrorCodes. * @param a_iPos [in] The position where the error was detected. * @param a_strTok [in] The token string representation associated with the error. * @throw ParserException always throws thats the only purpose of this function. */ void Q_NORETURN QmuParserBase::Error(EErrorCodes a_iErrc, int a_iPos, const QString &a_sTok) const { throw qmu::QmuParserError (a_iErrc, a_sTok, m_pTokenReader->GetExpr(), a_iPos); } //--------------------------------------------------------------------------------------------------------------------- /** * @brief Clear all user defined variables. * @throw nothrow * * Resets the parser to string parsing mode by calling #ReInit. */ // cppcheck-suppress unusedFunction void QmuParserBase::ClearVar() Q_DECL_NOEXCEPT { m_VarDef.clear(); ReInit(); } //--------------------------------------------------------------------------------------------------------------------- /** * @brief Remove a variable from internal storage. * @throw nothrow * * Removes a variable if it exists. If the Variable does not exist nothing will be done. */ void QmuParserBase::RemoveVar(const QString &a_strVarName) Q_DECL_NOEXCEPT { varmap_type::iterator item = m_VarDef.find(a_strVarName); if (item!=m_VarDef.end()) { m_VarDef.erase(item); ReInit(); } } //--------------------------------------------------------------------------------------------------------------------- /** * @brief Clear all functions. * @post Resets the parser to string parsing mode. * @throw nothrow */ // cppcheck-suppress unusedFunction void QmuParserBase::ClearFun() Q_DECL_NOEXCEPT { m_FunDef.clear(); ReInit(); } //--------------------------------------------------------------------------------------------------------------------- /** * @brief Clear all user defined constants. * * Both numeric and string constants will be removed from the internal storage. * @post Resets the parser to string parsing mode. * @throw nothrow */ void QmuParserBase::ClearConst() Q_DECL_NOEXCEPT { m_ConstDef.clear(); m_StrVarDef.clear(); ReInit(); } //--------------------------------------------------------------------------------------------------------------------- /** * @brief Clear all user defined postfix operators. * @post Resets the parser to string parsing mode. * @throw nothrow */ void QmuParserBase::ClearPostfixOprt() Q_DECL_NOEXCEPT { m_PostOprtDef.clear(); ReInit(); } //--------------------------------------------------------------------------------------------------------------------- /** * @brief Clear all user defined binary operators. * @post Resets the parser to string parsing mode. * @throw nothrow */ // cppcheck-suppress unusedFunction void QmuParserBase::ClearOprt() Q_DECL_NOEXCEPT { m_OprtDef.clear(); ReInit(); } //--------------------------------------------------------------------------------------------------------------------- /** * @brief Clear the user defined Prefix operators. * @post Resets the parser to string parser mode. * @throw nothrow */ // cppcheck-suppress unusedFunction void QmuParserBase::ClearInfixOprt() Q_DECL_NOEXCEPT { m_InfixOprtDef.clear(); ReInit(); } //--------------------------------------------------------------------------------------------------------------------- /** * @brief Enable or disable the formula optimization feature. * @post Resets the parser to string parser mode. * @throw nothrow */ void QmuParserBase::EnableOptimizer(bool a_bIsOn) Q_DECL_NOEXCEPT { m_vRPN.EnableOptimizer(a_bIsOn); ReInit(); } //--------------------------------------------------------------------------------------------------------------------- /** * @brief Enable the dumping of bytecode amd stack content on the console. * @param bDumpCmd Flag to enable dumping of the current bytecode to the console. * @param bDumpStack Flag to enable dumping of the stack content is written to the console. * * This function is for debug purposes only! */ // cppcheck-suppress unusedFunction void QmuParserBase::EnableDebugDump(bool bDumpCmd, bool bDumpStack) { QmuParserBase::g_DbgDumpCmdCode = bDumpCmd; QmuParserBase::g_DbgDumpStack = bDumpStack; } //--------------------------------------------------------------------------------------------------------------------- /** * @brief Enable or disable the built in binary operators. * @throw nothrow * @sa m_bBuiltInOp, ReInit() * * If you disable the built in binary operators there will be no binary operators defined. Thus you must add them * manually one by one. It is not possible to disable built in operators selectively. This function will Reinitialize * the parser by calling ReInit(). */ void QmuParserBase::EnableBuiltInOprt(bool a_bIsOn) Q_DECL_NOEXCEPT { m_bBuiltInOp = a_bIsOn; ReInit(); } //--------------------------------------------------------------------------------------------------------------------- /** * @brief Get the argument separator character. */ QChar QmuParserBase::GetArgSep() const { return m_pTokenReader->GetArgSep(); } //--------------------------------------------------------------------------------------------------------------------- /** * @brief Set argument separator. * @param cArgSep the argument separator character. */ void QmuParserBase::SetArgSep(char_type cArgSep) { m_pTokenReader->SetArgSep(cArgSep); } //--------------------------------------------------------------------------------------------------------------------- /** * @brief Dump stack content. * * This function is used for debugging only. */ void QmuParserBase::StackDump(const QStack &a_stVal, const QStack &a_stOprt) const { QStack stOprt(a_stOprt), stVal(a_stVal); qDebug() << "\nValue stack:\n"; while ( stVal.empty() == false ) { token_type val = stVal.pop(); if (val.GetType()==tpSTR) { qDebug() << " \"" << val.GetAsString() << "\" "; } else { qDebug() << " " << val.GetVal() << " "; } } qDebug() << "\nOperator stack:\n"; while ( stOprt.empty() == false ) { if (stOprt.top().GetCode()<=cmASSIGN) { qDebug() << "OPRT_INTRNL \"" << QmuParserBase::c_DefaultOprt[stOprt.top().GetCode()] << "\" \n"; } else { switch ( stOprt.top().GetCode()) { case cmVAR: qDebug() << "VAR\n"; break; case cmVAL: qDebug() << "VAL\n"; break; case cmFUNC: qDebug() << "FUNC \"" << stOprt.top().GetAsString() << "\"\n"; break; case cmFUNC_BULK: qDebug() << "FUNC_BULK \"" << stOprt.top().GetAsString() << "\"\n"; break; case cmOPRT_INFIX: qDebug() << "OPRT_INFIX \"" << stOprt.top().GetAsString() << "\"\n"; break; case cmOPRT_BIN: qDebug() << "OPRT_BIN \"" << stOprt.top().GetAsString() << "\"\n"; break; case cmFUNC_STR: qDebug() << "FUNC_STR\n"; break; case cmEND: qDebug() << "END\n"; break; case cmUNKNOWN: qDebug() << "UNKNOWN\n"; break; case cmBO: qDebug() << "BRACKET \"(\"\n"; break; case cmBC: qDebug() << "BRACKET \")\"\n"; break; case cmIF: qDebug() << "IF\n"; break; case cmELSE: qDebug() << "ELSE\n"; break; case cmENDIF: qDebug() << "ENDIF\n"; break; case cmLE: case cmGE: case cmNEQ: case cmEQ: case cmLT: case cmGT: case cmADD: case cmSUB: case cmMUL: case cmDIV: case cmPOW: case cmLAND: case cmLOR: case cmASSIGN: case cmARG_SEP: case cmVARPOW2: case cmVARPOW3: case cmVARPOW4: case cmVARMUL: case cmPOW2: case cmSTRING: case cmOPRT_POSTFIX: default: qDebug() << stOprt.top().GetCode() << " "; break; } } stOprt.pop(); } qDebug() << dec; } //--------------------------------------------------------------------------------------------------------------------- /** @brief Evaluate an expression containing comma seperated subexpressions * @param [out] nStackSize The total number of results available * @return Pointer to the array containing all expression results * * This member function can be used to retriev all results of an expression made up of multiple comma seperated * subexpressions (i.e. "x+y,sin(x),cos(y)") */ qreal* QmuParserBase::Eval(int &nStackSize) const { (this->*m_pParseFormula)(); nStackSize = m_nFinalResultIdx; // (for historic reasons the stack starts at position 1) return &m_vStackBuffer[1]; } //--------------------------------------------------------------------------------------------------------------------- void QmuParserBase::Eval(qreal *results, int nBulkSize) const { CreateRPN(); int i = 0; #ifdef QMUP_USE_OPENMP //#define DEBUG_OMP_STUFF #ifdef DEBUG_OMP_STUFF int *pThread = new int[nBulkSize]; int *pIdx = new int[nBulkSize]; #endif int nMaxThreads = qMin(omp_get_max_threads(), s_MaxNumOpenMPThreads); // cppcheck-suppress variableScope int nThreadID; // cppcheck-suppress unreadVariable int ct=0; omp_set_num_threads(nMaxThreads); #pragma omp parallel for schedule(static, nBulkSize/nMaxThreads) private(nThreadID) for (i=0; i