diff --git a/src/libs/qmuparser/qmuparser.cpp b/src/libs/qmuparser/qmuparser.cpp index bfcf74bdf..05744f952 100644 --- a/src/libs/qmuparser/qmuparser.cpp +++ b/src/libs/qmuparser/qmuparser.cpp @@ -24,322 +24,366 @@ #include #include -//--- Standard includes ------------------------------------------------------------------------ -#include -#include - using namespace std; -/** \file - \brief Implementation of the standard floating point QmuParser. -*/ +/** + * @file + * @brief Implementation of the standard floating point QmuParser. + */ -/** \brief Namespace for mathematical applications. */ +/** + * @brief Namespace for mathematical applications. + */ namespace qmu { - //--------------------------------------------------------------------------- - // Trigonometric function - qreal QmuParser::Sinh(qreal v) { return sinh(v); } - qreal QmuParser::Cosh(qreal v) { return cosh(v); } - qreal QmuParser::Tanh(qreal v) { return tanh(v); } - qreal QmuParser::ASinh(qreal v) { return log(v + qSqrt(v * v + 1)); } - qreal QmuParser::ACosh(qreal v) { return log(v + qSqrt(v * v - 1)); } - qreal QmuParser::ATanh(qreal v) { return ((qreal)0.5 * log((1 + v) / (1 - v))); } +// Trigonometric function +qreal QmuParser::Sinh(qreal v) +{ + return sinh(v); +} - //--------------------------------------------------------------------------- - // Logarithm functions +qreal QmuParser::Cosh(qreal v) +{ + return cosh(v); +} - // Logarithm base 2 - qreal QmuParser::Log2(qreal v) - { - #ifdef MUP_MATH_EXCEPTIONS - if (v<=0) - throw QmuParserError(ecDOMAIN_ERROR, "Log2"); - #endif +qreal QmuParser::Tanh(qreal v) +{ + return tanh(v); +} - return log(v)/log((qreal)2); - } +qreal QmuParser::ASinh(qreal v) +{ + return log(v + qSqrt(v * v + 1)); +} - // Logarithm base 10 - qreal QmuParser::Log10(qreal v) - { - #ifdef MUP_MATH_EXCEPTIONS - if (v<=0) - throw QmuParserError(ecDOMAIN_ERROR, "Log10"); - #endif +qreal QmuParser::ACosh(qreal v) +{ + return log(v + qSqrt(v * v - 1)); +} - return log10(v); - } +qreal QmuParser::ATanh(qreal v) +{ + return ((qreal)0.5 * log((1 + v) / (1 - v))); +} +//---------------------------------------------------------------------------------------------------------------------- +// Logarithm functions - //--------------------------------------------------------------------------- - // misc - qreal QmuParser::Abs(qreal v) { return qAbs(v); } - qreal QmuParser::Rint(qreal v) { return qFloor(v + (qreal)0.5); } - qreal QmuParser::Sign(qreal v) { return (qreal)((v<0) ? -1 : (v>0) ? 1 : 0); } +// Logarithm base 2 +qreal QmuParser::Log2(qreal v) +{ +#ifdef MUP_MATH_EXCEPTIONS + if (v<=0) + { + throw QmuParserError(ecDOMAIN_ERROR, "Log2"); + } +#endif + return log(v)/log((qreal)2); +} - //--------------------------------------------------------------------------- - /** \brief Callback for the unary minus operator. - \param v The value to negate - \return -v - */ - qreal QmuParser::UnaryMinus(qreal v) - { - return -v; - } +// Logarithm base 10 +qreal QmuParser::Log10(qreal v) +{ +#ifdef MUP_MATH_EXCEPTIONS + if (v<=0) + { + throw QmuParserError(ecDOMAIN_ERROR, "Log10"); + } +#endif + return log10(v); +} - //--------------------------------------------------------------------------- - /** \brief Callback for adding multiple values. - \param [in] a_afArg Vector with the function arguments - \param [in] a_iArgc The size of a_afArg - */ - qreal QmuParser::Sum(const qreal *a_afArg, int a_iArgc) - { - if (!a_iArgc) - throw exception_type("too few arguments for function sum."); +//---------------------------------------------------------------------------------------------------------------------- +// misc +qreal QmuParser::Abs(qreal v) +{ + return qAbs(v); +} - qreal fRes=0; - for (int i=0; i0) ? 1 : 0); +} - qreal fRes=0; - for (int i=0; i> fVal; + stringstream_type::pos_type iEnd = stream.tellg(); // Position after reading -#if defined(_UNICODE) - std::wstring a_szExprStd = a_szExpr.toStdWString(); -#else - std::string a_szExprStd = a_szExpr.toStdString(); -#endif - stringstream_type stream(a_szExprStd); - stream.seekg(0); // todo: check if this really is necessary - stream.imbue(QmuParser::s_locale); - stream >> fVal; - stringstream_type::pos_type iEnd = stream.tellg(); // Position after reading + if (iEnd==(stringstream_type::pos_type)-1) + { + return 0; + } - if (iEnd==(stringstream_type::pos_type)-1) - return 0; + *a_iPos += (int)iEnd; + *a_fVal = fVal; + return 1; +} - *a_iPos += (int)iEnd; - *a_fVal = fVal; - return 1; - } +//---------------------------------------------------------------------------------------------------------------------- +/** + * @brief Constructor. + * + * Call QmuParserBase class constructor and trigger Function, Operator and Constant initialization. + */ +QmuParser::QmuParser():QmuParserBase() +{ + AddValIdent(IsVal); + InitCharSets(); + InitFun(); + InitConst(); + InitOprt(); +} - //--------------------------------------------------------------------------- - /** \brief Constructor. +//---------------------------------------------------------------------------------------------------------------------- +/** + * @brief Define the character sets. + * @sa DefineNameChars, DefineOprtChars, DefineInfixOprtChars + * + * This function is used for initializing the default character sets that define + * the characters to be useable in function and variable names and operators. + */ +void QmuParser::InitCharSets() +{ + DefineNameChars( "0123456789_abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" ); + DefineOprtChars( "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ+-*^/?<>=#!$%&|~'_{}" ); + DefineInfixOprtChars( "/+-*^?<>=#!$%&|~'_" ); +} - Call QmuParserBase class constructor and trigger Function, Operator and Constant initialization. - */ - QmuParser::QmuParser() - :QmuParserBase() - { - AddValIdent(IsVal); - - InitCharSets(); - InitFun(); - InitConst(); - InitOprt(); - } - - //--------------------------------------------------------------------------- - /** \brief Define the character sets. - \sa DefineNameChars, DefineOprtChars, DefineInfixOprtChars - - This function is used for initializing the default character sets that define - the characters to be useable in function and variable names and operators. - */ - void QmuParser::InitCharSets() - { - DefineNameChars( "0123456789_abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" ); - DefineOprtChars( "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ+-*^/?<>=#!$%&|~'_{}" ); - DefineInfixOprtChars( "/+-*^?<>=#!$%&|~'_" ); - } - -//--------------------------------------------------------------------------- -/** \brief Initialize the default functions. */ +//---------------------------------------------------------------------------------------------------------------------- +/** + * @brief Initialize the default functions. + */ void QmuParser::InitFun() { - // trigonometric functions - DefineFun("sin", qSin); - DefineFun("cos", qCos); - DefineFun("tan", qTan); - // arcus functions - DefineFun("asin", qAsin); - DefineFun("acos", qAcos); - DefineFun("atan", qAtan); - DefineFun("atan2", qAtan2); - // hyperbolic functions - DefineFun("sinh", Sinh); - DefineFun("cosh", Cosh); - DefineFun("tanh", Tanh); - // arcus hyperbolic functions - DefineFun("asinh", ASinh); - DefineFun("acosh", ACosh); - DefineFun("atanh", ATanh); - // Logarithm functions - DefineFun("log2", Log2); - DefineFun("log10", Log10); - DefineFun("log", Log10); - DefineFun("ln", qLn); - // misc - DefineFun("exp", qExp); - DefineFun("sqrt", qSqrt); - DefineFun("sign", Sign); - DefineFun("rint", Rint); - DefineFun("abs", Abs); - // Functions with variable number of arguments - DefineFun("sum", Sum); - DefineFun("avg", Avg); - DefineFun("min", Min); - DefineFun("max", Max); + // trigonometric functions + DefineFun("sin", qSin); + DefineFun("cos", qCos); + DefineFun("tan", qTan); + // arcus functions + DefineFun("asin", qAsin); + DefineFun("acos", qAcos); + DefineFun("atan", qAtan); + DefineFun("atan2", qAtan2); + // hyperbolic functions + DefineFun("sinh", Sinh); + DefineFun("cosh", Cosh); + DefineFun("tanh", Tanh); + // arcus hyperbolic functions + DefineFun("asinh", ASinh); + DefineFun("acosh", ACosh); + DefineFun("atanh", ATanh); + // Logarithm functions + DefineFun("log2", Log2); + DefineFun("log10", Log10); + DefineFun("log", Log10); + DefineFun("ln", qLn); + // misc + DefineFun("exp", qExp); + DefineFun("sqrt", qSqrt); + DefineFun("sign", Sign); + DefineFun("rint", Rint); + DefineFun("abs", Abs); + // Functions with variable number of arguments + DefineFun("sum", Sum); + DefineFun("avg", Avg); + DefineFun("min", Min); + DefineFun("max", Max); } - //--------------------------------------------------------------------------- - /** \brief Initialize constants. +//---------------------------------------------------------------------------------------------------------------------- +/** + * @brief Initialize constants. + * + * By default the QmuParser recognizes two constants. Pi ("pi") and the eulerian + * number ("_e"). + */ +void QmuParser::InitConst() +{ + DefineConst("_pi", (qreal)M_PI); + DefineConst("_e", (qreal)M_E); +} - By default the QmuParser recognizes two constants. Pi ("pi") and the eulerian - number ("_e"). - */ - void QmuParser::InitConst() - { - DefineConst("_pi", (qreal)M_PI); - DefineConst("_e", (qreal)M_E); - } +//---------------------------------------------------------------------------------------------------------------------- +/** + * @brief Initialize operators. + * + * By default only the unary minus operator is added. + */ +void QmuParser::InitOprt() +{ + DefineInfixOprt("-", UnaryMinus); +} - //--------------------------------------------------------------------------- - /** \brief Initialize operators. - - By default only the unary minus operator is added. - */ - void QmuParser::InitOprt() - { - DefineInfixOprt("-", UnaryMinus); - } - - //--------------------------------------------------------------------------- - void QmuParser::OnDetectVar(string_type * /*pExpr*/, int & /*nStart*/, int & /*nEnd*/) - { - // this is just sample code to illustrate modifying variable names on the fly. - // I'm not sure anyone really needs such a feature... - /* +//---------------------------------------------------------------------------------------------------------------------- +void QmuParser::OnDetectVar(string_type * /*pExpr*/, int & /*nStart*/, int & /*nEnd*/) +{ + // this is just sample code to illustrate modifying variable names on the fly. + // I'm not sure anyone really needs such a feature... + /* - string sVar(pExpr->begin()+nStart, pExpr->begin()+nEnd); - string sRepl = std::string("_") + sVar + "_"; + string sVar(pExpr->begin()+nStart, pExpr->begin()+nEnd); + string sRepl = std::string("_") + sVar + "_"; - int nOrigVarEnd = nEnd; - cout << "variable detected!\n"; - cout << " Expr: " << *pExpr << "\n"; - cout << " Start: " << nStart << "\n"; - cout << " End: " << nEnd << "\n"; - cout << " Var: \"" << sVar << "\"\n"; - cout << " Repl: \"" << sRepl << "\"\n"; - nEnd = nStart + sRepl.length(); - cout << " End: " << nEnd << "\n"; - pExpr->replace(pExpr->begin()+nStart, pExpr->begin()+nOrigVarEnd, sRepl); - cout << " New expr: " << *pExpr << "\n"; - */ - } + int nOrigVarEnd = nEnd; + cout << "variable detected!\n"; + cout << " Expr: " << *pExpr << "\n"; + cout << " Start: " << nStart << "\n"; + cout << " End: " << nEnd << "\n"; + cout << " Var: \"" << sVar << "\"\n"; + cout << " Repl: \"" << sRepl << "\"\n"; + nEnd = nStart + sRepl.length(); + cout << " End: " << nEnd << "\n"; + pExpr->replace(pExpr->begin()+nStart, pExpr->begin()+nOrigVarEnd, sRepl); + cout << " New expr: " << *pExpr << "\n"; + */ +} - //--------------------------------------------------------------------------- - /** \brief Numerically differentiate with regard to a variable. - \param [in] a_Var Pointer to the differentiation variable. - \param [in] a_fPos Position at which the differentiation should take place. - \param [in] a_fEpsilon Epsilon used for the numerical differentiation. +//---------------------------------------------------------------------------------------------------------------------- +/** + * @brief Numerically differentiate with regard to a variable. + * @param [in] a_Var Pointer to the differentiation variable. + * @param [in] a_fPos Position at which the differentiation should take place. + * @param [in] a_fEpsilon Epsilon used for the numerical differentiation. + * + * Numerical differentiation uses a 5 point operator yielding a 4th order + * formula. The default value for epsilon is 0.00074 which is + * numeric_limits::epsilon() ^ (1/5) as suggested in the muQmuParser + * forum: + * + * http://sourceforge.net/forum/forum.php?thread_id=1994611&forum_id=462843 + */ +qreal QmuParser::Diff(qreal *a_Var, qreal a_fPos, qreal a_fEpsilon) const +{ + qreal fRes(0), + fBuf(*a_Var), + f[4] = {0,0,0,0}, + fEpsilon(a_fEpsilon); - Numerical differentiation uses a 5 point operator yielding a 4th order - formula. The default value for epsilon is 0.00074 which is - numeric_limits::epsilon() ^ (1/5) as suggested in the muQmuParser - forum: + // Backwards compatible calculation of epsilon inc case the user doesnt provide + // his own epsilon + if (fEpsilon==0) + { + fEpsilon = (a_fPos==0) ? (qreal)1e-10 : (qreal)1e-7 * a_fPos; + } - http://sourceforge.net/forum/forum.php?thread_id=1994611&forum_id=462843 - */ - qreal QmuParser::Diff(qreal *a_Var, - qreal a_fPos, - qreal a_fEpsilon) const - { - qreal fRes(0), - fBuf(*a_Var), - f[4] = {0,0,0,0}, - fEpsilon(a_fEpsilon); + *a_Var = a_fPos+2 * fEpsilon; f[0] = Eval(); + *a_Var = a_fPos+1 * fEpsilon; f[1] = Eval(); + *a_Var = a_fPos-1 * fEpsilon; f[2] = Eval(); + *a_Var = a_fPos-2 * fEpsilon; f[3] = Eval(); + *a_Var = fBuf; // restore variable - // Backwards compatible calculation of epsilon inc case the user doesnt provide - // his own epsilon - if (fEpsilon==0) - fEpsilon = (a_fPos==0) ? (qreal)1e-10 : (qreal)1e-7 * a_fPos; - - *a_Var = a_fPos+2 * fEpsilon; f[0] = Eval(); - *a_Var = a_fPos+1 * fEpsilon; f[1] = Eval(); - *a_Var = a_fPos-1 * fEpsilon; f[2] = Eval(); - *a_Var = a_fPos-2 * fEpsilon; f[3] = Eval(); - *a_Var = fBuf; // restore variable - - fRes = (-f[0] + 8*f[1] - 8*f[2] + f[3]) / (12*fEpsilon); - return fRes; - } + fRes = (-f[0] + 8*f[1] - 8*f[2] + f[3]) / (12*fEpsilon); + return fRes; +} } // namespace qmu diff --git a/src/libs/qmuparser/qmuparser.h b/src/libs/qmuparser/qmuparser.h index e0b44d06a..685fecd02 100644 --- a/src/libs/qmuparser/qmuparser.h +++ b/src/libs/qmuparser/qmuparser.h @@ -24,67 +24,63 @@ #define QMUPARSER_H #include "qmuparser_global.h" - -//--- Parser includes -------------------------------------------------------------------------- #include "qmuparserbase.h" -/** \file - \brief Definition of the standard floating point parser. -*/ +/** + * @file + * @brief Definition of the standard floating point parser. + */ namespace qmu { - /** \brief Mathematical expressions parser. - - Standard implementation of the mathematical expressions parser. - Can be used as a reference implementation for subclassing the parser. - - - (C) 2011 Ingo Berg
- muparser(at)gmx.de -
- */ -/* final */ class QMUPARSERSHARED_EXPORT QmuParser : public QmuParserBase -{ - -public: - QmuParser(); - virtual void InitCharSets(); - virtual void InitFun(); - virtual void InitConst(); - virtual void InitOprt(); - virtual void OnDetectVar(string_type *pExpr, int &nStart, int &nEnd); - - qreal Diff(qreal *a_Var, qreal a_fPos, qreal a_fEpsilon = 0) const; -protected: - // Trigonometric functions - static qreal Tan2(qreal, qreal); - // hyperbolic functions - static qreal Sinh(qreal); - static qreal Cosh(qreal); - static qreal Tanh(qreal); - // arcus hyperbolic functions - static qreal ASinh(qreal); - static qreal ACosh(qreal); - static qreal ATanh(qreal); - // Logarithm functions - static qreal Log2(qreal); // Logarithm Base 2 - static qreal Log10(qreal); // Logarithm Base 10 - // misc - static qreal Abs(qreal); - static qreal Rint(qreal); - static qreal Sign(qreal); - // Prefix operators - // !!! Unary Minus is a MUST if you want to use negative signs !!! - static qreal UnaryMinus(qreal); - // Functions with variable number of arguments - static qreal Sum(const qreal*, int); // sum - static qreal Avg(const qreal*, int); // mean value - static qreal Min(const qreal*, int); // minimum - static qreal Max(const qreal*, int); // maximum - - static int IsVal(const QString &a_szExpr, int *a_iPos, qreal *a_fVal); -}; + /** @brief Mathematical expressions parser. + * + * Standard implementation of the mathematical expressions parser. + * Can be used as a reference implementation for subclassing the parser. + * + * + * (C) 2011 Ingo Berg
+ * muparser(at)gmx.de + *
+ */ + /* final */ class QMUPARSERSHARED_EXPORT QmuParser : public QmuParserBase + { + public: + QmuParser(); + virtual void InitCharSets(); + virtual void InitFun(); + virtual void InitConst(); + virtual void InitOprt(); + virtual void OnDetectVar(string_type *pExpr, int &nStart, int &nEnd); + qreal Diff(qreal *a_Var, qreal a_fPos, qreal a_fEpsilon = 0) const; + protected: + static int IsVal(const QString &a_szExpr, int *a_iPos, qreal *a_fVal); + // Trigonometric functions + static qreal Tan2(qreal, qreal); + // hyperbolic functions + static qreal Sinh(qreal); + static qreal Cosh(qreal); + static qreal Tanh(qreal); + // arcus hyperbolic functions + static qreal ASinh(qreal); + static qreal ACosh(qreal); + static qreal ATanh(qreal); + // Logarithm functions + static qreal Log2(qreal); // Logarithm Base 2 + static qreal Log10(qreal); // Logarithm Base 10 + // misc + static qreal Abs(qreal); + static qreal Rint(qreal); + static qreal Sign(qreal); + // Prefix operators + // !!! Unary Minus is a MUST if you want to use negative signs !!! + static qreal UnaryMinus(qreal); + // Functions with variable number of arguments + static qreal Sum(const qreal*, int); // sum + static qreal Avg(const qreal*, int); // mean value + static qreal Min(const qreal*, int); // minimum + static qreal Max(const qreal*, int); // maximum + }; } // namespace qmu diff --git a/src/libs/qmuparser/qmuparserbase.cpp b/src/libs/qmuparser/qmuparserbase.cpp index 7b7d1e9c3..0f46773c7 100644 --- a/src/libs/qmuparser/qmuparserbase.cpp +++ b/src/libs/qmuparser/qmuparserbase.cpp @@ -21,1762 +21,1980 @@ ******************************************************************************************************/ #include "qmuparserbase.h" - -//--- Standard includes ------------------------------------------------------------------------ -#include -#include -#include -#include -#include -#include - +#include +#include #ifdef QMUP_USE_OPENMP - #include + #include #endif using namespace std; -/** \file - \brief This file contains the basic implementation of the muparser engine. -*/ +/** + * @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('.')); +std::locale QmuParserBase::s_locale = std::locale(std::locale::classic(), new change_dec_sep('.')); - bool QmuParserBase::g_DbgDumpCmdCode = false; - bool QmuParserBase::g_DbgDumpStack = false; +bool QmuParserBase::g_DbgDumpCmdCode = false; +bool QmuParserBase::g_DbgDumpStack = false; - //------------------------------------------------------------------------------ - /** \brief Identifiers for built in binary operators. +/** + * @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() << "<=" << ">=" << "!=" << "==" << "<" << ">" + << "+" << "-" << "*" << "/" << "^" << "&&" + << "||" << "=" << "(" << ")" << "?" << ":"; - 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_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 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_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_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_pTokenReader.reset(new token_reader_type(this)); + Assign(a_Parser); +} - //--------------------------------------------------------------------------- - /** \brief Copy constructor. +//---------------------------------------------------------------------------------------------------------------------- +QmuParserBase::~QmuParserBase() +{} - 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_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_pTokenReader.reset(new token_reader_type(this)); - Assign(a_Parser); - } +//---------------------------------------------------------------------------------------------------------------------- +/** + * @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) +{ + Assign(a_Parser); + return *this; +} - //--------------------------------------------------------------------------- - QmuParserBase::~QmuParserBase() - {} +//---------------------------------------------------------------------------------------------------------------------- +/** + * @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; + } - //--------------------------------------------------------------------------- - /** \brief Assignement operator. + // Don't copy bytecode instead cause the parser to create new bytecode + // by resetting the parse function. + ReInit(); - 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) - { - Assign(a_Parser); - return *this; - } + 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)); - //--------------------------------------------------------------------------- - /** \brief Copy state of a parser object to 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 - Clears Variables and Functions of this parser. - Copies the states of all internal variables. - Resets parse function to string parse mode. + m_sNameChars = a_Parser.m_sNameChars; + m_sOprtChars = a_Parser.m_sOprtChars; + m_sInfixOprtChars = a_Parser.m_sInfixOprtChars; +} - \param a_Parser the source object. - */ - void QmuParserBase::Assign(const QmuParserBase &a_Parser) - { - if (&a_Parser==this) - return; +//---------------------------------------------------------------------------------------------------------------------- +/** + * @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. + */ +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)); +} - // Don't copy bytecode instead cause the parser to create new bytecode - // by resetting the parse function. - ReInit(); +//---------------------------------------------------------------------------------------------------------------------- +/** + * @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. + */ +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)); +} - 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)); +//---------------------------------------------------------------------------------------------------------------------- +/** + * @brief Resets the locale. + * + * The default locale used "." as decimal separator, no thousands separator and "," as function argument separator. + */ +void QmuParserBase::ResetLocale() +{ + s_locale = std::locale(std::locale("C"), new change_dec_sep('.')); + SetArgSep(','); +} - // 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 +//---------------------------------------------------------------------------------------------------------------------- +/** + * @brief Initialize the token reader. + * + * Create new token reader object and submit pointers to function, operator, constant and variable definitions. + * + * @post m_pTokenReader.get()!=0 + * @throw nothrow + */ +void QmuParserBase::InitTokenReader() +{ + m_pTokenReader.reset(new token_reader_type(this)); +} - m_sNameChars = a_Parser.m_sNameChars; - m_sOprtChars = a_Parser.m_sOprtChars; - m_sInfixOprtChars = a_Parser.m_sInfixOprtChars; - } +//---------------------------------------------------------------------------------------------------------------------- +/** + * @brief Reset parser to string parsing mode and clear internal buffers. + * + * Clear bytecode, reset the token reader. + * @throw nothrow + */ +void QmuParserBase::ReInit() const +{ + m_pParseFormula = &QmuParserBase::ParseString; + m_vStringBuf.clear(); + m_vRPN.clear(); + m_pTokenReader->ReInit(); + m_nIfElseCounter = 0; +} - //--------------------------------------------------------------------------- - /** \brief Set the decimal separator. - \param cDecSep Decimal separator as a character value. - \sa SetThousandsSep +//---------------------------------------------------------------------------------------------------------------------- +void QmuParserBase::OnDetectVar(QString * /*pExpr*/, int & /*nStart*/, int & /*nEnd*/) +{} - By default muparser uses the "C" locale. The decimal separator of this - locale is overwritten by the one provided here. - */ - 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 +//---------------------------------------------------------------------------------------------------------------------- +/** + * @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. + */ +QString QmuParserBase::GetVersion(EParserVersionInfo eInfo) const +{ + QString versionInfo; + QTextStream ss(&versionInfo); - By default muparser uses the "C" locale. The thousands separator of this - locale is overwritten by the one provided here. - */ - 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)); - } + ss << QMUP_VERSION; - //--------------------------------------------------------------------------- - /** \brief Resets the locale. + if (eInfo==pviFULL) + { + ss << " (" << QMUP_VERSION_DATE; + ss << "; " << sizeof(void*)*8 << "BIT"; - The default locale used "." as decimal separator, no thousands separator and - "," as function argument separator. - */ - void QmuParserBase::ResetLocale() - { - s_locale = std::locale(std::locale("C"), new change_dec_sep('.')); - SetArgSep(','); - } + #ifdef _DEBUG + ss << "; DEBUG"; + #else + ss << "; RELEASE"; + #endif - //--------------------------------------------------------------------------- - /** \brief Initialize the token reader. + #ifdef _UNICODE + ss << "; UNICODE"; + #else + #ifdef _MBCS + ss << "; MBCS"; + #else + ss << "; ASCII"; + #endif + #endif - Create new token reader object and submit pointers to function, operator, - constant and variable definitions. + #ifdef QMUP_USE_OPENMP + ss << "; OPENMP"; + //#else + // ss << "; NO_OPENMP"; + #endif - \post m_pTokenReader.get()!=0 - \throw nothrow - */ - void QmuParserBase::InitTokenReader() - { - m_pTokenReader.reset(new token_reader_type(this)); - } + #if defined(MUP_MATH_EXCEPTIONS) + ss << "; MATHEXC"; + //#else + // ss << "; NO_MATHEXC"; + #endif - //--------------------------------------------------------------------------- - /** \brief Reset parser to string parsing mode and clear internal buffers. + ss << ")"; + } + return versionInfo; +} - Clear bytecode, reset the token reader. - \throw nothrow - */ - void QmuParserBase::ReInit() const - { - m_pParseFormula = &QmuParserBase::ParseString; - m_vStringBuf.clear(); - m_vRPN.clear(); - m_pTokenReader->ReInit(); - m_nIfElseCounter = 0; - } +//---------------------------------------------------------------------------------------------------------------------- +/** + * @brief Add a value parsing function. + * + * When parsing an expression muParser tries to detect values in the expression string using different valident + * callbacks. Thuis it's possible to parse for hex values, binary values and floating point values. + */ +void QmuParserBase::AddValIdent(identfun_type a_pCallback) +{ + m_pTokenReader->AddValIdent(a_pCallback); +} - //--------------------------------------------------------------------------- - void QmuParserBase::OnDetectVar(QString * /*pExpr*/, int & /*nStart*/, int & /*nEnd*/) - {} +//---------------------------------------------------------------------------------------------------------------------- +/** + * @brief Set a function that can create variable pointer for unknown expression variables. + * @param a_pFactory A pointer to the variable factory. + * @param pUserData A user defined context pointer. + */ +void QmuParserBase::SetVarFactory(facfun_type a_pFactory, void *pUserData) +{ + m_pTokenReader->SetVarCreator(a_pFactory, pUserData); +} - //--------------------------------------------------------------------------- - /** \brief Returns the version of muparser. - \param eInfo A flag indicating whether the full version info should be - returned or not. +//---------------------------------------------------------------------------------------------------------------------- +/** + * @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); + } - Format is as follows: "MAJOR.MINOR (COMPILER_FLAGS)" The COMPILER_FLAGS - are returned only if eInfo==pviFULL. - */ - string_type QmuParserBase::GetVersion(EParserVersionInfo eInfo) const - { - string_type sCompileTimeSettings; - - stringstream_type ss; + const funmap_type *pFunMap = &a_Storage; - ss << QMUP_VERSION; + // 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 (eInfo==pviFULL) - { - ss << " (" << QMUP_VERSION_DATE; - ss << std::dec << "; " << sizeof(void*)*8 << "BIT"; + if ( pFunMap!=&m_PostOprtDef && m_PostOprtDef.find(a_strName)!=m_PostOprtDef.end() ) + { + Error(ecNAME_CONFLICT, -1, a_strName); + } -#ifdef _DEBUG - ss << "; DEBUG"; -#else - ss << "; RELEASE"; -#endif + if ( pFunMap!=&m_InfixOprtDef && pFunMap!=&m_OprtDef && m_InfixOprtDef.find(a_strName)!=m_InfixOprtDef.end() ) + { + Error(ecNAME_CONFLICT, -1, a_strName); + } -#ifdef _UNICODE - ss << "; UNICODE"; -#else - #ifdef _MBCS - ss << "; MBCS"; - #else - ss << "; ASCII"; - #endif -#endif + if ( pFunMap!=&m_InfixOprtDef && pFunMap!=&m_OprtDef && m_OprtDef.find(a_strName)!=m_OprtDef.end() ) + { + Error(ecNAME_CONFLICT, -1, a_strName); + } -#ifdef QMUP_USE_OPENMP - ss << "; OPENMP"; -//#else -// ss << "; NO_OPENMP"; -#endif + CheckOprt(a_strName, a_Callback, a_szCharSet); + a_Storage[a_strName] = a_Callback; + ReInit(); +} -#if defined(MUP_MATH_EXCEPTIONS) - ss << "; MATHEXC"; -//#else -// ss << "; NO_MATHEXC"; -#endif - - ss << ")"; - } - - return ss.str(); - } - - //--------------------------------------------------------------------------- - /** \brief Add a value parsing function. - - When parsing an expression muParser tries to detect values in the expression - string using different valident callbacks. Thuis it's possible to parse - for hex values, binary values and floating point values. - */ - void QmuParserBase::AddValIdent(identfun_type a_pCallback) - { - m_pTokenReader->AddValIdent(a_pCallback); - } - - //--------------------------------------------------------------------------- - /** \brief Set a function that can create variable pointer for unknown expression variables. - \param a_pFactory A pointer to the variable factory. - \param pUserData A user defined context pointer. - */ - void QmuParserBase::SetVarFactory(facfun_type a_pFactory, void *pUserData) - { - m_pTokenReader->SetVarCreator(a_pFactory, pUserData); - } - - //--------------------------------------------------------------------------- - /** \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 - { +//---------------------------------------------------------------------------------------------------------------------- +/** + * @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(); + 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(); + const std::string a_sNameStd = a_sName.toStdString(); + const std::string a_szCharSetStd = a_szCharSet.toStdString(); #endif - if ( !a_sNameStd.length() || (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); - case cmOPRT_INFIX: Error(ecINVALID_INFIX_IDENT, -1, a_sName); - default: Error(ecINVALID_NAME, -1, a_sName); - } - } - } + if ( !a_sNameStd.length() || (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; + default: + Error(ecINVALID_NAME, -1, a_sName); + break; + } + } +} -//--------------------------------------------------------------------------- -/** \brief Check if a name contains invalid characters. - - \throw ParserException if the name contains invalid charakters. -*/ +//---------------------------------------------------------------------------------------------------------------------- +/** + * @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() || (a_sNameStd.find_first_not_of(a_szCharSetStd)!=string_type::npos) || - (a_sNameStd[0]>='0' && a_sNameStd[0]<='9')) - { - Error(ecINVALID_NAME); - } +#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() || (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. -*/ +//---------------------------------------------------------------------------------------------------------------------- +/** + * @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); - } + // 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(); + // 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 Get the default symbols used for the built in operators. - \sa c_DefaultOprt - */ - const QStringList &QmuParserBase::GetOprtDef() const - { - return c_DefaultOprt; - } +//---------------------------------------------------------------------------------------------------------------------- +/** + * @brief Get the default symbols used for the built in operators. + * @sa c_DefaultOprt + */ +const QStringList &QmuParserBase::GetOprtDef() const +{ + return c_DefaultOprt; +} - //--------------------------------------------------------------------------- - /** \brief Define the set of valid characters to be used in names of - functions, variables, constants. - */ - void QmuParserBase::DefineNameChars(const QString &a_szCharset) - { - m_sNameChars = a_szCharset; - } +//---------------------------------------------------------------------------------------------------------------------- +/** + * @brief Define the set of valid characters to be used in names of functions, variables, constants. + */ +void QmuParserBase::DefineNameChars(const QString &a_szCharset) +{ + m_sNameChars = a_szCharset; +} - //--------------------------------------------------------------------------- - /** \brief Define the set of valid characters to be used in names of - binary operators and postfix operators. - */ - void QmuParserBase::DefineOprtChars(const QString &a_szCharset) - { - m_sOprtChars = a_szCharset; - } +//---------------------------------------------------------------------------------------------------------------------- +/** + * @brief Define the set of valid characters to be used in names of binary operators and postfix operators. + */ +void QmuParserBase::DefineOprtChars(const QString &a_szCharset) +{ + m_sOprtChars = a_szCharset; +} - //--------------------------------------------------------------------------- - /** \brief Define the set of valid characters to be used in names of - infix operators. - */ - void QmuParserBase::DefineInfixOprtChars(const QString &a_szCharset) - { - m_sInfixOprtChars = a_szCharset; - } +//---------------------------------------------------------------------------------------------------------------------- +/** + * @brief Define the set of valid characters to be used in names of infix operators. + */ +void QmuParserBase::DefineInfixOprtChars(const QString &a_szCharset) +{ + m_sInfixOprtChars = a_szCharset; +} -//--------------------------------------------------------------------------- -/** \brief Virtual function that defines the characters allowed in name identifiers. - \sa #ValidOprtChars, #ValidPrefixOprtChars -*/ +//---------------------------------------------------------------------------------------------------------------------- +/** + * @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; + 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 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 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 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(). - */ - void QmuParserBase::Init() - { - InitCharSets(); - InitFun(); - InitConst(); - InitOprt(); - } +//---------------------------------------------------------------------------------------------------------------------- +/** + * @brief Initialize user defined functions. + * + * Calls the virtual functions InitFun(), InitConst() and InitOprt(). + */ +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 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(exception_type &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 e; + } + return m_pTokenReader->GetUsedVar(); +} + +//---------------------------------------------------------------------------------------------------------------------- +/** + * @brief Return a map containing the used variables only. + */ +const varmap_type& QmuParserBase::GetVar() const +{ + return m_VarDef; +} + +//---------------------------------------------------------------------------------------------------------------------- +/** + * @brief Return a map containing all parser constants. + */ +const valmap_type& QmuParserBase::GetConst() const +{ + return m_ConstDef; +} + +//---------------------------------------------------------------------------------------------------------------------- +/** + * @brief Return prototypes of all parser functions. + * @return #m_FunDef + * @sa FunProt + * @throw nothrow + * + * The return type is a map of the public type #funmap_type containing the prototype definitions for all numerical + * parser functions. String functions are not part of this map. The Prototype definition is encapsulated in objects + * of the class FunProt one per parser function each associated with function names via a map construct. + */ +const funmap_type& QmuParserBase::GetFunDef() const +{ + return m_FunDef; +} + +//---------------------------------------------------------------------------------------------------------------------- +/** + * @brief Retrieve the formula. + */ +const QString& QmuParserBase::GetExpr() const +{ + return m_pTokenReader->GetExpr(); +} + +//---------------------------------------------------------------------------------------------------------------------- +/** + * @brief Execute a function that takes a single string argument. + * @param a_FunTok Function token. + * @throw exception_type 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 exception_type 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(), (int)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; + } + // 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( (vExpr.GetVal()!=0) ? 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; + 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] = Stack[sidx] != Stack[sidx+1]; + continue; + case cmEQ: + --sidx; + Stack[sidx] = 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; + Stack[sidx] = Stack[sidx] && Stack[sidx+1]; + continue; + case cmLOR: + --sidx; + Stack[sidx] = Stack[sidx] || Stack[sidx+1]; + continue; + case cmASSIGN: + --sidx; + Stack[sidx] = *pTok->Oprt.ptr = Stack[sidx+1]; + continue; + //case cmBO: // unused, listed for compiler optimization purposes + //case cmBC: + // Q_ASSERT(INVALID_CODE_IN_BYTECODE); + // continue; + case cmIF: + if (Stack[sidx--]==0) + { + pTok += pTok->Oprt.offset; + } + continue; + case cmELSE: + pTok += pTok->Oprt.offset; + continue; + case cmENDIF: + continue; + //case cmARG_SEP: + // Q_ASSERT(INVALID_CODE_IN_BYTECODE); + // 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] = (*(fun_type0)pTok->Fun.ptr)(); + continue; + case 1: + Stack[sidx] = (*(fun_type1)pTok->Fun.ptr)(Stack[sidx]); + continue; + case 2: + sidx -= 1; + Stack[sidx] = (*(fun_type2)pTok->Fun.ptr)(Stack[sidx], Stack[sidx+1]); + continue; + case 3: + sidx -= 2; + Stack[sidx] = (*(fun_type3)pTok->Fun.ptr)(Stack[sidx], Stack[sidx+1], Stack[sidx+2]); + continue; + case 4: + sidx -= 3; + Stack[sidx] = (*(fun_type4)pTok->Fun.ptr)(Stack[sidx], Stack[sidx+1], Stack[sidx+2], + Stack[sidx+3]); + continue; + case 5: + sidx -= 4; + Stack[sidx] = (*(fun_type5)pTok->Fun.ptr)(Stack[sidx], Stack[sidx+1], Stack[sidx+2], + Stack[sidx+3], Stack[sidx+4]); + continue; + case 6: + sidx -= 5; + Stack[sidx] = (*(fun_type6)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] = (*(fun_type7)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] = (*(fun_type8)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] = (*(fun_type9)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] = (*(fun_type10)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] =(*(multfun_type)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 && iIdxStack<(int)m_vStringBuf.size() ); + + switch(pTok->Fun.argc) // switch according to argument count + { + case 0: + Stack[sidx] = (*(strfun_type1)pTok->Fun.ptr)(m_vStringBuf.at(iIdxStack)); + continue; + case 1: + Stack[sidx] = (*(strfun_type2)pTok->Fun.ptr)(m_vStringBuf.at(iIdxStack), Stack[sidx]); + continue; + case 2: + Stack[sidx] = (*(strfun_type3)pTok->Fun.ptr)(m_vStringBuf.at(iIdxStack), Stack[sidx], + Stack[sidx+1]); + continue; + } + + continue; + } + + case cmFUNC_BULK: + { + int iArgCount = pTok->Fun.argc; + + // switch according to argument count + switch(iArgCount) + { + case 0: + sidx += 1; + Stack[sidx] = (*(bulkfun_type0 )pTok->Fun.ptr)(nOffset, nThreadID); + continue; + case 1: + Stack[sidx] = (*(bulkfun_type1 )pTok->Fun.ptr)(nOffset, nThreadID, Stack[sidx]); + continue; + case 2: + sidx -= 1; + Stack[sidx] = (*(bulkfun_type2 )pTok->Fun.ptr)(nOffset, nThreadID, Stack[sidx], + Stack[sidx+1]); + continue; + case 3: + sidx -= 2; + Stack[sidx] = (*(bulkfun_type3 )pTok->Fun.ptr)(nOffset, nThreadID, Stack[sidx], + Stack[sidx+1], Stack[sidx+2]); + continue; + case 4: + sidx -= 3; + Stack[sidx] = (*(bulkfun_type4 )pTok->Fun.ptr)(nOffset, nThreadID, Stack[sidx], + Stack[sidx+1], Stack[sidx+2], Stack[sidx+3]); + continue; + case 5: + sidx -= 4; + Stack[sidx] = (*(bulkfun_type5 )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] = (*(bulkfun_type6 )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] = (*(bulkfun_type7 )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] = (*(bulkfun_type8 )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] = (*(bulkfun_type9 )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] = (*(bulkfun_type10)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]; + + 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()) + { + 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((int)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; + 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(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 QmuParserBase::Error(EErrorCodes a_iErrc, int a_iPos, const QString &a_sTok) const +{ + throw exception_type(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. + */ +void QmuParserBase::ClearVar() +{ + 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) +{ + 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 + */ +void QmuParserBase::ClearFun() +{ + 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() +{ + 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() +{ + m_PostOprtDef.clear(); + ReInit(); +} + +//---------------------------------------------------------------------------------------------------------------------- +/** + * @brief Clear all user defined binary operators. + * @post Resets the parser to string parsing mode. + * @throw nothrow + */ +void QmuParserBase::ClearOprt() +{ + m_OprtDef.clear(); + ReInit(); +} + +//---------------------------------------------------------------------------------------------------------------------- +/** + * @brief Clear the user defined Prefix operators. + * @post Resets the parser to string parser mode. + * @throw nothrow + */ +void QmuParserBase::ClearInfixOprt() +{ + 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) +{ + 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! + */ +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) +{ + m_bBuiltInOp = a_bIsOn; + ReInit(); +} + +//---------------------------------------------------------------------------------------------------------------------- +/** + * @brief Query status of built in variables. + * @return #m_bBuiltInOp; true if built in operators are enabled. + * @throw nothrow + */ +bool QmuParserBase::HasBuiltInOprt() const +{ + return m_bBuiltInOp; +} + +//---------------------------------------------------------------------------------------------------------------------- +/** + * @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() ) + { + token_type val = stVal.pop(); + if (val.GetType()==tpSTR) + { + qDebug() << " \"" << val.GetAsString() << "\" "; + } + else + { + qDebug() << " " << val.GetVal() << " "; + } + } + qDebug() << "\nOperator stack:\n"; + + while ( !stOprt.empty() ) + { + 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; + 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)") */ - void QmuParserBase::DefineVar(const QString &a_sName, qreal *a_pVar) - { - if (a_pVar==0) - { - Error(ecINVALID_VAR_PTR); - } - - // Test if a constant with that names already exists - if (m_ConstDef.find(a_sName)!=m_ConstDef.end()) - { - Error(ecNAME_CONFLICT); - } - - CheckName(a_sName, ValidNameChars()); - m_VarDef[a_sName] = a_pVar; - ReInit(); - } - - //--------------------------------------------------------------------------- - /** \brief Add a user defined constant. - \param [in] a_sName The name of the constant. - \param [in] a_fVal the value of the constant. - \post Will reset the Parser to string parsing mode. - \throw ParserException in case the name contains invalid signs. - */ - void QmuParserBase::DefineConst(const QString &a_sName, qreal a_fVal) - { - CheckName(a_sName, ValidNameChars()); - m_ConstDef[a_sName] = a_fVal; - ReInit(); - } - - //--------------------------------------------------------------------------- - /** \brief Get operator priority. - \throw ParserException if a_Oprt is no operator code - */ - int QmuParserBase::GetOprtPrecedence(const token_type &a_Tok) const - { - switch (a_Tok.GetCode()) - { - // built in operators - case cmEND: return -5; - case cmARG_SEP: return -4; - case cmASSIGN: return -1; - case cmELSE: - case cmIF: return 0; - case cmLAND: return prLAND; - case cmLOR: return prLOR; - case cmLT: - case cmGT: - case cmLE: - case cmGE: - case cmNEQ: - case cmEQ: return prCMP; - case cmADD: - case cmSUB: return prADD_SUB; - case cmMUL: - case cmDIV: return prMUL_DIV; - case cmPOW: return prPOW; - - // user defined binary operators - case cmOPRT_INFIX: - case cmOPRT_BIN: return a_Tok.GetPri(); - default: Error(ecINTERNAL_ERROR, 5); - return 999; - } - } - - //--------------------------------------------------------------------------- - /** \brief Get operator priority. - \throw ParserException if a_Oprt is no operator code - */ - EOprtAssociativity QmuParserBase::GetOprtAssociativity(const token_type &a_Tok) const - { - switch (a_Tok.GetCode()) - { - case cmASSIGN: - case cmLAND: - case cmLOR: - case cmLT: - case cmGT: - case cmLE: - case cmGE: - case cmNEQ: - case cmEQ: - case cmADD: - case cmSUB: - case cmMUL: - case cmDIV: return oaLEFT; - case cmPOW: return oaRIGHT; - case cmOPRT_BIN: return a_Tok.GetAssociativity(); - default: return oaNONE; - } - } - - //--------------------------------------------------------------------------- - /** \brief Return a map containing the used variables only. */ - const varmap_type& QmuParserBase::GetUsedVar() const - { - try - { - m_pTokenReader->IgnoreUndefVar(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(exception_type &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 e; - } - - return m_pTokenReader->GetUsedVar(); - } - - //--------------------------------------------------------------------------- - /** \brief Return a map containing the used variables only. */ - const varmap_type& QmuParserBase::GetVar() const - { - return m_VarDef; - } - - //--------------------------------------------------------------------------- - /** \brief Return a map containing all parser constants. */ - const valmap_type& QmuParserBase::GetConst() const - { - return m_ConstDef; - } - - //--------------------------------------------------------------------------- - /** \brief Return prototypes of all parser functions. - \return #m_FunDef - \sa FunProt - \throw nothrow - - The return type is a map of the public type #funmap_type containing the prototype - definitions for all numerical parser functions. String functions are not part of - this map. The Prototype definition is encapsulated in objects of the class FunProt - one per parser function each associated with function names via a map construct. - */ - const funmap_type& QmuParserBase::GetFunDef() const - { - return m_FunDef; - } - - //--------------------------------------------------------------------------- - /** \brief Retrieve the formula. */ - const QString& QmuParserBase::GetExpr() const - { - return m_pTokenReader->GetExpr(); - } - - //--------------------------------------------------------------------------- - /** \brief Execute a function that takes a single string argument. - \param a_FunTok Function token. - \throw exception_type 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); - } - } - 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 exception_type 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(), (int)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; - } - - // 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( (vExpr.GetVal()!=0) ? 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; - - default: - Error(ecINTERNAL_ERROR); - } - } - } - - //--------------------------------------------------------------------------- - /** \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] = Stack[sidx] != Stack[sidx+1]; continue; - case cmEQ: --sidx; Stack[sidx] = 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] = std::pow(Stack[sidx], Stack[1+sidx]); - continue; - - case cmLAND: --sidx; Stack[sidx] = Stack[sidx] && Stack[sidx+1]; continue; - case cmLOR: --sidx; Stack[sidx] = Stack[sidx] || Stack[sidx+1]; continue; - - case cmASSIGN: - --sidx; Stack[sidx] = *pTok->Oprt.ptr = Stack[sidx+1]; continue; - - //case cmBO: // unused, listed for compiler optimization purposes - //case cmBC: - // Q_ASSERT(INVALID_CODE_IN_BYTECODE); - // continue; - - case cmIF: - if (Stack[sidx--]==0) - pTok += pTok->Oprt.offset; - continue; - - case cmELSE: - pTok += pTok->Oprt.offset; - continue; - - case cmENDIF: - continue; - - //case cmARG_SEP: - // Q_ASSERT(INVALID_CODE_IN_BYTECODE); - // 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] = (*(fun_type0)pTok->Fun.ptr)(); continue; - case 1: Stack[sidx] = (*(fun_type1)pTok->Fun.ptr)(Stack[sidx]); continue; - case 2: sidx -= 1; Stack[sidx] = (*(fun_type2)pTok->Fun.ptr)(Stack[sidx], Stack[sidx+1]); continue; - case 3: sidx -= 2; Stack[sidx] = (*(fun_type3)pTok->Fun.ptr)(Stack[sidx], Stack[sidx+1], Stack[sidx+2]); continue; - case 4: sidx -= 3; Stack[sidx] = (*(fun_type4)pTok->Fun.ptr)(Stack[sidx], Stack[sidx+1], Stack[sidx+2], Stack[sidx+3]); continue; - case 5: sidx -= 4; Stack[sidx] = (*(fun_type5)pTok->Fun.ptr)(Stack[sidx], Stack[sidx+1], Stack[sidx+2], Stack[sidx+3], Stack[sidx+4]); continue; - case 6: sidx -= 5; Stack[sidx] = (*(fun_type6)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] = (*(fun_type7)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] = (*(fun_type8)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] = (*(fun_type9)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] = (*(fun_type10)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] =(*(multfun_type)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 && iIdxStack<(int)m_vStringBuf.size() ); - - switch(pTok->Fun.argc) // switch according to argument count - { - case 0: Stack[sidx] = (*(strfun_type1)pTok->Fun.ptr)(m_vStringBuf.at(iIdxStack)); continue; - case 1: Stack[sidx] = (*(strfun_type2)pTok->Fun.ptr)(m_vStringBuf.at(iIdxStack), Stack[sidx]); continue; - case 2: Stack[sidx] = (*(strfun_type3)pTok->Fun.ptr)(m_vStringBuf.at(iIdxStack), Stack[sidx], Stack[sidx+1]); continue; - } - - continue; - } - - case cmFUNC_BULK: - { - int iArgCount = pTok->Fun.argc; - - // switch according to argument count - switch(iArgCount) - { - case 0: sidx += 1; Stack[sidx] = (*(bulkfun_type0 )pTok->Fun.ptr)(nOffset, nThreadID); continue; - case 1: Stack[sidx] = (*(bulkfun_type1 )pTok->Fun.ptr)(nOffset, nThreadID, Stack[sidx]); continue; - case 2: sidx -= 1; Stack[sidx] = (*(bulkfun_type2 )pTok->Fun.ptr)(nOffset, nThreadID, Stack[sidx], Stack[sidx+1]); continue; - case 3: sidx -= 2; Stack[sidx] = (*(bulkfun_type3 )pTok->Fun.ptr)(nOffset, nThreadID, Stack[sidx], Stack[sidx+1], Stack[sidx+2]); continue; - case 4: sidx -= 3; Stack[sidx] = (*(bulkfun_type4 )pTok->Fun.ptr)(nOffset, nThreadID, Stack[sidx], Stack[sidx+1], Stack[sidx+2], Stack[sidx+3]); continue; - case 5: sidx -= 4; Stack[sidx] = (*(bulkfun_type5 )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] = (*(bulkfun_type6 )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] = (*(bulkfun_type7 )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] = (*(bulkfun_type8 )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] = (*(bulkfun_type9 )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] = (*(bulkfun_type10)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]; - - 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()) - 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((int)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; - - 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(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 QmuParserBase::Error(EErrorCodes a_iErrc, int a_iPos, const QString &a_sTok) const - { - throw exception_type(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. - */ - void QmuParserBase::ClearVar() - { - 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) - { - 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 - */ - void QmuParserBase::ClearFun() - { - 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() - { - 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() - { - m_PostOprtDef.clear(); - ReInit(); - } - - //------------------------------------------------------------------------------ - /** \brief Clear all user defined binary operators. - \post Resets the parser to string parsing mode. - \throw nothrow - */ - void QmuParserBase::ClearOprt() - { - m_OprtDef.clear(); - ReInit(); - } - - //------------------------------------------------------------------------------ - /** \brief Clear the user defined Prefix operators. - \post Resets the parser to string parser mode. - \throw nothrow - */ - void QmuParserBase::ClearInfixOprt() - { - 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) - { - 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! - */ - 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) - { - m_bBuiltInOp = a_bIsOn; - ReInit(); - } - - //------------------------------------------------------------------------------ - /** \brief Query status of built in variables. - \return #m_bBuiltInOp; true if built in operators are enabled. - \throw nothrow - */ - bool QmuParserBase::HasBuiltInOprt() const - { - return m_bBuiltInOp; - } - - //------------------------------------------------------------------------------ - /** \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() ) - { - token_type val = stVal.pop(); - if (val.GetType()==tpSTR) - qDebug() << " \"" << val.GetAsString() << "\" "; - else - qDebug() << " " << val.GetVal() << " "; - } - qDebug() << "\nOperator stack:\n"; - - while ( !stOprt.empty() ) - { - 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; - 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]; - } - - //--------------------------------------------------------------------------- - /** \brief Return the number of results on the calculation stack. - - If the expression contains comma seperated subexpressions (i.e. "sin(y), x+y"). - There mey be more than one return value. This function returns the number of - available results. - */ - int QmuParserBase::GetNumResults() const - { - return m_nFinalResultIdx; - } - - //--------------------------------------------------------------------------- - /** \brief Calculate the result. - - A note on const correctness: - I consider it important that Calc is a const function. - Due to caching operations Calc changes only the state of internal variables with one exception - m_UsedVar this is reset during string parsing and accessible from the outside. Instead of making - Calc non const GetUsedVar is non const because it explicitely calls Eval() forcing this update. - - \pre A formula must be set. - \pre Variables must have been set (if needed) - - \sa #m_pParseFormula - \return The evaluation result - \throw ParseException if no Formula is set or in case of any other error related to the formula. - */ - qreal QmuParserBase::Eval() const - { - return (this->*m_pParseFormula)(); - } - - //--------------------------------------------------------------------------- - void QmuParserBase::Eval(qreal *results, int nBulkSize) - { - CreateRPN(); - - int i = 0; - -#ifdef MUP_USE_OPENMP -//#define DEBUG_OMP_STUFF - #ifdef DEBUG_OMP_STUFF - int *pThread = new int[nBulkSize]; - int *pIdx = new int[nBulkSize]; - #endif - - int nMaxThreads = std::min(omp_get_max_threads(), s_MaxNumOpenMPThreads); - int nThreadID, ct=0; - omp_set_num_threads(nMaxThreads); - - #pragma omp parallel for schedule(static, nBulkSize/nMaxThreads) private(nThreadID) - for (i=0; i*m_pParseFormula)(); + nStackSize = m_nFinalResultIdx; + + // (for historic reasons the stack starts at position 1) + return &m_vStackBuffer[1]; +} + +//---------------------------------------------------------------------------------------------------------------------- +/** + * @brief Return the number of results on the calculation stack. + * + * If the expression contains comma seperated subexpressions (i.e. "sin(y), x+y"). There mey be more than one return + * value. This function returns the number of available results. + */ +int QmuParserBase::GetNumResults() const +{ + return m_nFinalResultIdx; +} + +//---------------------------------------------------------------------------------------------------------------------- +/** + * @brief Calculate the result. + * + * A note on const correctness: + * I consider it important that Calc is a const function. + * Due to caching operations Calc changes only the state of internal variables with one exception + * m_UsedVar this is reset during string parsing and accessible from the outside. Instead of making + * Calc non const GetUsedVar is non const because it explicitely calls Eval() forcing this update. + * + * @pre A formula must be set. + * @pre Variables must have been set (if needed) + * + * @sa #m_pParseFormula + * @return The evaluation result + * @throw ParseException if no Formula is set or in case of any other error related to the formula. + */ +qreal QmuParserBase::Eval() const +{ + return (this->*m_pParseFormula)(); +} + +//---------------------------------------------------------------------------------------------------------------------- +void QmuParserBase::Eval(qreal *results, int nBulkSize) +{ + CreateRPN(); + + int i = 0; + + #ifdef MUP_USE_OPENMP + //#define DEBUG_OMP_STUFF + #ifdef DEBUG_OMP_STUFF + int *pThread = new int[nBulkSize]; + int *pIdx = new int[nBulkSize]; + #endif + + int nMaxThreads = std::min(omp_get_max_threads(), s_MaxNumOpenMPThreads); + int nThreadID, ct=0; + omp_set_num_threads(nMaxThreads); + + #pragma omp parallel for schedule(static, nBulkSize/nMaxThreads) private(nThreadID) + for (i=0; i -#include -#include -#include -#include -#include #include #include #include -//--- Parser includes -------------------------------------------------------------------------- #include "qmuparserdef.h" #include "qmuparsertokenreader.h" #include "qmuparserbytecode.h" @@ -43,261 +35,227 @@ namespace qmu { -/** \file - \brief This file contains the class definition of the qmuparser engine. -*/ + /** + * @file + * @brief This file contains the class definition of the qmuparser engine. + */ -//-------------------------------------------------------------------------------------------------- -/** \brief Mathematical expressions parser (base parser engine). - \author (C) 2013 Ingo Berg - - This is the implementation of a bytecode based mathematical expressions parser. - The formula will be parsed from string and converted into a bytecode. - Future calculations will be done with the bytecode instead the formula string - resulting in a significant performance increase. - Complementary to a set of internally implemented functions the parser is able to handle - user defined functions and variables. -*/ -class QmuParserBase -{ -friend class QmuParserTokenReader; - -private: - - /** \brief Typedef for the parse functions. - - The parse function do the actual work. The parser exchanges - the function pointer to the parser function depending on - which state it is in. (i.e. bytecode parser vs. string parser) - */ - typedef qreal (QmuParserBase::*ParseFunction)() const; - - /** \brief Type used for storing an array of values. */ - typedef QVector valbuf_type; - - /** \brief Type for a vector of strings. */ - typedef QVector stringbuf_type; - - /** \brief Typedef for the token reader. */ - typedef QmuParserTokenReader token_reader_type; - - /** \brief Type used for parser tokens. */ - typedef QmuParserToken token_type; - - /** \brief Maximum number of threads spawned by OpenMP when using the bulk mode. */ - static const int s_MaxNumOpenMPThreads = 4; - - public: - - /** \brief Type of the error class. - - Included for backwards compatibility. - */ - typedef QmuParserError exception_type; - - static void EnableDebugDump(bool bDumpCmd, bool bDumpStack); - - QmuParserBase(); - QmuParserBase(const QmuParserBase &a_Parser); - QmuParserBase& operator=(const QmuParserBase &a_Parser); - - virtual ~QmuParserBase(); - - qreal Eval() const; - qreal* Eval(int &nStackSize) const; - void Eval(qreal *results, int nBulkSize); - - int GetNumResults() const; - - void SetExpr(const QString &a_sExpr); - void SetVarFactory(facfun_type a_pFactory, void *pUserData = NULL); - - void SetDecSep(char_type cDecSep); - void SetThousandsSep(char_type cThousandsSep = 0); - void ResetLocale(); - - void EnableOptimizer(bool a_bIsOn=true); - void EnableBuiltInOprt(bool a_bIsOn=true); - - bool HasBuiltInOprt() const; - void AddValIdent(identfun_type a_pCallback); - - /** \fn void qmu::QmuParserBase::DefineFun(const string_type &a_strName, fun_type0 a_pFun, bool a_bAllowOpt = true) - \brief Define a parser function without arguments. - \param a_strName Name of the function - \param a_pFun Pointer to the callback function - \param a_bAllowOpt A flag indicating this function may be optimized - */ - template - void DefineFun(const QString &a_strName, T a_pFun, bool a_bAllowOpt = true) - { - AddCallback( a_strName, QmuParserCallback(a_pFun, a_bAllowOpt), m_FunDef, ValidNameChars() ); - } - - void DefineOprt(const QString &a_strName, - fun_type2 a_pFun, - unsigned a_iPri=0, - EOprtAssociativity a_eAssociativity = oaLEFT, - bool a_bAllowOpt = false); - void DefineConst(const QString &a_sName, qreal a_fVal); - void DefineStrConst(const QString &a_sName, const QString &a_strVal); - void DefineVar(const QString &a_sName, qreal *a_fVar); - void DefinePostfixOprt(const QString &a_strFun, fun_type1 a_pOprt, bool a_bAllowOpt=true); - void DefineInfixOprt(const QString &a_strName, fun_type1 a_pOprt, int a_iPrec=prINFIX, bool a_bAllowOpt=true); - - // Clear user defined variables, constants or functions - void ClearVar(); - void ClearFun(); - void ClearConst(); - void ClearInfixOprt(); - void ClearPostfixOprt(); - void ClearOprt(); - - void RemoveVar(const QString &a_strVarName); - const varmap_type& GetUsedVar() const; - const varmap_type& GetVar() const; - const valmap_type& GetConst() const; - const QString &GetExpr() const; - const funmap_type& GetFunDef() const; - string_type GetVersion(EParserVersionInfo eInfo = pviFULL) const; - - const QStringList &GetOprtDef() const; - void DefineNameChars(const QString &a_szCharset); - void DefineOprtChars(const QString &a_szCharset); - void DefineInfixOprtChars(const QString &a_szCharset); - - const QString& ValidNameChars() const; - const QString &ValidOprtChars() const; - const QString &ValidInfixOprtChars() const; - - void SetArgSep(char_type cArgSep); - QChar GetArgSep() const; - - void Error(EErrorCodes a_iErrc, - int a_iPos = -1, - const QString &a_strTok = QString() ) const; - - protected: - - void Init(); - - virtual void InitCharSets() = 0; - virtual void InitFun() = 0; - virtual void InitConst() = 0; - virtual void InitOprt() = 0; - - virtual void OnDetectVar(QString *pExpr, int &nStart, int &nEnd); - - static const QStringList c_DefaultOprt; - static std::locale s_locale; ///< The locale used by the parser - static bool g_DbgDumpCmdCode; - static bool g_DbgDumpStack; - - /** \brief A facet class used to change decimal and thousands separator. */ - template - class change_dec_sep : public std::numpunct + /** + * @brief Mathematical expressions parser (base parser engine). + * @author (C) 2013 Ingo Berg + * + * This is the implementation of a bytecode based mathematical expressions parser. + * The formula will be parsed from string and converted into a bytecode. + * Future calculations will be done with the bytecode instead the formula string + * resulting in a significant performance increase. + * Complementary to a set of internally implemented functions the parser is able to handle + * user defined functions and variables. + */ + class QmuParserBase { + friend class QmuParserTokenReader; public: - - explicit change_dec_sep(char_type cDecSep, char_type cThousandsSep = 0, int nGroup = 3) - :std::numpunct() - ,m_nGroup(nGroup) - ,m_cDecPoint(cDecSep) - ,m_cThousandsSep(cThousandsSep) - {} - + /** + * @brief Type of the error class. + * + * Included for backwards compatibility. + */ + typedef QmuParserError exception_type; + + QmuParserBase(); + QmuParserBase(const QmuParserBase &a_Parser); + QmuParserBase& operator=(const QmuParserBase &a_Parser); + virtual ~QmuParserBase(); + + static void EnableDebugDump(bool bDumpCmd, bool bDumpStack); + qreal Eval() const; + qreal* Eval(int &nStackSize) const; + void Eval(qreal *results, int nBulkSize); + int GetNumResults() const; + void SetExpr(const QString &a_sExpr); + void SetVarFactory(facfun_type a_pFactory, void *pUserData = NULL); + void SetDecSep(char_type cDecSep); + void SetThousandsSep(char_type cThousandsSep = 0); + void ResetLocale(); + void EnableOptimizer(bool a_bIsOn=true); + void EnableBuiltInOprt(bool a_bIsOn=true); + bool HasBuiltInOprt() const; + void AddValIdent(identfun_type a_pCallback); + void DefineOprt(const QString &a_strName, fun_type2 a_pFun, unsigned a_iPri=0, + EOprtAssociativity a_eAssociativity = oaLEFT, bool a_bAllowOpt = false); + void DefineConst(const QString &a_sName, qreal a_fVal); + void DefineStrConst(const QString &a_sName, const QString &a_strVal); + void DefineVar(const QString &a_sName, qreal *a_fVar); + void DefinePostfixOprt(const QString &a_strFun, fun_type1 a_pOprt, bool a_bAllowOpt=true); + void DefineInfixOprt(const QString &a_strName, fun_type1 a_pOprt, int a_iPrec=prINFIX, + bool a_bAllowOpt=true); + // Clear user defined variables, constants or functions + void ClearVar(); + void ClearFun(); + void ClearConst(); + void ClearInfixOprt(); + void ClearPostfixOprt(); + void ClearOprt(); + void RemoveVar(const QString &a_strVarName); + const varmap_type& GetUsedVar() const; + const varmap_type& GetVar() const; + const valmap_type& GetConst() const; + const QString& GetExpr() const; + const funmap_type& GetFunDef() const; + QString GetVersion(EParserVersionInfo eInfo = pviFULL) const; + const QStringList& GetOprtDef() const; + void DefineNameChars(const QString &a_szCharset); + void DefineOprtChars(const QString &a_szCharset); + void DefineInfixOprtChars(const QString &a_szCharset); + const QString& ValidNameChars() const; + const QString& ValidOprtChars() const; + const QString& ValidInfixOprtChars() const; + void SetArgSep(char_type cArgSep); + QChar GetArgSep() const; + void Error(EErrorCodes a_iErrc, int a_iPos = -1, const QString &a_strTok = QString() ) const; + /** + * @fn void qmu::QmuParserBase::DefineFun(const string_type &a_strName, fun_type0 a_pFun, + * bool a_bAllowOpt = true) + * @brief Define a parser function without arguments. + * @param a_strName Name of the function + * @param a_pFun Pointer to the callback function + * @param a_bAllowOpt A flag indicating this function may be optimized + */ + template + void DefineFun(const QString &a_strName, T a_pFun, bool a_bAllowOpt = true) + { + AddCallback( a_strName, QmuParserCallback(a_pFun, a_bAllowOpt), m_FunDef, ValidNameChars() ); + } protected: - - virtual char_type do_decimal_point() const - { - return m_cDecPoint; - } + static const QStringList c_DefaultOprt; + static std::locale s_locale; ///< The locale used by the parser + static bool g_DbgDumpCmdCode; + static bool g_DbgDumpStack; + void Init(); + virtual void InitCharSets() = 0; + virtual void InitFun() = 0; + virtual void InitConst() = 0; + virtual void InitOprt() = 0; + virtual void OnDetectVar(QString *pExpr, int &nStart, int &nEnd); + /** + * @brief A facet class used to change decimal and thousands separator. + */ + template + class change_dec_sep : public std::numpunct + { + public: + explicit change_dec_sep(char_type cDecSep, char_type cThousandsSep = 0, int nGroup = 3) + :std::numpunct(), m_nGroup(nGroup), m_cDecPoint(cDecSep), m_cThousandsSep(cThousandsSep) + {} + protected: + virtual char_type do_decimal_point() const + { + return m_cDecPoint; + } - virtual char_type do_thousands_sep() const - { - return m_cThousandsSep; - } - - virtual std::string do_grouping() const - { - return std::string(1, m_nGroup); - } + virtual char_type do_thousands_sep() const + { + return m_cThousandsSep; + } + virtual std::string do_grouping() const + { + return std::string(1, m_nGroup); + } + private: + int m_nGroup; + char_type m_cDecPoint; + char_type m_cThousandsSep; + }; private: + /** + * @brief Typedef for the parse functions. + * + * The parse function do the actual work. The parser exchanges + * the function pointer to the parser function depending on + * which state it is in. (i.e. bytecode parser vs. string parser) + */ + typedef qreal (QmuParserBase::*ParseFunction)() const; - int m_nGroup; - char_type m_cDecPoint; - char_type m_cThousandsSep; + /** + * @brief Type used for storing an array of values. + */ + typedef QVector valbuf_type; + + /** + * @brief Type for a vector of strings. + */ + typedef QVector stringbuf_type; + + /** + * @brief Typedef for the token reader. + */ + typedef QmuParserTokenReader token_reader_type; + + /** + * @brief Type used for parser tokens. + */ + typedef QmuParserToken token_type; + + /** + * @brief Maximum number of threads spawned by OpenMP when using the bulk mode. + */ + static const int s_MaxNumOpenMPThreads = 4; + + /** + * @brief Pointer to the parser function. + * + * Eval() calls the function whose address is stored there. + */ + mutable ParseFunction m_pParseFormula; + mutable QmuParserByteCode m_vRPN; ///< The Bytecode class. + mutable stringbuf_type m_vStringBuf; ///< String buffer, used for storing string function arguments + stringbuf_type m_vStringVarBuf; + + std::auto_ptr m_pTokenReader; ///< Managed pointer to the token reader object. + + funmap_type m_FunDef; ///< Map of function names and pointers. + funmap_type m_PostOprtDef; ///< Postfix operator callbacks + funmap_type m_InfixOprtDef; ///< unary infix operator. + funmap_type m_OprtDef; ///< Binary operator callbacks + valmap_type m_ConstDef; ///< user constants. + strmap_type m_StrVarDef; ///< user defined string constants + varmap_type m_VarDef; ///< user defind variables. + + bool m_bBuiltInOp; ///< Flag that can be used for switching built in operators on and off + + QString m_sNameChars; ///< Charset for names + QString m_sOprtChars; ///< Charset for postfix/ binary operator tokens + QString m_sInfixOprtChars; ///< Charset for infix operator tokens + + mutable int m_nIfElseCounter; ///< Internal counter for keeping track of nested if-then-else clauses + + // items merely used for caching state information + mutable valbuf_type m_vStackBuffer; ///< This is merely a buffer used for the stack in the cmd parsing routine + mutable int m_nFinalResultIdx; + + void Assign(const QmuParserBase &a_Parser); + void InitTokenReader(); + void ReInit() const; + void AddCallback(const QString &a_strName, const QmuParserCallback &a_Callback, + funmap_type &a_Storage, const QString &a_szCharSet ); + void ApplyRemainingOprt(QStack &a_stOpt, QStack &a_stVal) const; + void ApplyBinOprt(QStack &a_stOpt, QStack &a_stVal) const; + void ApplyIfElse(QStack &a_stOpt, QStack &a_stVal) const; + void ApplyFunc(QStack &a_stOpt, QStack &a_stVal, int iArgCount) const; + token_type ApplyStrFunc(const token_type &a_FunTok, const QVector &a_vArg) const; + int GetOprtPrecedence(const token_type &a_Tok) const; + EOprtAssociativity GetOprtAssociativity(const token_type &a_Tok) const; + void CreateRPN() const; + qreal ParseString() const; + qreal ParseCmdCode() const; + qreal ParseCmdCodeBulk(int nOffset, int nThreadID) const; + void CheckName(const QString &a_strName, const QString &a_CharSet) const; + void CheckOprt(const QString &a_sName, const QmuParserCallback &a_Callback, + const QString &a_szCharSet) const; + void StackDump(const QStack &a_stVal, const QStack &a_stOprt) const; }; - private: - - void Assign(const QmuParserBase &a_Parser); - void InitTokenReader(); - void ReInit() const; - - void AddCallback(const QString &a_strName, - const QmuParserCallback &a_Callback, - funmap_type &a_Storage, - const QString &a_szCharSet ); - - void ApplyRemainingOprt(QStack &a_stOpt, QStack &a_stVal) const; - void ApplyBinOprt(QStack &a_stOpt, QStack &a_stVal) const; - void ApplyIfElse(QStack &a_stOpt, QStack &a_stVal) const; - void ApplyFunc(QStack &a_stOpt, QStack &a_stVal, int iArgCount) const; - - token_type ApplyStrFunc(const token_type &a_FunTok, - const QVector &a_vArg) const; - - int GetOprtPrecedence(const token_type &a_Tok) const; - EOprtAssociativity GetOprtAssociativity(const token_type &a_Tok) const; - - void CreateRPN() const; - - qreal ParseString() const; - qreal ParseCmdCode() const; - qreal ParseCmdCodeBulk(int nOffset, int nThreadID) const; - - void CheckName(const QString &a_strName, const QString &a_CharSet) const; - void CheckOprt(const QString &a_sName, - const QmuParserCallback &a_Callback, - const QString &a_szCharSet) const; - - void StackDump(const QStack &a_stVal, - const QStack &a_stOprt) const; - - /** \brief Pointer to the parser function. - - Eval() calls the function whose address is stored there. - */ - mutable ParseFunction m_pParseFormula; - mutable QmuParserByteCode m_vRPN; ///< The Bytecode class. - mutable stringbuf_type m_vStringBuf; ///< String buffer, used for storing string function arguments - stringbuf_type m_vStringVarBuf; - - std::auto_ptr m_pTokenReader; ///< Managed pointer to the token reader object. - - funmap_type m_FunDef; ///< Map of function names and pointers. - funmap_type m_PostOprtDef; ///< Postfix operator callbacks - funmap_type m_InfixOprtDef; ///< unary infix operator. - funmap_type m_OprtDef; ///< Binary operator callbacks - valmap_type m_ConstDef; ///< user constants. - strmap_type m_StrVarDef; ///< user defined string constants - varmap_type m_VarDef; ///< user defind variables. - - bool m_bBuiltInOp; ///< Flag that can be used for switching built in operators on and off - - QString m_sNameChars; ///< Charset for names - QString m_sOprtChars; ///< Charset for postfix/ binary operator tokens - QString m_sInfixOprtChars; ///< Charset for infix operator tokens - - mutable int m_nIfElseCounter; ///< Internal counter for keeping track of nested if-then-else clauses - - // items merely used for caching state information - mutable valbuf_type m_vStackBuffer; ///< This is merely a buffer used for the stack in the cmd parsing routine - mutable int m_nFinalResultIdx; -}; - } // namespace qmu #endif diff --git a/src/libs/qmuparser/qmuparserbytecode.cpp b/src/libs/qmuparser/qmuparserbytecode.cpp index 5665417f3..495f7f6a0 100644 --- a/src/libs/qmuparser/qmuparserbytecode.cpp +++ b/src/libs/qmuparser/qmuparserbytecode.cpp @@ -35,534 +35,617 @@ namespace qmu { - //--------------------------------------------------------------------------- - /** \brief Bytecode default constructor. */ - QmuParserByteCode::QmuParserByteCode() - :m_iStackPos(0) - ,m_iMaxStackSize(0) - ,m_vRPN() - ,m_bEnableOptimizer(true) - { - m_vRPN.reserve(50); - } - - //--------------------------------------------------------------------------- - /** \brief Copy constructor. - - Implemented in Terms of Assign(const QParserByteCode &a_ByteCode) - */ - QmuParserByteCode::QmuParserByteCode(const QmuParserByteCode &a_ByteCode) - { - Assign(a_ByteCode); - } - - //--------------------------------------------------------------------------- - /** \brief Assignment operator. - - Implemented in Terms of Assign(const QParserByteCode &a_ByteCode) - */ - QmuParserByteCode& QmuParserByteCode::operator=(const QmuParserByteCode &a_ByteCode) - { - Assign(a_ByteCode); - return *this; - } - - //--------------------------------------------------------------------------- - void QmuParserByteCode::EnableOptimizer(bool bStat) - { - m_bEnableOptimizer = bStat; - } - - //--------------------------------------------------------------------------- - /** \brief Copy state of another object to this. - - \throw nowthrow - */ - void QmuParserByteCode::Assign(const QmuParserByteCode &a_ByteCode) - { - if (this==&a_ByteCode) - return; - - m_iStackPos = a_ByteCode.m_iStackPos; - m_vRPN = a_ByteCode.m_vRPN; - m_iMaxStackSize = a_ByteCode.m_iMaxStackSize; - } - - //--------------------------------------------------------------------------- - /** \brief Add a Variable pointer to bytecode. - \param a_pVar Pointer to be added. - \throw nothrow - */ - void QmuParserByteCode::AddVar(qreal *a_pVar) - { - ++m_iStackPos; - m_iMaxStackSize = std::max(m_iMaxStackSize, (size_t)m_iStackPos); - - // optimization does not apply - SToken tok; - tok.Cmd = cmVAR; - tok.Val.ptr = a_pVar; - tok.Val.data = 1; - tok.Val.data2 = 0; - m_vRPN.push_back(tok); - } - - //--------------------------------------------------------------------------- - /** \brief Add a Variable pointer to bytecode. - - Value entries in byte code consist of: -
    -
  • value array position of the value
  • -
  • the operator code according to ParserToken::cmVAL
  • -
  • the value stored in #mc_iSizeVal number of bytecode entries.
  • -
- - \param a_pVal Value to be added. - \throw nothrow - */ - void QmuParserByteCode::AddVal(qreal a_fVal) - { - ++m_iStackPos; - m_iMaxStackSize = std::max(m_iMaxStackSize, (size_t)m_iStackPos); - - // If optimization does not apply - SToken tok; - tok.Cmd = cmVAL; - tok.Val.ptr = NULL; - tok.Val.data = 0; - tok.Val.data2 = a_fVal; - m_vRPN.push_back(tok); - } - - //--------------------------------------------------------------------------- - void QmuParserByteCode::ConstantFolding(ECmdCode a_Oprt) - { - std::size_t sz = m_vRPN.size(); - qreal &x = m_vRPN[sz-2].Val.data2, - &y = m_vRPN[sz-1].Val.data2; - switch (a_Oprt) - { - case cmLAND: x = (int)x && (int)y; m_vRPN.pop_back(); break; - case cmLOR: x = (int)x || (int)y; m_vRPN.pop_back(); break; - case cmLT: x = x < y; m_vRPN.pop_back(); break; - case cmGT: x = x > y; m_vRPN.pop_back(); break; - case cmLE: x = x <= y; m_vRPN.pop_back(); break; - case cmGE: x = x >= y; m_vRPN.pop_back(); break; - case cmNEQ: x = x != y; m_vRPN.pop_back(); break; - case cmEQ: x = x == y; m_vRPN.pop_back(); break; - case cmADD: x = x + y; m_vRPN.pop_back(); break; - case cmSUB: x = x - y; m_vRPN.pop_back(); break; - case cmMUL: x = x * y; m_vRPN.pop_back(); break; - case cmDIV: - -#if defined(MUP_MATH_EXCEPTIONS) - if (y==0) - throw ParserError(ecDIV_BY_ZERO, "0"); -#endif - - x = x / y; - m_vRPN.pop_back(); - break; - - case cmPOW: x = std::pow(x, y); - m_vRPN.pop_back(); - break; - - default: - break; - } // switch opcode - } - - //--------------------------------------------------------------------------- - /** \brief Add an operator identifier to bytecode. - - Operator entries in byte code consist of: -
    -
  • value array position of the result
  • -
  • the operator code according to ParserToken::ECmdCode
  • -
- - \sa ParserToken::ECmdCode - */ - void QmuParserByteCode::AddOp(ECmdCode a_Oprt) - { - bool bOptimized = false; - - if (m_bEnableOptimizer) - { - std::size_t sz = m_vRPN.size(); - - // Check for foldable constants like: - // cmVAL cmVAL cmADD - // where cmADD can stand fopr any binary operator applied to - // two constant values. - if (sz>=2 && m_vRPN[sz-2].Cmd == cmVAL && m_vRPN[sz-1].Cmd == cmVAL) - { - ConstantFolding(a_Oprt); - bOptimized = true; - } - else - { - switch(a_Oprt) - { - case cmPOW: - // Optimization for ploynomials of low order - if (m_vRPN[sz-2].Cmd == cmVAR && m_vRPN[sz-1].Cmd == cmVAL) - { - if (m_vRPN[sz-1].Val.data2==2) - m_vRPN[sz-2].Cmd = cmVARPOW2; - else if (m_vRPN[sz-1].Val.data2==3) - m_vRPN[sz-2].Cmd = cmVARPOW3; - else if (m_vRPN[sz-1].Val.data2==4) - m_vRPN[sz-2].Cmd = cmVARPOW4; - else - break; - - m_vRPN.pop_back(); - bOptimized = true; - } - break; - - case cmSUB: - case cmADD: - // Simple optimization based on pattern recognition for a shitload of different - // bytecode combinations of addition/subtraction - if ( (m_vRPN[sz-1].Cmd == cmVAR && m_vRPN[sz-2].Cmd == cmVAL) || - (m_vRPN[sz-1].Cmd == cmVAL && m_vRPN[sz-2].Cmd == cmVAR) || - (m_vRPN[sz-1].Cmd == cmVAL && m_vRPN[sz-2].Cmd == cmVARMUL) || - (m_vRPN[sz-1].Cmd == cmVARMUL && m_vRPN[sz-2].Cmd == cmVAL) || - (m_vRPN[sz-1].Cmd == cmVAR && m_vRPN[sz-2].Cmd == cmVAR && m_vRPN[sz-2].Val.ptr == m_vRPN[sz-1].Val.ptr) || - (m_vRPN[sz-1].Cmd == cmVAR && m_vRPN[sz-2].Cmd == cmVARMUL && m_vRPN[sz-2].Val.ptr == m_vRPN[sz-1].Val.ptr) || - (m_vRPN[sz-1].Cmd == cmVARMUL && m_vRPN[sz-2].Cmd == cmVAR && m_vRPN[sz-2].Val.ptr == m_vRPN[sz-1].Val.ptr) || - (m_vRPN[sz-1].Cmd == cmVARMUL && m_vRPN[sz-2].Cmd == cmVARMUL && m_vRPN[sz-2].Val.ptr == m_vRPN[sz-1].Val.ptr) ) - { - assert( (m_vRPN[sz-2].Val.ptr==NULL && m_vRPN[sz-1].Val.ptr!=NULL) || - (m_vRPN[sz-2].Val.ptr!=NULL && m_vRPN[sz-1].Val.ptr==NULL) || - (m_vRPN[sz-2].Val.ptr == m_vRPN[sz-1].Val.ptr) ); - - m_vRPN[sz-2].Cmd = cmVARMUL; - m_vRPN[sz-2].Val.ptr = (qreal*)((long long)(m_vRPN[sz-2].Val.ptr) | (long long)(m_vRPN[sz-1].Val.ptr)); // variable - m_vRPN[sz-2].Val.data2 += ((a_Oprt==cmSUB) ? -1 : 1) * m_vRPN[sz-1].Val.data2; // offset - m_vRPN[sz-2].Val.data += ((a_Oprt==cmSUB) ? -1 : 1) * m_vRPN[sz-1].Val.data; // multiplikatior - m_vRPN.pop_back(); - bOptimized = true; - } - break; - - case cmMUL: - if ( (m_vRPN[sz-1].Cmd == cmVAR && m_vRPN[sz-2].Cmd == cmVAL) || - (m_vRPN[sz-1].Cmd == cmVAL && m_vRPN[sz-2].Cmd == cmVAR) ) - { - m_vRPN[sz-2].Cmd = cmVARMUL; - m_vRPN[sz-2].Val.ptr = (qreal*)((long long)(m_vRPN[sz-2].Val.ptr) | (long long)(m_vRPN[sz-1].Val.ptr)); - m_vRPN[sz-2].Val.data = m_vRPN[sz-2].Val.data2 + m_vRPN[sz-1].Val.data2; - m_vRPN[sz-2].Val.data2 = 0; - m_vRPN.pop_back(); - bOptimized = true; - } - else if ( (m_vRPN[sz-1].Cmd == cmVAL && m_vRPN[sz-2].Cmd == cmVARMUL) || - (m_vRPN[sz-1].Cmd == cmVARMUL && m_vRPN[sz-2].Cmd == cmVAL) ) - { - // Optimization: 2*(3*b+1) or (3*b+1)*2 -> 6*b+2 - m_vRPN[sz-2].Cmd = cmVARMUL; - m_vRPN[sz-2].Val.ptr = (qreal*)((long long)(m_vRPN[sz-2].Val.ptr) | (long long)(m_vRPN[sz-1].Val.ptr)); - if (m_vRPN[sz-1].Cmd == cmVAL) - { - m_vRPN[sz-2].Val.data *= m_vRPN[sz-1].Val.data2; - m_vRPN[sz-2].Val.data2 *= m_vRPN[sz-1].Val.data2; - } - else - { - m_vRPN[sz-2].Val.data = m_vRPN[sz-1].Val.data * m_vRPN[sz-2].Val.data2; - m_vRPN[sz-2].Val.data2 = m_vRPN[sz-1].Val.data2 * m_vRPN[sz-2].Val.data2; - } - m_vRPN.pop_back(); - bOptimized = true; - } - else if (m_vRPN[sz-1].Cmd == cmVAR && m_vRPN[sz-2].Cmd == cmVAR && - m_vRPN[sz-1].Val.ptr == m_vRPN[sz-2].Val.ptr) - { - // Optimization: a*a -> a^2 - m_vRPN[sz-2].Cmd = cmVARPOW2; - m_vRPN.pop_back(); - bOptimized = true; - } - break; - - case cmDIV: - if (m_vRPN[sz-1].Cmd == cmVAL && m_vRPN[sz-2].Cmd == cmVARMUL && m_vRPN[sz-1].Val.data2!=0) - { - // Optimization: 4*a/2 -> 2*a - m_vRPN[sz-2].Val.data /= m_vRPN[sz-1].Val.data2; - m_vRPN[sz-2].Val.data2 /= m_vRPN[sz-1].Val.data2; - m_vRPN.pop_back(); - bOptimized = true; - } - break; - - } // switch a_Oprt - } - } - - // If optimization can't be applied just write the value - if (!bOptimized) - { - --m_iStackPos; - SToken tok; - tok.Cmd = a_Oprt; - m_vRPN.push_back(tok); - } - } - - //--------------------------------------------------------------------------- - void QmuParserByteCode::AddIfElse(ECmdCode a_Oprt) - { - SToken tok; - tok.Cmd = a_Oprt; - m_vRPN.push_back(tok); - } - - //--------------------------------------------------------------------------- - /** \brief Add an assignement operator - - Operator entries in byte code consist of: -
    -
  • cmASSIGN code
  • -
  • the pointer of the destination variable
  • -
- - \sa ParserToken::ECmdCode - */ - void QmuParserByteCode::AddAssignOp(qreal *a_pVar) - { - --m_iStackPos; - - SToken tok; - tok.Cmd = cmASSIGN; - tok.Val.ptr = a_pVar; - m_vRPN.push_back(tok); - } - - //--------------------------------------------------------------------------- - /** \brief Add function to bytecode. - - \param a_iArgc Number of arguments, negative numbers indicate multiarg functions. - \param a_pFun Pointer to function callback. - */ - void QmuParserByteCode::AddFun(generic_fun_type a_pFun, int a_iArgc) - { - if (a_iArgc>=0) - { - m_iStackPos = m_iStackPos - a_iArgc + 1; - } - else - { - // function with unlimited number of arguments - m_iStackPos = m_iStackPos + a_iArgc + 1; - } - m_iMaxStackSize = std::max(m_iMaxStackSize, (size_t)m_iStackPos); - - SToken tok; - tok.Cmd = cmFUNC; - tok.Fun.argc = a_iArgc; - tok.Fun.ptr = a_pFun; - m_vRPN.push_back(tok); - } - - //--------------------------------------------------------------------------- - /** \brief Add a bulk function to bytecode. - - \param a_iArgc Number of arguments, negative numbers indicate multiarg functions. - \param a_pFun Pointer to function callback. - */ - void QmuParserByteCode::AddBulkFun(generic_fun_type a_pFun, int a_iArgc) - { - m_iStackPos = m_iStackPos - a_iArgc + 1; - m_iMaxStackSize = std::max(m_iMaxStackSize, (size_t)m_iStackPos); - - SToken tok; - tok.Cmd = cmFUNC_BULK; - tok.Fun.argc = a_iArgc; - tok.Fun.ptr = a_pFun; - m_vRPN.push_back(tok); - } - - //--------------------------------------------------------------------------- - /** \brief Add Strung function entry to the parser bytecode. - \throw nothrow - - A string function entry consists of the stack position of the return value, - followed by a cmSTRFUNC code, the function pointer and an index into the - string buffer maintained by the parser. - */ - void QmuParserByteCode::AddStrFun(generic_fun_type a_pFun, int a_iArgc, int a_iIdx) - { - m_iStackPos = m_iStackPos - a_iArgc + 1; - - SToken tok; - tok.Cmd = cmFUNC_STR; - tok.Fun.argc = a_iArgc; - tok.Fun.idx = a_iIdx; - tok.Fun.ptr = a_pFun; - m_vRPN.push_back(tok); - - m_iMaxStackSize = std::max(m_iMaxStackSize, (size_t)m_iStackPos); - } - - //--------------------------------------------------------------------------- - /** \brief Add end marker to bytecode. - - \throw nothrow - */ - void QmuParserByteCode::Finalize() - { - SToken tok; - tok.Cmd = cmEND; - m_vRPN.push_back(tok); - rpn_type(m_vRPN).swap(m_vRPN); // shrink bytecode vector to fit - - // Determine the if-then-else jump offsets - QStack stIf, stElse; - int idx; - for (int i=0; i<(int)m_vRPN.size(); ++i) - { - switch(m_vRPN[i].Cmd) - { - case cmIF: - stIf.push(i); - break; - - case cmELSE: - stElse.push(i); - idx = stIf.pop(); - m_vRPN[idx].Oprt.offset = i - idx; - break; - - case cmENDIF: - idx = stElse.pop(); - m_vRPN[idx].Oprt.offset = i - idx; - break; - - default: - break; - } - } - } - - //--------------------------------------------------------------------------- - const SToken* QmuParserByteCode::GetBase() const - { - if (m_vRPN.size()==0) - throw QmuParserError(ecINTERNAL_ERROR); - else - return &m_vRPN[0]; - } - - //--------------------------------------------------------------------------- - std::size_t QmuParserByteCode::GetMaxStackSize() const - { - return m_iMaxStackSize+1; - } - - //--------------------------------------------------------------------------- - /** \brief Returns the number of entries in the bytecode. */ - std::size_t QmuParserByteCode::GetSize() const - { - return m_vRPN.size(); - } - - //--------------------------------------------------------------------------- - /** \brief Delete the bytecode. - - \throw nothrow - - The name of this function is a violation of my own coding guidelines - but this way it's more in line with the STL functions thus more - intuitive. - */ - void QmuParserByteCode::clear() - { - m_vRPN.clear(); - m_iStackPos = 0; - m_iMaxStackSize = 0; - } - - //--------------------------------------------------------------------------- - /** \brief Dump bytecode (for debugging only!). */ - void QmuParserByteCode::AsciiDump() - { - if (!m_vRPN.size()) - { - qDebug() << "No bytecode available\n"; - return; - } - - qDebug() << "Number of RPN tokens:" << m_vRPN.size() << "\n"; - for (std::size_t i=0; i + *
  • value array position of the value
  • + *
  • the operator code according to ParserToken::cmVAL
  • + *
  • the value stored in #mc_iSizeVal number of bytecode entries.
  • + * + * + * @param a_pVal Value to be added. + * @throw nothrow + */ +void QmuParserByteCode::AddVal(qreal a_fVal) +{ + ++m_iStackPos; + m_iMaxStackSize = std::max(m_iMaxStackSize, (size_t)m_iStackPos); + + // If optimization does not apply + SToken tok; + tok.Cmd = cmVAL; + tok.Val.ptr = NULL; + tok.Val.data = 0; + tok.Val.data2 = a_fVal; + m_vRPN.push_back(tok); +} + +//---------------------------------------------------------------------------------------------------------------------- +void QmuParserByteCode::ConstantFolding(ECmdCode a_Oprt) +{ + std::size_t sz = m_vRPN.size(); + qreal &x = m_vRPN[sz-2].Val.data2, + &y = m_vRPN[sz-1].Val.data2; + switch (a_Oprt) + { + case cmLAND: + x = (int)x && (int)y; + m_vRPN.pop_back(); + break; + case cmLOR: + x = (int)x || (int)y; + m_vRPN.pop_back(); + break; + case cmLT: + x = x < y; + m_vRPN.pop_back(); + break; + case cmGT: + x = x > y; + m_vRPN.pop_back(); + break; + case cmLE: + x = x <= y; + m_vRPN.pop_back(); + break; + case cmGE: + x = x >= y; + m_vRPN.pop_back(); + break; + case cmNEQ: + x = x != y; + m_vRPN.pop_back(); + break; + case cmEQ: + x = x == y; + m_vRPN.pop_back(); + break; + case cmADD: + x = x + y; + m_vRPN.pop_back(); + break; + case cmSUB: + x = x - y; + m_vRPN.pop_back(); + break; + case cmMUL: + x = x * y; + m_vRPN.pop_back(); + break; + case cmDIV: + #if defined(MUP_MATH_EXCEPTIONS) + if (y==0) + { + throw ParserError(ecDIV_BY_ZERO, "0"); + } + #endif + x = x / y; + m_vRPN.pop_back(); + break; + case cmPOW: + x = qPow(x, y); + m_vRPN.pop_back(); + break; + default: + break; + } // switch opcode +} + +//---------------------------------------------------------------------------------------------------------------------- +/** + * @brief Add an operator identifier to bytecode. + * + * Operator entries in byte code consist of: + *
      + *
    • value array position of the result
    • + *
    • the operator code according to ParserToken::ECmdCode
    • + *
    + * + * @sa ParserToken::ECmdCode + */ +void QmuParserByteCode::AddOp(ECmdCode a_Oprt) +{ + bool bOptimized = false; + + if (m_bEnableOptimizer) + { + std::size_t sz = m_vRPN.size(); + + // Check for foldable constants like: + // cmVAL cmVAL cmADD + // where cmADD can stand fopr any binary operator applied to + // two constant values. + if (sz>=2 && m_vRPN[sz-2].Cmd == cmVAL && m_vRPN[sz-1].Cmd == cmVAL) + { + ConstantFolding(a_Oprt); + bOptimized = true; + } + else + { + switch(a_Oprt) + { + case cmPOW: + // Optimization for ploynomials of low order + if (m_vRPN[sz-2].Cmd == cmVAR && m_vRPN[sz-1].Cmd == cmVAL) + { + if (m_vRPN[sz-1].Val.data2==2) + { + m_vRPN[sz-2].Cmd = cmVARPOW2; + } + else if (m_vRPN[sz-1].Val.data2==3) + { + m_vRPN[sz-2].Cmd = cmVARPOW3; + } + else if (m_vRPN[sz-1].Val.data2==4) + { + m_vRPN[sz-2].Cmd = cmVARPOW4; + } + else + { + break; + } + m_vRPN.pop_back(); + bOptimized = true; + } + break; + + case cmSUB: + case cmADD: + // Simple optimization based on pattern recognition for a shitload of different + // bytecode combinations of addition/subtraction + if ( (m_vRPN[sz-1].Cmd == cmVAR && m_vRPN[sz-2].Cmd == cmVAL) || + (m_vRPN[sz-1].Cmd == cmVAL && m_vRPN[sz-2].Cmd == cmVAR) || + (m_vRPN[sz-1].Cmd == cmVAL && m_vRPN[sz-2].Cmd == cmVARMUL) || + (m_vRPN[sz-1].Cmd == cmVARMUL && m_vRPN[sz-2].Cmd == cmVAL) || + (m_vRPN[sz-1].Cmd == cmVAR && m_vRPN[sz-2].Cmd == cmVAR && + m_vRPN[sz-2].Val.ptr == m_vRPN[sz-1].Val.ptr) || + (m_vRPN[sz-1].Cmd == cmVAR && m_vRPN[sz-2].Cmd == cmVARMUL + && m_vRPN[sz-2].Val.ptr == m_vRPN[sz-1].Val.ptr) || + (m_vRPN[sz-1].Cmd == cmVARMUL && m_vRPN[sz-2].Cmd == cmVAR && + m_vRPN[sz-2].Val.ptr == m_vRPN[sz-1].Val.ptr) || + (m_vRPN[sz-1].Cmd == cmVARMUL && m_vRPN[sz-2].Cmd == cmVARMUL && + m_vRPN[sz-2].Val.ptr == m_vRPN[sz-1].Val.ptr) ) + { + assert( (m_vRPN[sz-2].Val.ptr==NULL && m_vRPN[sz-1].Val.ptr!=NULL) || + (m_vRPN[sz-2].Val.ptr!=NULL && m_vRPN[sz-1].Val.ptr==NULL) || + (m_vRPN[sz-2].Val.ptr == m_vRPN[sz-1].Val.ptr) ); + + m_vRPN[sz-2].Cmd = cmVARMUL; + m_vRPN[sz-2].Val.ptr = (qreal*)((long long)(m_vRPN[sz-2].Val.ptr) | + (long long)(m_vRPN[sz-1].Val.ptr)); // variable + m_vRPN[sz-2].Val.data2 += ((a_Oprt==cmSUB) ? -1 : 1) * m_vRPN[sz-1].Val.data2; // offset + m_vRPN[sz-2].Val.data += ((a_Oprt==cmSUB) ? -1 : 1) * m_vRPN[sz-1].Val.data; // multiplikatior + m_vRPN.pop_back(); + bOptimized = true; + } + break; + case cmMUL: + if ( (m_vRPN[sz-1].Cmd == cmVAR && m_vRPN[sz-2].Cmd == cmVAL) || + (m_vRPN[sz-1].Cmd == cmVAL && m_vRPN[sz-2].Cmd == cmVAR) ) + { + m_vRPN[sz-2].Cmd = cmVARMUL; + m_vRPN[sz-2].Val.ptr = (qreal*)((long long)(m_vRPN[sz-2].Val.ptr) | + (long long)(m_vRPN[sz-1].Val.ptr)); + m_vRPN[sz-2].Val.data = m_vRPN[sz-2].Val.data2 + m_vRPN[sz-1].Val.data2; + m_vRPN[sz-2].Val.data2 = 0; + m_vRPN.pop_back(); + bOptimized = true; + } + else if ( (m_vRPN[sz-1].Cmd == cmVAL && m_vRPN[sz-2].Cmd == cmVARMUL) || + (m_vRPN[sz-1].Cmd == cmVARMUL && m_vRPN[sz-2].Cmd == cmVAL) ) + { + // Optimization: 2*(3*b+1) or (3*b+1)*2 -> 6*b+2 + m_vRPN[sz-2].Cmd = cmVARMUL; + m_vRPN[sz-2].Val.ptr = (qreal*)((long long)(m_vRPN[sz-2].Val.ptr) | + (long long)(m_vRPN[sz-1].Val.ptr)); + if (m_vRPN[sz-1].Cmd == cmVAL) + { + m_vRPN[sz-2].Val.data *= m_vRPN[sz-1].Val.data2; + m_vRPN[sz-2].Val.data2 *= m_vRPN[sz-1].Val.data2; + } + else + { + m_vRPN[sz-2].Val.data = m_vRPN[sz-1].Val.data * m_vRPN[sz-2].Val.data2; + m_vRPN[sz-2].Val.data2 = m_vRPN[sz-1].Val.data2 * m_vRPN[sz-2].Val.data2; + } + m_vRPN.pop_back(); + bOptimized = true; + } + else if (m_vRPN[sz-1].Cmd == cmVAR && m_vRPN[sz-2].Cmd == cmVAR && + m_vRPN[sz-1].Val.ptr == m_vRPN[sz-2].Val.ptr) + { + // Optimization: a*a -> a^2 + m_vRPN[sz-2].Cmd = cmVARPOW2; + m_vRPN.pop_back(); + bOptimized = true; + } + break; + case cmDIV: + if (m_vRPN[sz-1].Cmd == cmVAL && m_vRPN[sz-2].Cmd == cmVARMUL && m_vRPN[sz-1].Val.data2!=0) + { + // Optimization: 4*a/2 -> 2*a + m_vRPN[sz-2].Val.data /= m_vRPN[sz-1].Val.data2; + m_vRPN[sz-2].Val.data2 /= m_vRPN[sz-1].Val.data2; + m_vRPN.pop_back(); + bOptimized = true; + } + break; + + } // switch a_Oprt + } + } + + // If optimization can't be applied just write the value + if (!bOptimized) + { + --m_iStackPos; + SToken tok; + tok.Cmd = a_Oprt; + m_vRPN.push_back(tok); + } +} + +//---------------------------------------------------------------------------------------------------------------------- +void QmuParserByteCode::AddIfElse(ECmdCode a_Oprt) +{ + SToken tok; + tok.Cmd = a_Oprt; + m_vRPN.push_back(tok); +} + +//---------------------------------------------------------------------------------------------------------------------- +/** + * @brief Add an assignement operator + * + * Operator entries in byte code consist of: + *
      + *
    • cmASSIGN code
    • + *
    • the pointer of the destination variable
    • + *
    + * + * @sa ParserToken::ECmdCode + */ +void QmuParserByteCode::AddAssignOp(qreal *a_pVar) +{ + --m_iStackPos; + + SToken tok; + tok.Cmd = cmASSIGN; + tok.Val.ptr = a_pVar; + m_vRPN.push_back(tok); +} + +//---------------------------------------------------------------------------------------------------------------------- +/** + * @brief Add function to bytecode. + * + * @param a_iArgc Number of arguments, negative numbers indicate multiarg functions. + * @param a_pFun Pointer to function callback. + */ +void QmuParserByteCode::AddFun(generic_fun_type a_pFun, int a_iArgc) +{ + if (a_iArgc>=0) + { + m_iStackPos = m_iStackPos - a_iArgc + 1; + } + else + { + // function with unlimited number of arguments + m_iStackPos = m_iStackPos + a_iArgc + 1; + } + m_iMaxStackSize = std::max(m_iMaxStackSize, (size_t)m_iStackPos); + + SToken tok; + tok.Cmd = cmFUNC; + tok.Fun.argc = a_iArgc; + tok.Fun.ptr = a_pFun; + m_vRPN.push_back(tok); +} + +//---------------------------------------------------------------------------------------------------------------------- +/** + * @brief Add a bulk function to bytecode. + * + * @param a_iArgc Number of arguments, negative numbers indicate multiarg functions. + * @param a_pFun Pointer to function callback. + */ +void QmuParserByteCode::AddBulkFun(generic_fun_type a_pFun, int a_iArgc) +{ + m_iStackPos = m_iStackPos - a_iArgc + 1; + m_iMaxStackSize = std::max(m_iMaxStackSize, (size_t)m_iStackPos); + + SToken tok; + tok.Cmd = cmFUNC_BULK; + tok.Fun.argc = a_iArgc; + tok.Fun.ptr = a_pFun; + m_vRPN.push_back(tok); +} + +//---------------------------------------------------------------------------------------------------------------------- +/** + * @brief Add Strung function entry to the parser bytecode. + * @throw nothrow + * + * A string function entry consists of the stack position of the return value, followed by a cmSTRFUNC code, the + * function pointer and an index into the string buffer maintained by the parser. + */ +void QmuParserByteCode::AddStrFun(generic_fun_type a_pFun, int a_iArgc, int a_iIdx) +{ + m_iStackPos = m_iStackPos - a_iArgc + 1; + + SToken tok; + tok.Cmd = cmFUNC_STR; + tok.Fun.argc = a_iArgc; + tok.Fun.idx = a_iIdx; + tok.Fun.ptr = a_pFun; + m_vRPN.push_back(tok); + + m_iMaxStackSize = std::max(m_iMaxStackSize, (size_t)m_iStackPos); +} + +//---------------------------------------------------------------------------------------------------------------------- +/** + * @brief Add end marker to bytecode. + * + * @throw nothrow + */ +void QmuParserByteCode::Finalize() +{ + SToken tok; + tok.Cmd = cmEND; + m_vRPN.push_back(tok); + rpn_type(m_vRPN).swap(m_vRPN); // shrink bytecode vector to fit + + // Determine the if-then-else jump offsets + QStack stIf, stElse; + int idx; + for (int i=0; i<(int)m_vRPN.size(); ++i) + { + switch(m_vRPN[i].Cmd) + { + case cmIF: + stIf.push(i); + break; + case cmELSE: + stElse.push(i); + idx = stIf.pop(); + m_vRPN[idx].Oprt.offset = i - idx; + break; + case cmENDIF: + idx = stElse.pop(); + m_vRPN[idx].Oprt.offset = i - idx; + break; + default: + break; + } + } +} + +//---------------------------------------------------------------------------------------------------------------------- +const SToken* QmuParserByteCode::GetBase() const +{ + if (m_vRPN.size()==0) + { + throw QmuParserError(ecINTERNAL_ERROR); + } + else + { + return &m_vRPN[0]; + } +} + +//---------------------------------------------------------------------------------------------------------------------- +std::size_t QmuParserByteCode::GetMaxStackSize() const +{ + return m_iMaxStackSize+1; +} + +//---------------------------------------------------------------------------------------------------------------------- +/** + * @brief Returns the number of entries in the bytecode. + */ +std::size_t QmuParserByteCode::GetSize() const +{ + return m_vRPN.size(); +} + +//---------------------------------------------------------------------------------------------------------------------- +/** + * @brief Delete the bytecode. + * + * @throw nothrow + * + * The name of this function is a violation of my own coding guidelines but this way it's more in line with the STL + * functions thus more intuitive. + */ +void QmuParserByteCode::clear() +{ + m_vRPN.clear(); + m_iStackPos = 0; + m_iMaxStackSize = 0; +} + +//---------------------------------------------------------------------------------------------------------------------- +/** + * @brief Dump bytecode (for debugging only!). + */ +void QmuParserByteCode::AsciiDump() +{ + if (!m_vRPN.size()) + { + qDebug() << "No bytecode available\n"; + return; + } + + qDebug() << "Number of RPN tokens:" << m_vRPN.size() << "\n"; + for (std::size_t i=0; i -#include -#include - #include "qmuparserdef.h" #include "qmuparsererror.h" #include "qmuparsertoken.h" -/** \file - \brief Definition of the parser bytecode class. -*/ - +/** + * @file + * @brief Definition of the parser bytecode class. + */ namespace qmu { - struct SToken - { - ECmdCode Cmd; - int StackPos; +struct SToken +{ + ECmdCode Cmd; + int StackPos; - union - { - struct //SValData - { - qreal *ptr; - qreal data; - qreal data2; - } Val; + union + { + struct //SValData + { + qreal *ptr; + qreal data; + qreal data2; + } Val; - struct //SFunData - { - // Note: generic_fun_type is merely a placeholder. The real type could be - // anything between gun_type1 and fun_type9. I can't use a void - // pointer due to constraints in the ANSI standard which allows - // data pointers and function pointers to differ in size. - generic_fun_type ptr; - int argc; - int idx; - } Fun; + struct //SFunData + { + // Note: generic_fun_type is merely a placeholder. The real type could be + // anything between gun_type1 and fun_type9. I can't use a void + // pointer due to constraints in the ANSI standard which allows + // data pointers and function pointers to differ in size. + generic_fun_type ptr; + int argc; + int idx; + } Fun; - struct //SOprtData - { - qreal *ptr; - int offset; - } Oprt; - }; - }; + struct //SOprtData + { + qreal *ptr; + int offset; + } Oprt; + }; +}; - /** \brief Bytecode implementation of the Math Parser. - - The bytecode contains the formula converted to revers polish notation stored in a continious - memory area. Associated with this data are operator codes, variable pointers, constant - values and function pointers. Those are necessary in order to calculate the result. - All those data items will be casted to the underlying datatype of the bytecode. - - \author (C) 2004-2013 Ingo Berg -*/ +/** + * @brief Bytecode implementation of the Math Parser. + * + * The bytecode contains the formula converted to revers polish notation stored in a continious + * memory area. Associated with this data are operator codes, variable pointers, constant + * values and function pointers. Those are necessary in order to calculate the result. + * All those data items will be casted to the underlying datatype of the bytecode. + * + * @author (C) 2004-2013 Ingo Berg + */ class QmuParserByteCode { private: - - /** \brief Token type for internal use only. */ + /** @brief Token type for internal use only. */ typedef QmuParserToken token_type; - /** \brief Token vector for storing the RPN. */ + /** @brief Token vector for storing the RPN. */ typedef QVector rpn_type; - /** \brief Position in the Calculation array. */ + /** @brief Position in the Calculation array. */ unsigned m_iStackPos; - /** \brief Maximum size needed for the stack. */ + /** @brief Maximum size needed for the stack. */ std::size_t m_iMaxStackSize; - /** \brief The actual rpn storage. */ + /** @brief The actual rpn storage. */ rpn_type m_vRPN; bool m_bEnableOptimizer; void ConstantFolding(ECmdCode a_Oprt); - public: - QmuParserByteCode(); QmuParserByteCode(const QmuParserByteCode &a_ByteCode); QmuParserByteCode& operator=(const QmuParserByteCode &a_ByteCode); - void Assign(const QmuParserByteCode &a_ByteCode); - - void AddVar(qreal *a_pVar); - void AddVal(qreal a_fVal); - void AddOp(ECmdCode a_Oprt); - void AddIfElse(ECmdCode a_Oprt); - void AddAssignOp(qreal *a_pVar); - void AddFun(generic_fun_type a_pFun, int a_iArgc); - void AddBulkFun(generic_fun_type a_pFun, int a_iArgc); - void AddStrFun(generic_fun_type a_pFun, int a_iArgc, int a_iIdx); - - void EnableOptimizer(bool bStat); - - void Finalize(); - void clear(); - std::size_t GetMaxStackSize() const; - std::size_t GetSize() const; - + void Assign(const QmuParserByteCode &a_ByteCode); + void AddVar(qreal *a_pVar); + void AddVal(qreal a_fVal); + void AddOp(ECmdCode a_Oprt); + void AddIfElse(ECmdCode a_Oprt); + void AddAssignOp(qreal *a_pVar); + void AddFun(generic_fun_type a_pFun, int a_iArgc); + void AddBulkFun(generic_fun_type a_pFun, int a_iArgc); + void AddStrFun(generic_fun_type a_pFun, int a_iArgc, int a_iIdx); + void EnableOptimizer(bool bStat); + void Finalize(); + void clear(); + std::size_t GetMaxStackSize() const; + std::size_t GetSize() const; const SToken* GetBase() const; - void AsciiDump(); + void AsciiDump(); }; - } // namespace qmu - #endif diff --git a/src/libs/qmuparser/qmuparsercallback.cpp b/src/libs/qmuparser/qmuparsercallback.cpp index 08bc21f2d..0a1db5ac9 100644 --- a/src/libs/qmuparser/qmuparsercallback.cpp +++ b/src/libs/qmuparser/qmuparsercallback.cpp @@ -1,460 +1,322 @@ -/*************************************************************************************************** - ** - ** Original work Copyright (C) 2013 Ingo Berg - ** Modified work Copyright 2014 Roman Telezhinsky - ** - ** 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 "qmuparsercallback.h" - -/** \file - \brief Implementation of the parser callback class. -*/ - - -namespace qmu -{ - //--------------------------------------------------------------------------- - QmuParserCallback::QmuParserCallback(fun_type0 a_pFun, bool a_bAllowOpti) - :m_pFun((void*)a_pFun) - ,m_iArgc(0) - ,m_iPri(-1) - ,m_eOprtAsct(oaNONE) - ,m_iCode(cmFUNC) - ,m_iType(tpDBL) - ,m_bAllowOpti(a_bAllowOpti) - {} - - //--------------------------------------------------------------------------- - QmuParserCallback::QmuParserCallback(fun_type1 a_pFun, bool a_bAllowOpti, int a_iPrec, ECmdCode a_iCode) - :m_pFun((void*)a_pFun) - ,m_iArgc(1) - ,m_iPri(a_iPrec) - ,m_eOprtAsct(oaNONE) - ,m_iCode(a_iCode) - ,m_iType(tpDBL) - ,m_bAllowOpti(a_bAllowOpti) - {} - - - //--------------------------------------------------------------------------- - /** \brief Constructor for constructing funcstion callbacks taking two arguments. - \throw nothrow - */ - QmuParserCallback::QmuParserCallback(fun_type2 a_pFun, bool a_bAllowOpti) - :m_pFun((void*)a_pFun) - ,m_iArgc(2) - ,m_iPri(-1) - ,m_eOprtAsct(oaNONE) - ,m_iCode(cmFUNC) - ,m_iType(tpDBL) - ,m_bAllowOpti(a_bAllowOpti) - {} - - //--------------------------------------------------------------------------- - /** \brief Constructor for constructing binary operator callbacks. - \param a_pFun Pointer to a static function taking two arguments - \param a_bAllowOpti A flag indicating this funcation can be optimized - \param a_iPrec The operator precedence - \param a_eOprtAsct The operators associativity - \throw nothrow - */ - QmuParserCallback::QmuParserCallback(fun_type2 a_pFun, - bool a_bAllowOpti, - int a_iPrec, - EOprtAssociativity a_eOprtAsct) - :m_pFun((void*)a_pFun) - ,m_iArgc(2) - ,m_iPri(a_iPrec) - ,m_eOprtAsct(a_eOprtAsct) - ,m_iCode(cmOPRT_BIN) - ,m_iType(tpDBL) - ,m_bAllowOpti(a_bAllowOpti) - {} - - //--------------------------------------------------------------------------- - QmuParserCallback::QmuParserCallback(fun_type3 a_pFun, bool a_bAllowOpti) - :m_pFun((void*)a_pFun) - ,m_iArgc(3) - ,m_iPri(-1) - ,m_eOprtAsct(oaNONE) - ,m_iCode(cmFUNC) - ,m_iType(tpDBL) - ,m_bAllowOpti(a_bAllowOpti) - {} - - - //--------------------------------------------------------------------------- - QmuParserCallback::QmuParserCallback(fun_type4 a_pFun, bool a_bAllowOpti) - :m_pFun((void*)a_pFun) - ,m_iArgc(4) - ,m_iPri(-1) - ,m_eOprtAsct(oaNONE) - ,m_iCode(cmFUNC) - ,m_iType(tpDBL) - ,m_bAllowOpti(a_bAllowOpti) - {} - - - //--------------------------------------------------------------------------- - QmuParserCallback::QmuParserCallback(fun_type5 a_pFun, bool a_bAllowOpti) - :m_pFun((void*)a_pFun) - ,m_iArgc(5) - ,m_iPri(-1) - ,m_eOprtAsct(oaNONE) - ,m_iCode(cmFUNC) - ,m_iType(tpDBL) - ,m_bAllowOpti(a_bAllowOpti) - {} - - //--------------------------------------------------------------------------- - QmuParserCallback::QmuParserCallback(fun_type6 a_pFun, bool a_bAllowOpti) - :m_pFun((void*)a_pFun) - ,m_iArgc(6) - ,m_iPri(-1) - ,m_eOprtAsct(oaNONE) - ,m_iCode(cmFUNC) - ,m_iType(tpDBL) - ,m_bAllowOpti(a_bAllowOpti) - {} - - //--------------------------------------------------------------------------- - QmuParserCallback::QmuParserCallback(fun_type7 a_pFun, bool a_bAllowOpti) - :m_pFun((void*)a_pFun) - ,m_iArgc(7) - ,m_iPri(-1) - ,m_eOprtAsct(oaNONE) - ,m_iCode(cmFUNC) - ,m_iType(tpDBL) - ,m_bAllowOpti(a_bAllowOpti) - {} - - //--------------------------------------------------------------------------- - QmuParserCallback::QmuParserCallback(fun_type8 a_pFun, bool a_bAllowOpti) - :m_pFun((void*)a_pFun) - ,m_iArgc(8) - ,m_iPri(-1) - ,m_eOprtAsct(oaNONE) - ,m_iCode(cmFUNC) - ,m_iType(tpDBL) - ,m_bAllowOpti(a_bAllowOpti) - {} - - //--------------------------------------------------------------------------- - QmuParserCallback::QmuParserCallback(fun_type9 a_pFun, bool a_bAllowOpti) - :m_pFun((void*)a_pFun) - ,m_iArgc(9) - ,m_iPri(-1) - ,m_eOprtAsct(oaNONE) - ,m_iCode(cmFUNC) - ,m_iType(tpDBL) - ,m_bAllowOpti(a_bAllowOpti) - {} - - //--------------------------------------------------------------------------- - QmuParserCallback::QmuParserCallback(fun_type10 a_pFun, bool a_bAllowOpti) - :m_pFun((void*)a_pFun) - ,m_iArgc(10) - ,m_iPri(-1) - ,m_eOprtAsct(oaNONE) - ,m_iCode(cmFUNC) - ,m_iType(tpDBL) - ,m_bAllowOpti(a_bAllowOpti) - {} - - //--------------------------------------------------------------------------- - QmuParserCallback::QmuParserCallback(bulkfun_type0 a_pFun, bool a_bAllowOpti) - :m_pFun((void*)a_pFun) - ,m_iArgc(0) - ,m_iPri(-1) - ,m_eOprtAsct(oaNONE) - ,m_iCode(cmFUNC_BULK) - ,m_iType(tpDBL) - ,m_bAllowOpti(a_bAllowOpti) - {} - - //--------------------------------------------------------------------------- - QmuParserCallback::QmuParserCallback(bulkfun_type1 a_pFun, bool a_bAllowOpti) - :m_pFun((void*)a_pFun) - ,m_iArgc(1) - ,m_iPri(-1) - ,m_eOprtAsct(oaNONE) - ,m_iCode(cmFUNC_BULK) - ,m_iType(tpDBL) - ,m_bAllowOpti(a_bAllowOpti) - {} - - - //--------------------------------------------------------------------------- - /** \brief Constructor for constructing funcstion callbacks taking two arguments. - \throw nothrow - */ - QmuParserCallback::QmuParserCallback(bulkfun_type2 a_pFun, bool a_bAllowOpti) - :m_pFun((void*)a_pFun) - ,m_iArgc(2) - ,m_iPri(-1) - ,m_eOprtAsct(oaNONE) - ,m_iCode(cmFUNC_BULK) - ,m_iType(tpDBL) - ,m_bAllowOpti(a_bAllowOpti) - {} - - //--------------------------------------------------------------------------- - QmuParserCallback::QmuParserCallback(bulkfun_type3 a_pFun, bool a_bAllowOpti) - :m_pFun((void*)a_pFun) - ,m_iArgc(3) - ,m_iPri(-1) - ,m_eOprtAsct(oaNONE) - ,m_iCode(cmFUNC_BULK) - ,m_iType(tpDBL) - ,m_bAllowOpti(a_bAllowOpti) - {} - - - //--------------------------------------------------------------------------- - QmuParserCallback::QmuParserCallback(bulkfun_type4 a_pFun, bool a_bAllowOpti) - :m_pFun((void*)a_pFun) - ,m_iArgc(4) - ,m_iPri(-1) - ,m_eOprtAsct(oaNONE) - ,m_iCode(cmFUNC_BULK) - ,m_iType(tpDBL) - ,m_bAllowOpti(a_bAllowOpti) - {} - - - //--------------------------------------------------------------------------- - QmuParserCallback::QmuParserCallback(bulkfun_type5 a_pFun, bool a_bAllowOpti) - :m_pFun((void*)a_pFun) - ,m_iArgc(5) - ,m_iPri(-1) - ,m_eOprtAsct(oaNONE) - ,m_iCode(cmFUNC_BULK) - ,m_iType(tpDBL) - ,m_bAllowOpti(a_bAllowOpti) - {} - - //--------------------------------------------------------------------------- - QmuParserCallback::QmuParserCallback(bulkfun_type6 a_pFun, bool a_bAllowOpti) - :m_pFun((void*)a_pFun) - ,m_iArgc(6) - ,m_iPri(-1) - ,m_eOprtAsct(oaNONE) - ,m_iCode(cmFUNC_BULK) - ,m_iType(tpDBL) - ,m_bAllowOpti(a_bAllowOpti) - {} - - //--------------------------------------------------------------------------- - QmuParserCallback::QmuParserCallback(bulkfun_type7 a_pFun, bool a_bAllowOpti) - :m_pFun((void*)a_pFun) - ,m_iArgc(7) - ,m_iPri(-1) - ,m_eOprtAsct(oaNONE) - ,m_iCode(cmFUNC_BULK) - ,m_iType(tpDBL) - ,m_bAllowOpti(a_bAllowOpti) - {} - - //--------------------------------------------------------------------------- - QmuParserCallback::QmuParserCallback(bulkfun_type8 a_pFun, bool a_bAllowOpti) - :m_pFun((void*)a_pFun) - ,m_iArgc(8) - ,m_iPri(-1) - ,m_eOprtAsct(oaNONE) - ,m_iCode(cmFUNC_BULK) - ,m_iType(tpDBL) - ,m_bAllowOpti(a_bAllowOpti) - {} - - //--------------------------------------------------------------------------- - QmuParserCallback::QmuParserCallback(bulkfun_type9 a_pFun, bool a_bAllowOpti) - :m_pFun((void*)a_pFun) - ,m_iArgc(9) - ,m_iPri(-1) - ,m_eOprtAsct(oaNONE) - ,m_iCode(cmFUNC_BULK) - ,m_iType(tpDBL) - ,m_bAllowOpti(a_bAllowOpti) - {} - - //--------------------------------------------------------------------------- - QmuParserCallback::QmuParserCallback(bulkfun_type10 a_pFun, bool a_bAllowOpti) - :m_pFun((void*)a_pFun) - ,m_iArgc(10) - ,m_iPri(-1) - ,m_eOprtAsct(oaNONE) - ,m_iCode(cmFUNC_BULK) - ,m_iType(tpDBL) - ,m_bAllowOpti(a_bAllowOpti) - {} - - - //--------------------------------------------------------------------------- - QmuParserCallback::QmuParserCallback(multfun_type a_pFun, bool a_bAllowOpti) - :m_pFun((void*)a_pFun) - ,m_iArgc(-1) - ,m_iPri(-1) - ,m_eOprtAsct(oaNONE) - ,m_iCode(cmFUNC) - ,m_iType(tpDBL) - ,m_bAllowOpti(a_bAllowOpti) - {} - - - //--------------------------------------------------------------------------- - QmuParserCallback::QmuParserCallback(strfun_type1 a_pFun, bool a_bAllowOpti) - :m_pFun((void*)a_pFun) - ,m_iArgc(0) - ,m_iPri(-1) - ,m_eOprtAsct(oaNONE) - ,m_iCode(cmFUNC_STR) - ,m_iType(tpSTR) - ,m_bAllowOpti(a_bAllowOpti) - {} - - - //--------------------------------------------------------------------------- - QmuParserCallback::QmuParserCallback(strfun_type2 a_pFun, bool a_bAllowOpti) - :m_pFun((void*)a_pFun) - ,m_iArgc(1) - ,m_iPri(-1) - ,m_eOprtAsct(oaNONE) - ,m_iCode(cmFUNC_STR) - ,m_iType(tpSTR) - ,m_bAllowOpti(a_bAllowOpti) - {} - - - //--------------------------------------------------------------------------- - QmuParserCallback::QmuParserCallback(strfun_type3 a_pFun, bool a_bAllowOpti) - :m_pFun((void*)a_pFun) - ,m_iArgc(2) - ,m_iPri(-1) - ,m_eOprtAsct(oaNONE) - ,m_iCode(cmFUNC_STR) - ,m_iType(tpSTR) - ,m_bAllowOpti(a_bAllowOpti) - {} - - - //--------------------------------------------------------------------------- - /** \brief Default constructor. - \throw nothrow - */ - QmuParserCallback::QmuParserCallback() - :m_pFun(0) - ,m_iArgc(0) - ,m_iPri(-1) - ,m_eOprtAsct(oaNONE) - ,m_iCode(cmUNKNOWN) - ,m_iType(tpVOID) - ,m_bAllowOpti(0) - {} - - - //--------------------------------------------------------------------------- - /** \brief Copy constructor. - \throw nothrow - */ - QmuParserCallback::QmuParserCallback(const QmuParserCallback &ref) - { - m_pFun = ref.m_pFun; - m_iArgc = ref.m_iArgc; - m_bAllowOpti = ref.m_bAllowOpti; - m_iCode = ref.m_iCode; - m_iType = ref.m_iType; - m_iPri = ref.m_iPri; - m_eOprtAsct = ref.m_eOprtAsct; - } - - //--------------------------------------------------------------------------- - /** \brief Clone this instance and return a pointer to the new instance. */ - QmuParserCallback* QmuParserCallback::Clone() const - { - return new QmuParserCallback(*this); - } - - //--------------------------------------------------------------------------- - /** \brief Return tru if the function is conservative. - - Conservative functions return always the same result for the same argument. - \throw nothrow - */ - bool QmuParserCallback::IsOptimizable() const - { - return m_bAllowOpti; - } - - //--------------------------------------------------------------------------- - /** \brief Get the callback address for the parser function. - - The type of the address is void. It needs to be recasted according to the - argument number to the right type. - - \throw nothrow - \return #pFun - */ - void* QmuParserCallback::GetAddr() const - { - return m_pFun; - } - - //--------------------------------------------------------------------------- - /** \brief Return the callback code. */ - ECmdCode QmuParserCallback::GetCode() const - { - return m_iCode; - } - - //--------------------------------------------------------------------------- - ETypeCode QmuParserCallback::GetType() const - { - return m_iType; - } - - - //--------------------------------------------------------------------------- - /** \brief Return the operator precedence. - \throw nothrown - - Only valid if the callback token is an operator token (binary or infix). - */ - int QmuParserCallback::GetPri() const - { - return m_iPri; - } - - //--------------------------------------------------------------------------- - /** \brief Return the operators associativity. - \throw nothrown - - Only valid if the callback token is a binary operator token. - */ - EOprtAssociativity QmuParserCallback::GetAssociativity() const - { - return m_eOprtAsct; - } - - //--------------------------------------------------------------------------- - /** \brief Returns the number of function Arguments. */ - int QmuParserCallback::GetArgc() const - { - return m_iArgc; - } -} // namespace qmu +/*************************************************************************************************** + ** + ** Original work Copyright (C) 2013 Ingo Berg + ** Modified work Copyright 2014 Roman Telezhinsky + ** + ** 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 "qmuparsercallback.h" + +/** + * @file + * @brief Implementation of the parser callback class. + */ + +namespace qmu +{ +//---------------------------------------------------------------------------------------------------------------------- +QmuParserCallback::QmuParserCallback ( fun_type0 a_pFun, bool a_bAllowOpti ) + : m_pFun ( ( void* ) a_pFun ), m_iArgc ( 0 ), m_iPri ( -1 ), m_eOprtAsct ( oaNONE ), m_iCode ( cmFUNC ), + m_iType ( tpDBL ), m_bAllowOpti ( a_bAllowOpti ) +{} + +//---------------------------------------------------------------------------------------------------------------------- +QmuParserCallback::QmuParserCallback ( fun_type1 a_pFun, bool a_bAllowOpti, int a_iPrec, ECmdCode a_iCode ) + : m_pFun ( ( void* ) a_pFun ), m_iArgc ( 1 ), m_iPri ( a_iPrec ), m_eOprtAsct ( oaNONE ), m_iCode ( a_iCode ), + m_iType ( tpDBL ), m_bAllowOpti ( a_bAllowOpti ) +{} + + +//---------------------------------------------------------------------------------------------------------------------- +/** + * @brief Constructor for constructing funcstion callbacks taking two arguments. + * @throw nothrow + */ +QmuParserCallback::QmuParserCallback ( fun_type2 a_pFun, bool a_bAllowOpti ) + : m_pFun ( ( void* ) a_pFun ), m_iArgc ( 2 ), m_iPri ( -1 ), m_eOprtAsct ( oaNONE ), m_iCode ( cmFUNC ), + m_iType ( tpDBL ), m_bAllowOpti ( a_bAllowOpti ) +{} + +//---------------------------------------------------------------------------------------------------------------------- +/** + * @brief Constructor for constructing binary operator callbacks. + * @param a_pFun Pointer to a static function taking two arguments + * @param a_bAllowOpti A flag indicating this funcation can be optimized + * @param a_iPrec The operator precedence + * @param a_eOprtAsct The operators associativity + * @throw nothrow + */ +QmuParserCallback::QmuParserCallback ( fun_type2 a_pFun, bool a_bAllowOpti, int a_iPrec, + EOprtAssociativity a_eOprtAsct ) + : m_pFun ( ( void* ) a_pFun ), m_iArgc ( 2 ), m_iPri ( a_iPrec ), m_eOprtAsct ( a_eOprtAsct ), + m_iCode ( cmOPRT_BIN ), m_iType ( tpDBL ), m_bAllowOpti ( a_bAllowOpti ) +{} + +//---------------------------------------------------------------------------------------------------------------------- +QmuParserCallback::QmuParserCallback ( fun_type3 a_pFun, bool a_bAllowOpti ) + : m_pFun ( ( void* ) a_pFun ), m_iArgc ( 3 ), m_iPri ( -1 ), m_eOprtAsct ( oaNONE ), m_iCode ( cmFUNC ), + m_iType ( tpDBL ), m_bAllowOpti ( a_bAllowOpti ) +{} + + +//---------------------------------------------------------------------------------------------------------------------- +QmuParserCallback::QmuParserCallback ( fun_type4 a_pFun, bool a_bAllowOpti ) + : m_pFun ( ( void* ) a_pFun ), m_iArgc ( 4 ), m_iPri ( -1 ), m_eOprtAsct ( oaNONE ), m_iCode ( cmFUNC ), + m_iType ( tpDBL ), m_bAllowOpti ( a_bAllowOpti ) +{} + + +//---------------------------------------------------------------------------------------------------------------------- +QmuParserCallback::QmuParserCallback ( fun_type5 a_pFun, bool a_bAllowOpti ) + : m_pFun ( ( void* ) a_pFun ), m_iArgc ( 5 ), m_iPri ( -1 ), m_eOprtAsct ( oaNONE ), m_iCode ( cmFUNC ), + m_iType ( tpDBL ), m_bAllowOpti ( a_bAllowOpti ) +{} + +//---------------------------------------------------------------------------------------------------------------------- +QmuParserCallback::QmuParserCallback ( fun_type6 a_pFun, bool a_bAllowOpti ) + : m_pFun ( ( void* ) a_pFun ), m_iArgc ( 6 ), m_iPri ( -1 ), m_eOprtAsct ( oaNONE ), m_iCode ( cmFUNC ), + m_iType ( tpDBL ), m_bAllowOpti ( a_bAllowOpti ) +{} + +//---------------------------------------------------------------------------------------------------------------------- +QmuParserCallback::QmuParserCallback ( fun_type7 a_pFun, bool a_bAllowOpti ) + : m_pFun ( ( void* ) a_pFun ), m_iArgc ( 7 ), m_iPri ( -1 ), m_eOprtAsct ( oaNONE ), m_iCode ( cmFUNC ), + m_iType ( tpDBL ), m_bAllowOpti ( a_bAllowOpti ) +{} + +//---------------------------------------------------------------------------------------------------------------------- +QmuParserCallback::QmuParserCallback ( fun_type8 a_pFun, bool a_bAllowOpti ) + : m_pFun ( ( void* ) a_pFun ), m_iArgc ( 8 ), m_iPri ( -1 ), m_eOprtAsct ( oaNONE ), m_iCode ( cmFUNC ), + m_iType ( tpDBL ), m_bAllowOpti ( a_bAllowOpti ) +{} + +//---------------------------------------------------------------------------------------------------------------------- +QmuParserCallback::QmuParserCallback ( fun_type9 a_pFun, bool a_bAllowOpti ) + : m_pFun ( ( void* ) a_pFun ), m_iArgc ( 9 ), m_iPri ( -1 ), m_eOprtAsct ( oaNONE ), m_iCode ( cmFUNC ), + m_iType ( tpDBL ), m_bAllowOpti ( a_bAllowOpti ) +{} + +//---------------------------------------------------------------------------------------------------------------------- +QmuParserCallback::QmuParserCallback ( fun_type10 a_pFun, bool a_bAllowOpti ) + : m_pFun ( ( void* ) a_pFun ), m_iArgc ( 10 ), m_iPri ( -1 ), m_eOprtAsct ( oaNONE ), m_iCode ( cmFUNC ), + m_iType ( tpDBL ), m_bAllowOpti ( a_bAllowOpti ) +{} + +//---------------------------------------------------------------------------------------------------------------------- +QmuParserCallback::QmuParserCallback ( bulkfun_type0 a_pFun, bool a_bAllowOpti ) + : m_pFun ( ( void* ) a_pFun ), m_iArgc ( 0 ), m_iPri ( -1 ), m_eOprtAsct ( oaNONE ), m_iCode ( cmFUNC_BULK ), + m_iType ( tpDBL ), m_bAllowOpti ( a_bAllowOpti ) +{} + +//---------------------------------------------------------------------------------------------------------------------- +QmuParserCallback::QmuParserCallback ( bulkfun_type1 a_pFun, bool a_bAllowOpti ) + : m_pFun ( ( void* ) a_pFun ), m_iArgc ( 1 ), m_iPri ( -1 ), m_eOprtAsct ( oaNONE ), m_iCode ( cmFUNC_BULK ), + m_iType ( tpDBL ), m_bAllowOpti ( a_bAllowOpti ) +{} + +//---------------------------------------------------------------------------------------------------------------------- +/** + * @brief Constructor for constructing funcstion callbacks taking two arguments. + * @throw nothrow + */ +QmuParserCallback::QmuParserCallback ( bulkfun_type2 a_pFun, bool a_bAllowOpti ) + : m_pFun ( ( void* ) a_pFun ), m_iArgc ( 2 ), m_iPri ( -1 ), m_eOprtAsct ( oaNONE ), m_iCode ( cmFUNC_BULK ), + m_iType ( tpDBL ), m_bAllowOpti ( a_bAllowOpti ) +{} + +//---------------------------------------------------------------------------------------------------------------------- +QmuParserCallback::QmuParserCallback ( bulkfun_type3 a_pFun, bool a_bAllowOpti ) + : m_pFun ( ( void* ) a_pFun ), m_iArgc ( 3 ), m_iPri ( -1 ), m_eOprtAsct ( oaNONE ), m_iCode ( cmFUNC_BULK ), + m_iType ( tpDBL ), m_bAllowOpti ( a_bAllowOpti ) +{} + +//---------------------------------------------------------------------------------------------------------------------- +QmuParserCallback::QmuParserCallback ( bulkfun_type4 a_pFun, bool a_bAllowOpti ) + : m_pFun ( ( void* ) a_pFun ), m_iArgc ( 4 ), m_iPri ( -1 ), m_eOprtAsct ( oaNONE ), m_iCode ( cmFUNC_BULK ), + m_iType ( tpDBL ), m_bAllowOpti ( a_bAllowOpti ) +{} + +//---------------------------------------------------------------------------------------------------------------------- +QmuParserCallback::QmuParserCallback ( bulkfun_type5 a_pFun, bool a_bAllowOpti ) + : m_pFun ( ( void* ) a_pFun ), m_iArgc ( 5 ), m_iPri ( -1 ), m_eOprtAsct ( oaNONE ), m_iCode ( cmFUNC_BULK ), + m_iType ( tpDBL ), m_bAllowOpti ( a_bAllowOpti ) +{} + +//---------------------------------------------------------------------------------------------------------------------- +QmuParserCallback::QmuParserCallback ( bulkfun_type6 a_pFun, bool a_bAllowOpti ) + : m_pFun ( ( void* ) a_pFun ), m_iArgc ( 6 ), m_iPri ( -1 ), m_eOprtAsct ( oaNONE ), m_iCode ( cmFUNC_BULK ), + m_iType ( tpDBL ), m_bAllowOpti ( a_bAllowOpti ) +{} + +//---------------------------------------------------------------------------------------------------------------------- +QmuParserCallback::QmuParserCallback ( bulkfun_type7 a_pFun, bool a_bAllowOpti ) + : m_pFun ( ( void* ) a_pFun ), m_iArgc ( 7 ), m_iPri ( -1 ), m_eOprtAsct ( oaNONE ), m_iCode ( cmFUNC_BULK ), + m_iType ( tpDBL ), m_bAllowOpti ( a_bAllowOpti ) +{} + +//---------------------------------------------------------------------------------------------------------------------- +QmuParserCallback::QmuParserCallback ( bulkfun_type8 a_pFun, bool a_bAllowOpti ) + : m_pFun ( ( void* ) a_pFun ), m_iArgc ( 8 ), m_iPri ( -1 ), m_eOprtAsct ( oaNONE ), m_iCode ( cmFUNC_BULK ), + m_iType ( tpDBL ), m_bAllowOpti ( a_bAllowOpti ) +{} + +//---------------------------------------------------------------------------------------------------------------------- +QmuParserCallback::QmuParserCallback ( bulkfun_type9 a_pFun, bool a_bAllowOpti ) + : m_pFun ( ( void* ) a_pFun ), m_iArgc ( 9 ), m_iPri ( -1 ), m_eOprtAsct ( oaNONE ), m_iCode ( cmFUNC_BULK ), + m_iType ( tpDBL ), m_bAllowOpti ( a_bAllowOpti ) +{} + +//---------------------------------------------------------------------------------------------------------------------- +QmuParserCallback::QmuParserCallback ( bulkfun_type10 a_pFun, bool a_bAllowOpti ) + : m_pFun ( ( void* ) a_pFun ), m_iArgc ( 10 ), m_iPri ( -1 ), m_eOprtAsct ( oaNONE ), m_iCode ( cmFUNC_BULK ), + m_iType ( tpDBL ), m_bAllowOpti ( a_bAllowOpti ) +{} + +//---------------------------------------------------------------------------------------------------------------------- +QmuParserCallback::QmuParserCallback ( multfun_type a_pFun, bool a_bAllowOpti ) + : m_pFun ( ( void* ) a_pFun ), m_iArgc ( -1 ), m_iPri ( -1 ), m_eOprtAsct ( oaNONE ), m_iCode ( cmFUNC ), + m_iType ( tpDBL ), m_bAllowOpti ( a_bAllowOpti ) +{} + +//---------------------------------------------------------------------------------------------------------------------- +QmuParserCallback::QmuParserCallback ( strfun_type1 a_pFun, bool a_bAllowOpti ) + : m_pFun ( ( void* ) a_pFun ), m_iArgc ( 0 ), m_iPri ( -1 ), m_eOprtAsct ( oaNONE ), + m_iCode ( cmFUNC_STR ), m_iType ( tpSTR ), m_bAllowOpti ( a_bAllowOpti ) +{} + +//---------------------------------------------------------------------------------------------------------------------- +QmuParserCallback::QmuParserCallback ( strfun_type2 a_pFun, bool a_bAllowOpti ) + : m_pFun ( ( void* ) a_pFun ), m_iArgc ( 1 ), m_iPri ( -1 ), m_eOprtAsct ( oaNONE ), m_iCode ( cmFUNC_STR ), + m_iType ( tpSTR ), m_bAllowOpti ( a_bAllowOpti ) +{} + +//---------------------------------------------------------------------------------------------------------------------- +QmuParserCallback::QmuParserCallback ( strfun_type3 a_pFun, bool a_bAllowOpti ) + : m_pFun ( ( void* ) a_pFun ), m_iArgc ( 2 ), m_iPri ( -1 ), m_eOprtAsct ( oaNONE ), m_iCode ( cmFUNC_STR ), + m_iType ( tpSTR ), m_bAllowOpti ( a_bAllowOpti ) +{} + +//---------------------------------------------------------------------------------------------------------------------- +/** + * @brief Default constructor. + * @throw nothrow + */ +QmuParserCallback::QmuParserCallback() + : m_pFun ( 0 ), m_iArgc ( 0 ), m_iPri ( -1 ), m_eOprtAsct ( oaNONE ), m_iCode ( cmUNKNOWN ), m_iType ( tpVOID ), + m_bAllowOpti ( 0 ) +{} + +//---------------------------------------------------------------------------------------------------------------------- +/** + * @brief Copy constructor. + * @throw nothrow + */ +QmuParserCallback::QmuParserCallback ( const QmuParserCallback &ref ) +{ + m_pFun = ref.m_pFun; + m_iArgc = ref.m_iArgc; + m_bAllowOpti = ref.m_bAllowOpti; + m_iCode = ref.m_iCode; + m_iType = ref.m_iType; + m_iPri = ref.m_iPri; + m_eOprtAsct = ref.m_eOprtAsct; +} + +//---------------------------------------------------------------------------------------------------------------------- +/** + * @brief Clone this instance and return a pointer to the new instance. + */ +QmuParserCallback* QmuParserCallback::Clone() const +{ + return new QmuParserCallback ( *this ); +} + +//---------------------------------------------------------------------------------------------------------------------- +/** + * @brief Return tru if the function is conservative. + * + * Conservative functions return always the same result for the same argument. + * @throw nothrow + */ +bool QmuParserCallback::IsOptimizable() const +{ + return m_bAllowOpti; +} + +//---------------------------------------------------------------------------------------------------------------------- +/** + * @brief Get the callback address for the parser function. + * + * The type of the address is void. It needs to be recasted according to the argument number to the right type. + * + * @throw nothrow + * @return #pFun + */ +void* QmuParserCallback::GetAddr() const +{ + return m_pFun; +} + +//---------------------------------------------------------------------------------------------------------------------- +/** + * @brief Return the callback code. +*/ +ECmdCode QmuParserCallback::GetCode() const +{ + return m_iCode; +} + +//---------------------------------------------------------------------------------------------------------------------- +ETypeCode QmuParserCallback::GetType() const +{ + return m_iType; +} + +//---------------------------------------------------------------------------------------------------------------------- +/** + * @brief Return the operator precedence. + * @throw nothrown + * + * Only valid if the callback token is an operator token (binary or infix). + */ +int QmuParserCallback::GetPri() const +{ + return m_iPri; +} + +//---------------------------------------------------------------------------------------------------------------------- +/** + * @brief Return the operators associativity. + * @throw nothrown + * + * Only valid if the callback token is a binary operator token. + */ +EOprtAssociativity QmuParserCallback::GetAssociativity() const +{ + return m_eOprtAsct; +} + +//---------------------------------------------------------------------------------------------------------------------- +/** + * @brief Returns the number of function Arguments. + */ +int QmuParserCallback::GetArgc() const +{ + return m_iArgc; +} +} // namespace qmu diff --git a/src/libs/qmuparser/qmuparsercallback.h b/src/libs/qmuparser/qmuparsercallback.h index de968d686..3fce55943 100644 --- a/src/libs/qmuparser/qmuparsercallback.h +++ b/src/libs/qmuparser/qmuparsercallback.h @@ -25,25 +25,24 @@ #include "qmuparserdef.h" -/** \file - \brief Definition of the parser callback class. -*/ +/** + * @file + * @brief Definition of the parser callback class. + */ namespace qmu { -/** \brief Encapsulation of prototypes for a numerical parser function. - - Encapsulates the prototyp for numerical parser functions. The class - stores the number of arguments for parser functions as well - as additional flags indication the function is non optimizeable. - The pointer to the callback function pointer is stored as void* - and needs to be casted according to the argument count. - Negative argument counts indicate a parser function with a variable number - of arguments. - - \author (C) 2004-2011 Ingo Berg -*/ +/** + * @brief Encapsulation of prototypes for a numerical parser function. + * + * Encapsulates the prototyp for numerical parser functions. The class stores the number of arguments for parser + * functions as well as additional flags indication the function is non optimizeable. The pointer to the callback + * function pointer is stored as void* and needs to be casted according to the argument count. Negative argument counts + * indicate a parser function with a variable number of arguments. + * + * @author (C) 2004-2011 Ingo Berg + */ class QmuParserCallback { public: @@ -80,23 +79,22 @@ public: QmuParserCallback(const QmuParserCallback &a_Fun); QmuParserCallback* Clone() const; - - bool IsOptimizable() const; - void* GetAddr() const; - ECmdCode GetCode() const; - ETypeCode GetType() const; - int GetPri() const; + bool IsOptimizable() const; + void* GetAddr() const; + ECmdCode GetCode() const; + ETypeCode GetType() const; + int GetPri() const; EOprtAssociativity GetAssociativity() const; - int GetArgc() const; - + int GetArgc() const; private: void *m_pFun; ///< Pointer to the callback function, casted to void - /** \brief Number of numeric function arguments - - This number is negative for functions with variable number of arguments. in this cases - they represent the actual number of arguments found. - */ + /** + * @brief Number of numeric function arguments + * + * This number is negative for functions with variable number of arguments. in this cases + * they represent the actual number of arguments found. + */ int m_iArgc; int m_iPri; ///< Valid only for binary and infix operators; Operator precedence. EOprtAssociativity m_eOprtAsct; ///< Operator associativity; Valid only for binary operators @@ -105,8 +103,10 @@ private: bool m_bAllowOpti; ///< Flag indication optimizeability }; -//------------------------------------------------------------------------------ -/** \brief Container for Callback objects. */ +//---------------------------------------------------------------------------------------------------------------------- +/** + * @brief Container for Callback objects. + */ typedef std::map funmap_type; } // namespace qmu diff --git a/src/libs/qmuparser/qmuparserdef.h b/src/libs/qmuparser/qmuparserdef.h index 0ecb3ac4a..4a1abd67e 100644 --- a/src/libs/qmuparser/qmuparserdef.h +++ b/src/libs/qmuparser/qmuparserdef.h @@ -1,281 +1,275 @@ -/*************************************************************************************************** - ** - ** Original work Copyright (C) 2013 Ingo Berg - ** Modified work Copyright 2014 Roman Telezhinsky - ** - ** 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. - ** - ******************************************************************************************************/ - -#ifndef QMUPDEF_H -#define QMUPDEF_H - -#include -#include -#include -#include -#include -#include - -#include "qmuparserfixes.h" - -/** \file - \brief This file contains standard definitions used by the parser. -*/ - -#define QMUP_VERSION "2.2.3" -#define QMUP_VERSION_DATE "20121222; SF" - -#define QMUP_CHARS "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" - -/** \brief If this macro is defined mathematical exceptions (div by zero) will be thrown as exceptions. */ -//#define QMUP_MATH_EXCEPTIONS - -/** \brief Activate this option in order to compile with OpenMP support. - - OpenMP is used only in the bulk mode it may increase the performance a bit. -*/ -//#define MUP_USE_OPENMP - -#if defined(_UNICODE) - /** \brief Definition of the basic parser string type. */ - #define QMUP_STRING_TYPE std::wstring -#else - /** \brief Definition of the basic parser string type. */ - #define QMUP_STRING_TYPE std::string -#endif - -namespace qmu -{ - //------------------------------------------------------------------------------ - /** \brief Bytecode values. - - \attention The order of the operator entries must match the order in ParserBase::c_DefaultOprt! - */ - enum ECmdCode - { - // The following are codes for built in binary operators - // apart from built in operators the user has the opportunity to - // add user defined operators. - cmLE = 0, ///< Operator item: less or equal - cmGE = 1, ///< Operator item: greater or equal - cmNEQ = 2, ///< Operator item: not equal - cmEQ = 3, ///< Operator item: equals - cmLT = 4, ///< Operator item: less than - cmGT = 5, ///< Operator item: greater than - cmADD = 6, ///< Operator item: add - cmSUB = 7, ///< Operator item: subtract - cmMUL = 8, ///< Operator item: multiply - cmDIV = 9, ///< Operator item: division - cmPOW = 10, ///< Operator item: y to the power of ... - cmLAND = 11, - cmLOR = 12, - cmASSIGN = 13, ///< Operator item: Assignment operator - cmBO = 14, ///< Operator item: opening bracket - cmBC = 15, ///< Operator item: closing bracket - cmIF = 16, ///< For use in the ternary if-then-else operator - cmELSE = 17, ///< For use in the ternary if-then-else operator - cmENDIF = 18, ///< For use in the ternary if-then-else operator - cmARG_SEP = 19, ///< function argument separator - cmVAR = 20, ///< variable item - cmVAL = 21, ///< value item - - // For optimization purposes - cmVARPOW2, - cmVARPOW3, - cmVARPOW4, - cmVARMUL, - cmPOW2, - - // operators and functions - cmFUNC, ///< Code for a generic function item - cmFUNC_STR, ///< Code for a function with a string parameter - cmFUNC_BULK, ///< Special callbacks for Bulk mode with an additional parameter for the bulk index - cmSTRING, ///< Code for a string token - cmOPRT_BIN, ///< user defined binary operator - cmOPRT_POSTFIX, ///< code for postfix operators - cmOPRT_INFIX, ///< code for infix operators - cmEND, ///< end of formula - cmUNKNOWN ///< uninitialized item - }; - - //------------------------------------------------------------------------------ - /** \brief Types internally used by the parser. - */ - enum ETypeCode - { - tpSTR = 0, ///< String type (Function arguments and constants only, no string variables) - tpDBL = 1, ///< Floating point variables - tpVOID = 2 ///< Undefined type. - }; - - //------------------------------------------------------------------------------ - enum EParserVersionInfo - { - pviBRIEF, - pviFULL - }; - - //------------------------------------------------------------------------------ - /** \brief Parser operator precedence values. */ - enum EOprtAssociativity - { - oaLEFT = 0, - oaRIGHT = 1, - oaNONE = 2 - }; - - //------------------------------------------------------------------------------ - /** \brief Parser operator precedence values. */ - enum EOprtPrecedence - { - // binary operators - prLOR = 1, - prLAND = 2, - prLOGIC = 3, ///< logic operators - prCMP = 4, ///< comparsion operators - prADD_SUB = 5, ///< addition - prMUL_DIV = 6, ///< multiplication/division - prPOW = 7, ///< power operator priority (highest) - - // infix operators - prINFIX = 6, ///< Signs have a higher priority than ADD_SUB, but lower than power operator - prPOSTFIX = 6 ///< Postfix operator priority (currently unused) - }; - - //------------------------------------------------------------------------------ - // basic types - - /** \brief The stringtype used by the parser. - - Depends on wether UNICODE is used or not. - */ - typedef QMUP_STRING_TYPE string_type; - - /** \brief The character type used by the parser. - - Depends on wether UNICODE is used or not. - */ - typedef string_type::value_type char_type; - - /** \brief Typedef for easily using stringstream that respect the parser stringtype. */ - typedef std::basic_stringstream, - std::allocator > stringstream_type; - - // Data container types - - /** \brief Type used for storing variables. */ - typedef std::map varmap_type; - - /** \brief Type used for storing constants. */ - typedef std::map valmap_type; - - /** \brief Type for assigning a string name to an index in the internal string table. */ - typedef std::map strmap_type; - - // Parser callbacks - - /** \brief Callback type used for functions without arguments. */ - typedef qreal (*generic_fun_type)(); - - /** \brief Callback type used for functions without arguments. */ - typedef qreal (*fun_type0)(); - - /** \brief Callback type used for functions with a single arguments. */ - typedef qreal (*fun_type1)(qreal); - - /** \brief Callback type used for functions with two arguments. */ - typedef qreal (*fun_type2)(qreal, qreal); - - /** \brief Callback type used for functions with three arguments. */ - typedef qreal (*fun_type3)(qreal, qreal, qreal); - - /** \brief Callback type used for functions with four arguments. */ - typedef qreal (*fun_type4)(qreal, qreal, qreal, qreal); - - /** \brief Callback type used for functions with five arguments. */ - typedef qreal (*fun_type5)(qreal, qreal, qreal, qreal, qreal); - - /** \brief Callback type used for functions with five arguments. */ - typedef qreal (*fun_type6)(qreal, qreal, qreal, qreal, qreal, qreal); - - /** \brief Callback type used for functions with five arguments. */ - typedef qreal (*fun_type7)(qreal, qreal, qreal, qreal, qreal, qreal, qreal); - - /** \brief Callback type used for functions with five arguments. */ - typedef qreal (*fun_type8)(qreal, qreal, qreal, qreal, qreal, qreal, qreal, qreal); - - /** \brief Callback type used for functions with five arguments. */ - typedef qreal (*fun_type9)(qreal, qreal, qreal, qreal, qreal, qreal, qreal, qreal, qreal); - - /** \brief Callback type used for functions with five arguments. */ - typedef qreal (*fun_type10)(qreal, qreal, qreal, qreal, qreal, qreal, qreal, qreal, qreal, qreal); - - /** \brief Callback type used for functions without arguments. */ - typedef qreal (*bulkfun_type0)(int, int); - - /** \brief Callback type used for functions with a single arguments. */ - typedef qreal (*bulkfun_type1)(int, int, qreal); - - /** \brief Callback type used for functions with two arguments. */ - typedef qreal (*bulkfun_type2)(int, int, qreal, qreal); - - /** \brief Callback type used for functions with three arguments. */ - typedef qreal (*bulkfun_type3)(int, int, qreal, qreal, qreal); - - /** \brief Callback type used for functions with four arguments. */ - typedef qreal (*bulkfun_type4)(int, int, qreal, qreal, qreal, qreal); - - /** \brief Callback type used for functions with five arguments. */ - typedef qreal (*bulkfun_type5)(int, int, qreal, qreal, qreal, qreal, qreal); - - /** \brief Callback type used for functions with five arguments. */ - typedef qreal (*bulkfun_type6)(int, int, qreal, qreal, qreal, qreal, qreal, qreal); - - /** \brief Callback type used for functions with five arguments. */ - typedef qreal (*bulkfun_type7)(int, int, qreal, qreal, qreal, qreal, qreal, qreal, qreal); - - /** \brief Callback type used for functions with five arguments. */ - typedef qreal (*bulkfun_type8)(int, int, qreal, qreal, qreal, qreal, qreal, qreal, qreal, qreal); - - /** \brief Callback type used for functions with five arguments. */ - typedef qreal (*bulkfun_type9)(int, int, qreal, qreal, qreal, qreal, qreal, qreal, qreal, qreal, qreal); - - /** \brief Callback type used for functions with five arguments. */ - typedef qreal (*bulkfun_type10)(int, int, qreal, qreal, qreal, qreal, qreal, qreal, qreal, qreal, qreal, qreal); - - /** \brief Callback type used for functions with a variable argument list. */ - typedef qreal (*multfun_type)(const qreal*, int); - - /** \brief Callback type used for functions taking a string as an argument. */ - typedef qreal (*strfun_type1)(const QString &); - - /** \brief Callback type used for functions taking a string and a value as arguments. */ - typedef qreal (*strfun_type2)(const QString &, qreal); - - /** \brief Callback type used for functions taking a string and two values as arguments. */ - typedef qreal (*strfun_type3)(const QString &, qreal, qreal); - - /** \brief Callback used for functions that identify values in a string. */ - typedef int (*identfun_type)(const QString &sExpr, int *nPos, qreal *fVal); - - /** \brief Callback used for variable creation factory functions. */ - typedef qreal* (*facfun_type)(const QString &, void*); -} // end of namespace - -#endif - +/*************************************************************************************************** + ** + ** Original work Copyright (C) 2013 Ingo Berg + ** Modified work Copyright 2014 Roman Telezhinsky + ** + ** 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. + ** + ******************************************************************************************************/ + +#ifndef QMUPDEF_H +#define QMUPDEF_H + +#include +#include + +#include "qmuparserfixes.h" + +/** @file + @brief This file contains standard definitions used by the parser. +*/ + +#define QMUP_VERSION "2.2.3" +#define QMUP_VERSION_DATE "20121222; SF" + +#define QMUP_CHARS "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" + +/** @brief If this macro is defined mathematical exceptions (div by zero) will be thrown as exceptions. */ +//#define QMUP_MATH_EXCEPTIONS + +/** @brief Activate this option in order to compile with OpenMP support. + + OpenMP is used only in the bulk mode it may increase the performance a bit. +*/ +//#define MUP_USE_OPENMP + +#if defined(_UNICODE) + /** @brief Definition of the basic parser string type. */ + #define QMUP_STRING_TYPE std::wstring +#else + /** @brief Definition of the basic parser string type. */ + #define QMUP_STRING_TYPE std::string +#endif + +namespace qmu +{ +//------------------------------------------------------------------------------ +/** @brief Bytecode values. + + \attention The order of the operator entries must match the order in ParserBase::c_DefaultOprt! +*/ +enum ECmdCode +{ + // The following are codes for built in binary operators + // apart from built in operators the user has the opportunity to + // add user defined operators. + cmLE = 0, ///< Operator item: less or equal + cmGE = 1, ///< Operator item: greater or equal + cmNEQ = 2, ///< Operator item: not equal + cmEQ = 3, ///< Operator item: equals + cmLT = 4, ///< Operator item: less than + cmGT = 5, ///< Operator item: greater than + cmADD = 6, ///< Operator item: add + cmSUB = 7, ///< Operator item: subtract + cmMUL = 8, ///< Operator item: multiply + cmDIV = 9, ///< Operator item: division + cmPOW = 10, ///< Operator item: y to the power of ... + cmLAND = 11, + cmLOR = 12, + cmASSIGN = 13, ///< Operator item: Assignment operator + cmBO = 14, ///< Operator item: opening bracket + cmBC = 15, ///< Operator item: closing bracket + cmIF = 16, ///< For use in the ternary if-then-else operator + cmELSE = 17, ///< For use in the ternary if-then-else operator + cmENDIF = 18, ///< For use in the ternary if-then-else operator + cmARG_SEP = 19, ///< function argument separator + cmVAR = 20, ///< variable item + cmVAL = 21, ///< value item + + // For optimization purposes + cmVARPOW2, + cmVARPOW3, + cmVARPOW4, + cmVARMUL, + cmPOW2, + + // operators and functions + cmFUNC, ///< Code for a generic function item + cmFUNC_STR, ///< Code for a function with a string parameter + cmFUNC_BULK, ///< Special callbacks for Bulk mode with an additional parameter for the bulk index + cmSTRING, ///< Code for a string token + cmOPRT_BIN, ///< user defined binary operator + cmOPRT_POSTFIX, ///< code for postfix operators + cmOPRT_INFIX, ///< code for infix operators + cmEND, ///< end of formula + cmUNKNOWN ///< uninitialized item +}; + +//------------------------------------------------------------------------------ +/** @brief Types internally used by the parser. +*/ +enum ETypeCode +{ + tpSTR = 0, ///< String type (Function arguments and constants only, no string variables) + tpDBL = 1, ///< Floating point variables + tpVOID = 2 ///< Undefined type. +}; + +//------------------------------------------------------------------------------ +enum EParserVersionInfo +{ + pviBRIEF, + pviFULL +}; + +//------------------------------------------------------------------------------ +/** @brief Parser operator precedence values. */ +enum EOprtAssociativity +{ + oaLEFT = 0, + oaRIGHT = 1, + oaNONE = 2 +}; + +//------------------------------------------------------------------------------ +/** @brief Parser operator precedence values. */ +enum EOprtPrecedence +{ + // binary operators + prLOR = 1, + prLAND = 2, + prLOGIC = 3, ///< logic operators + prCMP = 4, ///< comparsion operators + prADD_SUB = 5, ///< addition + prMUL_DIV = 6, ///< multiplication/division + prPOW = 7, ///< power operator priority (highest) + + // infix operators + prINFIX = 6, ///< Signs have a higher priority than ADD_SUB, but lower than power operator + prPOSTFIX = 6 ///< Postfix operator priority (currently unused) +}; + +//------------------------------------------------------------------------------ +// basic types + +/** @brief The stringtype used by the parser. + + Depends on wether UNICODE is used or not. +*/ +typedef QMUP_STRING_TYPE string_type; + +/** @brief The character type used by the parser. + + Depends on wether UNICODE is used or not. +*/ +typedef string_type::value_type char_type; + +/** @brief Typedef for easily using stringstream that respect the parser stringtype. */ +typedef std::basic_stringstream < char_type, std::char_traits, std::allocator > stringstream_type; + +// Data container types + +/** @brief Type used for storing variables. */ +typedef std::map varmap_type; + +/** @brief Type used for storing constants. */ +typedef std::map valmap_type; + +/** @brief Type for assigning a string name to an index in the internal string table. */ +typedef std::map strmap_type; + +// Parser callbacks + +/** @brief Callback type used for functions without arguments. */ +typedef qreal ( *generic_fun_type ) (); + +/** @brief Callback type used for functions without arguments. */ +typedef qreal ( *fun_type0 ) (); + +/** @brief Callback type used for functions with a single arguments. */ +typedef qreal ( *fun_type1 ) ( qreal ); + +/** @brief Callback type used for functions with two arguments. */ +typedef qreal ( *fun_type2 ) ( qreal, qreal ); + +/** @brief Callback type used for functions with three arguments. */ +typedef qreal ( *fun_type3 ) ( qreal, qreal, qreal ); + +/** @brief Callback type used for functions with four arguments. */ +typedef qreal ( *fun_type4 ) ( qreal, qreal, qreal, qreal ); + +/** @brief Callback type used for functions with five arguments. */ +typedef qreal ( *fun_type5 ) ( qreal, qreal, qreal, qreal, qreal ); + +/** @brief Callback type used for functions with five arguments. */ +typedef qreal ( *fun_type6 ) ( qreal, qreal, qreal, qreal, qreal, qreal ); + +/** @brief Callback type used for functions with five arguments. */ +typedef qreal ( *fun_type7 ) ( qreal, qreal, qreal, qreal, qreal, qreal, qreal ); + +/** @brief Callback type used for functions with five arguments. */ +typedef qreal ( *fun_type8 ) ( qreal, qreal, qreal, qreal, qreal, qreal, qreal, qreal ); + +/** @brief Callback type used for functions with five arguments. */ +typedef qreal ( *fun_type9 ) ( qreal, qreal, qreal, qreal, qreal, qreal, qreal, qreal, qreal ); + +/** @brief Callback type used for functions with five arguments. */ +typedef qreal ( *fun_type10 ) ( qreal, qreal, qreal, qreal, qreal, qreal, qreal, qreal, qreal, qreal ); + +/** @brief Callback type used for functions without arguments. */ +typedef qreal ( *bulkfun_type0 ) ( int, int ); + +/** @brief Callback type used for functions with a single arguments. */ +typedef qreal ( *bulkfun_type1 ) ( int, int, qreal ); + +/** @brief Callback type used for functions with two arguments. */ +typedef qreal ( *bulkfun_type2 ) ( int, int, qreal, qreal ); + +/** @brief Callback type used for functions with three arguments. */ +typedef qreal ( *bulkfun_type3 ) ( int, int, qreal, qreal, qreal ); + +/** @brief Callback type used for functions with four arguments. */ +typedef qreal ( *bulkfun_type4 ) ( int, int, qreal, qreal, qreal, qreal ); + +/** @brief Callback type used for functions with five arguments. */ +typedef qreal ( *bulkfun_type5 ) ( int, int, qreal, qreal, qreal, qreal, qreal ); + +/** @brief Callback type used for functions with five arguments. */ +typedef qreal ( *bulkfun_type6 ) ( int, int, qreal, qreal, qreal, qreal, qreal, qreal ); + +/** @brief Callback type used for functions with five arguments. */ +typedef qreal ( *bulkfun_type7 ) ( int, int, qreal, qreal, qreal, qreal, qreal, qreal, qreal ); + +/** @brief Callback type used for functions with five arguments. */ +typedef qreal ( *bulkfun_type8 ) ( int, int, qreal, qreal, qreal, qreal, qreal, qreal, qreal, qreal ); + +/** @brief Callback type used for functions with five arguments. */ +typedef qreal ( *bulkfun_type9 ) ( int, int, qreal, qreal, qreal, qreal, qreal, qreal, qreal, qreal, qreal ); + +/** @brief Callback type used for functions with five arguments. */ +typedef qreal ( *bulkfun_type10 ) ( int, int, qreal, qreal, qreal, qreal, qreal, qreal, qreal, qreal, qreal, qreal ); + +/** @brief Callback type used for functions with a variable argument list. */ +typedef qreal ( *multfun_type ) ( const qreal*, int ); + +/** @brief Callback type used for functions taking a string as an argument. */ +typedef qreal ( *strfun_type1 ) ( const QString & ); + +/** @brief Callback type used for functions taking a string and a value as arguments. */ +typedef qreal ( *strfun_type2 ) ( const QString &, qreal ); + +/** @brief Callback type used for functions taking a string and two values as arguments. */ +typedef qreal ( *strfun_type3 ) ( const QString &, qreal, qreal ); + +/** @brief Callback used for functions that identify values in a string. */ +typedef int ( *identfun_type ) ( const QString &sExpr, int *nPos, qreal *fVal ); + +/** @brief Callback used for variable creation factory functions. */ +typedef qreal* ( *facfun_type ) ( const QString &, void* ); +} // end of namespace + +#endif + diff --git a/src/libs/qmuparser/qmuparsererror.cpp b/src/libs/qmuparser/qmuparsererror.cpp index c073b68ea..f0c2f4c11 100644 --- a/src/libs/qmuparser/qmuparsererror.cpp +++ b/src/libs/qmuparser/qmuparsererror.cpp @@ -1,326 +1,329 @@ -/*************************************************************************************************** - ** - ** Original work Copyright (C) 2013 Ingo Berg - ** Modified work Copyright 2014 Roman Telezhinsky - ** - ** 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 "qmuparsererror.h" - -#include - - -namespace qmu -{ - const QmuParserErrorMsg QmuParserErrorMsg::m_Instance; - - //------------------------------------------------------------------------------ - const QmuParserErrorMsg& QmuParserErrorMsg::Instance() - { - return m_Instance; - } - - //------------------------------------------------------------------------------ - QString QmuParserErrorMsg::operator[](unsigned a_iIdx) const - { - return (a_iIdx + ** + ** 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 "qmuparsererror.h" + +#include + +namespace qmu +{ +const QmuParserErrorMsg QmuParserErrorMsg::m_Instance; + +//---------------------------------------------------------------------------------------------------------------------- +const QmuParserErrorMsg& QmuParserErrorMsg::Instance() +{ + return m_Instance; +} + +//---------------------------------------------------------------------------------------------------------------------- +QString QmuParserErrorMsg::operator[] ( unsigned a_iIdx ) const +{ + return ( a_iIdx < m_vErrMsg.size() ) ? m_vErrMsg[a_iIdx] : QString(); +} + +//---------------------------------------------------------------------------------------------------------------------- +QmuParserErrorMsg::~QmuParserErrorMsg() +{} + +//---------------------------------------------------------------------------------------------------------------------- +/** + * @brief Assignement operator is deactivated. + */ +QmuParserErrorMsg& QmuParserErrorMsg::operator= ( const QmuParserErrorMsg& ) +{ + assert ( false ); + return *this; +} + +//---------------------------------------------------------------------------------------------------------------------- +QmuParserErrorMsg::QmuParserErrorMsg ( const QmuParserErrorMsg& ) +{} + +//---------------------------------------------------------------------------------------------------------------------- +QmuParserErrorMsg::QmuParserErrorMsg() + : m_vErrMsg ( 0 ) +{ + m_vErrMsg.resize ( ecCOUNT ); + + m_vErrMsg[ecUNASSIGNABLE_TOKEN] = "Unexpected token \"$TOK$\" found at position $POS$."; + m_vErrMsg[ecINTERNAL_ERROR] = "Internal error"; + m_vErrMsg[ecINVALID_NAME] = "Invalid function-, variable- or constant name: \"$TOK$\"."; + m_vErrMsg[ecINVALID_BINOP_IDENT] = "Invalid binary operator identifier: \"$TOK$\"."; + m_vErrMsg[ecINVALID_INFIX_IDENT] = "Invalid infix operator identifier: \"$TOK$\"."; + m_vErrMsg[ecINVALID_POSTFIX_IDENT] = "Invalid postfix operator identifier: \"$TOK$\"."; + m_vErrMsg[ecINVALID_FUN_PTR] = "Invalid pointer to callback function."; + m_vErrMsg[ecEMPTY_EXPRESSION] = "Expression is empty."; + m_vErrMsg[ecINVALID_VAR_PTR] = "Invalid pointer to variable."; + m_vErrMsg[ecUNEXPECTED_OPERATOR] = "Unexpected operator \"$TOK$\" found at position $POS$"; + m_vErrMsg[ecUNEXPECTED_EOF] = "Unexpected end of expression at position $POS$"; + m_vErrMsg[ecUNEXPECTED_ARG_SEP] = "Unexpected argument separator at position $POS$"; + m_vErrMsg[ecUNEXPECTED_PARENS] = "Unexpected parenthesis \"$TOK$\" at position $POS$"; + m_vErrMsg[ecUNEXPECTED_FUN] = "Unexpected function \"$TOK$\" at position $POS$"; + m_vErrMsg[ecUNEXPECTED_VAL] = "Unexpected value \"$TOK$\" found at position $POS$"; + m_vErrMsg[ecUNEXPECTED_VAR] = "Unexpected variable \"$TOK$\" found at position $POS$"; + m_vErrMsg[ecUNEXPECTED_ARG] = "Function arguments used without a function (position: $POS$)"; + m_vErrMsg[ecMISSING_PARENS] = "Missing parenthesis"; + m_vErrMsg[ecTOO_MANY_PARAMS] = "Too many parameters for function \"$TOK$\" at expression position $POS$"; + m_vErrMsg[ecTOO_FEW_PARAMS] = "Too few parameters for function \"$TOK$\" at expression position $POS$"; + m_vErrMsg[ecDIV_BY_ZERO] = "Divide by zero"; + m_vErrMsg[ecDOMAIN_ERROR] = "Domain error"; + m_vErrMsg[ecNAME_CONFLICT] = "Name conflict"; + m_vErrMsg[ecOPT_PRI] = "Invalid value for operator priority (must be greater or equal to zero)."; + m_vErrMsg[ecBUILTIN_OVERLOAD] = "user defined binary operator \"$TOK$\" conflicts with a built in operator."; + m_vErrMsg[ecUNEXPECTED_STR] = "Unexpected string token found at position $POS$."; + m_vErrMsg[ecUNTERMINATED_STRING] = "Unterminated string starting at position $POS$."; + m_vErrMsg[ecSTRING_EXPECTED] = "String function called with a non string type of argument."; + m_vErrMsg[ecVAL_EXPECTED] = "String value used where a numerical argument is expected."; + m_vErrMsg[ecOPRT_TYPE_CONFLICT] = "No suitable overload for operator \"$TOK$\" at position $POS$."; + m_vErrMsg[ecSTR_RESULT] = "Function result is a string."; + m_vErrMsg[ecGENERIC] = "Parser error."; + m_vErrMsg[ecLOCALE] = "Decimal separator is identic to function argument separator."; + m_vErrMsg[ecUNEXPECTED_CONDITIONAL] = "The \"$TOK$\" operator must be preceeded by a closing bracket."; + m_vErrMsg[ecMISSING_ELSE_CLAUSE] = "If-then-else operator is missing an else clause"; + m_vErrMsg[ecMISPLACED_COLON] = "Misplaced colon at position $POS$"; + +#if defined(_DEBUG) + for ( int i = 0; i < ecCOUNT; ++i ) + { + if ( !m_vErrMsg[i].length() ) + { + assert ( false ); + } + } +#endif +} + +//---------------------------------------------------------------------------------------------------------------------- +// +// QParserError class +// +//---------------------------------------------------------------------------------------------------------------------- + +/** + * @brief Default constructor. + */ +QmuParserError::QmuParserError() + : m_strMsg(), m_strFormula(), m_strTok(), m_iPos ( -1 ), m_iErrc ( ecUNDEFINED ), + m_ErrMsg ( QmuParserErrorMsg::Instance() ) +{ +} + +//---------------------------------------------------------------------------------------------------------------------- +/** + * @brief This Constructor is used for internal exceptions only. + * + * It does not contain any information but the error code. + */ +QmuParserError::QmuParserError ( EErrorCodes a_iErrc ) + : m_strMsg(), m_strFormula(), m_strTok(), m_iPos ( -1 ), m_iErrc ( a_iErrc ), + m_ErrMsg ( QmuParserErrorMsg::Instance() ) +{ + m_strMsg = m_ErrMsg[m_iErrc]; + ReplaceSubString ( m_strMsg, "$POS$", QString().setNum ( m_iPos ) ); + ReplaceSubString ( m_strMsg, "$TOK$", m_strTok ); +} + +//---------------------------------------------------------------------------------------------------------------------- +/** + * @brief Construct an error from a message text. + */ +QmuParserError::QmuParserError ( const QString &sMsg ) + : m_ErrMsg ( QmuParserErrorMsg::Instance() ) +{ + Reset(); + m_strMsg = sMsg; +} + +//---------------------------------------------------------------------------------------------------------------------- +/** + * @brief Construct an error object. + * @param [in] a_iErrc the error code. + * @param [in] sTok The token string related to this error. + * @param [in] sExpr The expression related to the error. + * @param [in] a_iPos the position in the expression where the error occured. + */ +QmuParserError::QmuParserError ( EErrorCodes iErrc, + const QString &sTok, + const QString &sExpr, + int iPos ) + : m_strMsg(), m_strFormula ( sExpr ), m_strTok ( sTok ), m_iPos ( iPos ), m_iErrc ( iErrc ), + m_ErrMsg ( QmuParserErrorMsg::Instance() ) +{ + m_strMsg = m_ErrMsg[m_iErrc]; + ReplaceSubString ( m_strMsg, "$POS$", QString().setNum ( m_iPos ) ); + ReplaceSubString ( m_strMsg, "$TOK$", m_strTok ); +} + +//---------------------------------------------------------------------------------------------------------------------- +/** + * @brief Construct an error object. + * @param [in] iErrc the error code. + * @param [in] iPos the position in the expression where the error occured. + * @param [in] sTok The token string related to this error. + */ +QmuParserError::QmuParserError ( EErrorCodes iErrc, int iPos, const QString &sTok ) + : m_strMsg(), m_strFormula(), m_strTok ( sTok ), m_iPos ( iPos ), m_iErrc ( iErrc ), + m_ErrMsg ( QmuParserErrorMsg::Instance() ) +{ + m_strMsg = m_ErrMsg[m_iErrc]; + ReplaceSubString ( m_strMsg, "$POS$", QString().setNum ( m_iPos ) ); + ReplaceSubString ( m_strMsg, "$TOK$", m_strTok ); +} + +//---------------------------------------------------------------------------------------------------------------------- +/** @brief Construct an error object. + * @param [in] szMsg The error message text. + * @param [in] iPos the position related to the error. + * @param [in] sTok The token string related to this error. + */ +QmuParserError::QmuParserError ( const QString &szMsg, int iPos, const QString &sTok ) + : m_strMsg ( szMsg ), m_strFormula(), m_strTok ( sTok ), m_iPos ( iPos ), m_iErrc ( ecGENERIC ), + m_ErrMsg ( QmuParserErrorMsg::Instance() ) +{ + ReplaceSubString ( m_strMsg, "$POS$", QString().setNum ( m_iPos ) ); + ReplaceSubString ( m_strMsg, "$TOK$", m_strTok ); +} + +//---------------------------------------------------------------------------------------------------------------------- +/** @brief Copy constructor. */ +QmuParserError::QmuParserError ( const QmuParserError &a_Obj ) + : m_strMsg ( a_Obj.m_strMsg ), m_strFormula ( a_Obj.m_strFormula ), m_strTok ( a_Obj.m_strTok ), + m_iPos ( a_Obj.m_iPos ), m_iErrc ( a_Obj.m_iErrc ), m_ErrMsg ( QmuParserErrorMsg::Instance() ) +{ +} + +//---------------------------------------------------------------------------------------------------------------------- +/** @brief Assignment operator. */ +QmuParserError& QmuParserError::operator= ( const QmuParserError &a_Obj ) +{ + if ( this == &a_Obj ) + { + return *this; + } + + m_strMsg = a_Obj.m_strMsg; + m_strFormula = a_Obj.m_strFormula; + m_strTok = a_Obj.m_strTok; + m_iPos = a_Obj.m_iPos; + m_iErrc = a_Obj.m_iErrc; + return *this; +} + +//---------------------------------------------------------------------------------------------------------------------- +QmuParserError::~QmuParserError() +{} + +//---------------------------------------------------------------------------------------------------------------------- +/** + * @brief Replace all ocuurences of a substring with another string. + * @param strFind The string that shall be replaced. + * @param strReplaceWith The string that should be inserted instead of strFind + */ +void QmuParserError::ReplaceSubString ( QString &strSource, const QString &strFind, const QString &strReplaceWith ) +{ + QString strResult; + int iPos ( 0 ), iNext ( 0 ); + + for ( ;; ) + { + iNext = strSource.indexOf ( strFind, iPos ); + strResult.append ( strSource.mid ( iPos, iNext - iPos ) ); + + if ( iNext == -1 ) + break; + + strResult.append ( strReplaceWith ); + iPos = iNext + strFind.length(); + } + + strSource.swap ( strResult ); +} + +//---------------------------------------------------------------------------------------------------------------------- +/** + * @brief Reset the erro object. + */ +void QmuParserError::Reset() +{ + m_strMsg = ""; + m_strFormula = ""; + m_strTok = ""; + m_iPos = -1; + m_iErrc = ecUNDEFINED; +} + +//---------------------------------------------------------------------------------------------------------------------- +/** + * @brief Set the expression related to this error. + */ +void QmuParserError::SetFormula ( const QString &a_strFormula ) +{ + m_strFormula = a_strFormula; +} + +//---------------------------------------------------------------------------------------------------------------------- +/** + * @brief gets the expression related tp this error. + */ +const QString& QmuParserError::GetExpr() const +{ + return m_strFormula; +} + +//---------------------------------------------------------------------------------------------------------------------- +/** + * @brief Returns the message string for this error. + */ +const QString& QmuParserError::GetMsg() const +{ + return m_strMsg; +} + +//---------------------------------------------------------------------------------------------------------------------- +/** + * @brief Return the formula position related to the error. + * + * If the error is not related to a distinct position this will return -1 + */ +std::size_t QmuParserError::GetPos() const +{ + return m_iPos; +} + +//---------------------------------------------------------------------------------------------------------------------- +/** + * @brief Return string related with this token (if available). + */ +const QString& QmuParserError::GetToken() const +{ + return m_strTok; +} + +//---------------------------------------------------------------------------------------------------------------------- +/** + * @brief Return the error code. + */ +EErrorCodes QmuParserError::GetCode() const +{ + return m_iErrc; +} +} // namespace qmu diff --git a/src/libs/qmuparser/qmuparsererror.h b/src/libs/qmuparser/qmuparsererror.h index 97623a9fa..0bf9328d3 100644 --- a/src/libs/qmuparser/qmuparsererror.h +++ b/src/libs/qmuparser/qmuparsererror.h @@ -1,163 +1,159 @@ -/*************************************************************************************************** - ** - ** Original work Copyright (C) 2013 Ingo Berg - ** Modified work Copyright 2014 Roman Telezhinsky - ** - ** 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. - ** - ******************************************************************************************************/ - -#ifndef QMUPARSERERROR_H -#define QMUPARSERERROR_H - -#include -#include -#include -#include -#include - -#include "qmuparserdef.h" - -/** \file - \brief This file defines the error class used by the parser. -*/ - -namespace qmu -{ - -/** \brief Error codes. */ -enum EErrorCodes -{ - // Formula syntax errors - ecUNEXPECTED_OPERATOR = 0, ///< Unexpected binary operator found - ecUNASSIGNABLE_TOKEN = 1, ///< Token cant be identified. - ecUNEXPECTED_EOF = 2, ///< Unexpected end of formula. (Example: "2+sin(") - ecUNEXPECTED_ARG_SEP = 3, ///< An unexpected comma has been found. (Example: "1,23") - ecUNEXPECTED_ARG = 4, ///< An unexpected argument has been found - ecUNEXPECTED_VAL = 5, ///< An unexpected value token has been found - ecUNEXPECTED_VAR = 6, ///< An unexpected variable token has been found - ecUNEXPECTED_PARENS = 7, ///< Unexpected Parenthesis, opening or closing - ecUNEXPECTED_STR = 8, ///< A string has been found at an inapropriate position - ecSTRING_EXPECTED = 9, ///< A string function has been called with a different type of argument - ecVAL_EXPECTED = 10, ///< A numerical function has been called with a non value type of argument - ecMISSING_PARENS = 11, ///< Missing parens. (Example: "3*sin(3") - ecUNEXPECTED_FUN = 12, ///< Unexpected function found. (Example: "sin(8)cos(9)") - ecUNTERMINATED_STRING = 13, ///< unterminated string constant. (Example: "3*valueof("hello)") - ecTOO_MANY_PARAMS = 14, ///< Too many function parameters - ecTOO_FEW_PARAMS = 15, ///< Too few function parameters. (Example: "ite(1<2,2)") - ecOPRT_TYPE_CONFLICT = 16, ///< binary operators may only be applied to value items of the same type - ecSTR_RESULT = 17, ///< result is a string - - // Invalid Parser input Parameters - ecINVALID_NAME = 18, ///< Invalid function, variable or constant name. - ecINVALID_BINOP_IDENT = 19, ///< Invalid binary operator identifier - ecINVALID_INFIX_IDENT = 20, ///< Invalid function, variable or constant name. - ecINVALID_POSTFIX_IDENT = 21, ///< Invalid function, variable or constant name. - - ecBUILTIN_OVERLOAD = 22, ///< Trying to overload builtin operator - ecINVALID_FUN_PTR = 23, ///< Invalid callback function pointer - ecINVALID_VAR_PTR = 24, ///< Invalid variable pointer - ecEMPTY_EXPRESSION = 25, ///< The Expression is empty - ecNAME_CONFLICT = 26, ///< Name conflict - ecOPT_PRI = 27, ///< Invalid operator priority - // - ecDOMAIN_ERROR = 28, ///< catch division by zero, sqrt(-1), log(0) (currently unused) - ecDIV_BY_ZERO = 29, ///< Division by zero (currently unused) - ecGENERIC = 30, ///< Generic error - ecLOCALE = 31, ///< Conflict with current locale - - ecUNEXPECTED_CONDITIONAL = 32, - ecMISSING_ELSE_CLAUSE = 33, - ecMISPLACED_COLON = 34, - - // internal errors - ecINTERNAL_ERROR = 35, ///< Internal error of any kind. - - // The last two are special entries - ecCOUNT, ///< This is no error code, It just stores just the total number of error codes - ecUNDEFINED = -1 ///< Undefined message, placeholder to detect unassigned error messages -}; - -//--------------------------------------------------------------------------- -/** \brief A class that handles the error messages. -*/ -class QmuParserErrorMsg -{ -public: - typedef QmuParserErrorMsg self_type; - - QmuParserErrorMsg& operator=(const QmuParserErrorMsg &); - QmuParserErrorMsg(const QmuParserErrorMsg&); - QmuParserErrorMsg(); - - ~QmuParserErrorMsg(); - - static const QmuParserErrorMsg& Instance(); - QString operator[](unsigned a_iIdx) const; - -private: - QVector m_vErrMsg; ///< A vector with the predefined error messages - static const self_type m_Instance; ///< The instance pointer -}; - -//--------------------------------------------------------------------------- -/** \brief Error class of the parser. - \author Ingo Berg - - Part of the math parser package. -*/ -class QmuParserError -{ -private: - - /** \brief Replace all ocuurences of a substring with another string. */ - void ReplaceSubString(QString &strSource, - const QString &strFind, - const QString &strReplaceWith); - void Reset(); - -public: - - QmuParserError(); - explicit QmuParserError(EErrorCodes a_iErrc); - explicit QmuParserError(const QString &sMsg); - QmuParserError( EErrorCodes a_iErrc, const QString &sTok, const QString &sFormula = QString(), int a_iPos = -1); - QmuParserError(EErrorCodes a_iErrc, int a_iPos, const QString &sTok); - QmuParserError(const QString &a_szMsg, int a_iPos, const QString &sTok = QString()); - QmuParserError(const QmuParserError &a_Obj); - QmuParserError& operator=(const QmuParserError &a_Obj); - ~QmuParserError(); - - void SetFormula(const QString &a_strFormula); - const QString &GetExpr() const; - const QString &GetMsg() const; - std::size_t GetPos() const; - const QString &GetToken() const; - EErrorCodes GetCode() const; - -private: - QString m_strMsg; ///< The message string - QString m_strFormula; ///< Formula string - QString m_strTok; ///< Token related with the error - int m_iPos; ///< Formula position related to the error - EErrorCodes m_iErrc; ///< Error code - const QmuParserErrorMsg &m_ErrMsg; -}; - -} // namespace qmu - -#endif - +/*************************************************************************************************** + ** + ** Original work Copyright (C) 2013 Ingo Berg + ** Modified work Copyright 2014 Roman Telezhinsky + ** + ** 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. + ** + ******************************************************************************************************/ + +#ifndef QMUPARSERERROR_H +#define QMUPARSERERROR_H + +#include +#include +#include +#include +#include + +#include "qmuparserdef.h" + +/** @file + @brief This file defines the error class used by the parser. +*/ + +namespace qmu +{ + +/** @brief Error codes. */ +enum EErrorCodes +{ + // Formula syntax errors + ecUNEXPECTED_OPERATOR = 0, ///< Unexpected binary operator found + ecUNASSIGNABLE_TOKEN = 1, ///< Token cant be identified. + ecUNEXPECTED_EOF = 2, ///< Unexpected end of formula. (Example: "2+sin(") + ecUNEXPECTED_ARG_SEP = 3, ///< An unexpected comma has been found. (Example: "1,23") + ecUNEXPECTED_ARG = 4, ///< An unexpected argument has been found + ecUNEXPECTED_VAL = 5, ///< An unexpected value token has been found + ecUNEXPECTED_VAR = 6, ///< An unexpected variable token has been found + ecUNEXPECTED_PARENS = 7, ///< Unexpected Parenthesis, opening or closing + ecUNEXPECTED_STR = 8, ///< A string has been found at an inapropriate position + ecSTRING_EXPECTED = 9, ///< A string function has been called with a different type of argument + ecVAL_EXPECTED = 10, ///< A numerical function has been called with a non value type of argument + ecMISSING_PARENS = 11, ///< Missing parens. (Example: "3*sin(3") + ecUNEXPECTED_FUN = 12, ///< Unexpected function found. (Example: "sin(8)cos(9)") + ecUNTERMINATED_STRING = 13, ///< unterminated string constant. (Example: "3*valueof("hello)") + ecTOO_MANY_PARAMS = 14, ///< Too many function parameters + ecTOO_FEW_PARAMS = 15, ///< Too few function parameters. (Example: "ite(1<2,2)") + ecOPRT_TYPE_CONFLICT = 16, ///< binary operators may only be applied to value items of the same type + ecSTR_RESULT = 17, ///< result is a string + + // Invalid Parser input Parameters + ecINVALID_NAME = 18, ///< Invalid function, variable or constant name. + ecINVALID_BINOP_IDENT = 19, ///< Invalid binary operator identifier + ecINVALID_INFIX_IDENT = 20, ///< Invalid function, variable or constant name. + ecINVALID_POSTFIX_IDENT = 21, ///< Invalid function, variable or constant name. + + ecBUILTIN_OVERLOAD = 22, ///< Trying to overload builtin operator + ecINVALID_FUN_PTR = 23, ///< Invalid callback function pointer + ecINVALID_VAR_PTR = 24, ///< Invalid variable pointer + ecEMPTY_EXPRESSION = 25, ///< The Expression is empty + ecNAME_CONFLICT = 26, ///< Name conflict + ecOPT_PRI = 27, ///< Invalid operator priority + // + ecDOMAIN_ERROR = 28, ///< catch division by zero, sqrt(-1), log(0) (currently unused) + ecDIV_BY_ZERO = 29, ///< Division by zero (currently unused) + ecGENERIC = 30, ///< Generic error + ecLOCALE = 31, ///< Conflict with current locale + + ecUNEXPECTED_CONDITIONAL = 32, + ecMISSING_ELSE_CLAUSE = 33, + ecMISPLACED_COLON = 34, + + // internal errors + ecINTERNAL_ERROR = 35, ///< Internal error of any kind. + + // The last two are special entries + ecCOUNT, ///< This is no error code, It just stores just the total number of error codes + ecUNDEFINED = -1 ///< Undefined message, placeholder to detect unassigned error messages +}; + +//--------------------------------------------------------------------------- +/** @brief A class that handles the error messages. +*/ +class QmuParserErrorMsg +{ +public: + typedef QmuParserErrorMsg self_type; + + QmuParserErrorMsg& operator= ( const QmuParserErrorMsg & ); + QmuParserErrorMsg ( const QmuParserErrorMsg& ); + QmuParserErrorMsg(); + ~QmuParserErrorMsg(); + + static const QmuParserErrorMsg& Instance(); + QString operator[] ( unsigned a_iIdx ) const; + +private: + QVector m_vErrMsg; ///< A vector with the predefined error messages + static const self_type m_Instance; ///< The instance pointer +}; + +//--------------------------------------------------------------------------- +/** @brief Error class of the parser. + @author Ingo Berg + + Part of the math parser package. +*/ +class QmuParserError +{ +public: + + QmuParserError(); + explicit QmuParserError ( EErrorCodes a_iErrc ); + explicit QmuParserError ( const QString &sMsg ); + QmuParserError ( EErrorCodes a_iErrc, const QString &sTok, const QString &sFormula = QString(), int a_iPos = -1 ); + QmuParserError ( EErrorCodes a_iErrc, int a_iPos, const QString &sTok ); + QmuParserError ( const QString &a_szMsg, int a_iPos, const QString &sTok = QString() ); + QmuParserError ( const QmuParserError &a_Obj ); + QmuParserError& operator= ( const QmuParserError &a_Obj ); + ~QmuParserError(); + + void SetFormula ( const QString &a_strFormula ); + const QString& GetExpr() const; + const QString& GetMsg() const; + std::size_t GetPos() const; + const QString& GetToken() const; + EErrorCodes GetCode() const; + +private: + QString m_strMsg; ///< The message string + QString m_strFormula; ///< Formula string + QString m_strTok; ///< Token related with the error + int m_iPos; ///< Formula position related to the error + EErrorCodes m_iErrc; ///< Error code + const QmuParserErrorMsg &m_ErrMsg; + /** + * @brief Replace all ocuurences of a substring with another string. + */ + void ReplaceSubString ( QString &strSource, const QString &strFind, const QString &strReplaceWith ); + void Reset(); +}; + +} // namespace qmu + +#endif + diff --git a/src/libs/qmuparser/qmuparserfixes.h b/src/libs/qmuparser/qmuparserfixes.h index f0d529a63..1cabcda6d 100644 --- a/src/libs/qmuparser/qmuparserfixes.h +++ b/src/libs/qmuparser/qmuparserfixes.h @@ -23,8 +23,8 @@ #ifndef QMUPARSERFIXES_H #define QMUPARSERFIXES_H -/** \file - \brief This file contains compatibility fixes for some platforms. +/** @file + @brief This file contains compatibility fixes for some platforms. */ // diff --git a/src/libs/qmuparser/qmuparsertest.cpp b/src/libs/qmuparser/qmuparsertest.cpp index d4d22919a..16900978b 100644 --- a/src/libs/qmuparser/qmuparsertest.cpp +++ b/src/libs/qmuparser/qmuparsertest.cpp @@ -1,1326 +1,1415 @@ -/*************************************************************************************************** - ** - ** Original work Copyright (C) 2013 Ingo Berg - ** Modified work Copyright 2014 Roman Telezhinsky - ** - ** 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 "qmuparsertest.h" -#include -#include - -#include -#include -#include - -using namespace std; - -/** \file - \brief This file contains the implementation of parser test cases. -*/ - -namespace qmu -{ - namespace Test - { - int QmuParserTester::c_iCount = 0; - - //--------------------------------------------------------------------------------------------- - QmuParserTester::QmuParserTester() - :m_vTestFun() - { - AddTest(&QmuParserTester::TestNames); - AddTest(&QmuParserTester::TestSyntax); - AddTest(&QmuParserTester::TestPostFix); - AddTest(&QmuParserTester::TestInfixOprt); - AddTest(&QmuParserTester::TestVarConst); - AddTest(&QmuParserTester::TestMultiArg); - AddTest(&QmuParserTester::TestExpression); - AddTest(&QmuParserTester::TestIfThenElse); - AddTest(&QmuParserTester::TestInterface); - AddTest(&QmuParserTester::TestBinOprt); - AddTest(&QmuParserTester::TestException); - AddTest(&QmuParserTester::TestStrArg); - - QmuParserTester::c_iCount = 0; - } - - //--------------------------------------------------------------------------------------------- - int QmuParserTester::IsHexVal(const QString &a_szExpr, int *a_iPos, qreal *a_fVal) - { - if (a_szExpr[1]==0 || (a_szExpr[0]!='0' || a_szExpr[1]!='x') ) - return 0; - - unsigned iVal(0); - bool ok = false; - iVal = a_szExpr.toUInt(&ok, 16); - - if(ok) - { - int nPos = a_szExpr.indexOf(QString().setNum(iVal, 16)); - if(nPos == 0) - return 1; - - *a_iPos += (int)(2 + nPos); - *a_fVal = (qreal)iVal; - return 1; - } - else - { - return 0; - } - - } - - //--------------------------------------------------------------------------------------------- - int QmuParserTester::TestInterface() - { - int iStat = 0; - qDebug() << "testing member functions..."; - - // Test RemoveVar - qreal afVal[3] = {1,2,3}; - QmuParser p; - - try - { - p.DefineVar( "a", &afVal[0]); - p.DefineVar( "b", &afVal[1]); - p.DefineVar( "c", &afVal[2]); - p.SetExpr( "a+b+c" ); - p.Eval(); - } - catch(...) - { - iStat += 1; // this is not supposed to happen - } - - try - { - p.RemoveVar( "c" ); - p.Eval(); - iStat += 1; // not supposed to reach this, nonexisting variable "c" deleted... - } - catch(...) - { - // failure is expected... - } - - if (iStat==0) - qDebug() << "passed"; - else - qDebug() << "\n failed with " << iStat << " errors"; - - return iStat; - } - - //--------------------------------------------------------------------------------------------- - int QmuParserTester::TestStrArg() - { - int iStat = 0; - qDebug() << "testing string arguments..."; - - iStat += EqnTest("valueof(\"\")", 123, true); // empty string arguments caused a crash - iStat += EqnTest("valueof(\"aaa\")+valueof(\"bbb\") ", 246, true); - iStat += EqnTest("2*(valueof(\"aaa\")-23)+valueof(\"bbb\")", 323, true); - // use in expressions with variables - iStat += EqnTest("a*(atof(\"10\")-b)", 8, true); - iStat += EqnTest("a-(atof(\"10\")*b)", -19, true); - // string + numeric arguments - iStat += EqnTest("strfun1(\"100\")", 100, true); - iStat += EqnTest("strfun2(\"100\",1)", 101, true); - iStat += EqnTest("strfun3(\"99\",1,2)", 102, true); - - if (iStat==0) - qDebug() << "passed"; - else - qDebug() << "\n failed with " << iStat << " errors"; - - return iStat; - } - - //--------------------------------------------------------------------------------------------- - int QmuParserTester::TestBinOprt() - { - int iStat = 0; - qDebug() << "testing binary operators..."; - - // built in operators - // xor operator - //iStat += EqnTest("1 xor 2", 3, true); - //iStat += EqnTest("a xor b", 3, true); // with a=1 and b=2 - //iStat += EqnTest("1 xor 2 xor 3", 0, true); - //iStat += EqnTest("a xor b xor 3", 0, true); // with a=1 and b=2 - //iStat += EqnTest("a xor b xor c", 0, true); // with a=1 and b=2 - //iStat += EqnTest("(1 xor 2) xor 3", 0, true); - //iStat += EqnTest("(a xor b) xor c", 0, true); // with a=1 and b=2 - //iStat += EqnTest("(a) xor (b) xor c", 0, true); // with a=1 and b=2 - //iStat += EqnTest("1 or 2"), 3, true; - //iStat += EqnTest("a or b"), 3, true; // with a=1 and b=2 - iStat += EqnTest("a++b", 3, true); - iStat += EqnTest("a ++ b", 3, true); - iStat += EqnTest("1++2", 3, true); - iStat += EqnTest("1 ++ 2", 3, true); - iStat += EqnTest("a add b", 3, true); - iStat += EqnTest("1 add 2", 3, true); - iStat += EqnTest("aa", 1, true); - iStat += EqnTest("a>a", 0, true); - iStat += EqnTest("aa", 0, true); - iStat += EqnTest("a<=a", 1, true); - iStat += EqnTest("a<=b", 1, true); - iStat += EqnTest("b<=a", 0, true); - iStat += EqnTest("a>=a", 1, true); - iStat += EqnTest("b>=a", 1, true); - iStat += EqnTest("a>=b", 0, true); - - // Test logical operators, expecially if user defined "&" and the internal "&&" collide - iStat += EqnTest("1 && 1", 1, true); - iStat += EqnTest("1 && 0", 0, true); - iStat += EqnTest("(aa)", 1, true); - iStat += EqnTest("(ab)", 0, true); - //iStat += EqnTest("12 and 255", 12, true); - //iStat += EqnTest("12 and 0", 0, true); - iStat += EqnTest("12 & 255", 12, true); - iStat += EqnTest("12 & 0", 0, true); - iStat += EqnTest("12&255", 12, true); - iStat += EqnTest("12&0", 0, true); - - // Assignement operator - iStat += EqnTest("a = b", 2, true); - iStat += EqnTest("a = sin(b)", 0.909297, true); - iStat += EqnTest("a = 1+sin(b)", 1.909297, true); - iStat += EqnTest("(a=b)*2", 4, true); - iStat += EqnTest("2*(a=b)", 4, true); - iStat += EqnTest("2*(a=b+1)", 6, true); - iStat += EqnTest("(a=b+1)*2", 6, true); - - iStat += EqnTest("2^2^3", 256, true); - iStat += EqnTest("1/2/3", 1.0/6.0, true); - - // reference: http://www.wolframalpha.com/input/?i=3%2B4*2%2F%281-5%29^2^3 - iStat += EqnTest("3+4*2/(1-5)^2^3", 3.0001220703125, true); - - if (iStat==0) - qDebug() << "passed"; - else - qDebug() << "\n failed with " << iStat << " errors"; - - return iStat; - } - - //--------------------------------------------------------------------------------------------- - /** \brief Check muParser name restriction enforcement. */ - int QmuParserTester::TestNames() - { - int iStat= 0, - iErr = 0; - - qDebug() << "testing name restriction enforcement..."; - - QmuParser p; - - #define PARSER_THROWCHECK(DOMAIN, FAIL, EXPR, ARG) \ - iErr = 0; \ - QmuParserTester::c_iCount++; \ - try \ - { \ - p.Define##DOMAIN(EXPR, ARG); \ - } \ - catch(QmuParser::exception_type&) \ - { \ - iErr = (FAIL==false) ? 0 : 1; \ - } \ - iStat += iErr; - - // constant names - PARSER_THROWCHECK(Const, false, "0a", 1) - PARSER_THROWCHECK(Const, false, "9a", 1) - PARSER_THROWCHECK(Const, false, "+a", 1) - PARSER_THROWCHECK(Const, false, "-a", 1) - PARSER_THROWCHECK(Const, false, "a-", 1) - PARSER_THROWCHECK(Const, false, "a*", 1) - PARSER_THROWCHECK(Const, false, "a?", 1) - PARSER_THROWCHECK(Const, true, "a", 1) - PARSER_THROWCHECK(Const, true, "a_min", 1) - PARSER_THROWCHECK(Const, true, "a_min0", 1) - PARSER_THROWCHECK(Const, true, "a_min9", 1) - // variable names - qreal a; - p.ClearConst(); - PARSER_THROWCHECK(Var, false, "123abc", &a) - PARSER_THROWCHECK(Var, false, "9a", &a) - PARSER_THROWCHECK(Var, false, "0a", &a) - PARSER_THROWCHECK(Var, false, "+a", &a) - PARSER_THROWCHECK(Var, false, "-a", &a) - PARSER_THROWCHECK(Var, false, "?a", &a) - PARSER_THROWCHECK(Var, false, "!a", &a) - PARSER_THROWCHECK(Var, false, "a+", &a) - PARSER_THROWCHECK(Var, false, "a-", &a) - PARSER_THROWCHECK(Var, false, "a*", &a) - PARSER_THROWCHECK(Var, false, "a?", &a) - PARSER_THROWCHECK(Var, true, "a", &a) - PARSER_THROWCHECK(Var, true, "a_min", &a) - PARSER_THROWCHECK(Var, true, "a_min0", &a) - PARSER_THROWCHECK(Var, true, "a_min9", &a) - PARSER_THROWCHECK(Var, false, "a_min9", 0) - // Postfix operators - // fail - PARSER_THROWCHECK(PostfixOprt, false, "(k", f1of1) - PARSER_THROWCHECK(PostfixOprt, false, "9+", f1of1) - PARSER_THROWCHECK(PostfixOprt, false, "+", 0) - // pass - PARSER_THROWCHECK(PostfixOprt, true, "-a", f1of1) - PARSER_THROWCHECK(PostfixOprt, true, "?a", f1of1) - PARSER_THROWCHECK(PostfixOprt, true, "_", f1of1) - PARSER_THROWCHECK(PostfixOprt, true, "#", f1of1) - PARSER_THROWCHECK(PostfixOprt, true, "&&", f1of1) - PARSER_THROWCHECK(PostfixOprt, true, "||", f1of1) - PARSER_THROWCHECK(PostfixOprt, true, "&", f1of1) - PARSER_THROWCHECK(PostfixOprt, true, "|", f1of1) - PARSER_THROWCHECK(PostfixOprt, true, "++", f1of1) - PARSER_THROWCHECK(PostfixOprt, true, "--", f1of1) - PARSER_THROWCHECK(PostfixOprt, true, "?>", f1of1) - PARSER_THROWCHECK(PostfixOprt, true, "?<", f1of1) - PARSER_THROWCHECK(PostfixOprt, true, "**", f1of1) - PARSER_THROWCHECK(PostfixOprt, true, "xor", f1of1) - PARSER_THROWCHECK(PostfixOprt, true, "and", f1of1) - PARSER_THROWCHECK(PostfixOprt, true, "or", f1of1) - PARSER_THROWCHECK(PostfixOprt, true, "not", f1of1) - PARSER_THROWCHECK(PostfixOprt, true, "!", f1of1) - // Binary operator - // The following must fail with builtin operators activated - // p.EnableBuiltInOp(true); -> this is the default - p.ClearPostfixOprt(); - PARSER_THROWCHECK(Oprt, false, "+", f1of2) - PARSER_THROWCHECK(Oprt, false, "-", f1of2) - PARSER_THROWCHECK(Oprt, false, "*", f1of2) - PARSER_THROWCHECK(Oprt, false, "/", f1of2) - PARSER_THROWCHECK(Oprt, false, "^", f1of2) - PARSER_THROWCHECK(Oprt, false, "&&", f1of2) - PARSER_THROWCHECK(Oprt, false, "||", f1of2) - // without activated built in operators it should work - p.EnableBuiltInOprt(false); - PARSER_THROWCHECK(Oprt, true, "+", f1of2) - PARSER_THROWCHECK(Oprt, true, "-", f1of2) - PARSER_THROWCHECK(Oprt, true, "*", f1of2) - PARSER_THROWCHECK(Oprt, true, "/", f1of2) - PARSER_THROWCHECK(Oprt, true, "^", f1of2) - PARSER_THROWCHECK(Oprt, true, "&&", f1of2) - PARSER_THROWCHECK(Oprt, true, "||", f1of2) - #undef PARSER_THROWCHECK - - if (iStat==0) - qDebug() << "passed"; - else - qDebug() << "\n failed with " << iStat << " errors"; - - return iStat; - } - - //--------------------------------------------------------------------------- - int QmuParserTester::TestSyntax() - { - int iStat = 0; - qDebug() << "testing syntax engine..."; - - iStat += ThrowTest("1,", ecUNEXPECTED_EOF); // incomplete hex definition - iStat += ThrowTest("a,", ecUNEXPECTED_EOF); // incomplete hex definition - iStat += ThrowTest("sin(8),", ecUNEXPECTED_EOF); // incomplete hex definition - iStat += ThrowTest("(sin(8)),", ecUNEXPECTED_EOF); // incomplete hex definition - iStat += ThrowTest("a{m},", ecUNEXPECTED_EOF); // incomplete hex definition - - iStat += EqnTest("(1+ 2*a)", 3, true); // Spaces within formula - iStat += EqnTest("sqrt((4))", 2, true); // Multiple brackets - iStat += EqnTest("sqrt((2)+2)", 2, true);// Multiple brackets - iStat += EqnTest("sqrt(2+(2))", 2, true);// Multiple brackets - iStat += EqnTest("sqrt(a+(3))", 2, true);// Multiple brackets - iStat += EqnTest("sqrt((3)+a)", 2, true);// Multiple brackets - iStat += EqnTest("order(1,2)", 1, true); // May not cause name collision with operator "or" - iStat += EqnTest("(2+", 0, false); // missing closing bracket - iStat += EqnTest("2++4", 0, false); // unexpected operator - iStat += EqnTest("2+-4", 0, false); // unexpected operator - iStat += EqnTest("(2+)", 0, false); // unexpected closing bracket - iStat += EqnTest("--2", 0, false); // double sign - iStat += EqnTest("ksdfj", 0, false); // unknown token - iStat += EqnTest("()", 0, false); // empty bracket without a function - iStat += EqnTest("5+()", 0, false); // empty bracket without a function - iStat += EqnTest("sin(cos)", 0, false); // unexpected function - iStat += EqnTest("5t6", 0, false); // unknown token - iStat += EqnTest("5 t 6", 0, false); // unknown token - iStat += EqnTest("8*", 0, false); // unexpected end of formula - iStat += EqnTest(",3", 0, false); // unexpected comma - iStat += EqnTest("3,5", 0, false); // unexpected comma - iStat += EqnTest("sin(8,8)", 0, false); // too many function args - iStat += EqnTest("(7,8)", 0, false); // too many function args - iStat += EqnTest("sin)", 0, false); // unexpected closing bracket - iStat += EqnTest("a)", 0, false); // unexpected closing bracket - iStat += EqnTest("pi)", 0, false); // unexpected closing bracket - iStat += EqnTest("sin(())", 0, false); // unexpected closing bracket - iStat += EqnTest("sin()", 0, false); // unexpected closing bracket - - if (iStat==0) - qDebug() << "passed"; - else - qDebug() << "\n failed with " << iStat << " errors"; - - return iStat; - } - - //--------------------------------------------------------------------------- - int QmuParserTester::TestVarConst() - { - int iStat = 0; - qDebug() << "testing variable/constant detection..."; - - // Test if the result changes when a variable changes - iStat += EqnTestWithVarChange( "a", 1, 1, 2, 2 ); - iStat += EqnTestWithVarChange( "2*a", 2, 4, 3, 6 ); - - // distinguish constants with same basename - iStat += EqnTest( "const", 1, true); - iStat += EqnTest( "const1", 2, true); - iStat += EqnTest( "const2", 3, true); - iStat += EqnTest( "2*const", 2, true); - iStat += EqnTest( "2*const1", 4, true); - iStat += EqnTest( "2*const2", 6, true); - iStat += EqnTest( "2*const+1", 3, true); - iStat += EqnTest( "2*const1+1", 5, true); - iStat += EqnTest( "2*const2+1", 7, true); - iStat += EqnTest( "const", 0, false); - iStat += EqnTest( "const1", 0, false); - iStat += EqnTest( "const2", 0, false); - - // distinguish variables with same basename - iStat += EqnTest( "a", 1, true); - iStat += EqnTest( "aa", 2, true); - iStat += EqnTest( "2*a", 2, true); - iStat += EqnTest( "2*aa", 4, true); - iStat += EqnTest( "2*a-1", 1, true); - iStat += EqnTest( "2*aa-1", 3, true); - - // custom value recognition - iStat += EqnTest( "0xff", 255, true); - iStat += EqnTest( "0x97 + 0xff", 406, true); - - // Finally test querying of used variables - try - { - int idx; - qmu::QmuParser p; - qreal vVarVal[] = { 1, 2, 3, 4, 5}; - p.DefineVar( "a", &vVarVal[0]); - p.DefineVar( "b", &vVarVal[1]); - p.DefineVar( "c", &vVarVal[2]); - p.DefineVar( "d", &vVarVal[3]); - p.DefineVar( "e", &vVarVal[4]); - - // Test lookup of defined variables - // 4 used variables - p.SetExpr( "a+b+c+d" ); - qmu::varmap_type UsedVar = p.GetUsedVar(); - int iCount = (int)UsedVar.size(); - if (iCount!=4) - throw false; - - // the next check will fail if the parser - // erroneousely creates new variables internally - if (p.GetVar().size()!=5) - throw false; - - qmu::varmap_type::const_iterator item = UsedVar.begin(); - for (idx=0; item!=UsedVar.end(); ++item) - { - if (&vVarVal[idx++]!=item->second) - throw false; - } - - // Test lookup of undefined variables - p.SetExpr( "undef1+undef2+undef3" ); - UsedVar = p.GetUsedVar(); - iCount = (int)UsedVar.size(); - if (iCount!=3) - throw false; - - // the next check will fail if the parser - // erroneousely creates new variables internally - if (p.GetVar().size()!=5) - throw false; - - for (item = UsedVar.begin(); item!=UsedVar.end(); ++item) - { - if (item->second!=0) - throw false; // all pointers to undefined variables must be null - } - - // 1 used variables - p.SetExpr( "a+b" ); - UsedVar = p.GetUsedVar(); - iCount = (int)UsedVar.size(); - if (iCount!=2) throw false; - item = UsedVar.begin(); - for (idx=0; item!=UsedVar.end(); ++item) - if (&vVarVal[idx++]!=item->second) throw false; - - } - catch(...) - { - iStat += 1; - } - - if (iStat==0) - qDebug() << "passed"; - else - qDebug() << "\n failed with " << iStat << " errors"; - - return iStat; - } - - //--------------------------------------------------------------------------- - int QmuParserTester::TestMultiArg() - { - int iStat = 0; - qDebug() << "testing multiarg functions..."; - - // Compound expressions - iStat += EqnTest( "1,2,3", 3, true); - iStat += EqnTest( "a,b,c", 3, true); - iStat += EqnTest( "a=10,b=20,c=a*b", 200, true); - iStat += EqnTest( "1,\n2,\n3", 3, true); - iStat += EqnTest( "a,\nb,\nc", 3, true); - iStat += EqnTest( "a=10,\nb=20,\nc=a*b", 200, true); - iStat += EqnTest( "1,\r\n2,\r\n3", 3, true); - iStat += EqnTest( "a,\r\nb,\r\nc", 3, true); - iStat += EqnTest( "a=10,\r\nb=20,\r\nc=a*b", 200, true); - - // picking the right argument - iStat += EqnTest( "f1of1(1)", 1, true); - iStat += EqnTest( "f1of2(1, 2)", 1, true); - iStat += EqnTest( "f2of2(1, 2)", 2, true); - iStat += EqnTest( "f1of3(1, 2, 3)", 1, true); - iStat += EqnTest( "f2of3(1, 2, 3)", 2, true); - iStat += EqnTest( "f3of3(1, 2, 3)", 3, true); - iStat += EqnTest( "f1of4(1, 2, 3, 4)", 1, true); - iStat += EqnTest( "f2of4(1, 2, 3, 4)", 2, true); - iStat += EqnTest( "f3of4(1, 2, 3, 4)", 3, true); - iStat += EqnTest( "f4of4(1, 2, 3, 4)", 4, true); - iStat += EqnTest( "f1of5(1, 2, 3, 4, 5)", 1, true); - iStat += EqnTest( "f2of5(1, 2, 3, 4, 5)", 2, true); - iStat += EqnTest( "f3of5(1, 2, 3, 4, 5)", 3, true); - iStat += EqnTest( "f4of5(1, 2, 3, 4, 5)", 4, true); - iStat += EqnTest( "f5of5(1, 2, 3, 4, 5)", 5, true); - // Too few arguments / Too many arguments - iStat += EqnTest( "1+ping()", 11, true); - iStat += EqnTest( "ping()+1", 11, true); - iStat += EqnTest( "2*ping()", 20, true); - iStat += EqnTest( "ping()*2", 20, true); - iStat += EqnTest( "ping(1,2)", 0, false); - iStat += EqnTest( "1+ping(1,2)", 0, false); - iStat += EqnTest( "f1of1(1,2)", 0, false); - iStat += EqnTest( "f1of1()", 0, false); - iStat += EqnTest( "f1of2(1, 2, 3)", 0, false); - iStat += EqnTest( "f1of2(1)", 0, false); - iStat += EqnTest( "f1of3(1, 2, 3, 4)", 0, false); - iStat += EqnTest( "f1of3(1)", 0, false); - iStat += EqnTest( "f1of4(1, 2, 3, 4, 5)", 0, false); - iStat += EqnTest( "f1of4(1)", 0, false); - iStat += EqnTest( "(1,2,3)", 0, false); - iStat += EqnTest( "1,2,3", 0, false); - iStat += EqnTest( "(1*a,2,3)", 0, false); - iStat += EqnTest( "1,2*a,3", 0, false); - - // correct calculation of arguments - iStat += EqnTest( "min(a, 1)", 1, true); - iStat += EqnTest( "min(3*2, 1)", 1, true); - iStat += EqnTest( "min(3*2, 1)", 6, false); - iStat += EqnTest( "firstArg(2,3,4)", 2, true); - iStat += EqnTest( "lastArg(2,3,4)", 4, true); - iStat += EqnTest( "min(3*a+1, 1)", 1, true); - iStat += EqnTest( "max(3*a+1, 1)", 4, true); - iStat += EqnTest( "max(3*a+1, 1)*2", 8, true); - iStat += EqnTest( "2*max(3*a+1, 1)+2", 10, true); - - // functions with Variable argument count - iStat += EqnTest( "sum(a)", 1, true); - iStat += EqnTest( "sum(1,2,3)", 6, true); - iStat += EqnTest( "sum(a,b,c)", 6, true); - iStat += EqnTest( "sum(1,-max(1,2),3)*2", 4, true); - iStat += EqnTest( "2*sum(1,2,3)", 12, true); - iStat += EqnTest( "2*sum(1,2,3)+2", 14, true); - iStat += EqnTest( "2*sum(-1,2,3)+2", 10, true); - iStat += EqnTest( "2*sum(-1,2,-(-a))+2", 6, true); - iStat += EqnTest( "2*sum(-1,10,-a)+2", 18, true); - iStat += EqnTest( "2*sum(1,2,3)*2", 24, true); - iStat += EqnTest( "sum(1,-max(1,2),3)*2", 4, true); - iStat += EqnTest( "sum(1*3, 4, a+2)", 10, true); - iStat += EqnTest( "sum(1*3, 2*sum(1,2,2), a+2)", 16, true); - iStat += EqnTest( "sum(1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2)", 24, true); - - // some failures - iStat += EqnTest( "sum()", 0, false); - iStat += EqnTest( "sum(,)", 0, false); - iStat += EqnTest( "sum(1,2,)", 0, false); - iStat += EqnTest( "sum(,1,2)", 0, false); - - if (iStat==0) - qDebug() << "passed"; - else - qDebug() << "\n failed with " << iStat << " errors"; - - return iStat; - } - - - //--------------------------------------------------------------------------- - int QmuParserTester::TestInfixOprt() - { - int iStat(0); - qDebug() << "testing infix operators..."; - - iStat += EqnTest( "-1", -1, true); - iStat += EqnTest( "-(-1)", 1, true); - iStat += EqnTest( "-(-1)*2", 2, true); - iStat += EqnTest( "-(-2)*sqrt(4)", 4, true); - iStat += EqnTest( "-_pi", -M_PI, true); - iStat += EqnTest( "-a", -1, true); - iStat += EqnTest( "-(a)", -1, true); - iStat += EqnTest( "-(-a)", 1, true); - iStat += EqnTest( "-(-a)*2", 2, true); - iStat += EqnTest( "-(8)", -8, true); - iStat += EqnTest( "-8", -8, true); - iStat += EqnTest( "-(2+1)", -3, true); - iStat += EqnTest( "-(f1of1(1+2*3)+1*2)", -9, true); - iStat += EqnTest( "-(-f1of1(1+2*3)+1*2)", 5, true); - iStat += EqnTest( "-sin(8)", -0.989358, true); - iStat += EqnTest( "3-(-a)", 4, true); - iStat += EqnTest( "3--a", 4, true); - iStat += EqnTest( "-1*3", -3, true); - - // Postfix / infix priorities - iStat += EqnTest( "~2#", 8, true); - iStat += EqnTest( "~f1of1(2)#", 8, true); - iStat += EqnTest( "~(b)#", 8, true); - iStat += EqnTest( "(~b)#", 12, true); - iStat += EqnTest( "~(2#)", 8, true); - iStat += EqnTest( "~(f1of1(2)#)", 8, true); - // - iStat += EqnTest( "-2^2",-4, true); - iStat += EqnTest( "-(a+b)^2",-9, true); - iStat += EqnTest( "(-3)^2",9, true); - iStat += EqnTest( "-(-2^2)",4, true); - iStat += EqnTest( "3+-3^2",-6, true); - // The following assumes use of sqr as postfix operator ("") together - // with a sign operator of low priority: - iStat += EqnTest( "-2'", -4, true); - iStat += EqnTest( "-(1+1)'",-4, true); - iStat += EqnTest( "2+-(1+1)'",-2, true); - iStat += EqnTest( "2+-2'", -2, true); - // This is the classic behaviour of the infix sign operator (here: "$") which is - // now deprecated: - iStat += EqnTest( "$2^2",4, true); - iStat += EqnTest( "$(a+b)^2",9, true); - iStat += EqnTest( "($3)^2",9, true); - iStat += EqnTest( "$($2^2)",-4, true); - iStat += EqnTest( "3+$3^2",12, true); - - // infix operators sharing the first few characters - iStat += EqnTest( "~ 123", 123+2, true); - iStat += EqnTest( "~~ 123", 123+2, true); - - if (iStat==0) - qDebug() << "passed"; - else - qDebug() << "\n failed with " << iStat << " errors"; - - return iStat; - } - - - //--------------------------------------------------------------------------- - int QmuParserTester::TestPostFix() - { - int iStat = 0; - qDebug() << "testing postfix operators..."; - - // application - iStat += EqnTest( "3{m}+5", 5.003, true); - iStat += EqnTest( "1000{m}", 1, true); - iStat += EqnTest( "1000 {m}", 1, true); - iStat += EqnTest( "(a){m}", 1e-3, true); - iStat += EqnTest( "a{m}", 1e-3, true); - iStat += EqnTest( "a {m}", 1e-3, true); - iStat += EqnTest( "-(a){m}", -1e-3, true); - iStat += EqnTest( "-2{m}", -2e-3, true); - iStat += EqnTest( "-2 {m}", -2e-3, true); - iStat += EqnTest( "f1of1(1000){m}", 1, true); - iStat += EqnTest( "-f1of1(1000){m}", -1, true); - iStat += EqnTest( "-f1of1(-1000){m}", 1, true); - iStat += EqnTest( "f4of4(0,0,0,1000){m}", 1, true); - iStat += EqnTest( "2+(a*1000){m}", 3, true); - - // can postfix operators "m" und "meg" be told apart properly? - iStat += EqnTest( "2*3000meg+2", 2*3e9+2, true); - - // some incorrect results - iStat += EqnTest( "1000{m}", 0.1, false); - iStat += EqnTest( "(a){m}", 2, false); - // failure due to syntax checking - iStat += ThrowTest("0x", ecUNASSIGNABLE_TOKEN); // incomplete hex definition - iStat += ThrowTest("3+", ecUNEXPECTED_EOF); - iStat += ThrowTest( "4 + {m}", ecUNASSIGNABLE_TOKEN); - iStat += ThrowTest( "{m}4", ecUNASSIGNABLE_TOKEN); - iStat += ThrowTest( "sin({m})", ecUNASSIGNABLE_TOKEN); - iStat += ThrowTest( "{m} {m}", ecUNASSIGNABLE_TOKEN); - iStat += ThrowTest( "{m}(8)", ecUNASSIGNABLE_TOKEN); - iStat += ThrowTest( "4,{m}", ecUNASSIGNABLE_TOKEN); - iStat += ThrowTest( "-{m}", ecUNASSIGNABLE_TOKEN); - iStat += ThrowTest( "2(-{m})", ecUNEXPECTED_PARENS); - iStat += ThrowTest( "2({m})", ecUNEXPECTED_PARENS); - - iStat += ThrowTest( "multi*1.0", ecUNASSIGNABLE_TOKEN); - - if (iStat==0) - qDebug() << "passed"; - else - qDebug() << "\n failed with " << iStat << " errors"; - - return iStat; - } - - //--------------------------------------------------------------------------- - int QmuParserTester::TestExpression() - { - int iStat = 0; - qDebug() << "testing expression samples..."; - - qreal b = 2; - - // Optimization - iStat += EqnTest( "2*b*5", 20, true); - iStat += EqnTest( "2*b*5 + 4*b", 28, true); - iStat += EqnTest( "2*a/3", 2.0/3.0, true); - - // Addition auf cmVARMUL - iStat += EqnTest( "3+b", b+3, true); - iStat += EqnTest( "b+3", b+3, true); - iStat += EqnTest( "b*3+2", b*3+2, true); - iStat += EqnTest( "3*b+2", b*3+2, true); - iStat += EqnTest( "2+b*3", b*3+2, true); - iStat += EqnTest( "2+3*b", b*3+2, true); - iStat += EqnTest( "b+3*b", b+3*b, true); - iStat += EqnTest( "3*b+b", b+3*b, true); - - iStat += EqnTest( "2+b*3+b", 2+b*3+b, true); - iStat += EqnTest( "b+2+b*3", b+2+b*3, true); - - iStat += EqnTest( "(2*b+1)*4", (2*b+1)*4, true); - iStat += EqnTest( "4*(2*b+1)", (2*b+1)*4, true); - - // operator precedencs - iStat += EqnTest( "1+2-3*4/5^6", 2.99923, true); - iStat += EqnTest( "1^2/3*4-5+6", 2.33333333, true); - iStat += EqnTest( "1+2*3", 7, true); - iStat += EqnTest( "1+2*3", 7, true); - iStat += EqnTest( "(1+2)*3", 9, true); - iStat += EqnTest( "(1+2)*(-3)", -9, true); - iStat += EqnTest( "2/4", 0.5, true); - - iStat += EqnTest( "exp(ln(7))", 7, true); - iStat += EqnTest( "e^ln(7)", 7, true); - iStat += EqnTest( "e^(ln(7))", 7, true); - iStat += EqnTest( "(e^(ln(7)))", 7, true); - iStat += EqnTest( "1-(e^(ln(7)))", -6, true); - iStat += EqnTest( "2*(e^(ln(7)))", 14, true); - iStat += EqnTest( "10^log(5)", 5, true); - iStat += EqnTest( "10^log10(5)", 5, true); - iStat += EqnTest( "2^log2(4)", 4, true); - iStat += EqnTest( "-(sin(0)+1)", -1, true); - iStat += EqnTest( "-(2^1.1)", -2.14354692, true); - - iStat += EqnTest( "(cos(2.41)/b)", -0.372056, true); - iStat += EqnTest( "(1*(2*(3*(4*(5*(6*(a+b)))))))", 2160, true); - iStat += EqnTest( "(1*(2*(3*(4*(5*(6*(7*(a+b))))))))", 15120, true); - iStat += EqnTest( "(a/((((b+(((e*(((((pi*((((3.45*((pi+a)+pi))+b)+b)*a))+0.68)+e)+a)/a))+a)+b))+b)*a)-pi))", 0.00377999, true); - - // long formula (Reference: Matlab) - iStat += EqnTest( - "(((-9))-e/(((((((pi-(((-7)+(-3)/4/e))))/(((-5))-2)-((pi+(-0))*(sqrt((e+e))*(-8))*(((-pi)+(-pi)-(-9)*(6*5))" - "/(-e)-e))/2)/((((sqrt(2/(-e)+6)-(4-2))+((5/(-2))/(1*(-pi)+3))/8)*pi*((pi/((-2)/(-6)*1*(-1))*(-6)+(-e)))))/" - "((e+(-2)+(-e)*((((-3)*9+(-e)))+(-9)))))))-((((e-7+(((5/pi-(3/1+pi)))))/e)/(-5))/(sqrt((((((1+(-7))))+((((-" - "e)*(-e)))-8))*(-5)/((-e)))*(-6)-((((((-2)-(-9)-(-e)-1)/3))))/(sqrt((8+(e-((-6))+(9*(-9))))*(((3+2-8))*(7+6" - "+(-5))+((0/(-e)*(-pi))+7)))+(((((-e)/e/e)+((-6)*5)*e+(3+(-5)/pi))))+pi))/sqrt((((9))+((((pi))-8+2))+pi))/e" - "*4)*((-5)/(((-pi))*(sqrt(e)))))-(((((((-e)*(e)-pi))/4+(pi)*(-9)))))))+(-pi)", -12.23016549, true); - - // long formula (Reference: Matlab) - iStat += EqnTest( - "(atan(sin((((((((((((((((pi/cos((a/((((0.53-b)-pi)*e)/b))))+2.51)+a)-0.54)/0.98)+b)*b)+e)/a)+b)+a)+b)+pi)/e" - ")+a)))*2.77)", -2.16995656, true); - - // long formula (Reference: Matlab) - iStat += EqnTest( "1+2-3*4/5^6*(2*(1-5+(3*7^9)*(4+6*7-3)))+12", -7995810.09926, true); - - if (iStat==0) - qDebug() << "passed"; - else - qDebug() << "\n failed with " << iStat << " errors"; - - return iStat; - } - - - - //--------------------------------------------------------------------------- - int QmuParserTester::TestIfThenElse() - { - int iStat = 0; - qDebug() << "testing if-then-else operator..."; - - // Test error detection - iStat += ThrowTest(":3", ecUNEXPECTED_CONDITIONAL); - iStat += ThrowTest("? 1 : 2", ecUNEXPECTED_CONDITIONAL); - iStat += ThrowTest("(ab) ? 10 : 11", 11, true); - iStat += EqnTest("(ab) ? c : d", -2, true); - - iStat += EqnTest("(a>b) ? 1 : 0", 0, true); - iStat += EqnTest("((a>b) ? 1 : 0) ? 1 : 2", 2, true); - iStat += EqnTest("((a>b) ? 1 : 0) ? 1 : sum((a>b) ? 1 : 2)", 2, true); - iStat += EqnTest("((a>b) ? 0 : 1) ? 1 : sum((a>b) ? 1 : 2)", 1, true); - - iStat += EqnTest("sum((a>b) ? 1 : 2)", 2, true); - iStat += EqnTest("sum((1) ? 1 : 2)", 1, true); - iStat += EqnTest("sum((a>b) ? 1 : 2, 100)", 102, true); - iStat += EqnTest("sum((1) ? 1 : 2, 100)", 101, true); - iStat += EqnTest("sum(3, (a>b) ? 3 : 10)", 13, true); - iStat += EqnTest("sum(3, (ab) ? 3 : 10)", 130, true); - iStat += EqnTest("10*sum(3, (ab) ? 3 : 10)*10", 130, true); - iStat += EqnTest("sum(3, (ab) ? sum(3, (ab) ? sum(3, (ab) ? sum(3, (ab)&&(a2)&&(1<2) ? 128 : 255", 255, true); - iStat += EqnTest("((1<2)&&(1<2)) ? 128 : 255", 128, true); - iStat += EqnTest("((1>2)&&(1<2)) ? 128 : 255", 255, true); - iStat += EqnTest("((ab)&&(a0 ? 1>2 ? 128 : 255 : 1>0 ? 32 : 64", 255, true); - iStat += EqnTest("1>0 ? 1>2 ? 128 : 255 :(1>0 ? 32 : 64)", 255, true); - iStat += EqnTest("1>0 ? 1>0 ? 128 : 255 : 1>2 ? 32 : 64", 128, true); - iStat += EqnTest("1>0 ? 1>0 ? 128 : 255 :(1>2 ? 32 : 64)", 128, true); - iStat += EqnTest("1>2 ? 1>2 ? 128 : 255 : 1>0 ? 32 : 64", 32, true); - iStat += EqnTest("1>2 ? 1>0 ? 128 : 255 : 1>2 ? 32 : 64", 64, true); - iStat += EqnTest("1>0 ? 50 : 1>0 ? 128 : 255", 50, true); - iStat += EqnTest("1>0 ? 50 : (1>0 ? 128 : 255)", 50, true); - iStat += EqnTest("1>0 ? 1>0 ? 128 : 255 : 50", 128, true); - iStat += EqnTest("1>2 ? 1>2 ? 128 : 255 : 1>0 ? 32 : 1>2 ? 64 : 16", 32, true); - iStat += EqnTest("1>2 ? 1>2 ? 128 : 255 : 1>0 ? 32 :(1>2 ? 64 : 16)", 32, true); - iStat += EqnTest("1>0 ? 1>2 ? 128 : 255 : 1>0 ? 32 :1>2 ? 64 : 16", 255, true); - iStat += EqnTest("1>0 ? 1>2 ? 128 : 255 : (1>0 ? 32 :1>2 ? 64 : 16)", 255, true); - iStat += EqnTest("1 ? 0 ? 128 : 255 : 1 ? 32 : 64", 255, true); - - // assignment operators - iStat += EqnTest("a= 0 ? 128 : 255, a", 255, true); - iStat += EqnTest("a=((a>b)&&(a - // this is now legal, for reference see: - // https://sourceforge.net/forum/message.php?msg_id=7411373 - // iStat += ThrowTest( "sin=9"), ecUNEXPECTED_OPERATOR); - //
    - - iStat += ThrowTest( "(8)=5", ecUNEXPECTED_OPERATOR); - iStat += ThrowTest( "(a)=5", ecUNEXPECTED_OPERATOR); - iStat += ThrowTest( "a=\"tttt\"", ecOPRT_TYPE_CONFLICT); - - if (iStat==0) - qDebug() << "passed" ; - else - qDebug() << "\n failed with " << iStat << " errors" ; - - return iStat; - } - - - //--------------------------------------------------------------------------- - void QmuParserTester::AddTest(testfun_type a_pFun) - { - m_vTestFun.push_back(a_pFun); - } - - //--------------------------------------------------------------------------- - void QmuParserTester::Run() - { - int iStat = 0; - try - { - for (int i=0; i<(int)m_vTestFun.size(); ++i) - iStat += (this->*m_vTestFun[i])(); - } - catch(QmuParser::exception_type &e) - { - qDebug() << "\n" << e.GetMsg() ; - qDebug() << e.GetToken() ; - Abort(); - } - catch(std::exception &e) - { - qDebug() << e.what() ; - Abort(); - } - catch(...) - { - qDebug() << "Internal error"; - Abort(); - } - - if (iStat==0) - { - qDebug() << "Test passed (" << QmuParserTester::c_iCount << " expressions)" ; - } - else - { - qDebug() << "Test failed with " << iStat - << " errors (" << QmuParserTester::c_iCount - << " expressions)" ; - } - QmuParserTester::c_iCount = 0; - } - - - //--------------------------------------------------------------------------- - int QmuParserTester::ThrowTest(const QString &a_str, int a_iErrc, bool a_bFail) - { - QmuParserTester::c_iCount++; - - try - { - qreal fVal[] = {1,1,1}; - QmuParser p; - - p.DefineVar( "a", &fVal[0]); - p.DefineVar( "b", &fVal[1]); - p.DefineVar( "c", &fVal[2]); - p.DefinePostfixOprt( "{m}", Milli); - p.DefinePostfixOprt( "m", Milli); - p.DefineFun( "ping", Ping); - p.DefineFun( "valueof", ValueOf); - p.DefineFun( "strfun1", StrFun1); - p.DefineFun( "strfun2", StrFun2); - p.DefineFun( "strfun3", StrFun3); - p.SetExpr(a_str); - p.Eval(); - } - catch(QmuParserError &e) - { - // output the formula in case of an failed test - if (a_bFail==false || (a_bFail==true && a_iErrc!=e.GetCode()) ) - { - qDebug() << "\n " - << "Expression: " << a_str - << " Code:" << e.GetCode() << "(" << e.GetMsg() << ")" - << " Expected:" << a_iErrc; - } - - return (a_iErrc==e.GetCode()) ? 0 : 1; - } - - // if a_bFail==false no exception is expected - bool bRet((a_bFail==false) ? 0 : 1); - if (bRet==1) - { - qDebug() << "\n " - << "Expression: " << a_str - << " did evaluate; Expected error:" << a_iErrc; - } - - return bRet; - } - - //--------------------------------------------------------------------------- - /** \brief Evaluate a tet expression. - - \return 1 in case of a failure, 0 otherwise. - */ - int QmuParserTester::EqnTestWithVarChange(const QString &a_str, - double a_fVar1, - double a_fRes1, - double a_fVar2, - double a_fRes2) - { - QmuParserTester::c_iCount++; - qreal fVal[2] = {-999, -999 }; // should be equalinitially - - try - { - QmuParser p; - - // variable - qreal var = 0; - p.DefineVar( "a", &var); - p.SetExpr(a_str); - - var = a_fVar1; - fVal[0] = p.Eval(); - - var = a_fVar2; - fVal[1] = p.Eval(); - - if ( fabs(a_fRes1-fVal[0]) > 0.0000000001) - throw std::runtime_error("incorrect result (first pass)"); - - if ( fabs(a_fRes2-fVal[1]) > 0.0000000001) - throw std::runtime_error("incorrect result (second pass)"); - } - catch(QmuParser::exception_type &e) - { - qDebug() << "\n fail: " << a_str << " (" << e.GetMsg() << ")"; - return 1; - } - catch(std::exception &e) - { - qDebug() << "\n fail: " << a_str << " (" << e.what() << ")"; - return 1; // always return a failure since this exception is not expected - } - catch(...) - { - qDebug() << "\n fail: " << a_str << " (unexpected exception)"; - return 1; // exceptions other than ParserException are not allowed - } - - return 0; - } - - //--------------------------------------------------------------------------- - /** \brief Evaluate a tet expression. - - \return 1 in case of a failure, 0 otherwise. - */ - int QmuParserTester::EqnTest(const QString &a_str, double a_fRes, bool a_fPass) - { - QmuParserTester::c_iCount++; - int iRet(0); - qreal fVal[5] = {-999, -998, -997, -996, -995}; // initially should be different - - try - { - std::auto_ptr p1; - QmuParser p2, p3; // three parser objects - // they will be used for testing copy and assihnment operators - // p1 is a pointer since i'm going to delete it in order to test if - // parsers after copy construction still refer to members of it. - // !! If this is the case this function will crash !! - - p1.reset(new qmu::QmuParser()); - // Add constants - p1->DefineConst( "pi", (qreal)M_PI); - p1->DefineConst( "e", (qreal)M_E); - p1->DefineConst( "const", 1); - p1->DefineConst( "const1", 2); - p1->DefineConst( "const2", 3); - // variables - qreal vVarVal[] = { 1, 2, 3, -2}; - p1->DefineVar( "a", &vVarVal[0]); - p1->DefineVar( "aa", &vVarVal[1]); - p1->DefineVar( "b", &vVarVal[1]); - p1->DefineVar( "c", &vVarVal[2]); - p1->DefineVar( "d", &vVarVal[3]); - - // custom value ident functions - p1->AddValIdent(&QmuParserTester::IsHexVal); - - // functions - p1->DefineFun( "ping", Ping); - p1->DefineFun( "f1of1", f1of1); // one parameter - p1->DefineFun( "f1of2", f1of2); // two parameter - p1->DefineFun( "f2of2", f2of2); - p1->DefineFun( "f1of3", f1of3); // three parameter - p1->DefineFun( "f2of3", f2of3); - p1->DefineFun( "f3of3", f3of3); - p1->DefineFun( "f1of4", f1of4); // four parameter - p1->DefineFun( "f2of4", f2of4); - p1->DefineFun( "f3of4", f3of4); - p1->DefineFun( "f4of4", f4of4); - p1->DefineFun( "f1of5", f1of5); // five parameter - p1->DefineFun( "f2of5", f2of5); - p1->DefineFun( "f3of5", f3of5); - p1->DefineFun( "f4of5", f4of5); - p1->DefineFun( "f5of5", f5of5); - - // binary operators - p1->DefineOprt( "add", add, 0); - p1->DefineOprt( "++", add, 0); - p1->DefineOprt( "&", land, prLAND); - - // sample functions - p1->DefineFun( "min", Min); - p1->DefineFun( "max", Max); - p1->DefineFun( "sum", Sum); - p1->DefineFun( "valueof", ValueOf); - p1->DefineFun( "atof", StrToFloat); - p1->DefineFun( "strfun1", StrFun1); - p1->DefineFun( "strfun2", StrFun2); - p1->DefineFun( "strfun3", StrFun3); - p1->DefineFun( "lastArg", LastArg); - p1->DefineFun( "firstArg", FirstArg); - p1->DefineFun( "order", FirstArg); - - // infix / postfix operator - // Note: Identifiers used here do not have any meaning - // they are mere placeholders to test certain features. - p1->DefineInfixOprt( "$", sign, prPOW+1); // sign with high priority - p1->DefineInfixOprt( "~", plus2); // high priority - p1->DefineInfixOprt( "~~", plus2); - p1->DefinePostfixOprt( "{m}", Milli); - p1->DefinePostfixOprt( "{M}", Mega); - p1->DefinePostfixOprt( "m", Milli); - p1->DefinePostfixOprt( "meg", Mega); - p1->DefinePostfixOprt( "#", times3); - p1->DefinePostfixOprt( "'", sqr); - p1->SetExpr(a_str); - - // Test bytecode integrity - // String parsing and bytecode parsing must yield the same result - fVal[0] = p1->Eval(); // result from stringparsing - fVal[1] = p1->Eval(); // result from bytecode - if (fVal[0]!=fVal[1]) - throw QmuParser::exception_type( "Bytecode / string parsing mismatch." ); - - // Test copy and assignement operators - try - { - // Test copy constructor - QVector vParser; - vParser.push_back(*(p1.get())); - qmu::QmuParser p2 = vParser[0]; // take parser from vector - - // destroy the originals from p2 - vParser.clear(); // delete the vector - p1.reset(0); - - fVal[2] = p2.Eval(); - - // Test assignement operator - // additionally disable Optimizer this time - qmu::QmuParser p3; - p3 = p2; - p3.EnableOptimizer(false); - fVal[3] = p3.Eval(); - - // Test Eval function for multiple return values - // use p2 since it has the optimizer enabled! - int nNum; - qreal *v = p2.Eval(nNum); - fVal[4] = v[nNum-1]; - } - catch(std::exception &e) - { - qDebug() << "\n " << e.what() << "\n"; - } - - // limited floating point accuracy requires the following test - bool bCloseEnough(true); - for (unsigned i=0; i::has_infinity) - bCloseEnough &= (fabs(fVal[i]) != numeric_limits::infinity()); - } - - iRet = ((bCloseEnough && a_fPass) || (!bCloseEnough && !a_fPass)) ? 0 : 1; - - - if (iRet==1) - { - qDebug() << "\n fail: " << a_str - << " (incorrect result; expected: " << a_fRes - << " ;calculated: " << fVal[0] << "," - << fVal[1] << "," - << fVal[2] << "," - << fVal[3] << "," - << fVal[4] << ")."; - } - } - catch(QmuParser::exception_type &e) - { - if (a_fPass) - { - if (fVal[0]!=fVal[2] && fVal[0]!=-999 && fVal[1]!=-998) - qDebug() << "\n fail: " << a_str << " (copy construction)"; - else - qDebug() << "\n fail: " << a_str << " (" << e.GetMsg() << ")"; - return 1; - } - } - catch(std::exception &e) - { - qDebug() << "\n fail: " << a_str << " (" << e.what() << ")"; - return 1; // always return a failure since this exception is not expected - } - catch(...) - { - qDebug() << "\n fail: " << a_str << " (unexpected exception)"; - return 1; // exceptions other than ParserException are not allowed - } - - return iRet; - } - - //--------------------------------------------------------------------------- - /** \brief Internal error in test class Test is going to be aborted. */ - void QmuParserTester::Abort() const - { - qDebug() << "Test failed (internal error in test class)" ; - while (!getchar()); - exit(-1); - } - } // namespace test -} // namespace qmu +/*************************************************************************************************** + ** + ** Original work Copyright (C) 2013 Ingo Berg + ** Modified work Copyright 2014 Roman Telezhinsky + ** + ** 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 "qmuparsertest.h" +#include +#include + +#include +#include +#include + +using namespace std; + +/** + * @file + * @brief This file contains the implementation of parser test cases. + */ + +namespace qmu +{ +namespace Test +{ +int QmuParserTester::c_iCount = 0; + +//---------------------------------------------------------------------------------------------------------------------- +QmuParserTester::QmuParserTester() + : m_vTestFun() +{ + AddTest ( &QmuParserTester::TestNames ); + AddTest ( &QmuParserTester::TestSyntax ); + AddTest ( &QmuParserTester::TestPostFix ); + AddTest ( &QmuParserTester::TestInfixOprt ); + AddTest ( &QmuParserTester::TestVarConst ); + AddTest ( &QmuParserTester::TestMultiArg ); + AddTest ( &QmuParserTester::TestExpression ); + AddTest ( &QmuParserTester::TestIfThenElse ); + AddTest ( &QmuParserTester::TestInterface ); + AddTest ( &QmuParserTester::TestBinOprt ); + AddTest ( &QmuParserTester::TestException ); + AddTest ( &QmuParserTester::TestStrArg ); + + QmuParserTester::c_iCount = 0; +} + +//---------------------------------------------------------------------------------------------------------------------- +int QmuParserTester::IsHexVal ( const QString &a_szExpr, int *a_iPos, qreal *a_fVal ) +{ + if ( a_szExpr[1] == 0 || ( a_szExpr[0] != '0' || a_szExpr[1] != 'x' ) ) + { + return 0; + } + + unsigned iVal ( 0 ); + bool ok = false; + iVal = a_szExpr.toUInt ( &ok, 16 ); + + if ( ok ) + { + int nPos = a_szExpr.indexOf ( QString().setNum ( iVal, 16 ) ); + if ( nPos == 0 ) + { + return 1; + } + + *a_iPos += ( int ) ( 2 + nPos ); + *a_fVal = ( qreal ) iVal; + return 1; + } + else + { + return 0; + } + +} + +//---------------------------------------------------------------------------------------------------------------------- +int QmuParserTester::TestInterface() +{ + int iStat = 0; + qDebug() << "testing member functions..."; + + // Test RemoveVar + qreal afVal[3] = {1, 2, 3}; + QmuParser p; + + try + { + p.DefineVar ( "a", &afVal[0] ); + p.DefineVar ( "b", &afVal[1] ); + p.DefineVar ( "c", &afVal[2] ); + p.SetExpr ( "a+b+c" ); + p.Eval(); + } + catch ( ... ) + { + iStat += 1; // this is not supposed to happen + } + + try + { + p.RemoveVar ( "c" ); + p.Eval(); + iStat += 1; // not supposed to reach this, nonexisting variable "c" deleted... + } + catch ( ... ) + { + // failure is expected... + } + + if ( iStat == 0 ) + { + qDebug() << "passed"; + } + else + { + qDebug() << "\n failed with " << iStat << " errors"; + } + + return iStat; +} + +//---------------------------------------------------------------------------------------------------------------------- +int QmuParserTester::TestStrArg() +{ + int iStat = 0; + qDebug() << "testing string arguments..."; + + iStat += EqnTest ( "valueof(\"\")", 123, true ); // empty string arguments caused a crash + iStat += EqnTest ( "valueof(\"aaa\")+valueof(\"bbb\") ", 246, true ); + iStat += EqnTest ( "2*(valueof(\"aaa\")-23)+valueof(\"bbb\")", 323, true ); + // use in expressions with variables + iStat += EqnTest ( "a*(atof(\"10\")-b)", 8, true ); + iStat += EqnTest ( "a-(atof(\"10\")*b)", -19, true ); + // string + numeric arguments + iStat += EqnTest ( "strfun1(\"100\")", 100, true ); + iStat += EqnTest ( "strfun2(\"100\",1)", 101, true ); + iStat += EqnTest ( "strfun3(\"99\",1,2)", 102, true ); + + if ( iStat == 0 ) + { + qDebug() << "passed"; + } + else + { + qDebug() << "\n failed with " << iStat << " errors"; + } + + return iStat; +} + +//---------------------------------------------------------------------------------------------------------------------- +int QmuParserTester::TestBinOprt() +{ + int iStat = 0; + qDebug() << "testing binary operators..."; + + // built in operators + // xor operator + //iStat += EqnTest("1 xor 2", 3, true); + //iStat += EqnTest("a xor b", 3, true); // with a=1 and b=2 + //iStat += EqnTest("1 xor 2 xor 3", 0, true); + //iStat += EqnTest("a xor b xor 3", 0, true); // with a=1 and b=2 + //iStat += EqnTest("a xor b xor c", 0, true); // with a=1 and b=2 + //iStat += EqnTest("(1 xor 2) xor 3", 0, true); + //iStat += EqnTest("(a xor b) xor c", 0, true); // with a=1 and b=2 + //iStat += EqnTest("(a) xor (b) xor c", 0, true); // with a=1 and b=2 + //iStat += EqnTest("1 or 2"), 3, true; + //iStat += EqnTest("a or b"), 3, true; // with a=1 and b=2 + iStat += EqnTest ( "a++b", 3, true ); + iStat += EqnTest ( "a ++ b", 3, true ); + iStat += EqnTest ( "1++2", 3, true ); + iStat += EqnTest ( "1 ++ 2", 3, true ); + iStat += EqnTest ( "a add b", 3, true ); + iStat += EqnTest ( "1 add 2", 3, true ); + iStat += EqnTest ( "aa", 1, true ); + iStat += EqnTest ( "a>a", 0, true ); + iStat += EqnTest ( "aa", 0, true ); + iStat += EqnTest ( "a<=a", 1, true ); + iStat += EqnTest ( "a<=b", 1, true ); + iStat += EqnTest ( "b<=a", 0, true ); + iStat += EqnTest ( "a>=a", 1, true ); + iStat += EqnTest ( "b>=a", 1, true ); + iStat += EqnTest ( "a>=b", 0, true ); + + // Test logical operators, expecially if user defined "&" and the internal "&&" collide + iStat += EqnTest ( "1 && 1", 1, true ); + iStat += EqnTest ( "1 && 0", 0, true ); + iStat += EqnTest ( "(aa)", 1, true ); + iStat += EqnTest ( "(ab)", 0, true ); + //iStat += EqnTest("12 and 255", 12, true); + //iStat += EqnTest("12 and 0", 0, true); + iStat += EqnTest ( "12 & 255", 12, true ); + iStat += EqnTest ( "12 & 0", 0, true ); + iStat += EqnTest ( "12&255", 12, true ); + iStat += EqnTest ( "12&0", 0, true ); + + // Assignement operator + iStat += EqnTest ( "a = b", 2, true ); + iStat += EqnTest ( "a = sin(b)", 0.909297, true ); + iStat += EqnTest ( "a = 1+sin(b)", 1.909297, true ); + iStat += EqnTest ( "(a=b)*2", 4, true ); + iStat += EqnTest ( "2*(a=b)", 4, true ); + iStat += EqnTest ( "2*(a=b+1)", 6, true ); + iStat += EqnTest ( "(a=b+1)*2", 6, true ); + + iStat += EqnTest ( "2^2^3", 256, true ); + iStat += EqnTest ( "1/2/3", 1.0 / 6.0, true ); + + // reference: http://www.wolframalpha.com/input/?i=3%2B4*2%2F%281-5%29^2^3 + iStat += EqnTest ( "3+4*2/(1-5)^2^3", 3.0001220703125, true ); + + if ( iStat == 0 ) + { + qDebug() << "passed"; + } + else + { + qDebug() << "\n failed with " << iStat << " errors"; + } + + return iStat; +} + +//---------------------------------------------------------------------------------------------------------------------- +/** @brief Check muParser name restriction enforcement. */ +int QmuParserTester::TestNames() +{ + int iStat = 0, + iErr = 0; + + qDebug() << "testing name restriction enforcement..."; + + QmuParser p; + +#define PARSER_THROWCHECK(DOMAIN, FAIL, EXPR, ARG) \ + iErr = 0; \ + QmuParserTester::c_iCount++; \ + try \ + { \ + p.Define##DOMAIN(EXPR, ARG); \ + } \ + catch(QmuParser::exception_type&) \ + { \ + iErr = (FAIL==false) ? 0 : 1; \ + } \ + iStat += iErr; + + // constant names + PARSER_THROWCHECK ( Const, false, "0a", 1 ) + PARSER_THROWCHECK ( Const, false, "9a", 1 ) + PARSER_THROWCHECK ( Const, false, "+a", 1 ) + PARSER_THROWCHECK ( Const, false, "-a", 1 ) + PARSER_THROWCHECK ( Const, false, "a-", 1 ) + PARSER_THROWCHECK ( Const, false, "a*", 1 ) + PARSER_THROWCHECK ( Const, false, "a?", 1 ) + PARSER_THROWCHECK ( Const, true, "a", 1 ) + PARSER_THROWCHECK ( Const, true, "a_min", 1 ) + PARSER_THROWCHECK ( Const, true, "a_min0", 1 ) + PARSER_THROWCHECK ( Const, true, "a_min9", 1 ) + // variable names + qreal a; + p.ClearConst(); + PARSER_THROWCHECK ( Var, false, "123abc", &a ) + PARSER_THROWCHECK ( Var, false, "9a", &a ) + PARSER_THROWCHECK ( Var, false, "0a", &a ) + PARSER_THROWCHECK ( Var, false, "+a", &a ) + PARSER_THROWCHECK ( Var, false, "-a", &a ) + PARSER_THROWCHECK ( Var, false, "?a", &a ) + PARSER_THROWCHECK ( Var, false, "!a", &a ) + PARSER_THROWCHECK ( Var, false, "a+", &a ) + PARSER_THROWCHECK ( Var, false, "a-", &a ) + PARSER_THROWCHECK ( Var, false, "a*", &a ) + PARSER_THROWCHECK ( Var, false, "a?", &a ) + PARSER_THROWCHECK ( Var, true, "a", &a ) + PARSER_THROWCHECK ( Var, true, "a_min", &a ) + PARSER_THROWCHECK ( Var, true, "a_min0", &a ) + PARSER_THROWCHECK ( Var, true, "a_min9", &a ) + PARSER_THROWCHECK ( Var, false, "a_min9", 0 ) + // Postfix operators + // fail + PARSER_THROWCHECK ( PostfixOprt, false, "(k", f1of1 ) + PARSER_THROWCHECK ( PostfixOprt, false, "9+", f1of1 ) + PARSER_THROWCHECK ( PostfixOprt, false, "+", 0 ) + // pass + PARSER_THROWCHECK ( PostfixOprt, true, "-a", f1of1 ) + PARSER_THROWCHECK ( PostfixOprt, true, "?a", f1of1 ) + PARSER_THROWCHECK ( PostfixOprt, true, "_", f1of1 ) + PARSER_THROWCHECK ( PostfixOprt, true, "#", f1of1 ) + PARSER_THROWCHECK ( PostfixOprt, true, "&&", f1of1 ) + PARSER_THROWCHECK ( PostfixOprt, true, "||", f1of1 ) + PARSER_THROWCHECK ( PostfixOprt, true, "&", f1of1 ) + PARSER_THROWCHECK ( PostfixOprt, true, "|", f1of1 ) + PARSER_THROWCHECK ( PostfixOprt, true, "++", f1of1 ) + PARSER_THROWCHECK ( PostfixOprt, true, "--", f1of1 ) + PARSER_THROWCHECK ( PostfixOprt, true, "?>", f1of1 ) + PARSER_THROWCHECK ( PostfixOprt, true, "?<", f1of1 ) + PARSER_THROWCHECK ( PostfixOprt, true, "**", f1of1 ) + PARSER_THROWCHECK ( PostfixOprt, true, "xor", f1of1 ) + PARSER_THROWCHECK ( PostfixOprt, true, "and", f1of1 ) + PARSER_THROWCHECK ( PostfixOprt, true, "or", f1of1 ) + PARSER_THROWCHECK ( PostfixOprt, true, "not", f1of1 ) + PARSER_THROWCHECK ( PostfixOprt, true, "!", f1of1 ) + // Binary operator + // The following must fail with builtin operators activated + // p.EnableBuiltInOp(true); -> this is the default + p.ClearPostfixOprt(); + PARSER_THROWCHECK ( Oprt, false, "+", f1of2 ) + PARSER_THROWCHECK ( Oprt, false, "-", f1of2 ) + PARSER_THROWCHECK ( Oprt, false, "*", f1of2 ) + PARSER_THROWCHECK ( Oprt, false, "/", f1of2 ) + PARSER_THROWCHECK ( Oprt, false, "^", f1of2 ) + PARSER_THROWCHECK ( Oprt, false, "&&", f1of2 ) + PARSER_THROWCHECK ( Oprt, false, "||", f1of2 ) + // without activated built in operators it should work + p.EnableBuiltInOprt ( false ); + PARSER_THROWCHECK ( Oprt, true, "+", f1of2 ) + PARSER_THROWCHECK ( Oprt, true, "-", f1of2 ) + PARSER_THROWCHECK ( Oprt, true, "*", f1of2 ) + PARSER_THROWCHECK ( Oprt, true, "/", f1of2 ) + PARSER_THROWCHECK ( Oprt, true, "^", f1of2 ) + PARSER_THROWCHECK ( Oprt, true, "&&", f1of2 ) + PARSER_THROWCHECK ( Oprt, true, "||", f1of2 ) +#undef PARSER_THROWCHECK + + if ( iStat == 0 ) + { + qDebug() << "passed"; + } + else + { + qDebug() << "\n failed with " << iStat << " errors"; + } + + return iStat; +} + +//---------------------------------------------------------------------------------------------------------------------- +int QmuParserTester::TestSyntax() +{ + int iStat = 0; + qDebug() << "testing syntax engine..."; + + iStat += ThrowTest ( "1,", ecUNEXPECTED_EOF ); // incomplete hex definition + iStat += ThrowTest ( "a,", ecUNEXPECTED_EOF ); // incomplete hex definition + iStat += ThrowTest ( "sin(8),", ecUNEXPECTED_EOF ); // incomplete hex definition + iStat += ThrowTest ( "(sin(8)),", ecUNEXPECTED_EOF ); // incomplete hex definition + iStat += ThrowTest ( "a{m},", ecUNEXPECTED_EOF ); // incomplete hex definition + + iStat += EqnTest ( "(1+ 2*a)", 3, true ); // Spaces within formula + iStat += EqnTest ( "sqrt((4))", 2, true ); // Multiple brackets + iStat += EqnTest ( "sqrt((2)+2)", 2, true ); // Multiple brackets + iStat += EqnTest ( "sqrt(2+(2))", 2, true ); // Multiple brackets + iStat += EqnTest ( "sqrt(a+(3))", 2, true ); // Multiple brackets + iStat += EqnTest ( "sqrt((3)+a)", 2, true ); // Multiple brackets + iStat += EqnTest ( "order(1,2)", 1, true ); // May not cause name collision with operator "or" + iStat += EqnTest ( "(2+", 0, false ); // missing closing bracket + iStat += EqnTest ( "2++4", 0, false ); // unexpected operator + iStat += EqnTest ( "2+-4", 0, false ); // unexpected operator + iStat += EqnTest ( "(2+)", 0, false ); // unexpected closing bracket + iStat += EqnTest ( "--2", 0, false ); // double sign + iStat += EqnTest ( "ksdfj", 0, false ); // unknown token + iStat += EqnTest ( "()", 0, false ); // empty bracket without a function + iStat += EqnTest ( "5+()", 0, false ); // empty bracket without a function + iStat += EqnTest ( "sin(cos)", 0, false ); // unexpected function + iStat += EqnTest ( "5t6", 0, false ); // unknown token + iStat += EqnTest ( "5 t 6", 0, false ); // unknown token + iStat += EqnTest ( "8*", 0, false ); // unexpected end of formula + iStat += EqnTest ( ",3", 0, false ); // unexpected comma + iStat += EqnTest ( "3,5", 0, false ); // unexpected comma + iStat += EqnTest ( "sin(8,8)", 0, false ); // too many function args + iStat += EqnTest ( "(7,8)", 0, false ); // too many function args + iStat += EqnTest ( "sin)", 0, false ); // unexpected closing bracket + iStat += EqnTest ( "a)", 0, false ); // unexpected closing bracket + iStat += EqnTest ( "pi)", 0, false ); // unexpected closing bracket + iStat += EqnTest ( "sin(())", 0, false ); // unexpected closing bracket + iStat += EqnTest ( "sin()", 0, false ); // unexpected closing bracket + + if ( iStat == 0 ) + { + qDebug() << "passed"; + } + else + { + qDebug() << "\n failed with " << iStat << " errors"; + } + + return iStat; +} + +//---------------------------------------------------------------------------------------------------------------------- +int QmuParserTester::TestVarConst() +{ + int iStat = 0; + qDebug() << "testing variable/constant detection..."; + + // Test if the result changes when a variable changes + iStat += EqnTestWithVarChange ( "a", 1, 1, 2, 2 ); + iStat += EqnTestWithVarChange ( "2*a", 2, 4, 3, 6 ); + + // distinguish constants with same basename + iStat += EqnTest ( "const", 1, true ); + iStat += EqnTest ( "const1", 2, true ); + iStat += EqnTest ( "const2", 3, true ); + iStat += EqnTest ( "2*const", 2, true ); + iStat += EqnTest ( "2*const1", 4, true ); + iStat += EqnTest ( "2*const2", 6, true ); + iStat += EqnTest ( "2*const+1", 3, true ); + iStat += EqnTest ( "2*const1+1", 5, true ); + iStat += EqnTest ( "2*const2+1", 7, true ); + iStat += EqnTest ( "const", 0, false ); + iStat += EqnTest ( "const1", 0, false ); + iStat += EqnTest ( "const2", 0, false ); + + // distinguish variables with same basename + iStat += EqnTest ( "a", 1, true ); + iStat += EqnTest ( "aa", 2, true ); + iStat += EqnTest ( "2*a", 2, true ); + iStat += EqnTest ( "2*aa", 4, true ); + iStat += EqnTest ( "2*a-1", 1, true ); + iStat += EqnTest ( "2*aa-1", 3, true ); + + // custom value recognition + iStat += EqnTest ( "0xff", 255, true ); + iStat += EqnTest ( "0x97 + 0xff", 406, true ); + + // Finally test querying of used variables + try + { + int idx; + qmu::QmuParser p; + qreal vVarVal[] = { 1, 2, 3, 4, 5}; + p.DefineVar ( "a", &vVarVal[0] ); + p.DefineVar ( "b", &vVarVal[1] ); + p.DefineVar ( "c", &vVarVal[2] ); + p.DefineVar ( "d", &vVarVal[3] ); + p.DefineVar ( "e", &vVarVal[4] ); + + // Test lookup of defined variables + // 4 used variables + p.SetExpr ( "a+b+c+d" ); + qmu::varmap_type UsedVar = p.GetUsedVar(); + int iCount = ( int ) UsedVar.size(); + if ( iCount != 4 ) + { + throw false; + } + + // the next check will fail if the parser + // erroneousely creates new variables internally + if ( p.GetVar().size() != 5 ) + { + throw false; + } + + qmu::varmap_type::const_iterator item = UsedVar.begin(); + for ( idx = 0; item != UsedVar.end(); ++item ) + { + if ( &vVarVal[idx++] != item->second ) + { + throw false; + } + } + + // Test lookup of undefined variables + p.SetExpr ( "undef1+undef2+undef3" ); + UsedVar = p.GetUsedVar(); + iCount = ( int ) UsedVar.size(); + if ( iCount != 3 ) + { + throw false; + } + + // the next check will fail if the parser + // erroneousely creates new variables internally + if ( p.GetVar().size() != 5 ) + { + throw false; + } + + for ( item = UsedVar.begin(); item != UsedVar.end(); ++item ) + { + if ( item->second != 0 ) + { + throw false; // all pointers to undefined variables must be null + } + } + + // 1 used variables + p.SetExpr ( "a+b" ); + UsedVar = p.GetUsedVar(); + iCount = ( int ) UsedVar.size(); + if ( iCount != 2 ) + { + throw false; + } + item = UsedVar.begin(); + for ( idx = 0; item != UsedVar.end(); ++item ) + { + if ( &vVarVal[idx++] != item->second ) + { + throw false; + } + } + + } + catch ( ... ) + { + iStat += 1; + } + + if ( iStat == 0 ) + { + qDebug() << "passed"; + } + else + { + qDebug() << "\n failed with " << iStat << " errors"; + } + + return iStat; +} + +//---------------------------------------------------------------------------------------------------------------------- +int QmuParserTester::TestMultiArg() +{ + int iStat = 0; + qDebug() << "testing multiarg functions..."; + + // Compound expressions + iStat += EqnTest ( "1,2,3", 3, true ); + iStat += EqnTest ( "a,b,c", 3, true ); + iStat += EqnTest ( "a=10,b=20,c=a*b", 200, true ); + iStat += EqnTest ( "1,\n2,\n3", 3, true ); + iStat += EqnTest ( "a,\nb,\nc", 3, true ); + iStat += EqnTest ( "a=10,\nb=20,\nc=a*b", 200, true ); + iStat += EqnTest ( "1,\r\n2,\r\n3", 3, true ); + iStat += EqnTest ( "a,\r\nb,\r\nc", 3, true ); + iStat += EqnTest ( "a=10,\r\nb=20,\r\nc=a*b", 200, true ); + + // picking the right argument + iStat += EqnTest ( "f1of1(1)", 1, true ); + iStat += EqnTest ( "f1of2(1, 2)", 1, true ); + iStat += EqnTest ( "f2of2(1, 2)", 2, true ); + iStat += EqnTest ( "f1of3(1, 2, 3)", 1, true ); + iStat += EqnTest ( "f2of3(1, 2, 3)", 2, true ); + iStat += EqnTest ( "f3of3(1, 2, 3)", 3, true ); + iStat += EqnTest ( "f1of4(1, 2, 3, 4)", 1, true ); + iStat += EqnTest ( "f2of4(1, 2, 3, 4)", 2, true ); + iStat += EqnTest ( "f3of4(1, 2, 3, 4)", 3, true ); + iStat += EqnTest ( "f4of4(1, 2, 3, 4)", 4, true ); + iStat += EqnTest ( "f1of5(1, 2, 3, 4, 5)", 1, true ); + iStat += EqnTest ( "f2of5(1, 2, 3, 4, 5)", 2, true ); + iStat += EqnTest ( "f3of5(1, 2, 3, 4, 5)", 3, true ); + iStat += EqnTest ( "f4of5(1, 2, 3, 4, 5)", 4, true ); + iStat += EqnTest ( "f5of5(1, 2, 3, 4, 5)", 5, true ); + // Too few arguments / Too many arguments + iStat += EqnTest ( "1+ping()", 11, true ); + iStat += EqnTest ( "ping()+1", 11, true ); + iStat += EqnTest ( "2*ping()", 20, true ); + iStat += EqnTest ( "ping()*2", 20, true ); + iStat += EqnTest ( "ping(1,2)", 0, false ); + iStat += EqnTest ( "1+ping(1,2)", 0, false ); + iStat += EqnTest ( "f1of1(1,2)", 0, false ); + iStat += EqnTest ( "f1of1()", 0, false ); + iStat += EqnTest ( "f1of2(1, 2, 3)", 0, false ); + iStat += EqnTest ( "f1of2(1)", 0, false ); + iStat += EqnTest ( "f1of3(1, 2, 3, 4)", 0, false ); + iStat += EqnTest ( "f1of3(1)", 0, false ); + iStat += EqnTest ( "f1of4(1, 2, 3, 4, 5)", 0, false ); + iStat += EqnTest ( "f1of4(1)", 0, false ); + iStat += EqnTest ( "(1,2,3)", 0, false ); + iStat += EqnTest ( "1,2,3", 0, false ); + iStat += EqnTest ( "(1*a,2,3)", 0, false ); + iStat += EqnTest ( "1,2*a,3", 0, false ); + + // correct calculation of arguments + iStat += EqnTest ( "min(a, 1)", 1, true ); + iStat += EqnTest ( "min(3*2, 1)", 1, true ); + iStat += EqnTest ( "min(3*2, 1)", 6, false ); + iStat += EqnTest ( "firstArg(2,3,4)", 2, true ); + iStat += EqnTest ( "lastArg(2,3,4)", 4, true ); + iStat += EqnTest ( "min(3*a+1, 1)", 1, true ); + iStat += EqnTest ( "max(3*a+1, 1)", 4, true ); + iStat += EqnTest ( "max(3*a+1, 1)*2", 8, true ); + iStat += EqnTest ( "2*max(3*a+1, 1)+2", 10, true ); + + // functions with Variable argument count + iStat += EqnTest ( "sum(a)", 1, true ); + iStat += EqnTest ( "sum(1,2,3)", 6, true ); + iStat += EqnTest ( "sum(a,b,c)", 6, true ); + iStat += EqnTest ( "sum(1,-max(1,2),3)*2", 4, true ); + iStat += EqnTest ( "2*sum(1,2,3)", 12, true ); + iStat += EqnTest ( "2*sum(1,2,3)+2", 14, true ); + iStat += EqnTest ( "2*sum(-1,2,3)+2", 10, true ); + iStat += EqnTest ( "2*sum(-1,2,-(-a))+2", 6, true ); + iStat += EqnTest ( "2*sum(-1,10,-a)+2", 18, true ); + iStat += EqnTest ( "2*sum(1,2,3)*2", 24, true ); + iStat += EqnTest ( "sum(1,-max(1,2),3)*2", 4, true ); + iStat += EqnTest ( "sum(1*3, 4, a+2)", 10, true ); + iStat += EqnTest ( "sum(1*3, 2*sum(1,2,2), a+2)", 16, true ); + iStat += EqnTest ( "sum(1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2)", 24, true ); + + // some failures + iStat += EqnTest ( "sum()", 0, false ); + iStat += EqnTest ( "sum(,)", 0, false ); + iStat += EqnTest ( "sum(1,2,)", 0, false ); + iStat += EqnTest ( "sum(,1,2)", 0, false ); + + if ( iStat == 0 ) + { + qDebug() << "passed"; + } + else + { + qDebug() << "\n failed with " << iStat << " errors"; + } + + return iStat; +} + + +//---------------------------------------------------------------------------------------------------------------------- +int QmuParserTester::TestInfixOprt() +{ + int iStat ( 0 ); + qDebug() << "testing infix operators..."; + + iStat += EqnTest ( "-1", -1, true ); + iStat += EqnTest ( "-(-1)", 1, true ); + iStat += EqnTest ( "-(-1)*2", 2, true ); + iStat += EqnTest ( "-(-2)*sqrt(4)", 4, true ); + iStat += EqnTest ( "-_pi", -M_PI, true ); + iStat += EqnTest ( "-a", -1, true ); + iStat += EqnTest ( "-(a)", -1, true ); + iStat += EqnTest ( "-(-a)", 1, true ); + iStat += EqnTest ( "-(-a)*2", 2, true ); + iStat += EqnTest ( "-(8)", -8, true ); + iStat += EqnTest ( "-8", -8, true ); + iStat += EqnTest ( "-(2+1)", -3, true ); + iStat += EqnTest ( "-(f1of1(1+2*3)+1*2)", -9, true ); + iStat += EqnTest ( "-(-f1of1(1+2*3)+1*2)", 5, true ); + iStat += EqnTest ( "-sin(8)", -0.989358, true ); + iStat += EqnTest ( "3-(-a)", 4, true ); + iStat += EqnTest ( "3--a", 4, true ); + iStat += EqnTest ( "-1*3", -3, true ); + + // Postfix / infix priorities + iStat += EqnTest ( "~2#", 8, true ); + iStat += EqnTest ( "~f1of1(2)#", 8, true ); + iStat += EqnTest ( "~(b)#", 8, true ); + iStat += EqnTest ( "(~b)#", 12, true ); + iStat += EqnTest ( "~(2#)", 8, true ); + iStat += EqnTest ( "~(f1of1(2)#)", 8, true ); + // + iStat += EqnTest ( "-2^2", -4, true ); + iStat += EqnTest ( "-(a+b)^2", -9, true ); + iStat += EqnTest ( "(-3)^2", 9, true ); + iStat += EqnTest ( "-(-2^2)", 4, true ); + iStat += EqnTest ( "3+-3^2", -6, true ); + // The following assumes use of sqr as postfix operator ("") together + // with a sign operator of low priority: + iStat += EqnTest ( "-2'", -4, true ); + iStat += EqnTest ( "-(1+1)'", -4, true ); + iStat += EqnTest ( "2+-(1+1)'", -2, true ); + iStat += EqnTest ( "2+-2'", -2, true ); + // This is the classic behaviour of the infix sign operator (here: "$") which is + // now deprecated: + iStat += EqnTest ( "$2^2", 4, true ); + iStat += EqnTest ( "$(a+b)^2", 9, true ); + iStat += EqnTest ( "($3)^2", 9, true ); + iStat += EqnTest ( "$($2^2)", -4, true ); + iStat += EqnTest ( "3+$3^2", 12, true ); + + // infix operators sharing the first few characters + iStat += EqnTest ( "~ 123", 123 + 2, true ); + iStat += EqnTest ( "~~ 123", 123 + 2, true ); + + if ( iStat == 0 ) + { + qDebug() << "passed"; + } + else + { + qDebug() << "\n failed with " << iStat << " errors"; + } + + return iStat; +} + + +//---------------------------------------------------------------------------------------------------------------------- +int QmuParserTester::TestPostFix() +{ + int iStat = 0; + qDebug() << "testing postfix operators..."; + + // application + iStat += EqnTest ( "3{m}+5", 5.003, true ); + iStat += EqnTest ( "1000{m}", 1, true ); + iStat += EqnTest ( "1000 {m}", 1, true ); + iStat += EqnTest ( "(a){m}", 1e-3, true ); + iStat += EqnTest ( "a{m}", 1e-3, true ); + iStat += EqnTest ( "a {m}", 1e-3, true ); + iStat += EqnTest ( "-(a){m}", -1e-3, true ); + iStat += EqnTest ( "-2{m}", -2e-3, true ); + iStat += EqnTest ( "-2 {m}", -2e-3, true ); + iStat += EqnTest ( "f1of1(1000){m}", 1, true ); + iStat += EqnTest ( "-f1of1(1000){m}", -1, true ); + iStat += EqnTest ( "-f1of1(-1000){m}", 1, true ); + iStat += EqnTest ( "f4of4(0,0,0,1000){m}", 1, true ); + iStat += EqnTest ( "2+(a*1000){m}", 3, true ); + + // can postfix operators "m" und "meg" be told apart properly? + iStat += EqnTest ( "2*3000meg+2", 2 * 3e9 + 2, true ); + + // some incorrect results + iStat += EqnTest ( "1000{m}", 0.1, false ); + iStat += EqnTest ( "(a){m}", 2, false ); + // failure due to syntax checking + iStat += ThrowTest ( "0x", ecUNASSIGNABLE_TOKEN ); // incomplete hex definition + iStat += ThrowTest ( "3+", ecUNEXPECTED_EOF ); + iStat += ThrowTest ( "4 + {m}", ecUNASSIGNABLE_TOKEN ); + iStat += ThrowTest ( "{m}4", ecUNASSIGNABLE_TOKEN ); + iStat += ThrowTest ( "sin({m})", ecUNASSIGNABLE_TOKEN ); + iStat += ThrowTest ( "{m} {m}", ecUNASSIGNABLE_TOKEN ); + iStat += ThrowTest ( "{m}(8)", ecUNASSIGNABLE_TOKEN ); + iStat += ThrowTest ( "4,{m}", ecUNASSIGNABLE_TOKEN ); + iStat += ThrowTest ( "-{m}", ecUNASSIGNABLE_TOKEN ); + iStat += ThrowTest ( "2(-{m})", ecUNEXPECTED_PARENS ); + iStat += ThrowTest ( "2({m})", ecUNEXPECTED_PARENS ); + + iStat += ThrowTest ( "multi*1.0", ecUNASSIGNABLE_TOKEN ); + + if ( iStat == 0 ) + { + qDebug() << "passed"; + } + else + { + qDebug() << "\n failed with " << iStat << " errors"; + } + + return iStat; +} + +//---------------------------------------------------------------------------------------------------------------------- +int QmuParserTester::TestExpression() +{ + int iStat = 0; + qDebug() << "testing expression samples..."; + + qreal b = 2; + + // Optimization + iStat += EqnTest ( "2*b*5", 20, true ); + iStat += EqnTest ( "2*b*5 + 4*b", 28, true ); + iStat += EqnTest ( "2*a/3", 2.0 / 3.0, true ); + + // Addition auf cmVARMUL + iStat += EqnTest ( "3+b", b + 3, true ); + iStat += EqnTest ( "b+3", b + 3, true ); + iStat += EqnTest ( "b*3+2", b * 3 + 2, true ); + iStat += EqnTest ( "3*b+2", b * 3 + 2, true ); + iStat += EqnTest ( "2+b*3", b * 3 + 2, true ); + iStat += EqnTest ( "2+3*b", b * 3 + 2, true ); + iStat += EqnTest ( "b+3*b", b + 3 * b, true ); + iStat += EqnTest ( "3*b+b", b + 3 * b, true ); + + iStat += EqnTest ( "2+b*3+b", 2 + b * 3 + b, true ); + iStat += EqnTest ( "b+2+b*3", b + 2 + b * 3, true ); + + iStat += EqnTest ( "(2*b+1)*4", ( 2 * b + 1 ) * 4, true ); + iStat += EqnTest ( "4*(2*b+1)", ( 2 * b + 1 ) * 4, true ); + + // operator precedencs + iStat += EqnTest ( "1+2-3*4/5^6", 2.99923, true ); + iStat += EqnTest ( "1^2/3*4-5+6", 2.33333333, true ); + iStat += EqnTest ( "1+2*3", 7, true ); + iStat += EqnTest ( "1+2*3", 7, true ); + iStat += EqnTest ( "(1+2)*3", 9, true ); + iStat += EqnTest ( "(1+2)*(-3)", -9, true ); + iStat += EqnTest ( "2/4", 0.5, true ); + + iStat += EqnTest ( "exp(ln(7))", 7, true ); + iStat += EqnTest ( "e^ln(7)", 7, true ); + iStat += EqnTest ( "e^(ln(7))", 7, true ); + iStat += EqnTest ( "(e^(ln(7)))", 7, true ); + iStat += EqnTest ( "1-(e^(ln(7)))", -6, true ); + iStat += EqnTest ( "2*(e^(ln(7)))", 14, true ); + iStat += EqnTest ( "10^log(5)", 5, true ); + iStat += EqnTest ( "10^log10(5)", 5, true ); + iStat += EqnTest ( "2^log2(4)", 4, true ); + iStat += EqnTest ( "-(sin(0)+1)", -1, true ); + iStat += EqnTest ( "-(2^1.1)", -2.14354692, true ); + + iStat += EqnTest ( "(cos(2.41)/b)", -0.372056, true ); + iStat += EqnTest ( "(1*(2*(3*(4*(5*(6*(a+b)))))))", 2160, true ); + iStat += EqnTest ( "(1*(2*(3*(4*(5*(6*(7*(a+b))))))))", 15120, true ); + iStat += EqnTest ( "(a/((((b+(((e*(((((pi*((((3.45*((pi+a)+pi))+b)+b)*a))+0.68)+e)+a)/a))+a)+b))+b)*a)-pi))", + 0.00377999, true ); + + // long formula (Reference: Matlab) + iStat += EqnTest ( + "(((-9))-e/(((((((pi-(((-7)+(-3)/4/e))))/(((-5))-2)-((pi+(-0))*(sqrt((e+e))*(-8))*(((-pi)+(-pi)-(-9)*(6*5))" + "/(-e)-e))/2)/((((sqrt(2/(-e)+6)-(4-2))+((5/(-2))/(1*(-pi)+3))/8)*pi*((pi/((-2)/(-6)*1*(-1))*(-6)+(-e)))))/" + "((e+(-2)+(-e)*((((-3)*9+(-e)))+(-9)))))))-((((e-7+(((5/pi-(3/1+pi)))))/e)/(-5))/(sqrt((((((1+(-7))))+((((-" + "e)*(-e)))-8))*(-5)/((-e)))*(-6)-((((((-2)-(-9)-(-e)-1)/3))))/(sqrt((8+(e-((-6))+(9*(-9))))*(((3+2-8))*(7+6" + "+(-5))+((0/(-e)*(-pi))+7)))+(((((-e)/e/e)+((-6)*5)*e+(3+(-5)/pi))))+pi))/sqrt((((9))+((((pi))-8+2))+pi))/e" + "*4)*((-5)/(((-pi))*(sqrt(e)))))-(((((((-e)*(e)-pi))/4+(pi)*(-9)))))))+(-pi)", -12.23016549, true ); + + // long formula (Reference: Matlab) + iStat += EqnTest ( + "(atan(sin((((((((((((((((pi/cos((a/((((0.53-b)-pi)*e)/b))))+2.51)+a)-0.54)/0.98)+b)*b)+e)/a)+b)+a)+b)+pi)/e" + ")+a)))*2.77)", -2.16995656, true ); + + // long formula (Reference: Matlab) + iStat += EqnTest ( "1+2-3*4/5^6*(2*(1-5+(3*7^9)*(4+6*7-3)))+12", -7995810.09926, true ); + + if ( iStat == 0 ) + { + qDebug() << "passed"; + } + else + { + qDebug() << "\n failed with " << iStat << " errors"; + } + + return iStat; +} + + + +//---------------------------------------------------------------------------------------------------------------------- +int QmuParserTester::TestIfThenElse() +{ + int iStat = 0; + qDebug() << "testing if-then-else operator..."; + + // Test error detection + iStat += ThrowTest ( ":3", ecUNEXPECTED_CONDITIONAL ); + iStat += ThrowTest ( "? 1 : 2", ecUNEXPECTED_CONDITIONAL ); + iStat += ThrowTest ( "(ab) ? 10 : 11", 11, true ); + iStat += EqnTest ( "(ab) ? c : d", -2, true ); + + iStat += EqnTest ( "(a>b) ? 1 : 0", 0, true ); + iStat += EqnTest ( "((a>b) ? 1 : 0) ? 1 : 2", 2, true ); + iStat += EqnTest ( "((a>b) ? 1 : 0) ? 1 : sum((a>b) ? 1 : 2)", 2, true ); + iStat += EqnTest ( "((a>b) ? 0 : 1) ? 1 : sum((a>b) ? 1 : 2)", 1, true ); + + iStat += EqnTest ( "sum((a>b) ? 1 : 2)", 2, true ); + iStat += EqnTest ( "sum((1) ? 1 : 2)", 1, true ); + iStat += EqnTest ( "sum((a>b) ? 1 : 2, 100)", 102, true ); + iStat += EqnTest ( "sum((1) ? 1 : 2, 100)", 101, true ); + iStat += EqnTest ( "sum(3, (a>b) ? 3 : 10)", 13, true ); + iStat += EqnTest ( "sum(3, (ab) ? 3 : 10)", 130, true ); + iStat += EqnTest ( "10*sum(3, (ab) ? 3 : 10)*10", 130, true ); + iStat += EqnTest ( "sum(3, (ab) ? sum(3, (ab) ? sum(3, (ab) ? sum(3, (ab)&&(a2)&&(1<2) ? 128 : 255", 255, true ); + iStat += EqnTest ( "((1<2)&&(1<2)) ? 128 : 255", 128, true ); + iStat += EqnTest ( "((1>2)&&(1<2)) ? 128 : 255", 255, true ); + iStat += EqnTest ( "((ab)&&(a0 ? 1>2 ? 128 : 255 : 1>0 ? 32 : 64", 255, true ); + iStat += EqnTest ( "1>0 ? 1>2 ? 128 : 255 :(1>0 ? 32 : 64)", 255, true ); + iStat += EqnTest ( "1>0 ? 1>0 ? 128 : 255 : 1>2 ? 32 : 64", 128, true ); + iStat += EqnTest ( "1>0 ? 1>0 ? 128 : 255 :(1>2 ? 32 : 64)", 128, true ); + iStat += EqnTest ( "1>2 ? 1>2 ? 128 : 255 : 1>0 ? 32 : 64", 32, true ); + iStat += EqnTest ( "1>2 ? 1>0 ? 128 : 255 : 1>2 ? 32 : 64", 64, true ); + iStat += EqnTest ( "1>0 ? 50 : 1>0 ? 128 : 255", 50, true ); + iStat += EqnTest ( "1>0 ? 50 : (1>0 ? 128 : 255)", 50, true ); + iStat += EqnTest ( "1>0 ? 1>0 ? 128 : 255 : 50", 128, true ); + iStat += EqnTest ( "1>2 ? 1>2 ? 128 : 255 : 1>0 ? 32 : 1>2 ? 64 : 16", 32, true ); + iStat += EqnTest ( "1>2 ? 1>2 ? 128 : 255 : 1>0 ? 32 :(1>2 ? 64 : 16)", 32, true ); + iStat += EqnTest ( "1>0 ? 1>2 ? 128 : 255 : 1>0 ? 32 :1>2 ? 64 : 16", 255, true ); + iStat += EqnTest ( "1>0 ? 1>2 ? 128 : 255 : (1>0 ? 32 :1>2 ? 64 : 16)", 255, true ); + iStat += EqnTest ( "1 ? 0 ? 128 : 255 : 1 ? 32 : 64", 255, true ); + + // assignment operators + iStat += EqnTest ( "a= 0 ? 128 : 255, a", 255, true ); + iStat += EqnTest ( "a=((a>b)&&(a + // this is now legal, for reference see: + // https://sourceforge.net/forum/message.php?msg_id=7411373 + // iStat += ThrowTest( "sin=9"), ecUNEXPECTED_OPERATOR); + //
    + + iStat += ThrowTest ( "(8)=5", ecUNEXPECTED_OPERATOR ); + iStat += ThrowTest ( "(a)=5", ecUNEXPECTED_OPERATOR ); + iStat += ThrowTest ( "a=\"tttt\"", ecOPRT_TYPE_CONFLICT ); + + if ( iStat == 0 ) + { + qDebug() << "passed" ; + } + else + { + qDebug() << "\n failed with " << iStat << " errors" ; + } + + return iStat; +} + + +//---------------------------------------------------------------------------------------------------------------------- +void QmuParserTester::AddTest ( testfun_type a_pFun ) +{ + m_vTestFun.push_back ( a_pFun ); +} + +//---------------------------------------------------------------------------------------------------------------------- +void QmuParserTester::Run() +{ + int iStat = 0; + try + { + for ( int i = 0; i < ( int ) m_vTestFun.size(); ++i ) + { + iStat += ( this->*m_vTestFun[i] ) (); + } + } + catch ( QmuParser::exception_type &e ) + { + qDebug() << "\n" << e.GetMsg() ; + qDebug() << e.GetToken() ; + Abort(); + } + catch ( std::exception &e ) + { + qDebug() << e.what() ; + Abort(); + } + catch ( ... ) + { + qDebug() << "Internal error"; + Abort(); + } + + if ( iStat == 0 ) + { + qDebug() << "Test passed (" << QmuParserTester::c_iCount << " expressions)" ; + } + else + { + qDebug() << "Test failed with " << iStat + << " errors (" << QmuParserTester::c_iCount + << " expressions)" ; + } + QmuParserTester::c_iCount = 0; +} + + +//---------------------------------------------------------------------------------------------------------------------- +int QmuParserTester::ThrowTest ( const QString &a_str, int a_iErrc, bool a_bFail ) +{ + QmuParserTester::c_iCount++; + + try + { + qreal fVal[] = {1, 1, 1}; + QmuParser p; + + p.DefineVar ( "a", &fVal[0] ); + p.DefineVar ( "b", &fVal[1] ); + p.DefineVar ( "c", &fVal[2] ); + p.DefinePostfixOprt ( "{m}", Milli ); + p.DefinePostfixOprt ( "m", Milli ); + p.DefineFun ( "ping", Ping ); + p.DefineFun ( "valueof", ValueOf ); + p.DefineFun ( "strfun1", StrFun1 ); + p.DefineFun ( "strfun2", StrFun2 ); + p.DefineFun ( "strfun3", StrFun3 ); + p.SetExpr ( a_str ); + p.Eval(); + } + catch ( QmuParserError &e ) + { + // output the formula in case of an failed test + if ( a_bFail == false || ( a_bFail == true && a_iErrc != e.GetCode() ) ) + { + qDebug() << "\n " + << "Expression: " << a_str + << " Code:" << e.GetCode() << "(" << e.GetMsg() << ")" + << " Expected:" << a_iErrc; + } + + return ( a_iErrc == e.GetCode() ) ? 0 : 1; + } + + // if a_bFail==false no exception is expected + bool bRet ( ( a_bFail == false ) ? 0 : 1 ); + if ( bRet == 1 ) + { + qDebug() << "\n " + << "Expression: " << a_str + << " did evaluate; Expected error:" << a_iErrc; + } + + return bRet; +} + +//---------------------------------------------------------------------------------------------------------------------- +/** + * @brief Evaluate a tet expression. + * + * @return 1 in case of a failure, 0 otherwise. + */ +int QmuParserTester::EqnTestWithVarChange ( const QString &a_str, double a_fVar1, double a_fRes1, double a_fVar2, + double a_fRes2 ) +{ + QmuParserTester::c_iCount++; + qreal fVal[2] = { -999, -999 }; // should be equalinitially + + try + { + QmuParser p; + + // variable + qreal var = 0; + p.DefineVar ( "a", &var ); + p.SetExpr ( a_str ); + + var = a_fVar1; + fVal[0] = p.Eval(); + + var = a_fVar2; + fVal[1] = p.Eval(); + + if ( fabs ( a_fRes1 - fVal[0] ) > 0.0000000001 ) + { + throw std::runtime_error ( "incorrect result (first pass)" ); + } + + if ( fabs ( a_fRes2 - fVal[1] ) > 0.0000000001 ) + { + throw std::runtime_error ( "incorrect result (second pass)" ); + } + } + catch ( QmuParser::exception_type &e ) + { + qDebug() << "\n fail: " << a_str << " (" << e.GetMsg() << ")"; + return 1; + } + catch ( std::exception &e ) + { + qDebug() << "\n fail: " << a_str << " (" << e.what() << ")"; + return 1; // always return a failure since this exception is not expected + } + catch ( ... ) + { + qDebug() << "\n fail: " << a_str << " (unexpected exception)"; + return 1; // exceptions other than ParserException are not allowed + } + + return 0; +} + +//---------------------------------------------------------------------------------------------------------------------- +/** + * @brief Evaluate a tet expression. + * + * @return 1 in case of a failure, 0 otherwise. + */ +int QmuParserTester::EqnTest ( const QString &a_str, double a_fRes, bool a_fPass ) +{ + QmuParserTester::c_iCount++; + int iRet ( 0 ); + qreal fVal[5] = { -999, -998, -997, -996, -995}; // initially should be different + + try + { + std::auto_ptr p1; + QmuParser p2, p3; // three parser objects + // they will be used for testing copy and assihnment operators + // p1 is a pointer since i'm going to delete it in order to test if + // parsers after copy construction still refer to members of it. + // !! If this is the case this function will crash !! + + p1.reset ( new qmu::QmuParser() ); + // Add constants + p1->DefineConst ( "pi", ( qreal ) M_PI ); + p1->DefineConst ( "e", ( qreal ) M_E ); + p1->DefineConst ( "const", 1 ); + p1->DefineConst ( "const1", 2 ); + p1->DefineConst ( "const2", 3 ); + // variables + qreal vVarVal[] = { 1, 2, 3, -2}; + p1->DefineVar ( "a", &vVarVal[0] ); + p1->DefineVar ( "aa", &vVarVal[1] ); + p1->DefineVar ( "b", &vVarVal[1] ); + p1->DefineVar ( "c", &vVarVal[2] ); + p1->DefineVar ( "d", &vVarVal[3] ); + + // custom value ident functions + p1->AddValIdent ( &QmuParserTester::IsHexVal ); + + // functions + p1->DefineFun ( "ping", Ping ); + p1->DefineFun ( "f1of1", f1of1 ); // one parameter + p1->DefineFun ( "f1of2", f1of2 ); // two parameter + p1->DefineFun ( "f2of2", f2of2 ); + p1->DefineFun ( "f1of3", f1of3 ); // three parameter + p1->DefineFun ( "f2of3", f2of3 ); + p1->DefineFun ( "f3of3", f3of3 ); + p1->DefineFun ( "f1of4", f1of4 ); // four parameter + p1->DefineFun ( "f2of4", f2of4 ); + p1->DefineFun ( "f3of4", f3of4 ); + p1->DefineFun ( "f4of4", f4of4 ); + p1->DefineFun ( "f1of5", f1of5 ); // five parameter + p1->DefineFun ( "f2of5", f2of5 ); + p1->DefineFun ( "f3of5", f3of5 ); + p1->DefineFun ( "f4of5", f4of5 ); + p1->DefineFun ( "f5of5", f5of5 ); + + // binary operators + p1->DefineOprt ( "add", add, 0 ); + p1->DefineOprt ( "++", add, 0 ); + p1->DefineOprt ( "&", land, prLAND ); + + // sample functions + p1->DefineFun ( "min", Min ); + p1->DefineFun ( "max", Max ); + p1->DefineFun ( "sum", Sum ); + p1->DefineFun ( "valueof", ValueOf ); + p1->DefineFun ( "atof", StrToFloat ); + p1->DefineFun ( "strfun1", StrFun1 ); + p1->DefineFun ( "strfun2", StrFun2 ); + p1->DefineFun ( "strfun3", StrFun3 ); + p1->DefineFun ( "lastArg", LastArg ); + p1->DefineFun ( "firstArg", FirstArg ); + p1->DefineFun ( "order", FirstArg ); + + // infix / postfix operator + // Note: Identifiers used here do not have any meaning + // they are mere placeholders to test certain features. + p1->DefineInfixOprt ( "$", sign, prPOW + 1 ); // sign with high priority + p1->DefineInfixOprt ( "~", plus2 ); // high priority + p1->DefineInfixOprt ( "~~", plus2 ); + p1->DefinePostfixOprt ( "{m}", Milli ); + p1->DefinePostfixOprt ( "{M}", Mega ); + p1->DefinePostfixOprt ( "m", Milli ); + p1->DefinePostfixOprt ( "meg", Mega ); + p1->DefinePostfixOprt ( "#", times3 ); + p1->DefinePostfixOprt ( "'", sqr ); + p1->SetExpr ( a_str ); + + // Test bytecode integrity + // String parsing and bytecode parsing must yield the same result + fVal[0] = p1->Eval(); // result from stringparsing + fVal[1] = p1->Eval(); // result from bytecode + if ( fVal[0] != fVal[1] ) + { + throw QmuParser::exception_type ( "Bytecode / string parsing mismatch." ); + } + + // Test copy and assignement operators + try + { + // Test copy constructor + QVector vParser; + vParser.push_back ( * ( p1.get() ) ); + qmu::QmuParser p2 = vParser[0]; // take parser from vector + + // destroy the originals from p2 + vParser.clear(); // delete the vector + p1.reset ( 0 ); + + fVal[2] = p2.Eval(); + + // Test assignement operator + // additionally disable Optimizer this time + qmu::QmuParser p3; + p3 = p2; + p3.EnableOptimizer ( false ); + fVal[3] = p3.Eval(); + + // Test Eval function for multiple return values + // use p2 since it has the optimizer enabled! + int nNum; + qreal *v = p2.Eval ( nNum ); + fVal[4] = v[nNum - 1]; + } + catch ( std::exception &e ) + { + qDebug() << "\n " << e.what() << "\n"; + } + + // limited floating point accuracy requires the following test + bool bCloseEnough ( true ); + for ( unsigned i = 0; i < sizeof ( fVal ) / sizeof ( qreal ); ++i ) + { + bCloseEnough &= ( fabs ( a_fRes - fVal[i] ) <= fabs ( fVal[i] * 0.00001 ) ); + + // The tests equations never result in infinity, if they do thats a bug. + // reference: + // http://sourceforge.net/projects/muparser/forums/forum/462843/topic/5037825 + if ( numeric_limits::has_infinity ) + { + bCloseEnough &= ( fabs ( fVal[i] ) != numeric_limits::infinity() ); + } + } + + iRet = ( ( bCloseEnough && a_fPass ) || ( !bCloseEnough && !a_fPass ) ) ? 0 : 1; + + + if ( iRet == 1 ) + { + qDebug() << "\n fail: " << a_str + << " (incorrect result; expected: " << a_fRes + << " ;calculated: " << fVal[0] << "," + << fVal[1] << "," + << fVal[2] << "," + << fVal[3] << "," + << fVal[4] << ")."; + } + } + catch ( QmuParser::exception_type &e ) + { + if ( a_fPass ) + { + if ( fVal[0] != fVal[2] && fVal[0] != -999 && fVal[1] != -998 ) + { + qDebug() << "\n fail: " << a_str << " (copy construction)"; + } + else + { + qDebug() << "\n fail: " << a_str << " (" << e.GetMsg() << ")"; + } + return 1; + } + } + catch ( std::exception &e ) + { + qDebug() << "\n fail: " << a_str << " (" << e.what() << ")"; + return 1; // always return a failure since this exception is not expected + } + catch ( ... ) + { + qDebug() << "\n fail: " << a_str << " (unexpected exception)"; + return 1; // exceptions other than ParserException are not allowed + } + + return iRet; +} + +//---------------------------------------------------------------------------------------------------------------------- +/** + * @brief Internal error in test class Test is going to be aborted. + */ +void QmuParserTester::Abort() const +{ + qDebug() << "Test failed (internal error in test class)" ; + while ( !getchar() ); + exit ( -1 ); +} +} // namespace test +} // namespace qmu diff --git a/src/libs/qmuparser/qmuparsertest.h b/src/libs/qmuparser/qmuparsertest.h index b92338b3f..561bc3d2c 100644 --- a/src/libs/qmuparser/qmuparsertest.h +++ b/src/libs/qmuparser/qmuparsertest.h @@ -1,200 +1,302 @@ -/*************************************************************************************************** - ** - ** Original work Copyright (C) 2013 Ingo Berg - ** Modified work Copyright 2014 Roman Telezhinsky - ** - ** 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. - ** - ******************************************************************************************************/ - -#ifndef QMUPARSERTEST_H -#define QMUPARSERTEST_H - -#include -#include -#include // for accumulate -#include "qmuparser.h" -#include - -/** \file - \brief This file contains the parser test class. -*/ - -namespace qmu -{ - /** \brief Namespace for test cases. */ - namespace Test - { - //------------------------------------------------------------------------------ - /** \brief Test cases for unit testing. - - (C) 2004-2011 Ingo Berg - */ - class QmuParserTester // final - { - private: - static int c_iCount; - - // Multiarg callbacks - static qreal f1of1(qreal v) { return v;} - - static qreal f1of2(qreal v, qreal ) {return v;} - static qreal f2of2(qreal , qreal v) {return v;} - - static qreal f1of3(qreal v, qreal , qreal ) {return v;} - static qreal f2of3(qreal , qreal v, qreal ) {return v;} - static qreal f3of3(qreal , qreal , qreal v) {return v;} - - static qreal f1of4(qreal v, qreal, qreal , qreal ) {return v;} - static qreal f2of4(qreal , qreal v, qreal , qreal ) {return v;} - static qreal f3of4(qreal , qreal, qreal v, qreal ) {return v;} - static qreal f4of4(qreal , qreal, qreal , qreal v) {return v;} - - static qreal f1of5(qreal v, qreal, qreal , qreal , qreal ) { return v; } - static qreal f2of5(qreal , qreal v, qreal , qreal , qreal ) { return v; } - static qreal f3of5(qreal , qreal, qreal v, qreal , qreal ) { return v; } - static qreal f4of5(qreal , qreal, qreal , qreal v, qreal ) { return v; } - static qreal f5of5(qreal , qreal, qreal , qreal , qreal v) { return v; } - - static qreal Min(qreal a_fVal1, qreal a_fVal2) { return (a_fVal1a_fVal2) ? a_fVal1 : a_fVal2; } - - static qreal plus2(qreal v1) { return v1+2; } - static qreal times3(qreal v1) { return v1*3; } - static qreal sqr(qreal v1) { return v1*v1; } - static qreal sign(qreal v) { return -v; } - static qreal add(qreal v1, qreal v2) { return v1+v2; } - static qreal land(qreal v1, qreal v2) { return (int)v1 & (int)v2; } - - - static qreal FirstArg(const qreal* a_afArg, int a_iArgc) - { - if (!a_iArgc) - throw qmu::QmuParser::exception_type( "too few arguments for function FirstArg." ); - - return a_afArg[0]; - } - - static qreal LastArg(const qreal* a_afArg, int a_iArgc) - { - if (!a_iArgc) - throw qmu::QmuParser::exception_type( "too few arguments for function LastArg." ); - - return a_afArg[a_iArgc-1]; - } - - static qreal Sum(const qreal* a_afArg, int a_iArgc) - { - if (!a_iArgc) - throw qmu::QmuParser::exception_type( "too few arguments for function sum." ); - - qreal fRes=0; - for (int i=0; i m_vTestFun; - void AddTest(testfun_type a_pFun); - - // Test Double Parser - int EqnTest(const QString &a_str, double a_fRes, bool a_fPass); - int EqnTestWithVarChange(const QString &a_str, - double a_fRes1, - double a_fVar1, - double a_fRes2, - double a_fVar2); - int ThrowTest(const QString &a_str, int a_iErrc, bool a_bFail = true); - }; - } // namespace Test -} // namespace qmu - -#endif - - +/*************************************************************************************************** + ** + ** Original work Copyright (C) 2013 Ingo Berg + ** Modified work Copyright 2014 Roman Telezhinsky + ** + ** 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. + ** + ******************************************************************************************************/ + +#ifndef QMUPARSERTEST_H +#define QMUPARSERTEST_H + +#include +#include +#include // for accumulate +#include "qmuparser.h" +#include + +/** + * @file + * @brief This file contains the parser test class. + */ + +namespace qmu +{ +/** + * @brief Namespace for test cases. + */ +namespace Test +{ +//---------------------------------------------------------------------------------------------------------------------- +/** + * @brief Test cases for unit testing. + * + * (C) 2004-2011 Ingo Berg + */ +class QmuParserTester // final +{ +public: + typedef int ( QmuParserTester::*testfun_type ) (); + + QmuParserTester(); + void Run(); +private: + QVector m_vTestFun; + static int c_iCount; + + void AddTest ( testfun_type a_pFun ); + + // Test Double Parser + int EqnTest ( const QString &a_str, double a_fRes, bool a_fPass ); + int EqnTestWithVarChange ( const QString &a_str, double a_fRes1, double a_fVar1, double a_fRes2, double a_fVar2 ); + int ThrowTest ( const QString &a_str, int a_iErrc, bool a_bFail = true ); + + // Multiarg callbacks + static qreal f1of1 ( qreal v ) + { + return v; + } + + static qreal f1of2 ( qreal v, qreal ) + { + return v; + } + + static qreal f2of2 ( qreal , qreal v ) + { + return v; + } + + static qreal f1of3 ( qreal v, qreal , qreal ) + { + return v; + } + + static qreal f2of3 ( qreal , qreal v, qreal ) + { + return v; + } + + static qreal f3of3 ( qreal , qreal , qreal v ) + { + return v; + } + + static qreal f1of4 ( qreal v, qreal, qreal , qreal ) + { + return v; + } + + static qreal f2of4 ( qreal , qreal v, qreal , qreal ) + { + return v; + } + + static qreal f3of4 ( qreal , qreal, qreal v, qreal ) + { + return v; + } + + static qreal f4of4 ( qreal , qreal, qreal , qreal v ) + { + return v; + } + + static qreal f1of5 ( qreal v, qreal, qreal , qreal , qreal ) + { + return v; + } + + static qreal f2of5 ( qreal , qreal v, qreal , qreal , qreal ) + { + return v; + } + + static qreal f3of5 ( qreal , qreal, qreal v, qreal , qreal ) + { + return v; + } + + static qreal f4of5 ( qreal , qreal, qreal , qreal v, qreal ) + { + return v; + } + + static qreal f5of5 ( qreal , qreal, qreal , qreal , qreal v ) + { + return v; + } + + static qreal Min ( qreal a_fVal1, qreal a_fVal2 ) + { + return ( a_fVal1 < a_fVal2 ) ? a_fVal1 : a_fVal2; + } + + static qreal Max ( qreal a_fVal1, qreal a_fVal2 ) + { + return ( a_fVal1 > a_fVal2 ) ? a_fVal1 : a_fVal2; + } + + static qreal plus2 ( qreal v1 ) + { + return v1 + 2; + } + + static qreal times3 ( qreal v1 ) + { + return v1 * 3; + } + + static qreal sqr ( qreal v1 ) + { + return v1 * v1; + } + + static qreal sign ( qreal v ) + { + return -v; + } + + static qreal add ( qreal v1, qreal v2 ) + { + return v1 + v2; + } + + static qreal land ( qreal v1, qreal v2 ) + { + return ( int ) v1 & ( int ) v2; + } + + static qreal FirstArg ( const qreal* a_afArg, int a_iArgc ) + { + if ( !a_iArgc ) + { + throw qmu::QmuParser::exception_type ( "too few arguments for function FirstArg." ); + } + + return a_afArg[0]; + } + + static qreal LastArg ( const qreal* a_afArg, int a_iArgc ) + { + if ( !a_iArgc ) + { + throw qmu::QmuParser::exception_type ( "too few arguments for function LastArg." ); + } + + return a_afArg[a_iArgc - 1]; + } + + static qreal Sum ( const qreal* a_afArg, int a_iArgc ) + { + if ( !a_iArgc ) + { + throw qmu::QmuParser::exception_type ( "too few arguments for function sum." ); + } + + qreal fRes = 0; + for ( int i = 0; i < a_iArgc; ++i ) + { + fRes += a_afArg[i]; + } + return fRes; + } + + static qreal Rnd ( qreal v ) + { + return ( qreal ) ( 1 + ( v * std::rand() / ( RAND_MAX + 1.0 ) ) ); + } + + static qreal RndWithString ( const char_type* ) + { + return ( qreal ) ( 1 + ( 1000.0f * std::rand() / ( RAND_MAX + 1.0 ) ) ); + } + + static qreal Ping() + { + return 10; + } + + static qreal ValueOf ( const QString & ) + { + return 123; + } + + static qreal StrFun1 ( const QString & v1 ) + { + int val = v1.toInt(); + return ( qreal ) val; + } + + static qreal StrFun2 ( const QString & v1, qreal v2 ) + { + int val = v1.toInt(); + return ( qreal ) ( val + v2 ); + } + + static qreal StrFun3 ( const QString & v1, qreal v2, qreal v3 ) + { + int val = v1.toInt(); + return val + v2 + v3; + } + + static qreal StrToFloat ( const QString & a_szMsg ) + { + qreal val = a_szMsg.toDouble(); + return val; + } + + // postfix operator callback + static qreal Mega ( qreal a_fVal ) + { + return a_fVal * ( qreal ) 1e6; + } + + static qreal Micro ( qreal a_fVal ) + { + return a_fVal * ( qreal ) 1e-6; + } + + static qreal Milli ( qreal a_fVal ) + { + return a_fVal / ( qreal ) 1e3; + } + + // Custom value recognition + static int IsHexVal ( const QString &a_szExpr, int *a_iPos, qreal *a_fVal ); + + int TestNames(); + int TestSyntax(); + int TestMultiArg(); + int TestPostFix(); + int TestExpression(); + int TestInfixOprt(); + int TestBinOprt(); + int TestVarConst(); + int TestInterface(); + int TestException(); + int TestStrArg(); + int TestIfThenElse(); + + void Abort() const; +}; +} // namespace Test +} // namespace qmu + +#endif + + diff --git a/src/libs/qmuparser/qmuparsertoken.h b/src/libs/qmuparser/qmuparsertoken.h index 20e2b51bc..7253e234c 100644 --- a/src/libs/qmuparser/qmuparsertoken.h +++ b/src/libs/qmuparser/qmuparsertoken.h @@ -32,13 +32,13 @@ #include "qmuparsererror.h" #include "qmuparsercallback.h" -/** \file - \brief This file contains the parser token definition. +/** @file + @brief This file contains the parser token definition. */ namespace qmu { - /** \brief Encapsulation of the data for a single formula token. + /** @brief Encapsulation of the data for a single formula token. Formula token implementation. Part of the Math Parser Package. Formula tokens can be either one of the following: @@ -52,7 +52,7 @@ namespace qmu
  • binary operator
  • - \author (C) 2004-2013 Ingo Berg + @author (C) 2004-2013 Ingo Berg */ template class QmuParserToken @@ -71,11 +71,11 @@ namespace qmu public: //--------------------------------------------------------------------------- - /** \brief Constructor (default). + /** @brief Constructor (default). Sets token to an neutral state of type cmUNKNOWN. - \throw nothrow - \sa ECmdCode + @throw nothrow + @sa ECmdCode */ QmuParserToken() :m_iCode(cmUNKNOWN) @@ -87,12 +87,12 @@ namespace qmu {} //------------------------------------------------------------------------------ - /** \brief Create token from another one. + /** @brief Create token from another one. Implemented by calling Assign(...) - \throw nothrow - \post m_iType==cmUNKNOWN - \sa #Assign + @throw nothrow + @post m_iType==cmUNKNOWN + @sa #Assign */ QmuParserToken(const QmuParserToken &a_Tok) { @@ -100,11 +100,11 @@ namespace qmu } //------------------------------------------------------------------------------ - /** \brief Assignement operator. + /** @brief Assignement operator. Copy token state from another token and return this. Implemented by calling Assign(...). - \throw nothrow + @throw nothrow */ QmuParserToken& operator=(const QmuParserToken &a_Tok) { @@ -113,9 +113,9 @@ namespace qmu } //------------------------------------------------------------------------------ - /** \brief Copy token information from argument. + /** @brief Copy token information from argument. - \throw nothrow + @throw nothrow */ void Assign(const QmuParserToken &a_Tok) { @@ -131,15 +131,15 @@ namespace qmu } //------------------------------------------------------------------------------ - /** \brief Assign a token type. + /** @brief Assign a token type. Token may not be of type value, variable or function. Those have seperate set functions. \pre [assert] a_iType!=cmVAR \pre [assert] a_iType!=cmVAL \pre [assert] a_iType!=cmFUNC - \post m_fVal = 0 - \post m_pTok = 0 + @post m_fVal = 0 + @post m_pTok = 0 */ QmuParserToken& Set(ECmdCode a_iType, const TString &a_strTok=TString()) { @@ -158,7 +158,7 @@ namespace qmu } //------------------------------------------------------------------------------ - /** \brief Set Callback type. */ + /** @brief Set Callback type. */ QmuParserToken& Set(const QmuParserCallback &a_pCallback, const TString &a_sTok) { assert(a_pCallback.GetAddr()); @@ -175,10 +175,10 @@ namespace qmu } //------------------------------------------------------------------------------ - /** \brief Make this token a value token. + /** @brief Make this token a value token. Member variables not necessary for value tokens will be invalidated. - \throw nothrow + @throw nothrow */ QmuParserToken& SetVal(TBase a_fVal, const TString &a_strTok=TString()) { @@ -195,10 +195,10 @@ namespace qmu } //------------------------------------------------------------------------------ - /** \brief make this token a variable token. + /** @brief make this token a variable token. Member variables not necessary for variable tokens will be invalidated. - \throw nothrow + @throw nothrow */ QmuParserToken& SetVar(TBase *a_pVar, const TString &a_strTok) { @@ -212,10 +212,10 @@ namespace qmu } //------------------------------------------------------------------------------ - /** \brief Make this token a variable token. + /** @brief Make this token a variable token. Member variables not necessary for variable tokens will be invalidated. - \throw nothrow + @throw nothrow */ QmuParserToken& SetString(const TString &a_strTok, std::size_t a_iSize) { @@ -230,11 +230,11 @@ namespace qmu } //------------------------------------------------------------------------------ - /** \brief Set an index associated with the token related data. + /** @brief Set an index associated with the token related data. In cmSTRFUNC - This is the index to a string table in the main parser. - \param a_iIdx The index the string function result will take in the bytecode parser. - \throw exception_type if #a_iIdx<0 or #m_iType!=cmSTRING + * @param a_iIdx The index the string function result will take in the bytecode parser. + @throw exception_type if #a_iIdx<0 or #m_iType!=cmSTRING */ void SetIdx(int a_iIdx) { @@ -245,12 +245,12 @@ namespace qmu } //------------------------------------------------------------------------------ - /** \brief Return Index associated with the token related data. + /** @brief Return Index associated with the token related data. In cmSTRFUNC - This is the index to a string table in the main parser. - \throw exception_type if #m_iIdx<0 or #m_iType!=cmSTRING - \return The index the result will take in the Bytecode calculatin array (#m_iIdx). + @throw exception_type if #m_iIdx<0 or #m_iType!=cmSTRING + * @return The index the result will take in the Bytecode calculatin array (#m_iIdx). */ int GetIdx() const { @@ -261,10 +261,10 @@ namespace qmu } //------------------------------------------------------------------------------ - /** \brief Return the token type. + /** @brief Return the token type. - \return #m_iType - \throw nothrow + * @return #m_iType + @throw nothrow */ ECmdCode GetCode() const { @@ -313,11 +313,11 @@ namespace qmu } //------------------------------------------------------------------------------ - /** \brief Return the address of the callback function assoziated with + /** @brief Return the address of the callback function assoziated with function and operator tokens. - \return The pointer stored in #m_pTok. - \throw exception_type if token type is non of: + * @return The pointer stored in #m_pTok. + @throw exception_type if token type is non of:
    • cmFUNC
    • cmSTRFUNC
    • @@ -325,7 +325,7 @@ namespace qmu
    • cmINFIXOP
    • cmOPRT_BIN
    - \sa ECmdCode + @sa ECmdCode */ generic_fun_type GetFuncAddr() const { @@ -336,7 +336,7 @@ namespace qmu /** \biref Get value of the token. Only applicable to variable and value tokens. - \throw exception_type if token is no value/variable token. + @throw exception_type if token is no value/variable token. */ TBase GetVal() const { @@ -349,10 +349,10 @@ namespace qmu } //------------------------------------------------------------------------------ - /** \brief Get address of a variable token. + /** @brief Get address of a variable token. Valid only if m_iType==CmdVar. - \throw exception_type if token is no variable token. + @throw exception_type if token is no variable token. */ TBase* GetVar() const { @@ -363,7 +363,7 @@ namespace qmu } //------------------------------------------------------------------------------ - /** \brief Return the number of function arguments. + /** @brief Return the number of function arguments. Valid only if m_iType==CmdFUNC. */ @@ -378,13 +378,13 @@ namespace qmu } //------------------------------------------------------------------------------ - /** \brief Return the token identifier. + /** @brief Return the token identifier. If #m_iType is cmSTRING the token identifier is the value of the string argument for a string function. - \return #m_strTok - \throw nothrow - \sa m_strTok + * @return #m_strTok + @throw nothrow + @sa m_strTok */ const TString& GetAsString() const { diff --git a/src/libs/qmuparser/qmuparsertokenreader.cpp b/src/libs/qmuparser/qmuparsertokenreader.cpp index f21e80bf8..c47d33851 100644 --- a/src/libs/qmuparser/qmuparsertokenreader.cpp +++ b/src/libs/qmuparser/qmuparsertokenreader.cpp @@ -1,995 +1,997 @@ -/*************************************************************************************************** - ** - ** Original work Copyright (C) 2013 Ingo Berg - ** Modified work Copyright 2014 Roman Telezhinsky - ** - ** 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 -#include -#include -#include -#include -#include - -#include - -#include "qmuparsertokenreader.h" -#include "qmuparserbase.h" - -/** \file - \brief This file contains the parser token reader implementation. -*/ - - -namespace qmu -{ - - // Forward declaration - class QmuParserBase; - - //--------------------------------------------------------------------------- - /** \brief Copy constructor. - - \sa Assign - \throw nothrow - */ - QmuParserTokenReader::QmuParserTokenReader(const QmuParserTokenReader &a_Reader) - { - Assign(a_Reader); - } - - //--------------------------------------------------------------------------- - /** \brief Assignement operator. - - Self assignement will be suppressed otherwise #Assign is called. - - \param a_Reader Object to copy to this token reader. - \throw nothrow - */ - QmuParserTokenReader& QmuParserTokenReader::operator=(const QmuParserTokenReader &a_Reader) - { - if (&a_Reader!=this) - Assign(a_Reader); - - return *this; - } - - //--------------------------------------------------------------------------- - /** \brief Assign state of a token reader to this token reader. - - \param a_Reader Object from which the state should be copied. - \throw nothrow - */ - void QmuParserTokenReader::Assign(const QmuParserTokenReader &a_Reader) - { - m_pParser = a_Reader.m_pParser; - m_strFormula = a_Reader.m_strFormula; - m_iPos = a_Reader.m_iPos; - m_iSynFlags = a_Reader.m_iSynFlags; - - m_UsedVar = a_Reader.m_UsedVar; - m_pFunDef = a_Reader.m_pFunDef; - m_pConstDef = a_Reader.m_pConstDef; - m_pVarDef = a_Reader.m_pVarDef; - m_pStrVarDef = a_Reader.m_pStrVarDef; - m_pPostOprtDef = a_Reader.m_pPostOprtDef; - m_pInfixOprtDef = a_Reader.m_pInfixOprtDef; - m_pOprtDef = a_Reader.m_pOprtDef; - m_bIgnoreUndefVar = a_Reader.m_bIgnoreUndefVar; - m_vIdentFun = a_Reader.m_vIdentFun; - m_pFactory = a_Reader.m_pFactory; - m_pFactoryData = a_Reader.m_pFactoryData; - m_iBrackets = a_Reader.m_iBrackets; - m_cArgSep = a_Reader.m_cArgSep; - } - - //--------------------------------------------------------------------------- - /** \brief Constructor. - - Create a Token reader and bind it to a parser object. - - \pre [assert] a_pParser may not be NULL - \post #m_pParser==a_pParser - \param a_pParent Parent parser object of the token reader. - */ - QmuParserTokenReader::QmuParserTokenReader(QmuParserBase *a_pParent) - :m_pParser(a_pParent) - ,m_strFormula() - ,m_iPos(0) - ,m_iSynFlags(0) - ,m_bIgnoreUndefVar(false) - ,m_pFunDef(NULL) - ,m_pPostOprtDef(NULL) - ,m_pInfixOprtDef(NULL) - ,m_pOprtDef(NULL) - ,m_pConstDef(NULL) - ,m_pStrVarDef(NULL) - ,m_pVarDef(NULL) - ,m_pFactory(NULL) - ,m_pFactoryData(NULL) - ,m_vIdentFun() - ,m_UsedVar() - ,m_fZero(0) - ,m_iBrackets(0) - ,m_lastTok() - ,m_cArgSep(',') - { - assert(m_pParser); - SetParent(m_pParser); - } - - //--------------------------------------------------------------------------- - /** \brief Create instance of a QParserTokenReader identical with this - and return its pointer. - - This is a factory method the calling function must take care of the object destruction. - - \return A new QParserTokenReader object. - \throw nothrow - */ - QmuParserTokenReader* QmuParserTokenReader::Clone(QmuParserBase *a_pParent) const - { - std::auto_ptr ptr(new QmuParserTokenReader(*this)); - ptr->SetParent(a_pParent); - return ptr.release(); - } - - //--------------------------------------------------------------------------- - QmuParserTokenReader::token_type& QmuParserTokenReader::SaveBeforeReturn(const token_type &tok) - { - m_lastTok = tok; - return m_lastTok; - } - - //--------------------------------------------------------------------------- - void QmuParserTokenReader::AddValIdent(identfun_type a_pCallback) - { - // Use push_front is used to give user defined callbacks a higher priority than - // the built in ones. Otherwise reading hex numbers would not work - // since the "0" in "0xff" would always be read first making parsing of - // the rest impossible. - // reference: - // http://sourceforge.net/projects/muparser/forums/forum/462843/topic/4824956 - m_vIdentFun.push_front(a_pCallback); - } - - //--------------------------------------------------------------------------- - void QmuParserTokenReader::SetVarCreator(facfun_type a_pFactory, void *pUserData) - { - m_pFactory = a_pFactory; - m_pFactoryData = pUserData; - } - - //--------------------------------------------------------------------------- - /** \brief Return the current position of the token reader in the formula string. - - \return #m_iPos - \throw nothrow - */ - int QmuParserTokenReader::GetPos() const - { - return m_iPos; - } - - //--------------------------------------------------------------------------- - /** \brief Return a reference to the formula. - - \return #m_strFormula - \throw nothrow - */ - const QString& QmuParserTokenReader::GetExpr() const - { - return m_strFormula; - } - - //--------------------------------------------------------------------------- - /** \brief Return a map containing the used variables only. */ - varmap_type& QmuParserTokenReader::GetUsedVar() - { - return m_UsedVar; - } - - //--------------------------------------------------------------------------- - /** \brief Initialize the token Reader. - - Sets the formula position index to zero and set Syntax flags to default for initial formula parsing. - \pre [assert] triggered if a_szFormula==0 - */ - void QmuParserTokenReader::SetFormula(const QString &a_strFormula) - { - m_strFormula = a_strFormula; - ReInit(); - } - - //--------------------------------------------------------------------------- - /** \brief Set Flag that contronls behaviour in case of undefined variables beeing found. - - If true, the parser does not throw an exception if an undefined variable is found. - otherwise it does. This variable is used internally only! - It supresses a "undefined variable" exception in GetUsedVar(). - Those function should return a complete list of variables including - those the are not defined by the time of it's call. - */ - void QmuParserTokenReader::IgnoreUndefVar(bool bIgnore) - { - m_bIgnoreUndefVar = bIgnore; - } - - //--------------------------------------------------------------------------- - /** \brief Reset the token reader to the start of the formula. - - The syntax flags will be reset to a value appropriate for the - start of a formula. - \post #m_iPos==0, #m_iSynFlags = noOPT | noBC | noPOSTOP | noSTR - \throw nothrow - \sa ESynCodes - */ - void QmuParserTokenReader::ReInit() - { - m_iPos = 0; - m_iSynFlags = sfSTART_OF_LINE; - m_iBrackets = 0; - m_UsedVar.clear(); - m_lastTok = token_type(); - } - - //--------------------------------------------------------------------------- - /** \brief Read the next token from the string. */ - QmuParserTokenReader::token_type QmuParserTokenReader::ReadNextToken() - { - assert(m_pParser); - - //std::stack FunArgs; -#if defined(_UNICODE) - const char_type *szFormula = m_strFormula.toStdWString().c_str(); -#else - const char_type *szFormula = m_strFormula.toStdString().c_str(); -#endif - token_type tok; - - // Ignore all non printable characters when reading the expression - while (szFormula[m_iPos]>0 && szFormula[m_iPos]<=0x20) - { - ++m_iPos; - } - - if ( IsEOF(tok) ) return SaveBeforeReturn(tok); // Check for end of formula - if ( IsOprt(tok) ) return SaveBeforeReturn(tok); // Check for user defined binary operator - if ( IsFunTok(tok) ) return SaveBeforeReturn(tok); // Check for function token - if ( IsBuiltIn(tok) ) return SaveBeforeReturn(tok); // Check built in operators / tokens - if ( IsArgSep(tok) ) return SaveBeforeReturn(tok); // Check for function argument separators - if ( IsValTok(tok) ) return SaveBeforeReturn(tok); // Check for values / constant tokens - if ( IsVarTok(tok) ) return SaveBeforeReturn(tok); // Check for variable tokens - if ( IsStrVarTok(tok) ) return SaveBeforeReturn(tok); // Check for string variables - if ( IsString(tok) ) return SaveBeforeReturn(tok); // Check for String tokens - if ( IsInfixOpTok(tok) ) return SaveBeforeReturn(tok); // Check for unary operators - if ( IsPostOpTok(tok) ) return SaveBeforeReturn(tok); // Check for unary operators - - // Check String for undefined variable token. Done only if a - // flag is set indicating to ignore undefined variables. - // This is a way to conditionally avoid an error if - // undefined variables occur. - // (The GetUsedVar function must suppress the error for - // undefined variables in order to collect all variable - // names including the undefined ones.) - if ( (m_bIgnoreUndefVar || m_pFactory) && IsUndefVarTok(tok) ) - { - return SaveBeforeReturn(tok); - } - - // Check for unknown token - // - // !!! From this point on there is no exit without an exception possible... - // - QString strTok; - int iEnd = ExtractToken(m_pParser->ValidNameChars(), strTok, m_iPos); - if (iEnd!=m_iPos) - { - Error(ecUNASSIGNABLE_TOKEN, m_iPos, strTok); - } - - Error(ecUNASSIGNABLE_TOKEN, m_iPos, m_strFormula.mid(m_iPos)); - return token_type(); // never reached - } - - //--------------------------------------------------------------------------- - void QmuParserTokenReader::SetParent(QmuParserBase *a_pParent) - { - m_pParser = a_pParent; - m_pFunDef = &a_pParent->m_FunDef; - m_pOprtDef = &a_pParent->m_OprtDef; - m_pInfixOprtDef = &a_pParent->m_InfixOprtDef; - m_pPostOprtDef = &a_pParent->m_PostOprtDef; - m_pVarDef = &a_pParent->m_VarDef; - m_pStrVarDef = &a_pParent->m_StrVarDef; - m_pConstDef = &a_pParent->m_ConstDef; - } - -//--------------------------------------------------------------------------- -/** \brief Extract all characters that belong to a certain charset. - -\param a_szCharSet [in] Const char array of the characters allowed in the token. -\param a_strTok [out] The string that consists entirely of characters listed in a_szCharSet. -\param a_iPos [in] Position in the string from where to start reading. -\return The Position of the first character not listed in a_szCharSet. -\throw nothrow -*/ -int QmuParserTokenReader::ExtractToken(const QString &a_szCharSet, QString &a_sTok, int a_iPos) const -{ -#if defined(_UNICODE) - const std::wstring m_strFormulaStd = m_strFormula.toStdWString(); - const std::wstring a_szCharSetstd = a_szCharSet.toStdWString(); -#else - const std::string m_strFormulaStd = m_strFormula.toStdString(); - const std::string a_szCharSetStd = a_szCharSet.toStdString(); -#endif - - int iEnd = (int)m_strFormulaStd.find_first_not_of(a_szCharSetStd, a_iPos); - - if (iEnd==(int)string_type::npos) - { - iEnd = (int)m_strFormulaStd.length(); - } - - // Assign token string if there was something found - if (a_iPos!=iEnd) - { - #if defined(_UNICODE) - a_sTok = QString().fromStdWString(std::wstring( m_strFormulaStd.begin()+a_iPos, m_strFormulaStd.begin()+iEnd)); - #else - a_sTok = QString().fromStdString(std::string( m_strFormulaStd.begin()+a_iPos, m_strFormulaStd.begin()+iEnd)); - #endif - } - - return iEnd; -} - -//--------------------------------------------------------------------------- -/** \brief Check Expression for the presence of a binary operator token. - -Userdefined binary operator "++" gives inconsistent parsing result for -the equations "a++b" and "a ++ b" if alphabetic characters are allowed -in operator tokens. To avoid this this function checks specifically -for operator tokens. -*/ -int QmuParserTokenReader::ExtractOperatorToken(QString &a_sTok, int a_iPos) const -{ -#if defined(_UNICODE) - const std::wstring m_strFormulaStd = m_strFormula.toStdWString(); - const std::wstring oprtCharsStd = m_pParser->ValidInfixOprtChars().toStdWString(); -#else - const std::string m_strFormulaStd = m_strFormula.toStdString(); - const std::string oprtCharsStd = m_pParser->ValidInfixOprtChars().toStdString(); -#endif - int iEnd = (int)m_strFormulaStd.find_first_not_of(oprtCharsStd, a_iPos); - if (iEnd==(int)string_type::npos) - { - iEnd = (int)m_strFormulaStd.length(); - } - - // Assign token string if there was something found - if (a_iPos!=iEnd) - { - #if defined(_UNICODE) - a_sTok = QString().fromStdWString(string_type(m_strFormulaStd.begin() + a_iPos, - m_strFormulaStd.begin() + iEnd)); - #else - a_sTok = QString().fromStdString(string_type(m_strFormulaStd.begin() + a_iPos, m_strFormulaStd.begin() + iEnd)); - #endif - return iEnd; - } - else - { - // There is still the chance of having to deal with an operator consisting exclusively - // of alphabetic characters. - return ExtractToken(QMUP_CHARS, a_sTok, a_iPos); - } -} - - //--------------------------------------------------------------------------- - /** \brief Check if a built in operator or other token can be found - \param a_Tok [out] Operator token if one is found. This can either be a binary operator or an infix operator token. - \return true if an operator token has been found. - */ - bool QmuParserTokenReader::IsBuiltIn(token_type &a_Tok) - { - const QStringList pOprtDef = m_pParser->GetOprtDef(); - - // Compare token with function and operator strings - // check string for operator/function - for (int i=0; i < pOprtDef.size(); ++i) - { - int len = pOprtDef.at(i).length(); - if ( pOprtDef.at(i) == m_strFormula.mid(m_iPos, m_iPos + len) ) - { - switch(i) - { - //case cmAND: - //case cmOR: - //case cmXOR: - 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: - //if (len!=sTok.length()) - // continue; - - // The assignement operator need special treatment - if (i==cmASSIGN && m_iSynFlags & noASSIGN) - Error(ecUNEXPECTED_OPERATOR, m_iPos, pOprtDef.at(i)); - - if (!m_pParser->HasBuiltInOprt()) continue; - if (m_iSynFlags & noOPT) - { - // Maybe its an infix operator not an operator - // Both operator types can share characters in - // their identifiers - if ( IsInfixOpTok(a_Tok) ) - return true; - - Error(ecUNEXPECTED_OPERATOR, m_iPos, pOprtDef.at(i)); - } - - m_iSynFlags = noBC | noOPT | noARG_SEP | noPOSTOP | noASSIGN | noIF | noELSE; - m_iSynFlags |= ( (i != cmEND) && ( i != cmBC) ) ? noEND : 0; - break; - - case cmBO: - if (m_iSynFlags & noBO) - Error(ecUNEXPECTED_PARENS, m_iPos, pOprtDef.at(i)); - - if (m_lastTok.GetCode()==cmFUNC) - m_iSynFlags = noOPT | noEND | noARG_SEP | noPOSTOP | noASSIGN | noIF | noELSE; - else - m_iSynFlags = noBC | noOPT | noEND | noARG_SEP | noPOSTOP | noASSIGN| noIF | noELSE; - - ++m_iBrackets; - break; - - case cmBC: - if (m_iSynFlags & noBC) - Error(ecUNEXPECTED_PARENS, m_iPos, pOprtDef.at(i)); - - m_iSynFlags = noBO | noVAR | noVAL | noFUN | noINFIXOP | noSTR | noASSIGN; - - if (--m_iBrackets<0) - Error(ecUNEXPECTED_PARENS, m_iPos, pOprtDef.at(i)); - break; - - case cmELSE: - if (m_iSynFlags & noELSE) - Error(ecUNEXPECTED_CONDITIONAL, m_iPos, pOprtDef.at(i)); - - m_iSynFlags = noBC | noPOSTOP | noEND | noOPT | noIF | noELSE; - break; - - case cmIF: - if (m_iSynFlags & noIF) - Error(ecUNEXPECTED_CONDITIONAL, m_iPos, pOprtDef.at(i)); - - m_iSynFlags = noBC | noPOSTOP | noEND | noOPT | noIF | noELSE; - break; - - default: // The operator is listed in c_DefaultOprt, but not here. This is a bad thing... - Error(ecINTERNAL_ERROR); - } // switch operator id - - m_iPos += len; - a_Tok.Set( (ECmdCode)i, pOprtDef.at(i) ); - return true; - } // if operator string found - } // end of for all operator strings - - return false; - } - - //--------------------------------------------------------------------------- - bool QmuParserTokenReader::IsArgSep(token_type &a_Tok) - { - if (m_strFormula.at(m_iPos)==m_cArgSep) - { - // copy the separator into null terminated string - QString szSep; - szSep[0] = m_cArgSep; - szSep[1] = 0; - - if (m_iSynFlags & noARG_SEP) - { - Error(ecUNEXPECTED_ARG_SEP, m_iPos, szSep); - } - - m_iSynFlags = noBC | noOPT | noEND | noARG_SEP | noPOSTOP | noASSIGN; - m_iPos++; - a_Tok.Set(cmARG_SEP, szSep); - return true; - } - - return false; - } - - //--------------------------------------------------------------------------- - /** \brief Check for End of Formula. - - \return true if an end of formula is found false otherwise. - \param a_Tok [out] If an eof is found the corresponding token will be stored there. - \throw nothrow - \sa IsOprt, IsFunTok, IsStrFunTok, IsValTok, IsVarTok, IsString, IsInfixOpTok, IsPostOpTok - */ - bool QmuParserTokenReader::IsEOF(token_type &a_Tok) - { -#if defined(_UNICODE) - const char_type* szFormula = m_strFormula.toStdWString().c_str(); -#else - const char_type* szFormula = m_strFormula.toStdString().c_str(); -#endif - - // check for EOF - if ( !szFormula[m_iPos] /*|| szFormula[m_iPos] == '\n'*/) - { - if ( m_iSynFlags & noEND ) - Error(ecUNEXPECTED_EOF, m_iPos); - - if (m_iBrackets>0) - Error(ecMISSING_PARENS, m_iPos, ")"); - - m_iSynFlags = 0; - a_Tok.Set(cmEND); - return true; - } - - return false; - } - - //--------------------------------------------------------------------------- - /** \brief Check if a string position contains a unary infix operator. - \return true if a function token has been found false otherwise. - */ - bool QmuParserTokenReader::IsInfixOpTok(token_type &a_Tok) - { - QString sTok; - int iEnd = ExtractToken(m_pParser->ValidInfixOprtChars(), sTok, m_iPos); - if (iEnd==m_iPos) - return false; - - // iteraterate over all postfix operator strings - funmap_type::const_reverse_iterator it = m_pInfixOprtDef->rbegin(); - for ( ; it!=m_pInfixOprtDef->rend(); ++it) - { - if (sTok.indexOf(it->first)!=0) - continue; - - a_Tok.Set(it->second, it->first); - m_iPos += (int)it->first.length(); - - if (m_iSynFlags & noINFIXOP) - Error(ecUNEXPECTED_OPERATOR, m_iPos, a_Tok.GetAsString()); - - m_iSynFlags = noPOSTOP | noINFIXOP | noOPT | noBC | noSTR | noASSIGN; - return true; - } - - return false; - -/* - a_Tok.Set(item->second, sTok); - m_iPos = (int)iEnd; - - if (m_iSynFlags & noINFIXOP) - Error(ecUNEXPECTED_OPERATOR, m_iPos, a_Tok.GetAsString()); - - m_iSynFlags = noPOSTOP | noINFIXOP | noOPT | noBC | noSTR | noASSIGN; - return true; -*/ - } - - //--------------------------------------------------------------------------- - /** \brief Check whether the token at a given position is a function token. - \param a_Tok [out] If a value token is found it will be placed here. - \throw ParserException if Syntaxflags do not allow a function at a_iPos - \return true if a function token has been found false otherwise. - \pre [assert] m_pParser!=0 - */ - bool QmuParserTokenReader::IsFunTok(token_type &a_Tok) - { - QString strTok; - int iEnd = ExtractToken(m_pParser->ValidNameChars(), strTok, m_iPos); - if (iEnd==m_iPos) - return false; - - funmap_type::const_iterator item = m_pFunDef->find(strTok); - if (item==m_pFunDef->end()) - return false; - - // Check if the next sign is an opening bracket - if (m_strFormula.at(iEnd)!='(') - return false; - - a_Tok.Set(item->second, strTok); - - m_iPos = iEnd; - if (m_iSynFlags & noFUN) - Error(ecUNEXPECTED_FUN, m_iPos-(int)a_Tok.GetAsString().length(), a_Tok.GetAsString()); - - m_iSynFlags = noANY ^ noBO; - return true; - } - - //--------------------------------------------------------------------------- - /** \brief Check if a string position contains a binary operator. - \param a_Tok [out] Operator token if one is found. This can either be a binary operator or an infix operator token. - \return true if an operator token has been found. - */ - bool QmuParserTokenReader::IsOprt(token_type &a_Tok) - { - QString strTok; - - int iEnd = ExtractOperatorToken(strTok, m_iPos); - if (iEnd==m_iPos) - return false; - - // Check if the operator is a built in operator, if so ignore it here - const QStringList pOprtDef = m_pParser->GetOprtDef(); - QStringList::const_iterator constIterator; - for (constIterator = pOprtDef.constBegin(); m_pParser->HasBuiltInOprt() && constIterator != pOprtDef.constEnd(); - ++constIterator) - { - if ((*constIterator)==strTok) - return false; - } - - // Note: - // All tokens in oprt_bin_maptype are have been sorted by their length - // Long operators must come first! Otherwise short names (like: "add") that - // are part of long token names (like: "add123") will be found instead - // of the long ones. - // Length sorting is done with ascending length so we use a reverse iterator here. - funmap_type::const_reverse_iterator it = m_pOprtDef->rbegin(); - for ( ; it!=m_pOprtDef->rend(); ++it) - { - const QString &sID = it->first; - if ( sID == m_strFormula.mid(m_iPos, m_iPos + sID.length()) ) - { - a_Tok.Set(it->second, strTok); - - // operator was found - if (m_iSynFlags & noOPT) - { - // An operator was found but is not expected to occur at - // this position of the formula, maybe it is an infix - // operator, not a binary operator. Both operator types - // can share characters in their identifiers. - if ( IsInfixOpTok(a_Tok) ) - return true; - else - { - // nope, no infix operator - return false; - //Error(ecUNEXPECTED_OPERATOR, m_iPos, a_Tok.GetAsString()); - } - - } - - m_iPos += (int)sID.length(); - m_iSynFlags = noBC | noOPT | noARG_SEP | noPOSTOP | noEND | noBC | noASSIGN; - return true; - } - } - - return false; - } - - //--------------------------------------------------------------------------- - /** \brief Check if a string position contains a unary post value operator. */ - bool QmuParserTokenReader::IsPostOpTok(token_type &a_Tok) - { - // Do not check for postfix operators if they are not allowed at - // the current expression index. - // - // This will fix the bug reported here: - // - // http://sourceforge.net/tracker/index.php?func=detail&aid=3343891&group_id=137191&atid=737979 - // - if (m_iSynFlags & noPOSTOP) - return false; - // - - // Tricky problem with equations like "3m+5": - // m is a postfix operator, + is a valid sign for postfix operators and - // for binary operators parser detects "m+" as operator string and - // finds no matching postfix operator. - // - // This is a special case so this routine slightly differs from the other - // token readers. - - // Test if there could be a postfix operator - QString sTok; - int iEnd = ExtractToken(m_pParser->ValidOprtChars(), sTok, m_iPos); - if (iEnd==m_iPos) - return false; - - // iteraterate over all postfix operator strings - funmap_type::const_reverse_iterator it = m_pPostOprtDef->rbegin(); - for ( ; it!=m_pPostOprtDef->rend(); ++it) - { - if (sTok.indexOf(it->first)!=0) - continue; - - a_Tok.Set(it->second, sTok); - m_iPos += it->first.length(); - - m_iSynFlags = noVAL | noVAR | noFUN | noBO | noPOSTOP | noSTR | noASSIGN; - return true; - } - - return false; - } - - //--------------------------------------------------------------------------- - /** \brief Check whether the token at a given position is a value token. - - Value tokens are either values or constants. - - \param a_Tok [out] If a value token is found it will be placed here. - \return true if a value token has been found. - */ - bool QmuParserTokenReader::IsValTok(token_type &a_Tok) - { - assert(m_pConstDef); - assert(m_pParser); - - QString strTok; - qreal fVal(0); - int iEnd(0); - - // 2.) Check for user defined constant - // Read everything that could be a constant name - iEnd = ExtractToken(m_pParser->ValidNameChars(), strTok, m_iPos); - if (iEnd!=m_iPos) - { - valmap_type::const_iterator item = m_pConstDef->find(strTok); - if (item!=m_pConstDef->end()) - { - m_iPos = iEnd; - a_Tok.SetVal(item->second, strTok); - - if (m_iSynFlags & noVAL) - Error(ecUNEXPECTED_VAL, m_iPos - strTok.length(), strTok); - - m_iSynFlags = noVAL | noVAR | noFUN | noBO | noINFIXOP | noSTR | noASSIGN; - return true; - } - } - - // 3.call the value recognition functions provided by the user - // Call user defined value recognition functions - std::list::const_iterator item = m_vIdentFun.begin(); - for (item = m_vIdentFun.begin(); item!=m_vIdentFun.end(); ++item) - { - int iStart = m_iPos; - if ( (*item)(m_strFormula.mid(m_iPos), &m_iPos, &fVal)==1 ) - { - strTok = m_strFormula.mid(iStart, m_iPos); - if (m_iSynFlags & noVAL) - Error(ecUNEXPECTED_VAL, m_iPos - strTok.length(), strTok); - - a_Tok.SetVal(fVal, strTok); - m_iSynFlags = noVAL | noVAR | noFUN | noBO | noINFIXOP | noSTR | noASSIGN; - return true; - } - } - - return false; - } - - //--------------------------------------------------------------------------- - /** \brief Check wheter a token at a given position is a variable token. - \param a_Tok [out] If a variable token has been found it will be placed here. - \return true if a variable token has been found. - */ - bool QmuParserTokenReader::IsVarTok(token_type &a_Tok) - { - if (!m_pVarDef->size()) - return false; - - QString strTok; - int iEnd = ExtractToken(m_pParser->ValidNameChars(), strTok, m_iPos); - if (iEnd==m_iPos) - return false; - - varmap_type::const_iterator item = m_pVarDef->find(strTok); - if (item==m_pVarDef->end()) - return false; - - if (m_iSynFlags & noVAR) - Error(ecUNEXPECTED_VAR, m_iPos, strTok); - - m_pParser->OnDetectVar(&m_strFormula, m_iPos, iEnd); - - m_iPos = iEnd; - a_Tok.SetVar(item->second, strTok); - m_UsedVar[item->first] = item->second; // Add variable to used-var-list - - m_iSynFlags = noVAL | noVAR | noFUN | noBO | noINFIXOP | noSTR; - -// Zur Info hier die SynFlags von IsVal(): -// m_iSynFlags = noVAL | noVAR | noFUN | noBO | noINFIXOP | noSTR | noASSIGN; - return true; - } - - //--------------------------------------------------------------------------- - bool QmuParserTokenReader::IsStrVarTok(token_type &a_Tok) - { - if (!m_pStrVarDef || !m_pStrVarDef->size()) - return false; - - QString strTok; - int iEnd = ExtractToken(m_pParser->ValidNameChars(), strTok, m_iPos); - if (iEnd==m_iPos) - return false; - - strmap_type::const_iterator item = m_pStrVarDef->find(strTok); - if (item==m_pStrVarDef->end()) - return false; - - if (m_iSynFlags & noSTR) - Error(ecUNEXPECTED_VAR, m_iPos, strTok); - - m_iPos = iEnd; - if (!m_pParser->m_vStringVarBuf.size()) - Error(ecINTERNAL_ERROR); - - a_Tok.SetString(m_pParser->m_vStringVarBuf[item->second], m_pParser->m_vStringVarBuf.size() ); - - m_iSynFlags = noANY ^ ( noBC | noOPT | noEND | noARG_SEP); - return true; - } - - - //--------------------------------------------------------------------------- - /** \brief Check wheter a token at a given position is an undefined variable. - - \param a_Tok [out] If a variable tom_pParser->m_vStringBufken has been found it will be placed here. - \return true if a variable token has been found. - \throw nothrow - */ - bool QmuParserTokenReader::IsUndefVarTok(token_type &a_Tok) - { - QString strTok; - int iEnd( ExtractToken(m_pParser->ValidNameChars(), strTok, m_iPos) ); - if ( iEnd==m_iPos ) - return false; - - if (m_iSynFlags & noVAR) - { - // 20061021 added token string strTok instead of a_Tok.GetAsString() as the - // token identifier. - // related bug report: - // http://sourceforge.net/tracker/index.php?func=detail&aid=1578779&group_id=137191&atid=737979 - Error(ecUNEXPECTED_VAR, m_iPos - (int)a_Tok.GetAsString().length(), strTok); - } - - // If a factory is available implicitely create new variables - if (m_pFactory) - { - qreal *fVar = m_pFactory(strTok, m_pFactoryData); - a_Tok.SetVar(fVar, strTok ); - - // Do not use m_pParser->DefineVar( strTok, fVar ); - // in order to define the new variable, it will clear the - // m_UsedVar array which will kill previousely defined variables - // from the list - // This is safe because the new variable can never override an existing one - // because they are checked first! - (*m_pVarDef)[strTok] = fVar; - m_UsedVar[strTok] = fVar; // Add variable to used-var-list - } - else - { - a_Tok.SetVar((qreal*)&m_fZero, strTok); - m_UsedVar[strTok] = 0; // Add variable to used-var-list - } - - m_iPos = iEnd; - - // Call the variable factory in order to let it define a new parser variable - m_iSynFlags = noVAL | noVAR | noFUN | noBO | noPOSTOP | noINFIXOP | noSTR; - return true; - } - - - //--------------------------------------------------------------------------- - /** \brief Check wheter a token at a given position is a string. - \param a_Tok [out] If a variable token has been found it will be placed here. - \return true if a string token has been found. - \sa IsOprt, IsFunTok, IsStrFunTok, IsValTok, IsVarTok, IsEOF, IsInfixOpTok, IsPostOpTok - \throw nothrow - */ - bool QmuParserTokenReader::IsString(token_type &a_Tok) - { - if (m_strFormula[m_iPos]!='"') - return false; - - QString strBuf(m_strFormula[m_iPos+1]); - int iEnd(0), iSkip(0); - - // parser over escaped '\"' end replace them with '"' - for(iEnd=strBuf.indexOf( "\"" ); iEnd!=0 && iEnd!=-1; iEnd=strBuf.indexOf( "\"", iEnd)) - { - if (strBuf[iEnd-1]!='\\') break; - strBuf.replace(iEnd-1, 2, "\"" ); - iSkip++; - } - - if (iEnd==-1) - Error(ecUNTERMINATED_STRING, m_iPos, "\"" ); - - QString strTok = strBuf.mid(0, iEnd); - - if (m_iSynFlags & noSTR) - Error(ecUNEXPECTED_STR, m_iPos, strTok); - - m_pParser->m_vStringBuf.push_back(strTok); // Store string in internal buffer - a_Tok.SetString(strTok, m_pParser->m_vStringBuf.size()); - - m_iPos += (int)strTok.length() + 2 + iSkip; // +2 wg Anfhrungszeichen; +iSkip fr entfernte escape zeichen - m_iSynFlags = noANY ^ ( noARG_SEP | noBC | noOPT | noEND ); - - return true; - } - - //--------------------------------------------------------------------------- - /** \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 QmuParserTokenReader::Error( EErrorCodes a_iErrc, - int a_iPos, - const QString &a_sTok) const - { - m_pParser->Error(a_iErrc, a_iPos, a_sTok); - } - - //--------------------------------------------------------------------------- - void QmuParserTokenReader::SetArgSep(char_type cArgSep) - { - m_cArgSep = cArgSep; - } - - //--------------------------------------------------------------------------- - QChar QmuParserTokenReader::GetArgSep() const - { - return m_cArgSep; - } -} // namespace qmu - +/*************************************************************************************************** + ** + ** Original work Copyright (C) 2013 Ingo Berg + ** Modified work Copyright 2014 Roman Telezhinsky + ** + ** 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 "qmuparsertokenreader.h" +#include "qmuparserbase.h" + +#include + +/** + * @file + * @brief This file contains the parser token reader implementation. + */ + +namespace qmu +{ + +// Forward declaration +class QmuParserBase; + +//---------------------------------------------------------------------------------------------------------------------- +/** + * @brief Copy constructor. + * + * @sa Assign + * @throw nothrow + */ +QmuParserTokenReader::QmuParserTokenReader ( const QmuParserTokenReader &a_Reader ) +{ + Assign ( a_Reader ); +} + +//---------------------------------------------------------------------------------------------------------------------- +/** + * @brief Assignement operator. + * + * Self assignement will be suppressed otherwise #Assign is called. + * + * @param a_Reader Object to copy to this token reader. + * @throw nothrow + */ +QmuParserTokenReader& QmuParserTokenReader::operator= ( const QmuParserTokenReader &a_Reader ) +{ + if ( &a_Reader != this ) + { + Assign ( a_Reader ); + } + + return *this; +} + +//---------------------------------------------------------------------------------------------------------------------- +/** + * @brief Assign state of a token reader to this token reader. + * + * @param a_Reader Object from which the state should be copied. + * @throw nothrow + */ +void QmuParserTokenReader::Assign ( const QmuParserTokenReader &a_Reader ) +{ + m_pParser = a_Reader.m_pParser; + m_strFormula = a_Reader.m_strFormula; + m_iPos = a_Reader.m_iPos; + m_iSynFlags = a_Reader.m_iSynFlags; + + m_UsedVar = a_Reader.m_UsedVar; + m_pFunDef = a_Reader.m_pFunDef; + m_pConstDef = a_Reader.m_pConstDef; + m_pVarDef = a_Reader.m_pVarDef; + m_pStrVarDef = a_Reader.m_pStrVarDef; + m_pPostOprtDef = a_Reader.m_pPostOprtDef; + m_pInfixOprtDef = a_Reader.m_pInfixOprtDef; + m_pOprtDef = a_Reader.m_pOprtDef; + m_bIgnoreUndefVar = a_Reader.m_bIgnoreUndefVar; + m_vIdentFun = a_Reader.m_vIdentFun; + m_pFactory = a_Reader.m_pFactory; + m_pFactoryData = a_Reader.m_pFactoryData; + m_iBrackets = a_Reader.m_iBrackets; + m_cArgSep = a_Reader.m_cArgSep; +} + +//---------------------------------------------------------------------------------------------------------------------- +/** + * @brief Constructor. + * + * Create a Token reader and bind it to a parser object. + * + * @pre [assert] a_pParser may not be NULL + * @post #m_pParser==a_pParser + * @param a_pParent Parent parser object of the token reader. + */ +QmuParserTokenReader::QmuParserTokenReader ( QmuParserBase *a_pParent ) + : m_pParser ( a_pParent ), m_strFormula(), m_iPos ( 0 ), m_iSynFlags ( 0 ), m_bIgnoreUndefVar ( false ), + m_pFunDef ( NULL ), m_pPostOprtDef ( NULL ), m_pInfixOprtDef ( NULL ), m_pOprtDef ( NULL ), m_pConstDef ( NULL ), + m_pStrVarDef ( NULL ), m_pVarDef ( NULL ), m_pFactory ( NULL ), m_pFactoryData ( NULL ), m_vIdentFun(), + m_UsedVar(), m_fZero ( 0 ), m_iBrackets ( 0 ), m_lastTok(), m_cArgSep ( ',' ) +{ + assert ( m_pParser ); + SetParent ( m_pParser ); +} + +//---------------------------------------------------------------------------------------------------------------------- +/** + * @brief Create instance of a QParserTokenReader identical with this and return its pointer. + * + * This is a factory method the calling function must take care of the object destruction. + * + * @return A new QParserTokenReader object. + * @throw nothrow + */ +QmuParserTokenReader* QmuParserTokenReader::Clone ( QmuParserBase *a_pParent ) const +{ + std::auto_ptr ptr ( new QmuParserTokenReader ( *this ) ); + ptr->SetParent ( a_pParent ); + return ptr.release(); +} + +//---------------------------------------------------------------------------------------------------------------------- +QmuParserTokenReader::token_type& QmuParserTokenReader::SaveBeforeReturn ( const token_type &tok ) +{ + m_lastTok = tok; + return m_lastTok; +} + +//---------------------------------------------------------------------------------------------------------------------- +void QmuParserTokenReader::AddValIdent ( identfun_type a_pCallback ) +{ + // Use push_front is used to give user defined callbacks a higher priority than + // the built in ones. Otherwise reading hex numbers would not work + // since the "0" in "0xff" would always be read first making parsing of + // the rest impossible. + // reference: + // http://sourceforge.net/projects/muparser/forums/forum/462843/topic/4824956 + m_vIdentFun.push_front ( a_pCallback ); +} + +//---------------------------------------------------------------------------------------------------------------------- +void QmuParserTokenReader::SetVarCreator ( facfun_type a_pFactory, void *pUserData ) +{ + m_pFactory = a_pFactory; + m_pFactoryData = pUserData; +} + +//---------------------------------------------------------------------------------------------------------------------- +/** + * @brief Return the current position of the token reader in the formula string. + * + * @return #m_iPos + * @throw nothrow + */ +int QmuParserTokenReader::GetPos() const +{ + return m_iPos; +} + +//---------------------------------------------------------------------------------------------------------------------- +/** + * @brief Return a reference to the formula. + * + * @return #m_strFormula + * @throw nothrow + */ +const QString& QmuParserTokenReader::GetExpr() const +{ + return m_strFormula; +} + +//---------------------------------------------------------------------------------------------------------------------- +/** + * @brief Return a map containing the used variables only. + */ +varmap_type& QmuParserTokenReader::GetUsedVar() +{ + return m_UsedVar; +} + +//---------------------------------------------------------------------------------------------------------------------- +/** + * @brief Initialize the token Reader. + * + * Sets the formula position index to zero and set Syntax flags to default for initial formula parsing. + * @pre [assert] triggered if a_szFormula==0 + */ +void QmuParserTokenReader::SetFormula ( const QString &a_strFormula ) +{ + m_strFormula = a_strFormula; + ReInit(); +} + +//---------------------------------------------------------------------------------------------------------------------- +/** + * @brief Set Flag that contronls behaviour in case of undefined variables beeing found. + * + * If true, the parser does not throw an exception if an undefined variable is found. Otherwise it does. This variable + * is used internally only! It supresses a "undefined variable" exception in GetUsedVar(). + * Those function should return a complete list of variables including + * those the are not defined by the time of it's call. + */ +void QmuParserTokenReader::IgnoreUndefVar ( bool bIgnore ) +{ + m_bIgnoreUndefVar = bIgnore; +} + +//---------------------------------------------------------------------------------------------------------------------- +/** + * @brief Reset the token reader to the start of the formula. + * + * The syntax flags will be reset to a value appropriate for the start of a formula. + * @post #m_iPos==0, #m_iSynFlags = noOPT | noBC | noPOSTOP | noSTR + * @throw nothrow + * @sa ESynCodes + */ +void QmuParserTokenReader::ReInit() +{ + m_iPos = 0; + m_iSynFlags = sfSTART_OF_LINE; + m_iBrackets = 0; + m_UsedVar.clear(); + m_lastTok = token_type(); +} + +//---------------------------------------------------------------------------------------------------------------------- +/** + * @brief Read the next token from the string. + */ +QmuParserTokenReader::token_type QmuParserTokenReader::ReadNextToken() +{ + assert ( m_pParser ); + +#if defined(_UNICODE) + const char_type *szFormula = m_strFormula.toStdWString().c_str(); +#else + const char_type *szFormula = m_strFormula.toStdString().c_str(); +#endif + token_type tok; + + // Ignore all non printable characters when reading the expression + while ( szFormula[m_iPos] > 0 && szFormula[m_iPos] <= 0x20 ) + { + ++m_iPos; + } + + if ( IsEOF ( tok ) ) return SaveBeforeReturn ( tok ); // Check for end of formula + if ( IsOprt ( tok ) ) return SaveBeforeReturn ( tok ); // Check for user defined binary operator + if ( IsFunTok ( tok ) ) return SaveBeforeReturn ( tok ); // Check for function token + if ( IsBuiltIn ( tok ) ) return SaveBeforeReturn ( tok ); // Check built in operators / tokens + if ( IsArgSep ( tok ) ) return SaveBeforeReturn ( tok ); // Check for function argument separators + if ( IsValTok ( tok ) ) return SaveBeforeReturn ( tok ); // Check for values / constant tokens + if ( IsVarTok ( tok ) ) return SaveBeforeReturn ( tok ); // Check for variable tokens + if ( IsStrVarTok ( tok ) ) return SaveBeforeReturn ( tok ); // Check for string variables + if ( IsString ( tok ) ) return SaveBeforeReturn ( tok ); // Check for String tokens + if ( IsInfixOpTok ( tok ) ) return SaveBeforeReturn ( tok ); // Check for unary operators + if ( IsPostOpTok ( tok ) ) return SaveBeforeReturn ( tok ); // Check for unary operators + + // Check String for undefined variable token. Done only if a + // flag is set indicating to ignore undefined variables. + // This is a way to conditionally avoid an error if + // undefined variables occur. + // (The GetUsedVar function must suppress the error for + // undefined variables in order to collect all variable + // names including the undefined ones.) + if ( ( m_bIgnoreUndefVar || m_pFactory ) && IsUndefVarTok ( tok ) ) + { + return SaveBeforeReturn ( tok ); + } + + // Check for unknown token + // + // !!! From this point on there is no exit without an exception possible... + // + QString strTok; + int iEnd = ExtractToken ( m_pParser->ValidNameChars(), strTok, m_iPos ); + if ( iEnd != m_iPos ) + { + Error ( ecUNASSIGNABLE_TOKEN, m_iPos, strTok ); + } + + Error ( ecUNASSIGNABLE_TOKEN, m_iPos, m_strFormula.mid ( m_iPos ) ); + return token_type(); // never reached +} + +//---------------------------------------------------------------------------------------------------------------------- +void QmuParserTokenReader::SetParent ( QmuParserBase *a_pParent ) +{ + m_pParser = a_pParent; + m_pFunDef = &a_pParent->m_FunDef; + m_pOprtDef = &a_pParent->m_OprtDef; + m_pInfixOprtDef = &a_pParent->m_InfixOprtDef; + m_pPostOprtDef = &a_pParent->m_PostOprtDef; + m_pVarDef = &a_pParent->m_VarDef; + m_pStrVarDef = &a_pParent->m_StrVarDef; + m_pConstDef = &a_pParent->m_ConstDef; +} + +//---------------------------------------------------------------------------------------------------------------------- +/** + * @brief Extract all characters that belong to a certain charset. + * + * @param a_szCharSet [in] Const char array of the characters allowed in the token. + * @param a_strTok [out] The string that consists entirely of characters listed in a_szCharSet. + * @param a_iPos [in] Position in the string from where to start reading. + * @return The Position of the first character not listed in a_szCharSet. + * @throw nothrow + */ +int QmuParserTokenReader::ExtractToken ( const QString &a_szCharSet, QString &a_sTok, int a_iPos ) const +{ +#if defined(_UNICODE) + const std::wstring m_strFormulaStd = m_strFormula.toStdWString(); + const std::wstring a_szCharSetstd = a_szCharSet.toStdWString(); +#else + const std::string m_strFormulaStd = m_strFormula.toStdString(); + const std::string a_szCharSetStd = a_szCharSet.toStdString(); +#endif + + int iEnd = ( int ) m_strFormulaStd.find_first_not_of ( a_szCharSetStd, a_iPos ); + + if ( iEnd == ( int ) string_type::npos ) + { + iEnd = ( int ) m_strFormulaStd.length(); + } + + // Assign token string if there was something found + if ( a_iPos != iEnd ) + { +#if defined(_UNICODE) + a_sTok = QString().fromStdWString ( std::wstring ( m_strFormulaStd.begin() + a_iPos, m_strFormulaStd.begin() + iEnd ) ); +#else + a_sTok = QString().fromStdString ( std::string ( m_strFormulaStd.begin() + a_iPos, m_strFormulaStd.begin() + iEnd ) ); +#endif + } + + return iEnd; +} + +//---------------------------------------------------------------------------------------------------------------------- +/** + * @brief Check Expression for the presence of a binary operator token. + * + * Userdefined binary operator "++" gives inconsistent parsing result for the equations "a++b" and "a ++ b" if + * alphabetic characters are allowed in operator tokens. To avoid this this function checks specifically + * for operator tokens. + */ +int QmuParserTokenReader::ExtractOperatorToken ( QString &a_sTok, int a_iPos ) const +{ +#if defined(_UNICODE) + const std::wstring m_strFormulaStd = m_strFormula.toStdWString(); + const std::wstring oprtCharsStd = m_pParser->ValidInfixOprtChars().toStdWString(); +#else + const std::string m_strFormulaStd = m_strFormula.toStdString(); + const std::string oprtCharsStd = m_pParser->ValidInfixOprtChars().toStdString(); +#endif + int iEnd = ( int ) m_strFormulaStd.find_first_not_of ( oprtCharsStd, a_iPos ); + if ( iEnd == ( int ) string_type::npos ) + { + iEnd = ( int ) m_strFormulaStd.length(); + } + + // Assign token string if there was something found + if ( a_iPos != iEnd ) + { +#if defined(_UNICODE) + a_sTok = QString().fromStdWString ( string_type ( m_strFormulaStd.begin() + a_iPos, + m_strFormulaStd.begin() + iEnd ) ); +#else + a_sTok = QString().fromStdString ( string_type ( m_strFormulaStd.begin() + a_iPos, m_strFormulaStd.begin() + iEnd ) ); +#endif + return iEnd; + } + else + { + // There is still the chance of having to deal with an operator consisting exclusively + // of alphabetic characters. + return ExtractToken ( QMUP_CHARS, a_sTok, a_iPos ); + } +} + +//---------------------------------------------------------------------------------------------------------------------- +/** + * @brief Check if a built in operator or other token can be found + * @param a_Tok [out] Operator token if one is found. This can either be a binary operator or an infix operator token. + * @return true if an operator token has been found. + */ +bool QmuParserTokenReader::IsBuiltIn ( token_type &a_Tok ) +{ + const QStringList pOprtDef = m_pParser->GetOprtDef(); + + // Compare token with function and operator strings + // check string for operator/function + for ( int i = 0; i < pOprtDef.size(); ++i ) + { + int len = pOprtDef.at ( i ).length(); + if ( pOprtDef.at ( i ) == m_strFormula.mid ( m_iPos, m_iPos + len ) ) + { + switch ( i ) + { + //case cmAND: + //case cmOR: + //case cmXOR: + 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: + //if (len!=sTok.length()) + // continue; + + // The assignement operator need special treatment + if ( i == cmASSIGN && m_iSynFlags & noASSIGN ) + Error ( ecUNEXPECTED_OPERATOR, m_iPos, pOprtDef.at ( i ) ); + + if ( !m_pParser->HasBuiltInOprt() ) continue; + if ( m_iSynFlags & noOPT ) + { + // Maybe its an infix operator not an operator + // Both operator types can share characters in + // their identifiers + if ( IsInfixOpTok ( a_Tok ) ) + return true; + + Error ( ecUNEXPECTED_OPERATOR, m_iPos, pOprtDef.at ( i ) ); + } + + m_iSynFlags = noBC | noOPT | noARG_SEP | noPOSTOP | noASSIGN | noIF | noELSE; + m_iSynFlags |= ( ( i != cmEND ) && ( i != cmBC ) ) ? noEND : 0; + break; + + case cmBO: + if ( m_iSynFlags & noBO ) + Error ( ecUNEXPECTED_PARENS, m_iPos, pOprtDef.at ( i ) ); + + if ( m_lastTok.GetCode() == cmFUNC ) + m_iSynFlags = noOPT | noEND | noARG_SEP | noPOSTOP | noASSIGN | noIF | noELSE; + else + m_iSynFlags = noBC | noOPT | noEND | noARG_SEP | noPOSTOP | noASSIGN | noIF | noELSE; + + ++m_iBrackets; + break; + + case cmBC: + if ( m_iSynFlags & noBC ) + Error ( ecUNEXPECTED_PARENS, m_iPos, pOprtDef.at ( i ) ); + + m_iSynFlags = noBO | noVAR | noVAL | noFUN | noINFIXOP | noSTR | noASSIGN; + + if ( --m_iBrackets < 0 ) + Error ( ecUNEXPECTED_PARENS, m_iPos, pOprtDef.at ( i ) ); + break; + + case cmELSE: + if ( m_iSynFlags & noELSE ) + Error ( ecUNEXPECTED_CONDITIONAL, m_iPos, pOprtDef.at ( i ) ); + + m_iSynFlags = noBC | noPOSTOP | noEND | noOPT | noIF | noELSE; + break; + + case cmIF: + if ( m_iSynFlags & noIF ) + Error ( ecUNEXPECTED_CONDITIONAL, m_iPos, pOprtDef.at ( i ) ); + + m_iSynFlags = noBC | noPOSTOP | noEND | noOPT | noIF | noELSE; + break; + + default: // The operator is listed in c_DefaultOprt, but not here. This is a bad thing... + Error ( ecINTERNAL_ERROR ); + } // switch operator id + + m_iPos += len; + a_Tok.Set ( ( ECmdCode ) i, pOprtDef.at ( i ) ); + return true; + } // if operator string found + } // end of for all operator strings + + return false; +} + +//---------------------------------------------------------------------------------------------------------------------- +bool QmuParserTokenReader::IsArgSep ( token_type &a_Tok ) +{ + if ( m_strFormula.at ( m_iPos ) == m_cArgSep ) + { + // copy the separator into null terminated string + QString szSep; + szSep[0] = m_cArgSep; + szSep[1] = 0; + + if ( m_iSynFlags & noARG_SEP ) + { + Error ( ecUNEXPECTED_ARG_SEP, m_iPos, szSep ); + } + + m_iSynFlags = noBC | noOPT | noEND | noARG_SEP | noPOSTOP | noASSIGN; + m_iPos++; + a_Tok.Set ( cmARG_SEP, szSep ); + return true; + } + + return false; +} + +//---------------------------------------------------------------------------------------------------------------------- +/** + * @brief Check for End of Formula. + * + * @return true if an end of formula is found false otherwise. + * @param a_Tok [out] If an eof is found the corresponding token will be stored there. + * @throw nothrow + * @sa IsOprt, IsFunTok, IsStrFunTok, IsValTok, IsVarTok, IsString, IsInfixOpTok, IsPostOpTok + */ +bool QmuParserTokenReader::IsEOF ( token_type &a_Tok ) +{ +#if defined(_UNICODE) + const char_type* szFormula = m_strFormula.toStdWString().c_str(); +#else + const char_type* szFormula = m_strFormula.toStdString().c_str(); +#endif + + // check for EOF + if ( !szFormula[m_iPos] /*|| szFormula[m_iPos] == '\n'*/ ) + { + if ( m_iSynFlags & noEND ) + Error ( ecUNEXPECTED_EOF, m_iPos ); + + if ( m_iBrackets > 0 ) + Error ( ecMISSING_PARENS, m_iPos, ")" ); + + m_iSynFlags = 0; + a_Tok.Set ( cmEND ); + return true; + } + + return false; +} + +//---------------------------------------------------------------------------------------------------------------------- +/** + * @brief Check if a string position contains a unary infix operator. + * @return true if a function token has been found false otherwise. + */ +bool QmuParserTokenReader::IsInfixOpTok ( token_type &a_Tok ) +{ + QString sTok; + int iEnd = ExtractToken ( m_pParser->ValidInfixOprtChars(), sTok, m_iPos ); + if ( iEnd == m_iPos ) + return false; + + // iteraterate over all postfix operator strings + funmap_type::const_reverse_iterator it = m_pInfixOprtDef->rbegin(); + for ( ; it != m_pInfixOprtDef->rend(); ++it ) + { + if ( sTok.indexOf ( it->first ) != 0 ) + continue; + + a_Tok.Set ( it->second, it->first ); + m_iPos += ( int ) it->first.length(); + + if ( m_iSynFlags & noINFIXOP ) + Error ( ecUNEXPECTED_OPERATOR, m_iPos, a_Tok.GetAsString() ); + + m_iSynFlags = noPOSTOP | noINFIXOP | noOPT | noBC | noSTR | noASSIGN; + return true; + } + + return false; + + /* + a_Tok.Set(item->second, sTok); + m_iPos = (int)iEnd; + + if (m_iSynFlags & noINFIXOP) + Error(ecUNEXPECTED_OPERATOR, m_iPos, a_Tok.GetAsString()); + + m_iSynFlags = noPOSTOP | noINFIXOP | noOPT | noBC | noSTR | noASSIGN; + return true; + */ +} + +//---------------------------------------------------------------------------------------------------------------------- +/** + * @brief Check whether the token at a given position is a function token. + * @param a_Tok [out] If a value token is found it will be placed here. + * @throw ParserException if Syntaxflags do not allow a function at a_iPos + * @return true if a function token has been found false otherwise. + * @pre [assert] m_pParser!=0 + */ +bool QmuParserTokenReader::IsFunTok ( token_type &a_Tok ) +{ + QString strTok; + int iEnd = ExtractToken ( m_pParser->ValidNameChars(), strTok, m_iPos ); + if ( iEnd == m_iPos ) + return false; + + funmap_type::const_iterator item = m_pFunDef->find ( strTok ); + if ( item == m_pFunDef->end() ) + return false; + + // Check if the next sign is an opening bracket + if ( m_strFormula.at ( iEnd ) != '(' ) + return false; + + a_Tok.Set ( item->second, strTok ); + + m_iPos = iEnd; + if ( m_iSynFlags & noFUN ) + Error ( ecUNEXPECTED_FUN, m_iPos - ( int ) a_Tok.GetAsString().length(), a_Tok.GetAsString() ); + + m_iSynFlags = noANY ^ noBO; + return true; +} + +//---------------------------------------------------------------------------------------------------------------------- +/** + * @brief Check if a string position contains a binary operator. + * @param a_Tok [out] Operator token if one is found. This can either be a binary operator or an infix operator token. + * @return true if an operator token has been found. + */ +bool QmuParserTokenReader::IsOprt ( token_type &a_Tok ) +{ + QString strTok; + + int iEnd = ExtractOperatorToken ( strTok, m_iPos ); + if ( iEnd == m_iPos ) + return false; + + // Check if the operator is a built in operator, if so ignore it here + const QStringList pOprtDef = m_pParser->GetOprtDef(); + QStringList::const_iterator constIterator; + for ( constIterator = pOprtDef.constBegin(); m_pParser->HasBuiltInOprt() && constIterator != pOprtDef.constEnd(); + ++constIterator ) + { + if ( ( *constIterator ) == strTok ) + return false; + } + + // Note: + // All tokens in oprt_bin_maptype are have been sorted by their length + // Long operators must come first! Otherwise short names (like: "add") that + // are part of long token names (like: "add123") will be found instead + // of the long ones. + // Length sorting is done with ascending length so we use a reverse iterator here. + funmap_type::const_reverse_iterator it = m_pOprtDef->rbegin(); + for ( ; it != m_pOprtDef->rend(); ++it ) + { + const QString &sID = it->first; + if ( sID == m_strFormula.mid ( m_iPos, m_iPos + sID.length() ) ) + { + a_Tok.Set ( it->second, strTok ); + + // operator was found + if ( m_iSynFlags & noOPT ) + { + // An operator was found but is not expected to occur at + // this position of the formula, maybe it is an infix + // operator, not a binary operator. Both operator types + // can share characters in their identifiers. + if ( IsInfixOpTok ( a_Tok ) ) + return true; + else + { + // nope, no infix operator + return false; + //Error(ecUNEXPECTED_OPERATOR, m_iPos, a_Tok.GetAsString()); + } + + } + + m_iPos += ( int ) sID.length(); + m_iSynFlags = noBC | noOPT | noARG_SEP | noPOSTOP | noEND | noBC | noASSIGN; + return true; + } + } + + return false; +} + +//---------------------------------------------------------------------------------------------------------------------- +/** + * @brief Check if a string position contains a unary post value operator. + */ +bool QmuParserTokenReader::IsPostOpTok ( token_type &a_Tok ) +{ + // Do not check for postfix operators if they are not allowed at + // the current expression index. + // + // This will fix the bug reported here: + // + // http://sourceforge.net/tracker/index.php?func=detail&aid=3343891&group_id=137191&atid=737979 + // + if ( m_iSynFlags & noPOSTOP ) + return false; + // + + // Tricky problem with equations like "3m+5": + // m is a postfix operator, + is a valid sign for postfix operators and + // for binary operators parser detects "m+" as operator string and + // finds no matching postfix operator. + // + // This is a special case so this routine slightly differs from the other + // token readers. + + // Test if there could be a postfix operator + QString sTok; + int iEnd = ExtractToken ( m_pParser->ValidOprtChars(), sTok, m_iPos ); + if ( iEnd == m_iPos ) + return false; + + // iteraterate over all postfix operator strings + funmap_type::const_reverse_iterator it = m_pPostOprtDef->rbegin(); + for ( ; it != m_pPostOprtDef->rend(); ++it ) + { + if ( sTok.indexOf ( it->first ) != 0 ) + continue; + + a_Tok.Set ( it->second, sTok ); + m_iPos += it->first.length(); + + m_iSynFlags = noVAL | noVAR | noFUN | noBO | noPOSTOP | noSTR | noASSIGN; + return true; + } + + return false; +} + +//---------------------------------------------------------------------------------------------------------------------- +/** + * @brief Check whether the token at a given position is a value token. + * + * Value tokens are either values or constants. + * + * @param a_Tok [out] If a value token is found it will be placed here. + * @return true if a value token has been found. + */ +bool QmuParserTokenReader::IsValTok ( token_type &a_Tok ) +{ + assert ( m_pConstDef ); + assert ( m_pParser ); + + QString strTok; + qreal fVal ( 0 ); + int iEnd ( 0 ); + + // 2.) Check for user defined constant + // Read everything that could be a constant name + iEnd = ExtractToken ( m_pParser->ValidNameChars(), strTok, m_iPos ); + if ( iEnd != m_iPos ) + { + valmap_type::const_iterator item = m_pConstDef->find ( strTok ); + if ( item != m_pConstDef->end() ) + { + m_iPos = iEnd; + a_Tok.SetVal ( item->second, strTok ); + + if ( m_iSynFlags & noVAL ) + Error ( ecUNEXPECTED_VAL, m_iPos - strTok.length(), strTok ); + + m_iSynFlags = noVAL | noVAR | noFUN | noBO | noINFIXOP | noSTR | noASSIGN; + return true; + } + } + + // 3.call the value recognition functions provided by the user + // Call user defined value recognition functions + std::list::const_iterator item = m_vIdentFun.begin(); + for ( item = m_vIdentFun.begin(); item != m_vIdentFun.end(); ++item ) + { + int iStart = m_iPos; + if ( ( *item ) ( m_strFormula.mid ( m_iPos ), &m_iPos, &fVal ) == 1 ) + { + strTok = m_strFormula.mid ( iStart, m_iPos ); + if ( m_iSynFlags & noVAL ) + Error ( ecUNEXPECTED_VAL, m_iPos - strTok.length(), strTok ); + + a_Tok.SetVal ( fVal, strTok ); + m_iSynFlags = noVAL | noVAR | noFUN | noBO | noINFIXOP | noSTR | noASSIGN; + return true; + } + } + + return false; +} + +//---------------------------------------------------------------------------------------------------------------------- +/** + * @brief Check wheter a token at a given position is a variable token. + * @param a_Tok [out] If a variable token has been found it will be placed here. + * @return true if a variable token has been found. + */ +bool QmuParserTokenReader::IsVarTok ( token_type &a_Tok ) +{ + if ( !m_pVarDef->size() ) + return false; + + QString strTok; + int iEnd = ExtractToken ( m_pParser->ValidNameChars(), strTok, m_iPos ); + if ( iEnd == m_iPos ) + return false; + + varmap_type::const_iterator item = m_pVarDef->find ( strTok ); + if ( item == m_pVarDef->end() ) + return false; + + if ( m_iSynFlags & noVAR ) + Error ( ecUNEXPECTED_VAR, m_iPos, strTok ); + + m_pParser->OnDetectVar ( &m_strFormula, m_iPos, iEnd ); + + m_iPos = iEnd; + a_Tok.SetVar ( item->second, strTok ); + m_UsedVar[item->first] = item->second; // Add variable to used-var-list + + m_iSynFlags = noVAL | noVAR | noFUN | noBO | noINFIXOP | noSTR; + +// Zur Info hier die SynFlags von IsVal(): +// m_iSynFlags = noVAL | noVAR | noFUN | noBO | noINFIXOP | noSTR | noASSIGN; + return true; +} + +//---------------------------------------------------------------------------------------------------------------------- +bool QmuParserTokenReader::IsStrVarTok ( token_type &a_Tok ) +{ + if ( !m_pStrVarDef || !m_pStrVarDef->size() ) + return false; + + QString strTok; + int iEnd = ExtractToken ( m_pParser->ValidNameChars(), strTok, m_iPos ); + if ( iEnd == m_iPos ) + return false; + + strmap_type::const_iterator item = m_pStrVarDef->find ( strTok ); + if ( item == m_pStrVarDef->end() ) + return false; + + if ( m_iSynFlags & noSTR ) + Error ( ecUNEXPECTED_VAR, m_iPos, strTok ); + + m_iPos = iEnd; + if ( !m_pParser->m_vStringVarBuf.size() ) + Error ( ecINTERNAL_ERROR ); + + a_Tok.SetString ( m_pParser->m_vStringVarBuf[item->second], m_pParser->m_vStringVarBuf.size() ); + + m_iSynFlags = noANY ^ ( noBC | noOPT | noEND | noARG_SEP ); + return true; +} + + +//---------------------------------------------------------------------------------------------------------------------- +/** + * @brief Check wheter a token at a given position is an undefined variable. + * + * @param a_Tok [out] If a variable tom_pParser->m_vStringBufken has been found it will be placed here. + * @return true if a variable token has been found. + * @throw nothrow + */ +bool QmuParserTokenReader::IsUndefVarTok ( token_type &a_Tok ) +{ + QString strTok; + int iEnd ( ExtractToken ( m_pParser->ValidNameChars(), strTok, m_iPos ) ); + if ( iEnd == m_iPos ) + { + return false; + } + + if ( m_iSynFlags & noVAR ) + { + // 20061021 added token string strTok instead of a_Tok.GetAsString() as the + // token identifier. + // related bug report: + // http://sourceforge.net/tracker/index.php?func=detail&aid=1578779&group_id=137191&atid=737979 + Error ( ecUNEXPECTED_VAR, m_iPos - ( int ) a_Tok.GetAsString().length(), strTok ); + } + + // If a factory is available implicitely create new variables + if ( m_pFactory ) + { + qreal *fVar = m_pFactory ( strTok, m_pFactoryData ); + a_Tok.SetVar ( fVar, strTok ); + + // Do not use m_pParser->DefineVar( strTok, fVar ); + // in order to define the new variable, it will clear the + // m_UsedVar array which will kill previousely defined variables + // from the list + // This is safe because the new variable can never override an existing one + // because they are checked first! + ( *m_pVarDef ) [strTok] = fVar; + m_UsedVar[strTok] = fVar; // Add variable to used-var-list + } + else + { + a_Tok.SetVar ( ( qreal* ) &m_fZero, strTok ); + m_UsedVar[strTok] = 0; // Add variable to used-var-list + } + + m_iPos = iEnd; + + // Call the variable factory in order to let it define a new parser variable + m_iSynFlags = noVAL | noVAR | noFUN | noBO | noPOSTOP | noINFIXOP | noSTR; + return true; +} + + +//---------------------------------------------------------------------------------------------------------------------- +/** + * @brief Check wheter a token at a given position is a string. + * @param a_Tok [out] If a variable token has been found it will be placed here. + * @return true if a string token has been found. + * @sa IsOprt, IsFunTok, IsStrFunTok, IsValTok, IsVarTok, IsEOF, IsInfixOpTok, IsPostOpTok + * @throw nothrow + */ +bool QmuParserTokenReader::IsString ( token_type &a_Tok ) +{ + if ( m_strFormula[m_iPos] != '"' ) + return false; + + QString strBuf ( m_strFormula[m_iPos + 1] ); + int iEnd ( 0 ), iSkip ( 0 ); + + // parser over escaped '\"' end replace them with '"' + for ( iEnd = strBuf.indexOf ( "\"" ); iEnd != 0 && iEnd != -1; iEnd = strBuf.indexOf ( "\"", iEnd ) ) + { + if ( strBuf[iEnd - 1] != '\\' ) break; + strBuf.replace ( iEnd - 1, 2, "\"" ); + iSkip++; + } + + if ( iEnd == -1 ) + Error ( ecUNTERMINATED_STRING, m_iPos, "\"" ); + + QString strTok = strBuf.mid ( 0, iEnd ); + + if ( m_iSynFlags & noSTR ) + Error ( ecUNEXPECTED_STR, m_iPos, strTok ); + + m_pParser->m_vStringBuf.push_back ( strTok ); // Store string in internal buffer + a_Tok.SetString ( strTok, m_pParser->m_vStringBuf.size() ); + + m_iPos += ( int ) strTok.length() + 2 + iSkip; // +2 wg Anfhrungszeichen; +iSkip fr entfernte escape zeichen + m_iSynFlags = noANY ^ ( noARG_SEP | noBC | noOPT | noEND ); + + return true; +} + +//---------------------------------------------------------------------------------------------------------------------- +/** + * @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 QmuParserTokenReader::Error ( EErrorCodes a_iErrc, int a_iPos, const QString &a_sTok ) const +{ + m_pParser->Error ( a_iErrc, a_iPos, a_sTok ); +} + +//---------------------------------------------------------------------------------------------------------------------- +void QmuParserTokenReader::SetArgSep ( char_type cArgSep ) +{ + m_cArgSep = cArgSep; +} + +//---------------------------------------------------------------------------------------------------------------------- +QChar QmuParserTokenReader::GetArgSep() const +{ + return m_cArgSep; +} +} // namespace qmu + diff --git a/src/libs/qmuparser/qmuparsertokenreader.h b/src/libs/qmuparser/qmuparsertokenreader.h index de73388eb..48ea055be 100644 --- a/src/libs/qmuparser/qmuparsertokenreader.h +++ b/src/libs/qmuparser/qmuparsertokenreader.h @@ -35,8 +35,8 @@ #include "qmuparserdef.h" #include "qmuparsertoken.h" -/** \file - \brief This file contains the parser token reader definition. +/** @file + @brief This file contains the parser token reader definition. */ @@ -45,7 +45,7 @@ namespace qmu // Forward declaration class QmuParserBase; - /** \brief Token reader for the ParserBase class. + /** @brief Token reader for the ParserBase class. */ class QmuParserTokenReader @@ -75,7 +75,7 @@ namespace qmu private: - /** \brief Syntax codes. + /** @brief Syntax codes. The syntax codes control the syntax check done during the first time parsing of the expression string. They are flags that indicate which tokens are allowed next