From: David Wells Date: Mon, 28 Mar 2022 16:46:40 +0000 (-0400) Subject: Update bundled muParser to 2.3.3 X-Git-Tag: v9.4.0-rc1~318^2~2 X-Git-Url: https://gitweb.dealii.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=8e36bc3aa7d5e0a2a5a365b805bc06fd691eaf4b;p=dealii.git Update bundled muParser to 2.3.3 --- diff --git a/bundled/muparser_v2_2_4/License.txt b/bundled/muparser_v2_2_4/License.txt deleted file mode 100644 index c4c0d2b131..0000000000 --- a/bundled/muparser_v2_2_4/License.txt +++ /dev/null @@ -1,35 +0,0 @@ -####################################################################### -# # -# # -# __________ # -# _____ __ __\______ \_____ _______ ______ ____ _______ # -# / \ | | \| ___/\__ \ \_ __ \/ ___/_/ __ \\_ __ \ # -# | Y Y \| | /| | / __ \_| | \/\___ \ \ ___/ | | \/ # -# |__|_| /|____/ |____| (____ /|__| /____ > \___ >|__| # -# \/ \/ \/ \/ # -# Fast math parser Library # -# # -# Copyright (C) 2011 Ingo Berg # -# # -# Web: muparser.beltoforion.de # -# e-mail: muparser@beltoforion.de # -# # -# # -####################################################################### - - - 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. - OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/bundled/muparser_v2_2_4/include/muParser.h b/bundled/muparser_v2_2_4/include/muParser.h deleted file mode 100644 index a1caec4c8b..0000000000 --- a/bundled/muparser_v2_2_4/include/muParser.h +++ /dev/null @@ -1,115 +0,0 @@ -/* - __________ - _____ __ __\______ \_____ _______ ______ ____ _______ - / \ | | \| ___/\__ \ \_ __ \/ ___/_/ __ \\_ __ \ - | Y Y \| | /| | / __ \_| | \/\___ \ \ ___/ | | \/ - |__|_| /|____/ |____| (____ /|__| /____ > \___ >|__| - \/ \/ \/ \/ - Copyright (C) 2013 Ingo Berg - - 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 MU_PARSER_H -#define MU_PARSER_H - -//--- Standard includes ------------------------------------------------------------------------ -#include - -//--- Parser includes -------------------------------------------------------------------------- -#include "muParserBase.h" -#include "muParserTemplateMagic.h" - -/** \file - \brief Definition of the standard floating point parser. -*/ - -namespace mu -{ - /** \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)beltoforion.de -
- */ - /* final */ class Parser : public ParserBase - { - public: - - Parser(); - - virtual void InitCharSets() override; - virtual void InitFun() override; - virtual void InitConst() override; - virtual void InitOprt() override; - virtual void OnDetectVar(string_type *pExpr, int &nStart, int &nEnd) override; - - value_type Diff(value_type *a_Var, - value_type a_fPos, - value_type a_fEpsilon = 0) const; - - protected: - - // Trigonometric functions - static value_type Sin(value_type); - static value_type Cos(value_type); - static value_type Tan(value_type); - static value_type Tan2(value_type, value_type); - // arcus functions - static value_type ASin(value_type); - static value_type ACos(value_type); - static value_type ATan(value_type); - static value_type ATan2(value_type, value_type); - - // hyperbolic functions - static value_type Sinh(value_type); - static value_type Cosh(value_type); - static value_type Tanh(value_type); - // arcus hyperbolic functions - static value_type ASinh(value_type); - static value_type ACosh(value_type); - static value_type ATanh(value_type); - // Logarithm functions - static value_type Log2(value_type); // Logarithm Base 2 - static value_type Log10(value_type); // Logarithm Base 10 - static value_type Ln(value_type); // Logarithm Base e (natural logarithm) - // misc - static value_type Exp(value_type); - static value_type Abs(value_type); - static value_type Sqrt(value_type); - static value_type Rint(value_type); - static value_type Sign(value_type); - - // Prefix operators - // !!! Unary Minus is a MUST if you want to use negative signs !!! - static value_type UnaryMinus(value_type); - static value_type UnaryPlus(value_type); - - // Functions with variable number of arguments - static value_type Sum(const value_type*, int); // sum - static value_type Avg(const value_type*, int); // mean value - static value_type Min(const value_type*, int); // minimum - static value_type Max(const value_type*, int); // maximum - - static int IsVal(const char_type* a_szExpr, int *a_iPos, value_type *a_fVal); - }; -} // namespace mu - -#endif - diff --git a/bundled/muparser_v2_2_4/include/muParserBase.h b/bundled/muparser_v2_2_4/include/muParserBase.h deleted file mode 100644 index 871724be84..0000000000 --- a/bundled/muparser_v2_2_4/include/muParserBase.h +++ /dev/null @@ -1,317 +0,0 @@ -/* - __________ - _____ __ __\______ \_____ _______ ______ ____ _______ - / \ | | \| ___/\__ \ \_ __ \/ ___/_/ __ \\_ __ \ - | Y Y \| | /| | / __ \_| | \/\___ \ \ ___/ | | \/ - |__|_| /|____/ |____| (____ /|__| /____ > \___ >|__| - \/ \/ \/ \/ - Copyright (C) 2013 Ingo Berg - - 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 MU_PARSER_BASE_H -#define MU_PARSER_BASE_H - -//--- Standard includes ------------------------------------------------------------------------ -#include -#include -#include -#include -#include -#include -#include - -//--- Parser includes -------------------------------------------------------------------------- -#include "muParserDef.h" -#include "muParserStack.h" -#include "muParserTokenReader.h" -#include "muParserBytecode.h" -#include "muParserError.h" - - -namespace mu -{ -/** \file - \brief This file contains the class definition of the muparser 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 ParserBase -{ -friend class ParserTokenReader; - -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 value_type (ParserBase::*ParseFunction)() const; - - /** \brief Type used for storing an array of values. */ - typedef std::vector valbuf_type; - - /** \brief Type for a vector of strings. */ - typedef std::vector stringbuf_type; - - /** \brief Typedef for the token reader. */ - typedef ParserTokenReader token_reader_type; - - /** \brief Type used for parser tokens. */ - typedef ParserToken token_type; - - /** \brief Maximum number of threads spawned by OpenMP when using the bulk mode. */ - static const int s_MaxNumOpenMPThreads = 16; - - public: - - /** \brief Type of the error class. - - Included for backwards compatibility. - */ - typedef ParserError exception_type; - - static void EnableDebugDump(bool bDumpCmd, bool bDumpStack); - - ParserBase(); - ParserBase(const ParserBase &a_Parser); - ParserBase& operator=(const ParserBase &a_Parser); - - virtual ~ParserBase(); - - value_type Eval() const; - value_type* Eval(int &nStackSize) const; - void Eval(value_type *results, int nBulkSize); - - int GetNumResults() const; - - void SetExpr(const string_type &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 mu::ParserBase::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 string_type &a_strName, T a_pFun, bool a_bAllowOpt = true) - { - AddCallback( a_strName, ParserCallback(a_pFun, a_bAllowOpt), m_FunDef, ValidNameChars() ); - } - - void DefineOprt(const string_type &a_strName, - fun_type2 a_pFun, - unsigned a_iPri=0, - EOprtAssociativity a_eAssociativity = oaLEFT, - bool a_bAllowOpt = false); - void DefineConst(const string_type &a_sName, value_type a_fVal); - void DefineStrConst(const string_type &a_sName, const string_type &a_strVal); - void DefineVar(const string_type &a_sName, value_type *a_fVar); - void DefinePostfixOprt(const string_type &a_strFun, fun_type1 a_pOprt, bool a_bAllowOpt=true); - void DefineInfixOprt(const string_type &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 string_type &a_strVarName); - const varmap_type& GetUsedVar() const; - const varmap_type& GetVar() const; - const valmap_type& GetConst() const; - const string_type& GetExpr() const; - const funmap_type& GetFunDef() const; - string_type GetVersion(EParserVersionInfo eInfo = pviFULL) const; - - const char_type ** GetOprtDef() const; - void DefineNameChars(const char_type *a_szCharset); - void DefineOprtChars(const char_type *a_szCharset); - void DefineInfixOprtChars(const char_type *a_szCharset); - - const char_type* ValidNameChars() const; - const char_type* ValidOprtChars() const; - const char_type* ValidInfixOprtChars() const; - - void SetArgSep(char_type cArgSep); - char_type GetArgSep() const; - - void Error(EErrorCodes a_iErrc, - int a_iPos = (int)mu::string_type::npos, - const string_type &a_strTok = string_type() ) const; - - protected: - - void Init(); - - virtual void InitCharSets() = 0; - virtual void InitFun() = 0; - virtual void InitConst() = 0; - virtual void InitOprt() = 0; - - virtual void OnDetectVar(string_type *pExpr, int &nStart, int &nEnd); - - static const char_type *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 - { - 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 override - { - return m_cDecPoint; - } - - virtual char_type do_thousands_sep() const override - { - return m_cThousandsSep; - } - - virtual std::string do_grouping() const override - { - // fix for issue 4: https://code.google.com/p/muparser/issues/detail?id=4 - // courtesy of Jens Bartsch - // original code: - // return std::string(1, (char)m_nGroup); - // new code: - return std::string(1, (char)(m_cThousandsSep > 0 ? m_nGroup : CHAR_MAX)); - } - - private: - - int m_nGroup; - char_type m_cDecPoint; - char_type m_cThousandsSep; - }; - - private: - - void Assign(const ParserBase &a_Parser); - void InitTokenReader(); - void ReInit() const; - - void AddCallback( const string_type &a_strName, - const ParserCallback &a_Callback, - funmap_type &a_Storage, - const char_type *a_szCharSet ); - - void ApplyRemainingOprt(ParserStack &a_stOpt, - ParserStack &a_stVal) const; - void ApplyBinOprt(ParserStack &a_stOpt, - ParserStack &a_stVal) const; - - void ApplyIfElse(ParserStack &a_stOpt, - ParserStack &a_stVal) const; - - void ApplyFunc(ParserStack &a_stOpt, - ParserStack &a_stVal, - int iArgCount) const; - - token_type ApplyStrFunc(const token_type &a_FunTok, - const std::vector &a_vArg) const; - - int GetOprtPrecedence(const token_type &a_Tok) const; - EOprtAssociativity GetOprtAssociativity(const token_type &a_Tok) const; - - void CreateRPN() const; - - value_type ParseString() const; - value_type ParseCmdCode() const; - value_type ParseCmdCodeBulk(int nOffset, int nThreadID) const; - - void CheckName(const string_type &a_strName, const string_type &a_CharSet) const; - void CheckOprt(const string_type &a_sName, - const ParserCallback &a_Callback, - const string_type &a_szCharSet) const; - - void StackDump(const ParserStack &a_stVal, - const ParserStack &a_stOprt) const; - - /** \brief Pointer to the parser function. - - Eval() calls the function whose address is stored there. - */ - mutable ParseFunction m_pParseFormula; - mutable ParserByteCode m_vRPN; ///< The Bytecode class. - mutable stringbuf_type m_vStringBuf; ///< String buffer, used for storing string function arguments - stringbuf_type m_vStringVarBuf; - - std::unique_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 - - string_type m_sNameChars; ///< Charset for names - string_type m_sOprtChars; ///< Charset for postfix/ binary operator tokens - string_type 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 mu - -#endif - diff --git a/bundled/muparser_v2_2_4/include/muParserBytecode.h b/bundled/muparser_v2_2_4/include/muParserBytecode.h deleted file mode 100644 index f7bf9c6b9f..0000000000 --- a/bundled/muparser_v2_2_4/include/muParserBytecode.h +++ /dev/null @@ -1,141 +0,0 @@ -/* - __________ - _____ __ __\______ \_____ _______ ______ ____ _______ - / \ | | \| ___/\__ \ \_ __ \/ ___/_/ __ \\_ __ \ - | Y Y \| | /| | / __ \_| | \/\___ \ \ ___/ | | \/ - |__|_| /|____/ |____| (____ /|__| /____ > \___ >|__| - \/ \/ \/ \/ - Copyright (C) 2004-2013 Ingo Berg - - 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 MU_PARSER_BYTECODE_H -#define MU_PARSER_BYTECODE_H - -#include -#include -#include -#include - -#include "muParserDef.h" -#include "muParserError.h" -#include "muParserToken.h" - -/** \file - \brief Definition of the parser bytecode class. -*/ - - -namespace mu -{ - struct SToken - { - ECmdCode Cmd; - int StackPos; - - union - { - struct //SValData - { - value_type *ptr; - value_type data; - value_type 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 //SOprtData - { - value_type *ptr; - int offset; - } Oprt; - } u; - }; - - - /** \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 ParserByteCode -{ -private: - - /** \brief Token type for internal use only. */ - typedef ParserToken token_type; - - /** \brief Token vector for storing the RPN. */ - typedef std::vector rpn_type; - - /** \brief Position in the Calculation array. */ - unsigned m_iStackPos; - - /** \brief Maximum size needed for the stack. */ - std::size_t m_iMaxStackSize; - - /** \brief The actual rpn storage. */ - rpn_type m_vRPN; - - bool m_bEnableOptimizer; - - void ConstantFolding(ECmdCode a_Oprt); - -public: - - ParserByteCode(); - ParserByteCode(const ParserByteCode &a_ByteCode); - ParserByteCode& operator=(const ParserByteCode &a_ByteCode); - void Assign(const ParserByteCode &a_ByteCode); - - void AddVar(value_type *a_pVar); - void AddVal(value_type a_fVal); - void AddOp(ECmdCode a_Oprt); - void AddIfElse(ECmdCode a_Oprt); - void AddAssignOp(value_type *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(); -}; - -} // namespace mu - -#endif - - diff --git a/bundled/muparser_v2_2_4/include/muParserCallback.h b/bundled/muparser_v2_2_4/include/muParserCallback.h deleted file mode 100644 index cdaffbbe21..0000000000 --- a/bundled/muparser_v2_2_4/include/muParserCallback.h +++ /dev/null @@ -1,121 +0,0 @@ -/* - __________ - _____ __ __\______ \_____ _______ ______ ____ _______ - / \ | | \| ___/\__ \ \_ __ \/ ___/_/ __ \\_ __ \ - | Y Y \| | /| | / __ \_| | \/\___ \ \ ___/ | | \/ - |__|_| /|____/ |____| (____ /|__| /____ > \___ >|__| - \/ \/ \/ \/ - Copyright (C) 2004-2011 Ingo Berg - - 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 MU_PARSER_CALLBACK_H -#define MU_PARSER_CALLBACK_H - -#include "muParserDef.h" - -/** \file - \brief Definition of the parser callback class. -*/ - -namespace mu -{ - -/** \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 ParserCallback -{ -public: - ParserCallback(fun_type0 a_pFun, bool a_bAllowOpti); - ParserCallback(fun_type1 a_pFun, bool a_bAllowOpti, int a_iPrec = -1, ECmdCode a_iCode=cmFUNC); - ParserCallback(fun_type2 a_pFun, bool a_bAllowOpti, int a_iPrec, EOprtAssociativity a_eAssociativity); - ParserCallback(fun_type2 a_pFun, bool a_bAllowOpti); - ParserCallback(fun_type3 a_pFun, bool a_bAllowOpti); - ParserCallback(fun_type4 a_pFun, bool a_bAllowOpti); - ParserCallback(fun_type5 a_pFun, bool a_bAllowOpti); - ParserCallback(fun_type6 a_pFun, bool a_bAllowOpti); - ParserCallback(fun_type7 a_pFun, bool a_bAllowOpti); - ParserCallback(fun_type8 a_pFun, bool a_bAllowOpti); - ParserCallback(fun_type9 a_pFun, bool a_bAllowOpti); - ParserCallback(fun_type10 a_pFun, bool a_bAllowOpti); - - ParserCallback(bulkfun_type0 a_pFun, bool a_bAllowOpti); - ParserCallback(bulkfun_type1 a_pFun, bool a_bAllowOpti); - ParserCallback(bulkfun_type2 a_pFun, bool a_bAllowOpti); - ParserCallback(bulkfun_type3 a_pFun, bool a_bAllowOpti); - ParserCallback(bulkfun_type4 a_pFun, bool a_bAllowOpti); - ParserCallback(bulkfun_type5 a_pFun, bool a_bAllowOpti); - ParserCallback(bulkfun_type6 a_pFun, bool a_bAllowOpti); - ParserCallback(bulkfun_type7 a_pFun, bool a_bAllowOpti); - ParserCallback(bulkfun_type8 a_pFun, bool a_bAllowOpti); - ParserCallback(bulkfun_type9 a_pFun, bool a_bAllowOpti); - ParserCallback(bulkfun_type10 a_pFun, bool a_bAllowOpti); - - ParserCallback(multfun_type a_pFun, bool a_bAllowOpti); - ParserCallback(strfun_type1 a_pFun, bool a_bAllowOpti); - ParserCallback(strfun_type2 a_pFun, bool a_bAllowOpti); - ParserCallback(strfun_type3 a_pFun, bool a_bAllowOpti); - ParserCallback(); - ParserCallback(const ParserCallback &a_Fun); - - // deall.II: added the following line to remove Wdeprecated-copy warnings: - ParserCallback& operator=(const ParserCallback&)=default; - - ParserCallback* Clone() const; - - bool IsOptimizable() const; - void* GetAddr() const; - ECmdCode GetCode() const; - ETypeCode GetType() const; - int GetPri() const; - EOprtAssociativity GetAssociativity() 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. - */ - 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 - ECmdCode m_iCode; - ETypeCode m_iType; - bool m_bAllowOpti; ///< Flag indication optimizeability -}; - -//------------------------------------------------------------------------------ -/** \brief Container for Callback objects. */ -typedef std::map funmap_type; - -} // namespace mu - -#endif - diff --git a/bundled/muparser_v2_2_4/include/muParserDef.h b/bundled/muparser_v2_2_4/include/muParserDef.h deleted file mode 100644 index 5842921781..0000000000 --- a/bundled/muparser_v2_2_4/include/muParserDef.h +++ /dev/null @@ -1,368 +0,0 @@ -/* - __________ - _____ __ __\______ \_____ _______ ______ ____ _______ - / \ | | \| ___/\__ \ \_ __ \/ ___/_/ __ \\_ __ \ - | Y Y \| | /| | / __ \_| | \/\___ \ \ ___/ | | \/ - |__|_| /|____/ |____| (____ /|__| /____ > \___ >|__| - \/ \/ \/ \/ - Copyright (C) 2014 Ingo Berg - - 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 MUP_DEF_H -#define MUP_DEF_H - -#include -#include -#include -#include - -#include "muParserFixes.h" - -/** \file - \brief This file contains standard definitions used by the parser. -*/ - -#define MUP_VERSION _T("2.2.4") -#define MUP_VERSION_DATE _T("20141210; GC") - -#define MUP_CHARS _T("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ") - -/** \brief If this macro is defined mathematical exceptions (div by zero) will be thrown as exceptions. */ -//#define MUP_MATH_EXCEPTIONS - -/** \brief Define the base datatype for values. - - This datatype must be a built in value type. You can not use custom classes. - It should be working with all types except "int"! -*/ -#define MUP_BASETYPE double - -/** \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 MUP_STRING_TYPE std::wstring - - #if !defined(_T) - #define _T(x) L##x - #endif // not defined _T -#else - #ifndef _T - #define _T(x) x - #endif - - /** \brief Definition of the basic parser string type. */ - #define MUP_STRING_TYPE std::string -#endif - -#if defined(_DEBUG) - /** \brief Debug macro to force an abortion of the programm with a certain message. - */ - #define MUP_FAIL(MSG) \ - { \ - bool MSG=false; \ - assert(MSG); \ - } - - /** \brief An assertion that does not kill the program. - - This macro is neutralised in UNICODE builds. It's - too difficult to translate. - */ - #define MUP_ASSERT(COND) \ - if (!(COND)) \ - { \ - stringstream_type ss; \ - ss << _T("Assertion \"") _T(#COND) _T("\" failed: ") \ - << __FILE__ << _T(" line ") \ - << __LINE__ << _T("."); \ - throw ParserError( ss.str() ); \ - } -#else - #define MUP_FAIL(MSG) - #define MUP_ASSERT(COND) -#endif - - -namespace mu -{ -#if defined(_UNICODE) - - //------------------------------------------------------------------------------ - /** \brief Encapsulate wcout. */ - inline std::wostream& console() - { - return std::wcout; - } - - /** \brief Encapsulate cin. */ - inline std::wistream& console_in() - { - return std::wcin; - } - -#else - - /** \brief Encapsulate cout. - - Used for supporting UNICODE more easily. - */ - inline std::ostream& console() - { - return std::cout; - } - - /** \brief Encapsulate cin. - - Used for supporting UNICODE more easily. - */ - inline std::istream& console_in() - { - return std::cin; - } - -#endif - - //------------------------------------------------------------------------------ - /** \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 numeric datatype used by the parser. - - Normally this is a floating point type either single or double precision. - */ - typedef MUP_BASETYPE value_type; - - /** \brief The stringtype used by the parser. - - Depends on wether UNICODE is used or not. - */ - typedef MUP_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 value_type (*generic_fun_type)(); - - /** \brief Callback type used for functions without arguments. */ - typedef value_type (*fun_type0)(); - - /** \brief Callback type used for functions with a single arguments. */ - typedef value_type (*fun_type1)(value_type); - - /** \brief Callback type used for functions with two arguments. */ - typedef value_type (*fun_type2)(value_type, value_type); - - /** \brief Callback type used for functions with three arguments. */ - typedef value_type (*fun_type3)(value_type, value_type, value_type); - - /** \brief Callback type used for functions with four arguments. */ - typedef value_type (*fun_type4)(value_type, value_type, value_type, value_type); - - /** \brief Callback type used for functions with five arguments. */ - typedef value_type (*fun_type5)(value_type, value_type, value_type, value_type, value_type); - - /** \brief Callback type used for functions with five arguments. */ - typedef value_type (*fun_type6)(value_type, value_type, value_type, value_type, value_type, value_type); - - /** \brief Callback type used for functions with five arguments. */ - typedef value_type (*fun_type7)(value_type, value_type, value_type, value_type, value_type, value_type, value_type); - - /** \brief Callback type used for functions with five arguments. */ - typedef value_type (*fun_type8)(value_type, value_type, value_type, value_type, value_type, value_type, value_type, value_type); - - /** \brief Callback type used for functions with five arguments. */ - typedef value_type (*fun_type9)(value_type, value_type, value_type, value_type, value_type, value_type, value_type, value_type, value_type); - - /** \brief Callback type used for functions with five arguments. */ - typedef value_type (*fun_type10)(value_type, value_type, value_type, value_type, value_type, value_type, value_type, value_type, value_type, value_type); - - /** \brief Callback type used for functions without arguments. */ - typedef value_type (*bulkfun_type0)(int, int); - - /** \brief Callback type used for functions with a single arguments. */ - typedef value_type (*bulkfun_type1)(int, int, value_type); - - /** \brief Callback type used for functions with two arguments. */ - typedef value_type (*bulkfun_type2)(int, int, value_type, value_type); - - /** \brief Callback type used for functions with three arguments. */ - typedef value_type (*bulkfun_type3)(int, int, value_type, value_type, value_type); - - /** \brief Callback type used for functions with four arguments. */ - typedef value_type (*bulkfun_type4)(int, int, value_type, value_type, value_type, value_type); - - /** \brief Callback type used for functions with five arguments. */ - typedef value_type (*bulkfun_type5)(int, int, value_type, value_type, value_type, value_type, value_type); - - /** \brief Callback type used for functions with five arguments. */ - typedef value_type (*bulkfun_type6)(int, int, value_type, value_type, value_type, value_type, value_type, value_type); - - /** \brief Callback type used for functions with five arguments. */ - typedef value_type (*bulkfun_type7)(int, int, value_type, value_type, value_type, value_type, value_type, value_type, value_type); - - /** \brief Callback type used for functions with five arguments. */ - typedef value_type (*bulkfun_type8)(int, int, value_type, value_type, value_type, value_type, value_type, value_type, value_type, value_type); - - /** \brief Callback type used for functions with five arguments. */ - typedef value_type (*bulkfun_type9)(int, int, value_type, value_type, value_type, value_type, value_type, value_type, value_type, value_type, value_type); - - /** \brief Callback type used for functions with five arguments. */ - typedef value_type (*bulkfun_type10)(int, int, value_type, value_type, value_type, value_type, value_type, value_type, value_type, value_type, value_type, value_type); - - /** \brief Callback type used for functions with a variable argument list. */ - typedef value_type (*multfun_type)(const value_type*, int); - - /** \brief Callback type used for functions taking a string as an argument. */ - typedef value_type (*strfun_type1)(const char_type*); - - /** \brief Callback type used for functions taking a string and a value as arguments. */ - typedef value_type (*strfun_type2)(const char_type*, value_type); - - /** \brief Callback type used for functions taking a string and two values as arguments. */ - typedef value_type (*strfun_type3)(const char_type*, value_type, value_type); - - /** \brief Callback used for functions that identify values in a string. */ - typedef int (*identfun_type)(const char_type *sExpr, int *nPos, value_type *fVal); - - /** \brief Callback used for variable creation factory functions. */ - typedef value_type* (*facfun_type)(const char_type*, void*); -} // end of namespace - -#endif - diff --git a/bundled/muparser_v2_2_4/include/muParserError.h b/bundled/muparser_v2_2_4/include/muParserError.h deleted file mode 100644 index 7f88e99192..0000000000 --- a/bundled/muparser_v2_2_4/include/muParserError.h +++ /dev/null @@ -1,176 +0,0 @@ -/* - __________ - _____ __ __\______ \_____ _______ ______ ____ _______ - / \ | | \| ___/\__ \ \_ __ \/ ___/_/ __ \\_ __ \ - | Y Y \| | /| | / __ \_| | \/\___ \ \ ___/ | | \/ - |__|_| /|____/ |____| (____ /|__| /____ > \___ >|__| - \/ \/ \/ \/ - Copyright (C) 2004-2011 Ingo Berg - - 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 MU_PARSER_ERROR_H -#define MU_PARSER_ERROR_H - -#include -#include -#include -#include -#include -#include - -#include "muParserDef.h" - -/** \file - \brief This file defines the error class used by the parser. -*/ - -namespace mu -{ - -/** \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, - - ecUNREASONABLE_NUMBER_OF_COMPUTATIONS = 35, - - // internal errors - ecINTERNAL_ERROR = 36, ///< 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 ParserErrorMsg -{ -public: - typedef ParserErrorMsg self_type; - - ParserErrorMsg& operator=(const ParserErrorMsg &); - ParserErrorMsg(const ParserErrorMsg&); - ParserErrorMsg(); - - ~ParserErrorMsg(); - - static const ParserErrorMsg& Instance(); - string_type operator[](unsigned a_iIdx) const; - -private: - std::vector 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 ParserError -{ -private: - - /** \brief Replace all ocuurences of a substring with another string. */ - void ReplaceSubString( string_type &strSource, - const string_type &strFind, - const string_type &strReplaceWith); - void Reset(); - -public: - - ParserError(); - explicit ParserError(EErrorCodes a_iErrc); - explicit ParserError(const string_type &sMsg); - ParserError( EErrorCodes a_iErrc, - const string_type &sTok, - const string_type &sFormula = string_type(), - int a_iPos = -1); - ParserError( EErrorCodes a_iErrc, - int a_iPos, - const string_type &sTok); - ParserError( const char_type *a_szMsg, - int a_iPos = -1, - const string_type &sTok = string_type()); - ParserError(const ParserError &a_Obj); - ParserError& operator=(const ParserError &a_Obj); - ~ParserError(); - - void SetFormula(const string_type &a_strFormula); - const string_type& GetExpr() const; - const string_type& GetMsg() const; - int GetPos() const; - const string_type& GetToken() const; - EErrorCodes GetCode() const; - -private: - string_type m_strMsg; ///< The message string - string_type m_strFormula; ///< Formula string - string_type m_strTok; ///< Token related with the error - int m_iPos; ///< Formula position related to the error - EErrorCodes m_iErrc; ///< Error code - const ParserErrorMsg &m_ErrMsg; -}; - -} // namespace mu - -#endif - diff --git a/bundled/muparser_v2_2_4/include/muParserFixes.h b/bundled/muparser_v2_2_4/include/muParserFixes.h deleted file mode 100644 index 1cd15e02ed..0000000000 --- a/bundled/muparser_v2_2_4/include/muParserFixes.h +++ /dev/null @@ -1,62 +0,0 @@ -/* - __________ - _____ __ __\______ \_____ _______ ______ ____ _______ - / \ | | \| ___/\__ \ \_ __ \/ ___/_/ __ \\_ __ \ - | Y Y \| | /| | / __ \_| | \/\___ \ \ ___/ | | \/ - |__|_| /|____/ |____| (____ /|__| /____ > \___ >|__| - \/ \/ \/ \/ - Copyright (C) 2013 Ingo Berg - - 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 MU_PARSER_FIXES_H -#define MU_PARSER_FIXES_H - -/** \file - \brief This file contains compatibility fixes for some platforms. -*/ - -// -// Compatibility fixes -// - -//--------------------------------------------------------------------------- -// -// Intel Compiler -// -//--------------------------------------------------------------------------- - -#ifdef __INTEL_COMPILER - -// remark #981: operands are evaluated in unspecified order -// disabled -> completely pointless if the functions do not have side effects -// -#pragma warning(disable:981) - -// remark #383: value copied to temporary, reference to temporary used -#pragma warning(disable:383) - -// remark #1572: floating-point equality and inequality comparisons are unreliable -// disabled -> everyone knows it, the parser passes this problem -// deliberately to the user -#pragma warning(disable:1572) - -#endif - -#endif // include guard - - diff --git a/bundled/muparser_v2_2_4/include/muParserInt.h b/bundled/muparser_v2_2_4/include/muParserInt.h deleted file mode 100644 index 61e7740007..0000000000 --- a/bundled/muparser_v2_2_4/include/muParserInt.h +++ /dev/null @@ -1,140 +0,0 @@ -/* - __________ - _____ __ __\______ \_____ _______ ______ ____ _______ - / \ | | \| ___/\__ \ \_ __ \/ ___/_/ __ \\_ __ \ - | Y Y \| | /| | / __ \_| | \/\___ \ \ ___/ | | \/ - |__|_| /|____/ |____| (____ /|__| /____ > \___ >|__| - \/ \/ \/ \/ - Copyright (C) 2004-2013 Ingo Berg - - 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 MU_PARSER_INT_H -#define MU_PARSER_INT_H - -#include "muParserBase.h" -#include - - -/** \file - \brief Definition of a parser using integer value. -*/ - - -namespace mu -{ - -/** \brief Mathematical expressions parser. - - This version of the parser handles only integer numbers. It disables the built in operators thus it is - slower than muParser. Integer values are stored in the double value_type and converted if needed. -*/ -class ParserInt : public ParserBase -{ -private: - static int Round(value_type v) { return (int)(v + ((v>=0) ? 0.5 : -0.5) ); }; - - static value_type Abs(value_type); - static value_type Sign(value_type); - static value_type Ite(value_type, value_type, value_type); - // !! The unary Minus is a MUST, otherwise you cant use negative signs !! - static value_type UnaryMinus(value_type); - // Functions with variable number of arguments - static value_type Sum(const value_type* a_afArg, int a_iArgc); // sum - static value_type Min(const value_type* a_afArg, int a_iArgc); // minimum - static value_type Max(const value_type* a_afArg, int a_iArgc); // maximum - // binary operator callbacks - static value_type Add(value_type v1, value_type v2); - static value_type Sub(value_type v1, value_type v2); - static value_type Mul(value_type v1, value_type v2); - static value_type Div(value_type v1, value_type v2); - static value_type Mod(value_type v1, value_type v2); - static value_type Pow(value_type v1, value_type v2); - static value_type Shr(value_type v1, value_type v2); - static value_type Shl(value_type v1, value_type v2); - static value_type LogAnd(value_type v1, value_type v2); - static value_type LogOr(value_type v1, value_type v2); - static value_type And(value_type v1, value_type v2); - static value_type Or(value_type v1, value_type v2); - static value_type Xor(value_type v1, value_type v2); - static value_type Less(value_type v1, value_type v2); - static value_type Greater(value_type v1, value_type v2); - static value_type LessEq(value_type v1, value_type v2); - static value_type GreaterEq(value_type v1, value_type v2); - static value_type Equal(value_type v1, value_type v2); - static value_type NotEqual(value_type v1, value_type v2); - static value_type Not(value_type v1); - - static int IsHexVal(const char_type* a_szExpr, int *a_iPos, value_type *a_iVal); - static int IsBinVal(const char_type* a_szExpr, int *a_iPos, value_type *a_iVal); - static int IsVal (const char_type* a_szExpr, int *a_iPos, value_type *a_iVal); - - /** \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_cDecPoint(cDecSep) - ,m_cThousandsSep(cThousandsSep) - ,m_nGroup(nGroup) - {} - - 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 - { - // fix for issue 4: https://code.google.com/p/muparser/issues/detail?id=4 - // courtesy of Jens Bartsch - // original code: - // return std::string(1, (char)m_nGroup); - // new code: - return std::string(1, (char)(m_cThousandsSep > 0 ? m_nGroup : CHAR_MAX)); - } - - private: - - int m_nGroup; - char_type m_cDecPoint; - char_type m_cThousandsSep; - }; - -public: - ParserInt(); - - virtual void InitFun() override; - virtual void InitOprt() override; - virtual void InitConst() override; - virtual void InitCharSets() override; -}; - -} // namespace mu - -#endif - diff --git a/bundled/muparser_v2_2_4/include/muParserTemplateMagic.h b/bundled/muparser_v2_2_4/include/muParserTemplateMagic.h deleted file mode 100644 index 1626caea4e..0000000000 --- a/bundled/muparser_v2_2_4/include/muParserTemplateMagic.h +++ /dev/null @@ -1,113 +0,0 @@ -#ifndef MU_PARSER_TEMPLATE_MAGIC_H -#define MU_PARSER_TEMPLATE_MAGIC_H - -#include -#include "muParserError.h" - - -namespace mu -{ - //----------------------------------------------------------------------------------------------- - // - // Compile time type detection - // - //----------------------------------------------------------------------------------------------- - - /** \brief A class singling out integer types at compile time using - template meta programming. - */ - template - struct TypeInfo - { - static bool IsInteger() { return false; } - }; - - template<> - struct TypeInfo - { - static bool IsInteger() { return true; } - }; - - template<> - struct TypeInfo - { - static bool IsInteger() { return true; } - }; - - template<> - struct TypeInfo - { - static bool IsInteger() { return true; } - }; - - template<> - struct TypeInfo - { - static bool IsInteger() { return true; } - }; - - template<> - struct TypeInfo - { - static bool IsInteger() { return true; } - }; - - template<> - struct TypeInfo - { - static bool IsInteger() { return true; } - }; - - template<> - struct TypeInfo - { - static bool IsInteger() { return true; } - }; - - template<> - struct TypeInfo - { - static bool IsInteger() { return true; } - }; - - - //----------------------------------------------------------------------------------------------- - // - // Standard math functions with dummy overload for integer types - // - //----------------------------------------------------------------------------------------------- - - /** \brief A template class for providing wrappers for essential math functions. - - This template is spezialized for several types in order to provide a unified interface - for parser internal math function calls regardless of the data type. - */ - template - struct MathImpl - { - static T Sin(T v) { return sin(v); } - static T Cos(T v) { return cos(v); } - static T Tan(T v) { return tan(v); } - static T ASin(T v) { return asin(v); } - static T ACos(T v) { return acos(v); } - static T ATan(T v) { return atan(v); } - static T ATan2(T v1, T v2) { return atan2(v1, v2); } - static T Sinh(T v) { return sinh(v); } - static T Cosh(T v) { return cosh(v); } - static T Tanh(T v) { return tanh(v); } - static T ASinh(T v) { return log(v + sqrt(v * v + 1)); } - static T ACosh(T v) { return log(v + sqrt(v * v - 1)); } - static T ATanh(T v) { return ((T)0.5 * log((1 + v) / (1 - v))); } - static T Log(T v) { return log(v); } - static T Log2(T v) { return log(v)/log((T)2); } // Logarithm base 2 - static T Log10(T v) { return log10(v); } // Logarithm base 10 - static T Exp(T v) { return exp(v); } - static T Abs(T v) { return (v>=0) ? v : -v; } - static T Sqrt(T v) { return sqrt(v); } - static T Rint(T v) { return floor(v + (T)0.5); } - static T Sign(T v) { return (T)((v<0) ? -1 : (v>0) ? 1 : 0); } - static T Pow(T v1, T v2) { return std::pow(v1, v2); } - }; -} - -#endif diff --git a/bundled/muparser_v2_2_4/include/muParserTest.h b/bundled/muparser_v2_2_4/include/muParserTest.h deleted file mode 100644 index c02b0218a9..0000000000 --- a/bundled/muparser_v2_2_4/include/muParserTest.h +++ /dev/null @@ -1,214 +0,0 @@ -/* - __________ - _____ __ __\______ \_____ _______ ______ ____ _______ - / \ | | \| ___/\__ \ \_ __ \/ ___/_/ __ \\_ __ \ - | Y Y \| | /| | / __ \_| | \/\___ \ \ ___/ | | \/ - |__|_| /|____/ |____| (____ /|__| /____ > \___ >|__| - \/ \/ \/ \/ - Copyright (C) 2013 Ingo Berg - - 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 MU_PARSER_TEST_H -#define MU_PARSER_TEST_H - -#include -#include -#include // for accumulate -#include "muParser.h" -#include "muParserInt.h" - -/** \file - \brief This file contains the parser test class. -*/ - -namespace mu -{ - /** \brief Namespace for test cases. */ - namespace Test - { - //------------------------------------------------------------------------------ - /** \brief Test cases for unit testing. - - (C) 2004-2011 Ingo Berg - */ - class ParserTester // final - { - private: - static int c_iCount; - - // Multiarg callbacks - static value_type f1of1(value_type v) { return v;}; - - static value_type f1of2(value_type v, value_type ) {return v;}; - static value_type f2of2(value_type , value_type v) {return v;}; - - static value_type f1of3(value_type v, value_type , value_type ) {return v;}; - static value_type f2of3(value_type , value_type v, value_type ) {return v;}; - static value_type f3of3(value_type , value_type , value_type v) {return v;}; - - static value_type f1of4(value_type v, value_type, value_type , value_type ) {return v;} - static value_type f2of4(value_type , value_type v, value_type , value_type ) {return v;} - static value_type f3of4(value_type , value_type, value_type v, value_type ) {return v;} - static value_type f4of4(value_type , value_type, value_type , value_type v) {return v;} - - static value_type f1of5(value_type v, value_type, value_type , value_type , value_type ) { return v; } - static value_type f2of5(value_type , value_type v, value_type , value_type , value_type ) { return v; } - static value_type f3of5(value_type , value_type, value_type v, value_type , value_type ) { return v; } - static value_type f4of5(value_type , value_type, value_type , value_type v, value_type ) { return v; } - static value_type f5of5(value_type , value_type, value_type , value_type , value_type v) { return v; } - - static value_type Min(value_type a_fVal1, value_type a_fVal2) { return (a_fVal1a_fVal2) ? a_fVal1 : a_fVal2; } - - static value_type plus2(value_type v1) { return v1+2; } - static value_type times3(value_type v1) { return v1*3; } - static value_type sqr(value_type v1) { return v1*v1; } - static value_type sign(value_type v) { return -v; } - static value_type add(value_type v1, value_type v2) { return v1+v2; } - static value_type land(value_type v1, value_type v2) { return (int)v1 & (int)v2; } - - - static value_type FirstArg(const value_type* a_afArg, int a_iArgc) - { - if (!a_iArgc) - throw mu::Parser::exception_type( _T("too few arguments for function FirstArg.") ); - - return a_afArg[0]; - } - - static value_type LastArg(const value_type* a_afArg, int a_iArgc) - { - if (!a_iArgc) - throw mu::Parser::exception_type( _T("too few arguments for function LastArg.") ); - - return a_afArg[a_iArgc-1]; - } - - static value_type Sum(const value_type* a_afArg, int a_iArgc) - { - if (!a_iArgc) - throw mu::Parser::exception_type( _T("too few arguments for function sum.") ); - - value_type fRes=0; - for (int i=0; i> val; - return (value_type)val; - } - - static value_type StrFun2(const char_type* v1, value_type v2) - { - int val(0); - stringstream_type(v1) >> val; - return (value_type)(val + v2); - } - - static value_type StrFun3(const char_type* v1, value_type v2, value_type v3) - { - int val(0); - stringstream_type(v1) >> val; - return val + v2 + v3; - } - - static value_type StrToFloat(const char_type* a_szMsg) - { - value_type val(0); - stringstream_type(a_szMsg) >> val; - return val; - } - - // postfix operator callback - static value_type Mega(value_type a_fVal) { return a_fVal * (value_type)1e6; } - static value_type Micro(value_type a_fVal) { return a_fVal * (value_type)1e-6; } - static value_type Milli(value_type a_fVal) { return a_fVal / (value_type)1e3; } - - // Custom value recognition - static int IsHexVal(const char_type *a_szExpr, int *a_iPos, value_type *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(); - int TestBulkMode(); - - void Abort() const; - - public: - typedef int (ParserTester::*testfun_type)(); - - ParserTester(); - void Run(); - - private: - std::vector m_vTestFun; - void AddTest(testfun_type a_pFun); - - // Test Double Parser - int EqnTest(const string_type& a_str, double a_fRes, bool a_fPass); - int EqnTestWithVarChange(const string_type& a_str, - double a_fRes1, - double a_fVar1, - double a_fRes2, - double a_fVar2); - int ThrowTest(const string_type& a_str, int a_iErrc, bool a_bFail = true); - - // Test Int Parser - int EqnTestInt(const string_type& a_str, double a_fRes, bool a_fPass); - - // Test Bulkmode - int EqnTestBulk(const string_type& a_str, double a_fRes[4], bool a_fPass); - }; - } // namespace Test -} // namespace mu - -#endif - - diff --git a/bundled/muparser_v2_2_4/include/muParserToken.h b/bundled/muparser_v2_2_4/include/muParserToken.h deleted file mode 100644 index 0cca469b3f..0000000000 --- a/bundled/muparser_v2_2_4/include/muParserToken.h +++ /dev/null @@ -1,401 +0,0 @@ -/* - __________ - _____ __ __\______ \_____ _______ ______ ____ _______ - / \ | | \| ___/\__ \ \_ __ \/ ___/_/ __ \\_ __ \ - | Y Y \| | /| | / __ \_| | \/\___ \ \ ___/ | | \/ - |__|_| /|____/ |____| (____ /|__| /____ > \___ >|__| - \/ \/ \/ \/ - Copyright (C) 2004-2013 Ingo Berg - - 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 MU_PARSER_TOKEN_H -#define MU_PARSER_TOKEN_H - -#include -#include -#include -#include -#include - -#include "muParserError.h" -#include "muParserCallback.h" - -/** \file - \brief This file contains the parser token definition. -*/ - -namespace mu -{ - /** \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: -
    -
  • value
  • -
  • variable
  • -
  • function with numerical arguments
  • -
  • functions with a string as argument
  • -
  • prefix operators
  • -
  • infix operators
  • -
  • binary operator
  • -
- - \author (C) 2004-2013 Ingo Berg - */ - template - class ParserToken - { - private: - - ECmdCode m_iCode; ///< Type of the token; The token type is a constant of type #ECmdCode. - ETypeCode m_iType; - void *m_pTok; ///< Stores Token pointer; not applicable for all tokens - int m_iIdx; ///< An otional index to an external buffer storing the token data - TString m_strTok; ///< Token string - TString m_strVal; ///< Value for string variables - value_type m_fVal; ///< the value - std::unique_ptr m_pCallback; - - public: - - //--------------------------------------------------------------------------- - /** \brief Constructor (default). - - Sets token to an neutral state of type cmUNKNOWN. - \throw nothrow - \sa ECmdCode - */ - ParserToken() - :m_iCode(cmUNKNOWN) - ,m_iType(tpVOID) - ,m_pTok(0) - ,m_iIdx(-1) - ,m_strTok() - ,m_strVal() - ,m_fVal(0) - ,m_pCallback() - {} - - //------------------------------------------------------------------------------ - /** \brief Create token from another one. - - Implemented by calling Assign(...) - \throw nothrow - \post m_iType==cmUNKNOWN - \sa #Assign - */ - ParserToken(const ParserToken &a_Tok) - { - Assign(a_Tok); - } - - //------------------------------------------------------------------------------ - /** \brief Assignement operator. - - Copy token state from another token and return this. - Implemented by calling Assign(...). - \throw nothrow - */ - ParserToken& operator=(const ParserToken &a_Tok) - { - Assign(a_Tok); - return *this; - } - - //------------------------------------------------------------------------------ - /** \brief Copy token information from argument. - - \throw nothrow - */ - void Assign(const ParserToken &a_Tok) - { - m_iCode = a_Tok.m_iCode; - m_pTok = a_Tok.m_pTok; - m_strTok = a_Tok.m_strTok; - m_iIdx = a_Tok.m_iIdx; - m_strVal = a_Tok.m_strVal; - m_iType = a_Tok.m_iType; - m_fVal = a_Tok.m_fVal; - // create new callback object if a_Tok has one - m_pCallback.reset(a_Tok.m_pCallback.get() ? a_Tok.m_pCallback->Clone() : 0); - } - - //------------------------------------------------------------------------------ - /** \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 - */ - ParserToken& Set(ECmdCode a_iType, const TString &a_strTok=TString()) - { - // The following types cant be set this way, they have special Set functions - assert(a_iType!=cmVAR); - assert(a_iType!=cmVAL); - assert(a_iType!=cmFUNC); - - m_iCode = a_iType; - m_iType = tpVOID; - m_pTok = 0; - m_strTok = a_strTok; - m_iIdx = -1; - - return *this; - } - - //------------------------------------------------------------------------------ - /** \brief Set Callback type. */ - ParserToken& Set(const ParserCallback &a_pCallback, const TString &a_sTok) - { - assert(a_pCallback.GetAddr()); - - m_iCode = a_pCallback.GetCode(); - m_iType = tpVOID; - m_strTok = a_sTok; - m_pCallback.reset(new ParserCallback(a_pCallback)); - - m_pTok = 0; - m_iIdx = -1; - - return *this; - } - - //------------------------------------------------------------------------------ - /** \brief Make this token a value token. - - Member variables not necessary for value tokens will be invalidated. - \throw nothrow - */ - ParserToken& SetVal(TBase a_fVal, const TString &a_strTok=TString()) - { - m_iCode = cmVAL; - m_iType = tpDBL; - m_fVal = a_fVal; - m_strTok = a_strTok; - m_iIdx = -1; - - m_pTok = 0; - m_pCallback.reset(0); - - return *this; - } - - //------------------------------------------------------------------------------ - /** \brief make this token a variable token. - - Member variables not necessary for variable tokens will be invalidated. - \throw nothrow - */ - ParserToken& SetVar(TBase *a_pVar, const TString &a_strTok) - { - m_iCode = cmVAR; - m_iType = tpDBL; - m_strTok = a_strTok; - m_iIdx = -1; - m_pTok = (void*)a_pVar; - m_pCallback.reset(0); - return *this; - } - - //------------------------------------------------------------------------------ - /** \brief Make this token a variable token. - - Member variables not necessary for variable tokens will be invalidated. - \throw nothrow - */ - ParserToken& SetString(const TString &a_strTok, std::size_t a_iSize) - { - m_iCode = cmSTRING; - m_iType = tpSTR; - m_strTok = a_strTok; - m_iIdx = static_cast(a_iSize); - - m_pTok = 0; - m_pCallback.reset(0); - return *this; - } - - //------------------------------------------------------------------------------ - /** \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 - */ - void SetIdx(int a_iIdx) - { - if (m_iCode!=cmSTRING || a_iIdx<0) - throw ParserError(ecINTERNAL_ERROR); - - m_iIdx = a_iIdx; - } - - //------------------------------------------------------------------------------ - /** \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). - */ - int GetIdx() const - { - if (m_iIdx<0 || m_iCode!=cmSTRING ) - throw ParserError(ecINTERNAL_ERROR); - - return m_iIdx; - } - - //------------------------------------------------------------------------------ - /** \brief Return the token type. - - \return #m_iType - \throw nothrow - */ - ECmdCode GetCode() const - { - if (m_pCallback.get()) - { - return m_pCallback->GetCode(); - } - else - { - return m_iCode; - } - } - - //------------------------------------------------------------------------------ - ETypeCode GetType() const - { - if (m_pCallback.get()) - { - return m_pCallback->GetType(); - } - else - { - return m_iType; - } - } - - //------------------------------------------------------------------------------ - int GetPri() const - { - if ( !m_pCallback.get()) - throw ParserError(ecINTERNAL_ERROR); - - if ( m_pCallback->GetCode()!=cmOPRT_BIN && m_pCallback->GetCode()!=cmOPRT_INFIX) - throw ParserError(ecINTERNAL_ERROR); - - return m_pCallback->GetPri(); - } - - //------------------------------------------------------------------------------ - EOprtAssociativity GetAssociativity() const - { - if (m_pCallback.get()==NULL || m_pCallback->GetCode()!=cmOPRT_BIN) - throw ParserError(ecINTERNAL_ERROR); - - return m_pCallback->GetAssociativity(); - } - - //------------------------------------------------------------------------------ - /** \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: -
    -
  • cmFUNC
  • -
  • cmSTRFUNC
  • -
  • cmPOSTOP
  • -
  • cmINFIXOP
  • -
  • cmOPRT_BIN
  • -
- \sa ECmdCode - */ - generic_fun_type GetFuncAddr() const - { - return (m_pCallback.get()) ? (generic_fun_type)m_pCallback->GetAddr() : 0; - } - - //------------------------------------------------------------------------------ - /** \biref Get value of the token. - - Only applicable to variable and value tokens. - \throw exception_type if token is no value/variable token. - */ - TBase GetVal() const - { - switch (m_iCode) - { - case cmVAL: return m_fVal; - case cmVAR: return *((TBase*)m_pTok); - default: throw ParserError(ecVAL_EXPECTED); - } - } - - //------------------------------------------------------------------------------ - /** \brief Get address of a variable token. - - Valid only if m_iType==CmdVar. - \throw exception_type if token is no variable token. - */ - TBase* GetVar() const - { - if (m_iCode!=cmVAR) - throw ParserError(ecINTERNAL_ERROR); - - return (TBase*)m_pTok; - } - - //------------------------------------------------------------------------------ - /** \brief Return the number of function arguments. - - Valid only if m_iType==CmdFUNC. - */ - int GetArgCount() const - { - assert(m_pCallback.get()); - - if (!m_pCallback->GetAddr()) - throw ParserError(ecINTERNAL_ERROR); - - return m_pCallback->GetArgc(); - } - - //------------------------------------------------------------------------------ - /** \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 - */ - const TString& GetAsString() const - { - return m_strTok; - } - }; -} // namespace mu - -#endif diff --git a/bundled/muparser_v2_2_4/include/muParserTokenReader.h b/bundled/muparser_v2_2_4/include/muParserTokenReader.h deleted file mode 100644 index 9d96225d9a..0000000000 --- a/bundled/muparser_v2_2_4/include/muParserTokenReader.h +++ /dev/null @@ -1,161 +0,0 @@ -/* - __________ - _____ __ __\______ \_____ _______ ______ ____ _______ - / \ | | \| ___/\__ \ \_ __ \/ ___/_/ __ \\_ __ \ - | Y Y \| | /| | / __ \_| | \/\___ \ \ ___/ | | \/ - |__|_| /|____/ |____| (____ /|__| /____ > \___ >|__| - \/ \/ \/ \/ - Copyright (C) 2004-2013 Ingo Berg - - 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 MU_PARSER_TOKEN_READER_H -#define MU_PARSER_TOKEN_READER_H - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "muParserDef.h" -#include "muParserToken.h" - -/** \file - \brief This file contains the parser token reader definition. -*/ - - -namespace mu -{ - // Forward declaration - class ParserBase; - - /** \brief Token reader for the ParserBase class. - - */ - class ParserTokenReader - { - private: - - typedef ParserToken token_type; - - public: - - ParserTokenReader(ParserBase *a_pParent); - ParserTokenReader* Clone(ParserBase *a_pParent) const; - - void AddValIdent(identfun_type a_pCallback); - void SetVarCreator(facfun_type a_pFactory, void *pUserData); - void SetFormula(const string_type &a_strFormula); - void SetArgSep(char_type cArgSep); - - int GetPos() const; - const string_type& GetExpr() const; - varmap_type& GetUsedVar(); - char_type GetArgSep() const; - - void IgnoreUndefVar(bool bIgnore); - void ReInit(); - token_type ReadNextToken(); - - private: - - /** \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 - if certain tokens are identified. - */ - enum ESynCodes - { - noBO = 1 << 0, ///< to avoid i.e. "cos(7)(" - noBC = 1 << 1, ///< to avoid i.e. "sin)" or "()" - noVAL = 1 << 2, ///< to avoid i.e. "tan 2" or "sin(8)3.14" - noVAR = 1 << 3, ///< to avoid i.e. "sin a" or "sin(8)a" - noARG_SEP = 1 << 4, ///< to avoid i.e. ",," or "+," ... - noFUN = 1 << 5, ///< to avoid i.e. "sqrt cos" or "(1)sin" - noOPT = 1 << 6, ///< to avoid i.e. "(+)" - noPOSTOP = 1 << 7, ///< to avoid i.e. "(5!!)" "sin!" - noINFIXOP = 1 << 8, ///< to avoid i.e. "++4" "!!4" - noEND = 1 << 9, ///< to avoid unexpected end of formula - noSTR = 1 << 10, ///< to block numeric arguments on string functions - noASSIGN = 1 << 11, ///< to block assignement to constant i.e. "4=7" - noIF = 1 << 12, - noELSE = 1 << 13, - sfSTART_OF_LINE = noOPT | noBC | noPOSTOP | noASSIGN | noIF | noELSE | noARG_SEP, - noANY = ~0 ///< All of he above flags set - }; - - ParserTokenReader(const ParserTokenReader &a_Reader); - ParserTokenReader& operator=(const ParserTokenReader &a_Reader); - void Assign(const ParserTokenReader &a_Reader); - - void SetParent(ParserBase *a_pParent); - int ExtractToken(const char_type *a_szCharSet, - string_type &a_strTok, - int a_iPos) const; - int ExtractOperatorToken(string_type &a_sTok, int a_iPos) const; - - bool IsBuiltIn(token_type &a_Tok); - bool IsArgSep(token_type &a_Tok); - bool IsEOF(token_type &a_Tok); - bool IsInfixOpTok(token_type &a_Tok); - bool IsFunTok(token_type &a_Tok); - bool IsPostOpTok(token_type &a_Tok); - bool IsOprt(token_type &a_Tok); - bool IsValTok(token_type &a_Tok); - bool IsVarTok(token_type &a_Tok); - bool IsStrVarTok(token_type &a_Tok); - bool IsUndefVarTok(token_type &a_Tok); - bool IsString(token_type &a_Tok); - void Error(EErrorCodes a_iErrc, - int a_iPos = -1, - const string_type &a_sTok = string_type() ) const; - - token_type& SaveBeforeReturn(const token_type &tok); - - ParserBase *m_pParser; - string_type m_strFormula; - int m_iPos; - int m_iSynFlags; - bool m_bIgnoreUndefVar; - - const funmap_type *m_pFunDef; - const funmap_type *m_pPostOprtDef; - const funmap_type *m_pInfixOprtDef; - const funmap_type *m_pOprtDef; - const valmap_type *m_pConstDef; - const strmap_type *m_pStrVarDef; - varmap_type *m_pVarDef; ///< The only non const pointer to parser internals - facfun_type m_pFactory; - void *m_pFactoryData; - std::list m_vIdentFun; ///< Value token identification function - varmap_type m_UsedVar; - value_type m_fZero; ///< Dummy value of zero, referenced by undefined variables - int m_iBrackets; - token_type m_lastTok; - char_type m_cArgSep; ///< The character used for separating function arguments - }; -} // namespace mu - -#endif - - diff --git a/bundled/muparser_v2_2_4/src/muParser.cpp b/bundled/muparser_v2_2_4/src/muParser.cpp deleted file mode 100644 index eaa84493ad..0000000000 --- a/bundled/muparser_v2_2_4/src/muParser.cpp +++ /dev/null @@ -1,397 +0,0 @@ -/* - __________ - _____ __ __\______ \_____ _______ ______ ____ _______ - / \ | | \| ___/\__ \ \_ __ \/ ___/_/ __ \\_ __ \ - | Y Y \| | /| | / __ \_| | \/\___ \ \ ___/ | | \/ - |__|_| /|____/ |____| (____ /|__| /____ > \___ >|__| - \/ \/ \/ \/ - - Copyright (C) 2013 Ingo Berg - - 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 "muParser.h" -#include "muParserTemplateMagic.h" - -//--- Standard includes ------------------------------------------------------------------------ -#include -#include -#include - -/** \brief Pi (what else?). */ -#define PARSER_CONST_PI 3.141592653589793238462643 - -/** \brief The eulerian number. */ -#define PARSER_CONST_E 2.718281828459045235360287 - -using namespace std; - -/** \file - \brief Implementation of the standard floating point parser. -*/ - - - -/** \brief Namespace for mathematical applications. */ -namespace mu -{ - - - //--------------------------------------------------------------------------- - // Trigonometric function - value_type Parser::Sin(value_type v) { return MathImpl::Sin(v); } - value_type Parser::Cos(value_type v) { return MathImpl::Cos(v); } - value_type Parser::Tan(value_type v) { return MathImpl::Tan(v); } - value_type Parser::ASin(value_type v) { return MathImpl::ASin(v); } - value_type Parser::ACos(value_type v) { return MathImpl::ACos(v); } - value_type Parser::ATan(value_type v) { return MathImpl::ATan(v); } - value_type Parser::ATan2(value_type v1, value_type v2) { return MathImpl::ATan2(v1, v2); } - value_type Parser::Sinh(value_type v) { return MathImpl::Sinh(v); } - value_type Parser::Cosh(value_type v) { return MathImpl::Cosh(v); } - value_type Parser::Tanh(value_type v) { return MathImpl::Tanh(v); } - value_type Parser::ASinh(value_type v) { return MathImpl::ASinh(v); } - value_type Parser::ACosh(value_type v) { return MathImpl::ACosh(v); } - value_type Parser::ATanh(value_type v) { return MathImpl::ATanh(v); } - - //--------------------------------------------------------------------------- - // Logarithm functions - - // Logarithm base 2 - value_type Parser::Log2(value_type v) - { - #ifdef MUP_MATH_EXCEPTIONS - if (v<=0) - throw ParserError(ecDOMAIN_ERROR, _T("Log2")); - #endif - - return MathImpl::Log2(v); - } - - // Logarithm base 10 - value_type Parser::Log10(value_type v) - { - #ifdef MUP_MATH_EXCEPTIONS - if (v<=0) - throw ParserError(ecDOMAIN_ERROR, _T("Log10")); - #endif - - return MathImpl::Log10(v); - } - -// Logarithm base e (natural logarithm) - value_type Parser::Ln(value_type v) - { - #ifdef MUP_MATH_EXCEPTIONS - if (v<=0) - throw ParserError(ecDOMAIN_ERROR, _T("Ln")); - #endif - - return MathImpl::Log(v); - } - - //--------------------------------------------------------------------------- - // misc - value_type Parser::Exp(value_type v) { return MathImpl::Exp(v); } - value_type Parser::Abs(value_type v) { return MathImpl::Abs(v); } - value_type Parser::Sqrt(value_type v) - { - #ifdef MUP_MATH_EXCEPTIONS - if (v<0) - throw ParserError(ecDOMAIN_ERROR, _T("sqrt")); - #endif - - return MathImpl::Sqrt(v); - } - value_type Parser::Rint(value_type v) { return MathImpl::Rint(v); } - value_type Parser::Sign(value_type v) { return MathImpl::Sign(v); } - - //--------------------------------------------------------------------------- - /** \brief Callback for the unary minus operator. - \param v The value to negate - \return -v - */ - value_type Parser::UnaryMinus(value_type v) - { - return -v; - } - - //--------------------------------------------------------------------------- - /** \brief Callback for the unary minus operator. - \param v The value to negate - \return -v - */ - value_type Parser::UnaryPlus(value_type v) - { - return 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 - */ - value_type Parser::Sum(const value_type *a_afArg, int a_iArgc) - { - if (!a_iArgc) - throw exception_type(_T("too few arguments for function sum.")); - - value_type fRes=0; - for (int i=0; i> fVal; - stringstream_type::pos_type iEnd = stream.tellg(); // Position after reading - - if (iEnd==(stringstream_type::pos_type)-1) - return 0; - - *a_iPos += (int)iEnd; - *a_fVal = fVal; - return 1; - } - - - //--------------------------------------------------------------------------- - /** \brief Constructor. - - Call ParserBase class constructor and trigger Function, Operator and Constant initialization. - */ - Parser::Parser() - :ParserBase() - { - 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 Parser::InitCharSets() - { - DefineNameChars( _T("0123456789_abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ") ); - DefineOprtChars( _T("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ+-*^/?<>=#!$%&|~'_{}") ); - DefineInfixOprtChars( _T("/+-*^?<>=#!$%&|~'_") ); - } - - //--------------------------------------------------------------------------- - /** \brief Initialize the default functions. */ - void Parser::InitFun() - { - if (mu::TypeInfo::IsInteger()) - { - // When setting MUP_BASETYPE to an integer type - // Place functions for dealing with integer values here - // ... - // ... - // ... - } - else - { - // trigonometric functions - DefineFun(_T("sin"), Sin); - DefineFun(_T("cos"), Cos); - DefineFun(_T("tan"), Tan); - // arcus functions - DefineFun(_T("asin"), ASin); - DefineFun(_T("acos"), ACos); - DefineFun(_T("atan"), ATan); - DefineFun(_T("atan2"), ATan2); - // hyperbolic functions - DefineFun(_T("sinh"), Sinh); - DefineFun(_T("cosh"), Cosh); - DefineFun(_T("tanh"), Tanh); - // arcus hyperbolic functions - DefineFun(_T("asinh"), ASinh); - DefineFun(_T("acosh"), ACosh); - DefineFun(_T("atanh"), ATanh); - // Logarithm functions - DefineFun(_T("log2"), Log2); - DefineFun(_T("log10"), Log10); - DefineFun(_T("log"), Ln); - DefineFun(_T("ln"), Ln); - // misc - DefineFun(_T("exp"), Exp); - DefineFun(_T("sqrt"), Sqrt); - DefineFun(_T("sign"), Sign); - DefineFun(_T("rint"), Rint); - DefineFun(_T("abs"), Abs); - // Functions with variable number of arguments - DefineFun(_T("sum"), Sum); - DefineFun(_T("avg"), Avg); - DefineFun(_T("min"), Min); - DefineFun(_T("max"), Max); - } - } - - //--------------------------------------------------------------------------- - /** \brief Initialize constants. - - By default the parser recognizes two constants. Pi ("pi") and the eulerian - number ("_e"). - */ - void Parser::InitConst() - { - DefineConst(_T("_pi"), (value_type)PARSER_CONST_PI); - DefineConst(_T("_e"), (value_type)PARSER_CONST_E); - } - - //--------------------------------------------------------------------------- - /** \brief Initialize operators. - - By default only the unary minus operator is added. - */ - void Parser::InitOprt() - { - DefineInfixOprt(_T("-"), UnaryMinus); - DefineInfixOprt(_T("+"), UnaryPlus); - } - - //--------------------------------------------------------------------------- - void Parser::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 + "_"; - - 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. - - 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 muparser - forum: - - http://sourceforge.net/forum/forum.php?thread_id=1994611&forum_id=462843 - */ - value_type Parser::Diff(value_type *a_Var, - value_type a_fPos, - value_type a_fEpsilon) const - { - value_type fRes(0), - fBuf(*a_Var), - f[4] = {0,0,0,0}, - fEpsilon(a_fEpsilon); - - // Backwards compatible calculation of epsilon inc case the user doesnt provide - // his own epsilon - if (fEpsilon==0) - fEpsilon = (a_fPos==0) ? (value_type)1e-10 : (value_type)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; - } -} // namespace mu diff --git a/bundled/muparser_v2_2_4/src/muParserBase.cpp b/bundled/muparser_v2_2_4/src/muParserBase.cpp deleted file mode 100644 index 09b0230bf0..0000000000 --- a/bundled/muparser_v2_2_4/src/muParserBase.cpp +++ /dev/null @@ -1,1780 +0,0 @@ -/* - __________ - _____ __ __\______ \_____ _______ ______ ____ _______ - / \ | | \| ___/\__ \ \_ __ \/ ___/_/ __ \\_ __ \ - | Y Y \| | /| | / __ \_| | \/\___ \ \ ___/ | | \/ - |__|_| /|____/ |____| (____ /|__| /____ > \___ >|__| - \/ \/ \/ \/ - Copyright (C) 2011 Ingo Berg - - 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 "muParserBase.h" -#include "muParserTemplateMagic.h" - -//--- Standard includes ------------------------------------------------------------------------ -#include -#include -#include -#include -#include -#include -#include -#include - -#ifdef MUP_USE_OPENMP - #include -#endif - -using namespace std; - -/** \file - \brief This file contains the basic implementation of the muparser engine. -*/ - -namespace mu -{ - std::locale ParserBase::s_locale = std::locale(std::locale::classic(), new change_dec_sep('.')); - - bool ParserBase::g_DbgDumpCmdCode = false; - bool ParserBase::g_DbgDumpStack = false; - - //------------------------------------------------------------------------------ - /** \brief Identifiers for built in binary operators. - - When defining custom binary operators with #AddOprt(...) make sure not to choose - names conflicting with these definitions. - */ - const char_type* ParserBase::c_DefaultOprt[] = - { - _T("<="), _T(">="), _T("!="), - _T("=="), _T("<"), _T(">"), - _T("+"), _T("-"), _T("*"), - _T("/"), _T("^"), _T("&&"), - _T("||"), _T("="), _T("("), - _T(")"), _T("?"), _T(":"), 0 - }; - - //------------------------------------------------------------------------------ - /** \brief Constructor. - \param a_szFormula the formula to interpret. - \throw ParserException if a_szFormula is null. - */ - ParserBase::ParserBase() - :m_pParseFormula(&ParserBase::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. - */ - ParserBase::ParserBase(const ParserBase &a_Parser) - :m_pParseFormula(&ParserBase::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); - } - - //--------------------------------------------------------------------------- - ParserBase::~ParserBase() - {} - - //--------------------------------------------------------------------------- - /** \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 - */ - ParserBase& ParserBase::operator=(const ParserBase &a_Parser) - { - Assign(a_Parser); - return *this; - } - - //--------------------------------------------------------------------------- - /** \brief Copy state of a parser object to this. - - Clears Variables and Functions of this parser. - Copies the states of all internal variables. - Resets parse function to string parse mode. - - \param a_Parser the source object. - */ - void ParserBase::Assign(const ParserBase &a_Parser) - { - if (&a_Parser==this) - return; - - // Don't copy bytecode instead cause the parser to create new bytecode - // by resetting the parse function. - ReInit(); - - m_ConstDef = a_Parser.m_ConstDef; // Copy user define constants - m_VarDef = a_Parser.m_VarDef; // Copy user defined variables - m_bBuiltInOp = a_Parser.m_bBuiltInOp; - m_vStringBuf = a_Parser.m_vStringBuf; - m_vStackBuffer = a_Parser.m_vStackBuffer; - m_nFinalResultIdx = a_Parser.m_nFinalResultIdx; - m_StrVarDef = a_Parser.m_StrVarDef; - m_vStringVarBuf = a_Parser.m_vStringVarBuf; - m_nIfElseCounter = a_Parser.m_nIfElseCounter; - m_pTokenReader.reset(a_Parser.m_pTokenReader->Clone(this)); - - // Copy function and operator callbacks - m_FunDef = a_Parser.m_FunDef; // Copy function definitions - m_PostOprtDef = a_Parser.m_PostOprtDef; // post value unary operators - m_InfixOprtDef = a_Parser.m_InfixOprtDef; // unary operators for infix notation - m_OprtDef = a_Parser.m_OprtDef; // binary operators - - m_sNameChars = a_Parser.m_sNameChars; - m_sOprtChars = a_Parser.m_sOprtChars; - m_sInfixOprtChars = a_Parser.m_sInfixOprtChars; - } - - //--------------------------------------------------------------------------- - /** \brief Set the decimal separator. - \param cDecSep Decimal separator as a character value. - \sa SetThousandsSep - - By default muparser uses the "C" locale. The decimal separator of this - locale is overwritten by the one provided here. - */ - void ParserBase::SetDecSep(char_type cDecSep) - { - char_type cThousandsSep = std::use_facet< change_dec_sep >(s_locale).thousands_sep(); - s_locale = std::locale(std::locale("C"), new change_dec_sep(cDecSep, cThousandsSep)); - } - - //--------------------------------------------------------------------------- - /** \brief Sets the thousands operator. - \param cThousandsSep The thousands separator as a character - \sa SetDecSep - - By default muparser uses the "C" locale. The thousands separator of this - locale is overwritten by the one provided here. - */ - void ParserBase::SetThousandsSep(char_type cThousandsSep) - { - char_type cDecSep = std::use_facet< change_dec_sep >(s_locale).decimal_point(); - s_locale = std::locale(std::locale("C"), new change_dec_sep(cDecSep, cThousandsSep)); - } - - //--------------------------------------------------------------------------- - /** \brief Resets the locale. - - The default locale used "." as decimal separator, no thousands separator and - "," as function argument separator. - */ - void ParserBase::ResetLocale() - { - s_locale = std::locale(std::locale("C"), new change_dec_sep('.')); - SetArgSep(','); - } - - //--------------------------------------------------------------------------- - /** \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 ParserBase::InitTokenReader() - { - m_pTokenReader.reset(new token_reader_type(this)); - } - - //--------------------------------------------------------------------------- - /** \brief Reset parser to string parsing mode and clear internal buffers. - - Clear bytecode, reset the token reader. - \throw nothrow - */ - void ParserBase::ReInit() const - { - m_pParseFormula = &ParserBase::ParseString; - m_vStringBuf.clear(); - m_vRPN.clear(); - m_pTokenReader->ReInit(); - m_nIfElseCounter = 0; - } - - //--------------------------------------------------------------------------- - void ParserBase::OnDetectVar(string_type * /*pExpr*/, int & /*nStart*/, int & /*nEnd*/) - {} - - //--------------------------------------------------------------------------- - /** \brief Returns the version of muparser. - \param eInfo A flag indicating whether the full version info should be - returned or not. - - Format is as follows: "MAJOR.MINOR (COMPILER_FLAGS)" The COMPILER_FLAGS - are returned only if eInfo==pviFULL. - */ - string_type ParserBase::GetVersion(EParserVersionInfo eInfo) const - { - stringstream_type ss; - - ss << MUP_VERSION; - - if (eInfo==pviFULL) - { - ss << _T(" (") << MUP_VERSION_DATE; - ss << std::dec << _T("; ") << sizeof(void*)*8 << _T("BIT"); - -#ifdef _DEBUG - ss << _T("; DEBUG"); -#else - ss << _T("; RELEASE"); -#endif - -#ifdef _UNICODE - ss << _T("; UNICODE"); -#else - #ifdef _MBCS - ss << _T("; MBCS"); - #else - ss << _T("; ASCII"); - #endif -#endif - -#ifdef MUP_USE_OPENMP - ss << _T("; OPENMP"); -//#else -// ss << _T("; NO_OPENMP"); -#endif - -#if defined(MUP_MATH_EXCEPTIONS) - ss << _T("; MATHEXC"); -//#else -// ss << _T("; NO_MATHEXC"); -#endif - - ss << _T(")"); - } - - 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 ParserBase::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 ParserBase::SetVarFactory(facfun_type a_pFactory, void *pUserData) - { - m_pTokenReader->SetVarCreator(a_pFactory, pUserData); - } - - //--------------------------------------------------------------------------- - /** \brief Add a function or operator callback to the parser. */ - void ParserBase::AddCallback( const string_type &a_strName, - const ParserCallback &a_Callback, - funmap_type &a_Storage, - const char_type *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 ParserBase::CheckOprt(const string_type &a_sName, - const ParserCallback &a_Callback, - const string_type &a_szCharSet) const - { - if ( !a_sName.length() || - (a_sName.find_first_not_of(a_szCharSet)!=string_type::npos) || - (a_sName[0]>='0' && a_sName[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); - } - } - } - - //--------------------------------------------------------------------------- - /** \brief Check if a name contains invalid characters. - - \throw ParserException if the name contains invalid charakters. - */ - void ParserBase::CheckName(const string_type &a_sName, - const string_type &a_szCharSet) const - { - if ( !a_sName.length() || - (a_sName.find_first_not_of(a_szCharSet)!=string_type::npos) || - (a_sName[0]>='0' && a_sName[0]<='9')) - { - Error(ecINVALID_NAME); - } - } - - //--------------------------------------------------------------------------- - /** \brief Set the formula. - \param a_strFormula Formula as string_type - \throw ParserException in case of syntax errors. - - Triggers first time calculation thus the creation of the bytecode and - scanning of used variables. - */ - void ParserBase::SetExpr(const string_type &a_sExpr) - { - // Check locale compatibility - std::locale loc; - if (m_pTokenReader->GetArgSep()==std::use_facet >(loc).decimal_point()) - Error(ecLOCALE); - - // 20060222: Bugfix for Borland-Kylix: - // adding a space to the expression will keep Borlands KYLIX from going wild - // when calling tellg on a stringstream created from the expression after - // reading a value at the end of an expression. (mu::Parser::IsVal function) - // (tellg returns -1 otherwise causing the parser to ignore the value) - string_type sBuf(a_sExpr + _T(" ") ); - m_pTokenReader->SetFormula(sBuf); - ReInit(); - } - - //--------------------------------------------------------------------------- - /** \brief Get the default symbols used for the built in operators. - \sa c_DefaultOprt - */ - const char_type** ParserBase::GetOprtDef() const - { - return (const char_type **)(&c_DefaultOprt[0]); - } - - //--------------------------------------------------------------------------- - /** \brief Define the set of valid characters to be used in names of - functions, variables, constants. - */ - void ParserBase::DefineNameChars(const char_type *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 ParserBase::DefineOprtChars(const char_type *a_szCharset) - { - m_sOprtChars = a_szCharset; - } - - //--------------------------------------------------------------------------- - /** \brief Define the set of valid characters to be used in names of - infix operators. - */ - void ParserBase::DefineInfixOprtChars(const char_type *a_szCharset) - { - m_sInfixOprtChars = a_szCharset; - } - - //--------------------------------------------------------------------------- - /** \brief Virtual function that defines the characters allowed in name identifiers. - \sa #ValidOprtChars, #ValidPrefixOprtChars - */ - const char_type* ParserBase::ValidNameChars() const - { - assert(m_sNameChars.size()); - return m_sNameChars.c_str(); - } - - //--------------------------------------------------------------------------- - /** \brief Virtual function that defines the characters allowed in operator definitions. - \sa #ValidNameChars, #ValidPrefixOprtChars - */ - const char_type* ParserBase::ValidOprtChars() const - { - assert(m_sOprtChars.size()); - return m_sOprtChars.c_str(); - } - - //--------------------------------------------------------------------------- - /** \brief Virtual function that defines the characters allowed in infix operator definitions. - \sa #ValidNameChars, #ValidOprtChars - */ - const char_type* ParserBase::ValidInfixOprtChars() const - { - assert(m_sInfixOprtChars.size()); - return m_sInfixOprtChars.c_str(); - } - - //--------------------------------------------------------------------------- - /** \brief Add a user defined operator. - \post Will reset the Parser to string parsing mode. - */ - void ParserBase::DefinePostfixOprt(const string_type &a_sName, - fun_type1 a_pFun, - bool a_bAllowOpt) - { - AddCallback(a_sName, - ParserCallback(a_pFun, a_bAllowOpt, prPOSTFIX, cmOPRT_POSTFIX), - m_PostOprtDef, - ValidOprtChars() ); - } - - //--------------------------------------------------------------------------- - /** \brief Initialize user defined functions. - - Calls the virtual functions InitFun(), InitConst() and InitOprt(). - */ - void ParserBase::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 ParserBase::DefineInfixOprt(const string_type &a_sName, - fun_type1 a_pFun, - int a_iPrec, - bool a_bAllowOpt) - { - AddCallback(a_sName, - ParserCallback(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 ParserBase::DefineOprt( const string_type &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 = &ParserBase::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 = &ParserBase::ParseString; - m_pTokenReader->IgnoreUndefVar(false); - throw; - } - - return m_pTokenReader->GetUsedVar(); - } - - //--------------------------------------------------------------------------- - /** \brief Return a map containing the used variables only. */ - const varmap_type& ParserBase::GetVar() const - { - return m_VarDef; - } - - //--------------------------------------------------------------------------- - /** \brief Return a map containing all parser constants. */ - const valmap_type& ParserBase::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& ParserBase::GetFunDef() const - { - return m_FunDef; - } - - //--------------------------------------------------------------------------- - /** \brief Retrieve the formula. */ - const string_type& ParserBase::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 - */ - ParserBase::token_type ParserBase::ApplyStrFunc(const token_type &a_FunTok, - const std::vector &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(ParserError& ) - { - 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 ParserBase::ApplyFunc( ParserStack &a_stOpt, - ParserStack &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 - std::vector 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; - default: - break; - } - - // Push dummy value representing the function result to the stack - token_type token; - token.SetVal(1); - a_stVal.push(token); - } - - //--------------------------------------------------------------------------- - void ParserBase::ApplyIfElse(ParserStack &a_stOpt, - ParserStack &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(); - MUP_ASSERT(a_stOpt.size()>0); - - // Take the value associated with the else branch from the value stack - token_type vVal2 = a_stVal.pop(); - - MUP_ASSERT(a_stOpt.size()>0); - MUP_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(); - MUP_ASSERT(opElse.GetCode()==cmELSE); - MUP_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 ParserBase::ApplyBinOprt(ParserStack &a_stOpt, - ParserStack &a_stVal) const - { - // is it a user defined binary operator? - if (a_stOpt.top().GetCode()==cmOPRT_BIN) - { - ApplyFunc(a_stOpt, a_stVal, 2); - } - else - { - MUP_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, _T("=")); - - 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 ParserBase::ApplyRemainingOprt(ParserStack &stOpt, - ParserStack &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. - */ - value_type ParserBase::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 - */ - value_type ParserBase::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. - value_type *Stack = ((nOffset==0) && (nThreadID==0)) ? &m_vStackBuffer[0] : &m_vStackBuffer[nThreadID * (m_vStackBuffer.size() / s_MaxNumOpenMPThreads)]; - value_type 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] = MathImpl::Pow(Stack[sidx], Stack[1+sidx]); - continue; - - case cmLAND: --sidx; Stack[sidx] = (Stack[sidx] != 0.) && (Stack[sidx+1] != 0.); continue; - case cmLOR: --sidx; Stack[sidx] = (Stack[sidx] != 0.) || (Stack[sidx+1] != 0.); continue; - - case cmASSIGN: - // Bugfix for Bulkmode: - // for details see: - // https://groups.google.com/forum/embed/?place=forum/muparser-dev&showsearch=true&showpopout=true&showtabs=false&parenturl=http://muparser.beltoforion.de/mup_forum.html&afterlogin&pli=1#!topic/muparser-dev/szgatgoHTws - --sidx; Stack[sidx] = *(pTok->u.Oprt.ptr + nOffset) = Stack[sidx + 1]; continue; - // original code: - //--sidx; Stack[sidx] = *pTok->u.Oprt.ptr = Stack[sidx+1]; continue; - - //case cmBO: // unused, listed for compiler optimization purposes - //case cmBC: - // MUP_FAIL(INVALID_CODE_IN_BYTECODE); - // continue; - - case cmIF: - if (Stack[sidx--]==0) - pTok += pTok->u.Oprt.offset; - continue; - - case cmELSE: - pTok += pTok->u.Oprt.offset; - continue; - - case cmENDIF: - continue; - - //case cmARG_SEP: - // MUP_FAIL(INVALID_CODE_IN_BYTECODE); - // continue; - - // value and variable tokens - case cmVAR: Stack[++sidx] = *(pTok->u.Val.ptr + nOffset); continue; - case cmVAL: Stack[++sidx] = pTok->u.Val.data2; continue; - - case cmVARPOW2: buf = *(pTok->u.Val.ptr + nOffset); - Stack[++sidx] = buf*buf; - continue; - - case cmVARPOW3: buf = *(pTok->u.Val.ptr + nOffset); - Stack[++sidx] = buf*buf*buf; - continue; - - case cmVARPOW4: buf = *(pTok->u.Val.ptr + nOffset); - Stack[++sidx] = buf*buf*buf*buf; - continue; - - case cmVARMUL: Stack[++sidx] = *(pTok->u.Val.ptr + nOffset) * pTok->u.Val.data + pTok->u.Val.data2; - continue; - - // Next is treatment of numeric functions - case cmFUNC: - { - int iArgCount = pTok->u.Fun.argc; - - // switch according to argument count - switch(iArgCount) - { - case 0: sidx += 1; Stack[sidx] = (*(fun_type0)pTok->u.Fun.ptr)(); continue; - case 1: Stack[sidx] = (*(fun_type1)pTok->u.Fun.ptr)(Stack[sidx]); continue; - case 2: sidx -= 1; Stack[sidx] = (*(fun_type2)pTok->u.Fun.ptr)(Stack[sidx], Stack[sidx+1]); continue; - case 3: sidx -= 2; Stack[sidx] = (*(fun_type3)pTok->u.Fun.ptr)(Stack[sidx], Stack[sidx+1], Stack[sidx+2]); continue; - case 4: sidx -= 3; Stack[sidx] = (*(fun_type4)pTok->u.Fun.ptr)(Stack[sidx], Stack[sidx+1], Stack[sidx+2], Stack[sidx+3]); continue; - case 5: sidx -= 4; Stack[sidx] = (*(fun_type5)pTok->u.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->u.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->u.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->u.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->u.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->u.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->u.Fun.ptr)(&Stack[sidx], -iArgCount); - continue; - } - } - - // Next is treatment of string functions - case cmFUNC_STR: - { - sidx -= pTok->u.Fun.argc -1; - - // The index of the string argument in the string table - int iIdxStack = pTok->u.Fun.idx; - MUP_ASSERT( iIdxStack>=0 && iIdxStack<(int)m_vStringBuf.size() ); - - switch(pTok->u.Fun.argc) // switch according to argument count - { - case 0: Stack[sidx] = (*(strfun_type1)pTok->u.Fun.ptr)(m_vStringBuf[iIdxStack].c_str()); continue; - case 1: Stack[sidx] = (*(strfun_type2)pTok->u.Fun.ptr)(m_vStringBuf[iIdxStack].c_str(), Stack[sidx]); continue; - case 2: Stack[sidx] = (*(strfun_type3)pTok->u.Fun.ptr)(m_vStringBuf[iIdxStack].c_str(), Stack[sidx], Stack[sidx+1]); continue; - } - - continue; - } - - case cmFUNC_BULK: - { - int iArgCount = pTok->u.Fun.argc; - - // switch according to argument count - switch(iArgCount) - { - case 0: sidx += 1; Stack[sidx] = (*(bulkfun_type0 )pTok->u.Fun.ptr)(nOffset, nThreadID); continue; - case 1: Stack[sidx] = (*(bulkfun_type1 )pTok->u.Fun.ptr)(nOffset, nThreadID, Stack[sidx]); continue; - case 2: sidx -= 1; Stack[sidx] = (*(bulkfun_type2 )pTok->u.Fun.ptr)(nOffset, nThreadID, Stack[sidx], Stack[sidx+1]); continue; - case 3: sidx -= 2; Stack[sidx] = (*(bulkfun_type3 )pTok->u.Fun.ptr)(nOffset, nThreadID, Stack[sidx], Stack[sidx+1], Stack[sidx+2]); continue; - case 4: sidx -= 3; Stack[sidx] = (*(bulkfun_type4 )pTok->u.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->u.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->u.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->u.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->u.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->u.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->u.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; - } - } - - default: - Error(ecINTERNAL_ERROR, 3); - return 0; - } // switch CmdCode - } // for all bytecode tokens - - return Stack[m_nFinalResultIdx]; - } - - //--------------------------------------------------------------------------- - void ParserBase::CreateRPN() const - { - if (!m_pTokenReader->GetExpr().length()) - Error(ecUNEXPECTED_EOF, 0); - - ParserStack stOpt, stVal; - ParserStack stArgCount; - token_type opta, opt; // for storing operators - token_type val, tval; // for storing value - - 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 (ParserBase::g_DbgDumpStack) - { - StackDump(stVal, stOpt); - m_vRPN.AsciiDump(); - } - } // while (true) - - if (ParserBase::g_DbgDumpCmdCode) - m_vRPN.AsciiDump(); - - if (m_nIfElseCounter>0) - Error(ecMISSING_ELSE_CLAUSE); - - // get the last value (= final result) from the stack - MUP_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. - */ - value_type ParserBase::ParseString() const - { - try - { - CreateRPN(); - m_pParseFormula = &ParserBase::ParseCmdCode; - return (this->*m_pParseFormula)(); - } - catch(ParserError &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 ParserBase::Error(EErrorCodes a_iErrc, int a_iPos, const string_type &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 ParserBase::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 ParserBase::RemoveVar(const string_type &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 ParserBase::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 ParserBase::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 ParserBase::ClearPostfixOprt() - { - m_PostOprtDef.clear(); - ReInit(); - } - - //------------------------------------------------------------------------------ - /** \brief Clear all user defined binary operators. - \post Resets the parser to string parsing mode. - \throw nothrow - */ - void ParserBase::ClearOprt() - { - m_OprtDef.clear(); - ReInit(); - } - - //------------------------------------------------------------------------------ - /** \brief Clear the user defined Prefix operators. - \post Resets the parser to string parser mode. - \throw nothrow - */ - void ParserBase::ClearInfixOprt() - { - m_InfixOprtDef.clear(); - ReInit(); - } - - //------------------------------------------------------------------------------ - /** \brief Enable or disable the formula optimization feature. - \post Resets the parser to string parser mode. - \throw nothrow - */ - void ParserBase::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 ParserBase::EnableDebugDump(bool bDumpCmd, bool bDumpStack) - { - ParserBase::g_DbgDumpCmdCode = bDumpCmd; - ParserBase::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 ParserBase::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 ParserBase::HasBuiltInOprt() const - { - return m_bBuiltInOp; - } - - //------------------------------------------------------------------------------ - /** \brief Get the argument separator character. - */ - char_type ParserBase::GetArgSep() const - { - return m_pTokenReader->GetArgSep(); - } - - //------------------------------------------------------------------------------ - /** \brief Set argument separator. - \param cArgSep the argument separator character. - */ - void ParserBase::SetArgSep(char_type cArgSep) - { - m_pTokenReader->SetArgSep(cArgSep); - } - - //------------------------------------------------------------------------------ - /** \brief Dump stack content. - - This function is used for debugging only. - */ - void ParserBase::StackDump(const ParserStack &a_stVal, - const ParserStack &a_stOprt) const - { - ParserStack stOprt(a_stOprt), - stVal(a_stVal); - - mu::console() << _T("\nValue stack:\n"); - while ( !stVal.empty() ) - { - token_type val = stVal.pop(); - if (val.GetType()==tpSTR) - mu::console() << _T(" \"") << val.GetAsString() << _T("\" "); - else - mu::console() << _T(" ") << val.GetVal() << _T(" "); - } - mu::console() << "\nOperator stack:\n"; - - while ( !stOprt.empty() ) - { - if (stOprt.top().GetCode()<=cmASSIGN) - { - mu::console() << _T("OPRT_INTRNL \"") - << ParserBase::c_DefaultOprt[stOprt.top().GetCode()] - << _T("\" \n"); - } - else - { - switch(stOprt.top().GetCode()) - { - case cmVAR: mu::console() << _T("VAR\n"); break; - case cmVAL: mu::console() << _T("VAL\n"); break; - case cmFUNC: mu::console() << _T("FUNC \"") - << stOprt.top().GetAsString() - << _T("\"\n"); break; - case cmFUNC_BULK: mu::console() << _T("FUNC_BULK \"") - << stOprt.top().GetAsString() - << _T("\"\n"); break; - case cmOPRT_INFIX: mu::console() << _T("OPRT_INFIX \"") - << stOprt.top().GetAsString() - << _T("\"\n"); break; - case cmOPRT_BIN: mu::console() << _T("OPRT_BIN \"") - << stOprt.top().GetAsString() - << _T("\"\n"); break; - case cmFUNC_STR: mu::console() << _T("FUNC_STR\n"); break; - case cmEND: mu::console() << _T("END\n"); break; - case cmUNKNOWN: mu::console() << _T("UNKNOWN\n"); break; - case cmBO: mu::console() << _T("BRACKET \"(\"\n"); break; - case cmBC: mu::console() << _T("BRACKET \")\"\n"); break; - case cmIF: mu::console() << _T("IF\n"); break; - case cmELSE: mu::console() << _T("ELSE\n"); break; - case cmENDIF: mu::console() << _T("ENDIF\n"); break; - default: mu::console() << stOprt.top().GetCode() << _T(" "); break; - } - } - stOprt.pop(); - } - - mu::console() << dec << endl; - } - - //------------------------------------------------------------------------------ - /** \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)") - */ - value_type* ParserBase::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 ParserBase::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. - */ - value_type ParserBase::Eval() const - { - return (this->*m_pParseFormula)(); - } - - //--------------------------------------------------------------------------- - void ParserBase::Eval(value_type *results, int nBulkSize) - { -/* Commented because it is making a unit test impossible - - // Parallelization does not make sense for fewer than 10000 computations - // due to thread creation overhead. If the bulk size is below 2000 - // computation is refused. - if (nBulkSize<2000) - { - throw ParserError(ecUNREASONABLE_NUMBER_OF_COMPUTATIONS); - } -*/ - 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 = 0, ct = 0; - omp_set_num_threads(nMaxThreads); - - #pragma omp parallel for schedule(static, nBulkSize/nMaxThreads) private(nThreadID) - for (i=0; i \___ >|__| - \/ \/ \/ \/ - Copyright (C) 2011 Ingo Berg - - 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 "muParserBytecode.h" - -#include -#include -#include -#include -#include -#include - -#include "muParserDef.h" -#include "muParserError.h" -#include "muParserToken.h" -#include "muParserStack.h" -#include "muParserTemplateMagic.h" - - -namespace mu -{ - //--------------------------------------------------------------------------- - /** \brief Bytecode default constructor. */ - ParserByteCode::ParserByteCode() - :m_iStackPos(0) - ,m_iMaxStackSize(0) - ,m_vRPN() - ,m_bEnableOptimizer(true) - { - m_vRPN.reserve(50); - } - - //--------------------------------------------------------------------------- - /** \brief Copy constructor. - - Implemented in Terms of Assign(const ParserByteCode &a_ByteCode) - */ - ParserByteCode::ParserByteCode(const ParserByteCode &a_ByteCode) - { - Assign(a_ByteCode); - } - - //--------------------------------------------------------------------------- - /** \brief Assignment operator. - - Implemented in Terms of Assign(const ParserByteCode &a_ByteCode) - */ - ParserByteCode& ParserByteCode::operator=(const ParserByteCode &a_ByteCode) - { - Assign(a_ByteCode); - return *this; - } - - //--------------------------------------------------------------------------- - void ParserByteCode::EnableOptimizer(bool bStat) - { - m_bEnableOptimizer = bStat; - } - - //--------------------------------------------------------------------------- - /** \brief Copy state of another object to this. - - \throw nowthrow - */ - void ParserByteCode::Assign(const ParserByteCode &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; - m_bEnableOptimizer = a_ByteCode.m_bEnableOptimizer; - } - - //--------------------------------------------------------------------------- - /** \brief Add a Variable pointer to bytecode. - \param a_pVar Pointer to be added. - \throw nothrow - */ - void ParserByteCode::AddVar(value_type *a_pVar) - { - ++m_iStackPos; - m_iMaxStackSize = std::max(m_iMaxStackSize, (size_t)m_iStackPos); - - // optimization does not apply - SToken tok; - tok.Cmd = cmVAR; - tok.u.Val.ptr = a_pVar; - tok.u.Val.data = 1; - tok.u.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 ParserByteCode::AddVal(value_type 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.u.Val.ptr = NULL; - tok.u.Val.data = 0; - tok.u.Val.data2 = a_fVal; - m_vRPN.push_back(tok); - } - - //--------------------------------------------------------------------------- - void ParserByteCode::ConstantFolding(ECmdCode a_Oprt) - { - std::size_t sz = m_vRPN.size(); - value_type &x = m_vRPN[sz-2].u.Val.data2, - &y = m_vRPN[sz-1].u.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, _T("0")); -#endif - - x = x / y; - m_vRPN.pop_back(); - break; - - case cmPOW: x = MathImpl::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 ParserByteCode::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].u.Val.data2==2) - m_vRPN[sz-2].Cmd = cmVARPOW2; - else if (m_vRPN[sz-1].u.Val.data2==3) - m_vRPN[sz-2].Cmd = cmVARPOW3; - else if (m_vRPN[sz-1].u.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].u.Val.ptr == m_vRPN[sz-1].u.Val.ptr) || - (m_vRPN[sz-1].Cmd == cmVAR && m_vRPN[sz-2].Cmd == cmVARMUL && m_vRPN[sz-2].u.Val.ptr == m_vRPN[sz-1].u.Val.ptr) || - (m_vRPN[sz-1].Cmd == cmVARMUL && m_vRPN[sz-2].Cmd == cmVAR && m_vRPN[sz-2].u.Val.ptr == m_vRPN[sz-1].u.Val.ptr) || - (m_vRPN[sz-1].Cmd == cmVARMUL && m_vRPN[sz-2].Cmd == cmVARMUL && m_vRPN[sz-2].u.Val.ptr == m_vRPN[sz-1].u.Val.ptr) ) - { - assert( (m_vRPN[sz-2].u.Val.ptr==NULL && m_vRPN[sz-1].u.Val.ptr!=NULL) || - (m_vRPN[sz-2].u.Val.ptr!=NULL && m_vRPN[sz-1].u.Val.ptr==NULL) || - (m_vRPN[sz-2].u.Val.ptr == m_vRPN[sz-1].u.Val.ptr) ); - - m_vRPN[sz-2].Cmd = cmVARMUL; - m_vRPN[sz-2].u.Val.ptr = (value_type*)((long long)(m_vRPN[sz-2].u.Val.ptr) | (long long)(m_vRPN[sz-1].u.Val.ptr)); // variable - m_vRPN[sz-2].u.Val.data2 += ((a_Oprt==cmSUB) ? -1 : 1) * m_vRPN[sz-1].u.Val.data2; // offset - m_vRPN[sz-2].u.Val.data += ((a_Oprt==cmSUB) ? -1 : 1) * m_vRPN[sz-1].u.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].u.Val.ptr = (value_type*)((long long)(m_vRPN[sz-2].u.Val.ptr) | (long long)(m_vRPN[sz-1].u.Val.ptr)); - m_vRPN[sz-2].u.Val.data = m_vRPN[sz-2].u.Val.data2 + m_vRPN[sz-1].u.Val.data2; - m_vRPN[sz-2].u.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].u.Val.ptr = (value_type*)((long long)(m_vRPN[sz-2].u.Val.ptr) | (long long)(m_vRPN[sz-1].u.Val.ptr)); - if (m_vRPN[sz-1].Cmd == cmVAL) - { - m_vRPN[sz-2].u.Val.data *= m_vRPN[sz-1].u.Val.data2; - m_vRPN[sz-2].u.Val.data2 *= m_vRPN[sz-1].u.Val.data2; - } - else - { - m_vRPN[sz-2].u.Val.data = m_vRPN[sz-1].u.Val.data * m_vRPN[sz-2].u.Val.data2; - m_vRPN[sz-2].u.Val.data2 = m_vRPN[sz-1].u.Val.data2 * m_vRPN[sz-2].u.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].u.Val.ptr == m_vRPN[sz-2].u.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].u.Val.data2!=0) - { - // Optimization: 4*a/2 -> 2*a - m_vRPN[sz-2].u.Val.data /= m_vRPN[sz-1].u.Val.data2; - m_vRPN[sz-2].u.Val.data2 /= m_vRPN[sz-1].u.Val.data2; - m_vRPN.pop_back(); - bOptimized = true; - } - break; - default: - 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 ParserByteCode::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 ParserByteCode::AddAssignOp(value_type *a_pVar) - { - --m_iStackPos; - - SToken tok; - tok.Cmd = cmASSIGN; - tok.u.Oprt.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 ParserByteCode::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.u.Fun.argc = a_iArgc; - tok.u.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 ParserByteCode::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.u.Fun.argc = a_iArgc; - tok.u.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 ParserByteCode::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.u.Fun.argc = a_iArgc; - tok.u.Fun.idx = a_iIdx; - tok.u.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 ParserByteCode::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 - ParserStack 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].u.Oprt.offset = i - idx; - break; - - case cmENDIF: - idx = stElse.pop(); - m_vRPN[idx].u.Oprt.offset = i - idx; - break; - - default: - break; - } - } - } - - //--------------------------------------------------------------------------- - const SToken* ParserByteCode::GetBase() const - { - if (m_vRPN.size()==0) - throw ParserError(ecINTERNAL_ERROR); - else - return &m_vRPN[0]; - } - - //--------------------------------------------------------------------------- - std::size_t ParserByteCode::GetMaxStackSize() const - { - return m_iMaxStackSize+1; - } - - //--------------------------------------------------------------------------- - /** \brief Returns the number of entries in the bytecode. */ - std::size_t ParserByteCode::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 ParserByteCode::clear() - { - m_vRPN.clear(); - m_iStackPos = 0; - m_iMaxStackSize = 0; - } - - //--------------------------------------------------------------------------- - /** \brief Dump bytecode (for debugging only!). */ - void ParserByteCode::AsciiDump() - { - if (!m_vRPN.size()) - { - mu::console() << _T("No bytecode available\n"); - return; - } - - mu::console() << _T("Number of RPN tokens:") << (int)m_vRPN.size() << _T("\n"); - for (std::size_t i=0; i \___ >|__| - \/ \/ \/ \/ - Copyright (C) 2004-2011 Ingo Berg - - 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 "muParserCallback.h" - -/** \file - \brief Implementation of the parser callback class. -*/ - - -namespace mu -{ - //--------------------------------------------------------------------------- - ParserCallback::ParserCallback(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) - {} - - //--------------------------------------------------------------------------- - ParserCallback::ParserCallback(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 - */ - ParserCallback::ParserCallback(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 - */ - ParserCallback::ParserCallback(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) - {} - - //--------------------------------------------------------------------------- - ParserCallback::ParserCallback(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) - {} - - - //--------------------------------------------------------------------------- - ParserCallback::ParserCallback(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) - {} - - - //--------------------------------------------------------------------------- - ParserCallback::ParserCallback(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) - {} - - //--------------------------------------------------------------------------- - ParserCallback::ParserCallback(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) - {} - - //--------------------------------------------------------------------------- - ParserCallback::ParserCallback(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) - {} - - //--------------------------------------------------------------------------- - ParserCallback::ParserCallback(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) - {} - - //--------------------------------------------------------------------------- - ParserCallback::ParserCallback(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) - {} - - //--------------------------------------------------------------------------- - ParserCallback::ParserCallback(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) - {} - - //--------------------------------------------------------------------------- - ParserCallback::ParserCallback(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) - {} - - //--------------------------------------------------------------------------- - ParserCallback::ParserCallback(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 - */ - ParserCallback::ParserCallback(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) - {} - - //--------------------------------------------------------------------------- - ParserCallback::ParserCallback(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) - {} - - - //--------------------------------------------------------------------------- - ParserCallback::ParserCallback(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) - {} - - - //--------------------------------------------------------------------------- - ParserCallback::ParserCallback(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) - {} - - //--------------------------------------------------------------------------- - ParserCallback::ParserCallback(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) - {} - - //--------------------------------------------------------------------------- - ParserCallback::ParserCallback(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) - {} - - //--------------------------------------------------------------------------- - ParserCallback::ParserCallback(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) - {} - - //--------------------------------------------------------------------------- - ParserCallback::ParserCallback(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) - {} - - //--------------------------------------------------------------------------- - ParserCallback::ParserCallback(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) - {} - - - //--------------------------------------------------------------------------- - ParserCallback::ParserCallback(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) - {} - - - //--------------------------------------------------------------------------- - ParserCallback::ParserCallback(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) - {} - - - //--------------------------------------------------------------------------- - ParserCallback::ParserCallback(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) - {} - - - //--------------------------------------------------------------------------- - ParserCallback::ParserCallback(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 - */ - ParserCallback::ParserCallback() - :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 - */ - ParserCallback::ParserCallback(const ParserCallback &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. */ - ParserCallback* ParserCallback::Clone() const - { - return new ParserCallback(*this); - } - - //--------------------------------------------------------------------------- - /** \brief Return tru if the function is conservative. - - Conservative functions return always the same result for the same argument. - \throw nothrow - */ - bool ParserCallback::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* ParserCallback::GetAddr() const - { - return m_pFun; - } - - //--------------------------------------------------------------------------- - /** \brief Return the callback code. */ - ECmdCode ParserCallback::GetCode() const - { - return m_iCode; - } - - //--------------------------------------------------------------------------- - ETypeCode ParserCallback::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 ParserCallback::GetPri() const - { - return m_iPri; - } - - //--------------------------------------------------------------------------- - /** \brief Return the operators associativity. - \throw nothrown - - Only valid if the callback token is a binary operator token. - */ - EOprtAssociativity ParserCallback::GetAssociativity() const - { - return m_eOprtAsct; - } - - //--------------------------------------------------------------------------- - /** \brief Returns the number of function Arguments. */ - int ParserCallback::GetArgc() const - { - return m_iArgc; - } -} // namespace mu diff --git a/bundled/muparser_v2_2_4/src/muParserError.cpp b/bundled/muparser_v2_2_4/src/muParserError.cpp deleted file mode 100644 index e1ac7215a1..0000000000 --- a/bundled/muparser_v2_2_4/src/muParserError.cpp +++ /dev/null @@ -1,337 +0,0 @@ -/* - __________ - _____ __ __\______ \_____ _______ ______ ____ _______ - / \ | | \| ___/\__ \ \_ __ \/ ___/_/ __ \\_ __ \ - | Y Y \| | /| | / __ \_| | \/\___ \ \ ___/ | | \/ - |__|_| /|____/ |____| (____ /|__| /____ > \___ >|__| - \/ \/ \/ \/ - Copyright (C) 2011 Ingo Berg - - 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 "muParserError.h" - - -namespace mu -{ - const ParserErrorMsg ParserErrorMsg::m_Instance; - - //------------------------------------------------------------------------------ - const ParserErrorMsg& ParserErrorMsg::Instance() - { - return m_Instance; - } - - //------------------------------------------------------------------------------ - string_type ParserErrorMsg::operator[](unsigned a_iIdx) const - { - return (a_iIdx \___ >|__| - \/ \/ \/ \/ - Copyright (C) 2011 Ingo Berg - - 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 "muParserInt.h" - -#include -#include -#include - -using namespace std; - -/** \file - \brief Implementation of a parser using integer value. -*/ - -/** \brief Namespace for mathematical applications. */ -namespace mu -{ -value_type ParserInt::Abs(value_type v) { return (value_type)Round(fabs((double)v)); } -value_type ParserInt::Sign(value_type v) { return (Round(v)<0) ? -1 : (Round(v)>0) ? 1 : 0; } -value_type ParserInt::Ite(value_type v1, - value_type v2, - value_type v3) { return (Round(v1)==1) ? Round(v2) : Round(v3); } -value_type ParserInt::Add(value_type v1, value_type v2) { return Round(v1) + Round(v2); } -value_type ParserInt::Sub(value_type v1, value_type v2) { return Round(v1) - Round(v2); } -value_type ParserInt::Mul(value_type v1, value_type v2) { return Round(v1) * Round(v2); } -value_type ParserInt::Div(value_type v1, value_type v2) { return Round(v1) / Round(v2); } -value_type ParserInt::Mod(value_type v1, value_type v2) { return Round(v1) % Round(v2); } -value_type ParserInt::Shr(value_type v1, value_type v2) { return Round(v1) >> Round(v2); } -value_type ParserInt::Shl(value_type v1, value_type v2) { return Round(v1) << Round(v2); } -value_type ParserInt::LogAnd(value_type v1, value_type v2) { return Round(v1) & Round(v2); } -value_type ParserInt::LogOr(value_type v1, value_type v2) { return Round(v1) | Round(v2); } -value_type ParserInt::And(value_type v1, value_type v2) { return Round(v1) && Round(v2); } -value_type ParserInt::Or(value_type v1, value_type v2) { return Round(v1) || Round(v2); } -value_type ParserInt::Less(value_type v1, value_type v2) { return Round(v1) < Round(v2); } -value_type ParserInt::Greater(value_type v1, value_type v2) { return Round(v1) > Round(v2); } -value_type ParserInt::LessEq(value_type v1, value_type v2) { return Round(v1) <= Round(v2); } -value_type ParserInt::GreaterEq(value_type v1, value_type v2) { return Round(v1) >= Round(v2); } -value_type ParserInt::Equal(value_type v1, value_type v2) { return Round(v1) == Round(v2); } -value_type ParserInt::NotEqual(value_type v1, value_type v2) { return Round(v1) != Round(v2); } -value_type ParserInt::Not(value_type v) { return !Round(v); } - -value_type ParserInt::Pow(value_type v1, value_type v2) -{ - return std::pow((double)Round(v1), (double)Round(v2)); -} - -//--------------------------------------------------------------------------- -// Unary operator Callbacks: Infix operators -value_type ParserInt::UnaryMinus(value_type v) -{ - return -Round(v); -} - -//--------------------------------------------------------------------------- -value_type ParserInt::Sum(const value_type* a_afArg, int a_iArgc) -{ - if (!a_iArgc) - throw ParserError(_T("too few arguments for function sum.")); - - value_type fRes=0; - for (int i=0; i> iVal; - if (stream.fail()) - return 0; - - stringstream_type::pos_type iEnd = stream.tellg(); // Position after reading - if (stream.fail()) - iEnd = stream.str().length(); - - if (iEnd==(stringstream_type::pos_type)-1) - return 0; - - *a_iPos += (int)iEnd; - *a_fVal = (value_type)iVal; - return 1; -} - -//--------------------------------------------------------------------------- -/** \brief Check a given position in the expression for the presence of - a hex value. - \param a_szExpr Pointer to the expression string - \param [in/out] a_iPos Pointer to an interger value holding the current parsing - position in the expression. - \param [out] a_fVal Pointer to the position where the detected value shall be stored. - - Hey values must be prefixed with "0x" in order to be detected properly. -*/ -int ParserInt::IsHexVal(const char_type *a_szExpr, int *a_iPos, value_type *a_fVal) -{ - if (a_szExpr[1]==0 || (a_szExpr[0]!='0' || a_szExpr[1]!='x') ) - return 0; - - unsigned iVal(0); - - // New code based on streams for UNICODE compliance: - stringstream_type::pos_type nPos(0); - stringstream_type ss(a_szExpr + 2); - ss >> std::hex >> iVal; - nPos = ss.tellg(); - - if (nPos==(stringstream_type::pos_type)0) - return 1; - - *a_iPos += (int)(2 + nPos); - *a_fVal = (value_type)iVal; - return 1; -} - -//--------------------------------------------------------------------------- -int ParserInt::IsBinVal(const char_type *a_szExpr, int *a_iPos, value_type *a_fVal) -{ - if (a_szExpr[0]!='#') - return 0; - - unsigned iVal(0), - iBits(sizeof(iVal)*8), - i(0); - - for (i=0; (a_szExpr[i+1]=='0' || a_szExpr[i+1]=='1') && i> (iBits-i) ); - *a_iPos += i+1; - - return 1; -} - -//--------------------------------------------------------------------------- -/** \brief Constructor. - - Call ParserBase class constructor and trigger Function, Operator and Constant initialization. -*/ -ParserInt::ParserInt() - :ParserBase() -{ - AddValIdent(IsVal); // lowest priority - AddValIdent(IsBinVal); - AddValIdent(IsHexVal); // highest priority - - InitCharSets(); - InitFun(); - InitOprt(); -} - -//--------------------------------------------------------------------------- -void ParserInt::InitConst() -{ -} - -//--------------------------------------------------------------------------- -void ParserInt::InitCharSets() -{ - DefineNameChars( _T("0123456789_abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ") ); - DefineOprtChars( _T("+-*^/?<>=!%&|~'_") ); - DefineInfixOprtChars( _T("/+-*^?<>=!%&|~'_") ); -} - -//--------------------------------------------------------------------------- -/** \brief Initialize the default functions. */ -void ParserInt::InitFun() -{ - DefineFun( _T("sign"), Sign); - DefineFun( _T("abs"), Abs); - DefineFun( _T("if"), Ite); - DefineFun( _T("sum"), Sum); - DefineFun( _T("min"), Min); - DefineFun( _T("max"), Max); -} - -//--------------------------------------------------------------------------- -/** \brief Initialize operators. */ -void ParserInt::InitOprt() -{ - // disable all built in operators, not all of them usefull for integer numbers - // (they don't do rounding of values) - EnableBuiltInOprt(false); - - // Disable all built in operators, they wont work with integer numbers - // since they are designed for floating point numbers - DefineInfixOprt( _T("-"), UnaryMinus); - DefineInfixOprt( _T("!"), Not); - - DefineOprt( _T("&"), LogAnd, prLOGIC); - DefineOprt( _T("|"), LogOr, prLOGIC); - DefineOprt( _T("&&"), And, prLOGIC); - DefineOprt( _T("||"), Or, prLOGIC); - - DefineOprt( _T("<"), Less, prCMP); - DefineOprt( _T(">"), Greater, prCMP); - DefineOprt( _T("<="), LessEq, prCMP); - DefineOprt( _T(">="), GreaterEq, prCMP); - DefineOprt( _T("=="), Equal, prCMP); - DefineOprt( _T("!="), NotEqual, prCMP); - - DefineOprt( _T("+"), Add, prADD_SUB); - DefineOprt( _T("-"), Sub, prADD_SUB); - - DefineOprt( _T("*"), Mul, prMUL_DIV); - DefineOprt( _T("/"), Div, prMUL_DIV); - DefineOprt( _T("%"), Mod, prMUL_DIV); - - DefineOprt( _T("^"), Pow, prPOW, oaRIGHT); - DefineOprt( _T(">>"), Shr, prMUL_DIV+1); - DefineOprt( _T("<<"), Shl, prMUL_DIV+1); -} - -} // namespace mu diff --git a/bundled/muparser_v2_2_4/src/muParserTest.cpp b/bundled/muparser_v2_2_4/src/muParserTest.cpp deleted file mode 100644 index af2aa86d12..0000000000 --- a/bundled/muparser_v2_2_4/src/muParserTest.cpp +++ /dev/null @@ -1,1552 +0,0 @@ -/* - __________ - _____ __ __\______ \_____ _______ ______ ____ _______ - / \ | | \| ___/\__ \ \_ __ \/ ___/_/ __ \\_ __ \ - | Y Y \| | /| | / __ \_| | \/\___ \ \ ___/ | | \/ - |__|_| /|____/ |____| (____ /|__| /____ > \___ >|__| - \/ \/ \/ \/ - Copyright (C) 2013 Ingo Berg - - 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 "muParserTest.h" - -#include -#include -#include -#include - -#define PARSER_CONST_PI 3.141592653589793238462643 -#define PARSER_CONST_E 2.718281828459045235360287 - -using namespace std; - -/** \file - \brief This file contains the implementation of parser test cases. -*/ - -namespace mu -{ - namespace Test - { - int ParserTester::c_iCount = 0; - - //--------------------------------------------------------------------------------------------- - ParserTester::ParserTester() - :m_vTestFun() - { - AddTest(&ParserTester::TestNames); - AddTest(&ParserTester::TestSyntax); - AddTest(&ParserTester::TestPostFix); - AddTest(&ParserTester::TestInfixOprt); - AddTest(&ParserTester::TestVarConst); - AddTest(&ParserTester::TestMultiArg); - AddTest(&ParserTester::TestExpression); - AddTest(&ParserTester::TestIfThenElse); - AddTest(&ParserTester::TestInterface); - AddTest(&ParserTester::TestBinOprt); - AddTest(&ParserTester::TestException); - AddTest(&ParserTester::TestStrArg); - AddTest(&ParserTester::TestBulkMode); - - ParserTester::c_iCount = 0; - } - - //--------------------------------------------------------------------------------------------- - int ParserTester::IsHexVal(const char_type *a_szExpr, int *a_iPos, value_type *a_fVal) - { - if (a_szExpr[1]==0 || (a_szExpr[0]!='0' || a_szExpr[1]!='x') ) - return 0; - - unsigned iVal(0); - - // New code based on streams for UNICODE compliance: - stringstream_type::pos_type nPos(0); - stringstream_type ss(a_szExpr + 2); - ss >> std::hex >> iVal; - nPos = ss.tellg(); - - if (nPos==(stringstream_type::pos_type)0) - return 1; - - *a_iPos += (int)(2 + nPos); - *a_fVal = (value_type)iVal; - return 1; - } - - //--------------------------------------------------------------------------------------------- - int ParserTester::TestInterface() - { - int iStat = 0; - mu::console() << _T("testing member functions..."); - - // Test RemoveVar - value_type afVal[3] = {1,2,3}; - Parser p; - - try - { - p.DefineVar( _T("a"), &afVal[0]); - p.DefineVar( _T("b"), &afVal[1]); - p.DefineVar( _T("c"), &afVal[2]); - p.SetExpr( _T("a+b+c") ); - p.Eval(); - } - catch(...) - { - iStat += 1; // this is not supposed to happen - } - - try - { - p.RemoveVar( _T("c") ); - p.Eval(); - iStat += 1; // not supposed to reach this, nonexisting variable "c" deleted... - } - catch(...) - { - // failure is expected... - } - - if (iStat==0) - mu::console() << _T("passed") << endl; - else - mu::console() << _T("\n failed with ") << iStat << _T(" errors") << endl; - - return iStat; - } - - //--------------------------------------------------------------------------------------------- - int ParserTester::TestStrArg() - { - int iStat = 0; - mu::console() << _T("testing string arguments..."); - - iStat += EqnTest(_T("valueof(\"\")"), 123, true); // empty string arguments caused a crash - iStat += EqnTest(_T("valueof(\"aaa\")+valueof(\"bbb\") "), 246, true); - iStat += EqnTest(_T("2*(valueof(\"aaa\")-23)+valueof(\"bbb\")"), 323, true); - // use in expressions with variables - iStat += EqnTest(_T("a*(atof(\"10\")-b)"), 8, true); - iStat += EqnTest(_T("a-(atof(\"10\")*b)"), -19, true); - // string + numeric arguments - iStat += EqnTest(_T("strfun1(\"100\")"), 100, true); - iStat += EqnTest(_T("strfun2(\"100\",1)"), 101, true); - iStat += EqnTest(_T("strfun3(\"99\",1,2)"), 102, true); - // string constants - iStat += EqnTest(_T("atof(str1)+atof(str2)"), 3.33, true); - - if (iStat==0) - mu::console() << _T("passed") << endl; - else - mu::console() << _T("\n failed with ") << iStat << _T(" errors") << endl; - - return iStat; - } - - //--------------------------------------------------------------------------------------------- - int ParserTester::TestBulkMode() - { - int iStat = 0; - mu::console() << _T("testing bulkmode..."); - -#define EQN_TEST_BULK(EXPR, R1, R2, R3, R4, PASS) \ - { \ - double res[] = { R1, R2, R3, R4 }; \ - iStat += EqnTestBulk(_T(EXPR), res, (PASS)); \ - } - - // Bulk Variables for the test: - // a: 1,2,3,4 - // b: 2,2,2,2 - // c: 3,3,3,3 - // d: 5,4,3,2 - EQN_TEST_BULK("a", 1, 1, 1, 1, false) - EQN_TEST_BULK("a", 1, 2, 3, 4, true) - EQN_TEST_BULK("b=a", 1, 2, 3, 4, true) - EQN_TEST_BULK("b=a, b*10", 10, 20, 30, 40, true) - EQN_TEST_BULK("b=a, b*10, a", 1, 2, 3, 4, true) - EQN_TEST_BULK("a+b", 3, 4, 5, 6, true) - EQN_TEST_BULK("c*(a+b)", 9, 12, 15, 18, true) -#undef EQN_TEST_BULK - - if (iStat == 0) - mu::console() << _T("passed") << endl; - else - mu::console() << _T("\n failed with ") << iStat << _T(" errors") << endl; - - return iStat; - } - - //--------------------------------------------------------------------------------------------- - int ParserTester::TestBinOprt() - { - int iStat = 0; - mu::console() << _T("testing binary operators..."); - - // built in operators - // xor operator - - iStat += EqnTest(_T("a++b"), 3, true); - iStat += EqnTest(_T("a ++ b"), 3, true); - iStat += EqnTest(_T("1++2"), 3, true); - iStat += EqnTest(_T("1 ++ 2"), 3, true); - iStat += EqnTest(_T("a add b"), 3, true); - iStat += EqnTest(_T("1 add 2"), 3, true); - iStat += EqnTest(_T("aa"), 1, true); - iStat += EqnTest(_T("a>a"), 0, true); - iStat += EqnTest(_T("aa"), 0, true); - iStat += EqnTest(_T("a<=a"), 1, true); - iStat += EqnTest(_T("a<=b"), 1, true); - iStat += EqnTest(_T("b<=a"), 0, true); - iStat += EqnTest(_T("a>=a"), 1, true); - iStat += EqnTest(_T("b>=a"), 1, true); - iStat += EqnTest(_T("a>=b"), 0, true); - - // Test logical operators, expecially if user defined "&" and the internal "&&" collide - iStat += EqnTest(_T("1 && 1"), 1, true); - iStat += EqnTest(_T("1 && 0"), 0, true); - iStat += EqnTest(_T("(aa)"), 1, true); - iStat += EqnTest(_T("(ab)"), 0, true); - //iStat += EqnTest(_T("12 and 255"), 12, true); - //iStat += EqnTest(_T("12 and 0"), 0, true); - iStat += EqnTest(_T("12 & 255"), 12, true); - iStat += EqnTest(_T("12 & 0"), 0, true); - iStat += EqnTest(_T("12&255"), 12, true); - iStat += EqnTest(_T("12&0"), 0, true); - - // Assignement operator - iStat += EqnTest(_T("a = b"), 2, true); - iStat += EqnTest(_T("a = sin(b)"), 0.909297, true); - iStat += EqnTest(_T("a = 1+sin(b)"), 1.909297, true); - iStat += EqnTest(_T("(a=b)*2"), 4, true); - iStat += EqnTest(_T("2*(a=b)"), 4, true); - iStat += EqnTest(_T("2*(a=b+1)"), 6, true); - iStat += EqnTest(_T("(a=b+1)*2"), 6, true); - iStat += EqnTest(_T("a=c, a*10"), 30, true); - - iStat += EqnTest(_T("2^2^3"), 256, true); - iStat += EqnTest(_T("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(_T("3+4*2/(1-5)^2^3"), 3.0001220703125, true); - - // Test user defined binary operators - iStat += EqnTestInt(_T("1 | 2"), 3, true); - iStat += EqnTestInt(_T("1 || 2"), 1, true); - iStat += EqnTestInt(_T("123 & 456"), 72, true); - iStat += EqnTestInt(_T("(123 & 456) % 10"), 2, true); - iStat += EqnTestInt(_T("1 && 0"), 0, true); - iStat += EqnTestInt(_T("123 && 456"), 1, true); - iStat += EqnTestInt(_T("1 << 3"), 8, true); - iStat += EqnTestInt(_T("8 >> 3"), 1, true); - iStat += EqnTestInt(_T("9 / 4"), 2, true); - iStat += EqnTestInt(_T("9 % 4"), 1, true); - iStat += EqnTestInt(_T("if(5%2,1,0)"), 1, true); - iStat += EqnTestInt(_T("if(4%2,1,0)"), 0, true); - iStat += EqnTestInt(_T("-10+1"), -9, true); - iStat += EqnTestInt(_T("1+2*3"), 7, true); - iStat += EqnTestInt(_T("const1 != const2"), 1, true); - iStat += EqnTestInt(_T("const1 != const2"), 0, false); - iStat += EqnTestInt(_T("const1 == const2"), 0, true); - iStat += EqnTestInt(_T("const1 == 1"), 1, true); - iStat += EqnTestInt(_T("10*(const1 == 1)"), 10, true); - iStat += EqnTestInt(_T("2*(const1 | const2)"), 6, true); - iStat += EqnTestInt(_T("2*(const1 | const2)"), 7, false); - iStat += EqnTestInt(_T("const1 < const2"), 1, true); - iStat += EqnTestInt(_T("const2 > const1"), 1, true); - iStat += EqnTestInt(_T("const1 <= 1"), 1, true); - iStat += EqnTestInt(_T("const2 >= 2"), 1, true); - iStat += EqnTestInt(_T("2*(const1 + const2)"), 6, true); - iStat += EqnTestInt(_T("2*(const1 - const2)"), -2, true); - iStat += EqnTestInt(_T("a != b"), 1, true); - iStat += EqnTestInt(_T("a != b"), 0, false); - iStat += EqnTestInt(_T("a == b"), 0, true); - iStat += EqnTestInt(_T("a == 1"), 1, true); - iStat += EqnTestInt(_T("10*(a == 1)"), 10, true); - iStat += EqnTestInt(_T("2*(a | b)"), 6, true); - iStat += EqnTestInt(_T("2*(a | b)"), 7, false); - iStat += EqnTestInt(_T("a < b"), 1, true); - iStat += EqnTestInt(_T("b > a"), 1, true); - iStat += EqnTestInt(_T("a <= 1"), 1, true); - iStat += EqnTestInt(_T("b >= 2"), 1, true); - iStat += EqnTestInt(_T("2*(a + b)"), 6, true); - iStat += EqnTestInt(_T("2*(a - b)"), -2, true); - iStat += EqnTestInt(_T("a + (a << b)"), 5, true); - iStat += EqnTestInt(_T("-2^2"), -4, true); - iStat += EqnTestInt(_T("3--a"), 4, true); - iStat += EqnTestInt(_T("3+-3^2"), -6, true); - - // Test reading of hex values: - iStat += EqnTestInt(_T("0xff"), 255, true); - iStat += EqnTestInt(_T("10+0xff"), 265, true); - iStat += EqnTestInt(_T("0xff+10"), 265, true); - iStat += EqnTestInt(_T("10*0xff"), 2550, true); - iStat += EqnTestInt(_T("0xff*10"), 2550, true); - iStat += EqnTestInt(_T("10+0xff+1"), 266, true); - iStat += EqnTestInt(_T("1+0xff+10"), 266, true); - -// incorrect: '^' is yor here, not power -// iStat += EqnTestInt("-(1+2)^2", -9, true); -// iStat += EqnTestInt("-1^3", -1, true); - - // Test precedence - // a=1, b=2, c=3 - iStat += EqnTestInt(_T("a + b * c"), 7, true); - iStat += EqnTestInt(_T("a * b + c"), 5, true); - iStat += EqnTestInt(_T("a10"), 0, true); - iStat += EqnTestInt(_T("a"), f1of1) - PARSER_THROWCHECK(PostfixOprt, true, _T("?<"), f1of1) - PARSER_THROWCHECK(PostfixOprt, true, _T("**"), f1of1) - PARSER_THROWCHECK(PostfixOprt, true, _T("xor"), f1of1) - PARSER_THROWCHECK(PostfixOprt, true, _T("and"), f1of1) - PARSER_THROWCHECK(PostfixOprt, true, _T("or"), f1of1) - PARSER_THROWCHECK(PostfixOprt, true, _T("not"), f1of1) - PARSER_THROWCHECK(PostfixOprt, true, _T("!"), f1of1) - // Binary operator - // The following must fail with builtin operators activated - // p.EnableBuiltInOp(true); -> this is the default - p.ClearPostfixOprt(); - PARSER_THROWCHECK(Oprt, false, _T("+"), f1of2) - PARSER_THROWCHECK(Oprt, false, _T("-"), f1of2) - PARSER_THROWCHECK(Oprt, false, _T("*"), f1of2) - PARSER_THROWCHECK(Oprt, false, _T("/"), f1of2) - PARSER_THROWCHECK(Oprt, false, _T("^"), f1of2) - PARSER_THROWCHECK(Oprt, false, _T("&&"), f1of2) - PARSER_THROWCHECK(Oprt, false, _T("||"), f1of2) - // without activated built in operators it should work - p.EnableBuiltInOprt(false); - PARSER_THROWCHECK(Oprt, true, _T("+"), f1of2) - PARSER_THROWCHECK(Oprt, true, _T("-"), f1of2) - PARSER_THROWCHECK(Oprt, true, _T("*"), f1of2) - PARSER_THROWCHECK(Oprt, true, _T("/"), f1of2) - PARSER_THROWCHECK(Oprt, true, _T("^"), f1of2) - PARSER_THROWCHECK(Oprt, true, _T("&&"), f1of2) - PARSER_THROWCHECK(Oprt, true, _T("||"), f1of2) - #undef PARSER_THROWCHECK - - if (iStat==0) - mu::console() << _T("passed") << endl; - else - mu::console() << _T("\n failed with ") << iStat << _T(" errors") << endl; - - return iStat; - } - - //--------------------------------------------------------------------------- - int ParserTester::TestSyntax() - { - int iStat = 0; - mu::console() << _T("testing syntax engine..."); - - iStat += ThrowTest(_T("1,"), ecUNEXPECTED_EOF); // incomplete hex definition - iStat += ThrowTest(_T("a,"), ecUNEXPECTED_EOF); // incomplete hex definition - iStat += ThrowTest(_T("sin(8),"), ecUNEXPECTED_EOF); // incomplete hex definition - iStat += ThrowTest(_T("(sin(8)),"), ecUNEXPECTED_EOF); // incomplete hex definition - iStat += ThrowTest(_T("a{m},"), ecUNEXPECTED_EOF); // incomplete hex definition - - iStat += EqnTest(_T("(1+ 2*a)"), 3, true); // Spaces within formula - iStat += EqnTest(_T("sqrt((4))"), 2, true); // Multiple brackets - iStat += EqnTest(_T("sqrt((2)+2)"), 2, true);// Multiple brackets - iStat += EqnTest(_T("sqrt(2+(2))"), 2, true);// Multiple brackets - iStat += EqnTest(_T("sqrt(a+(3))"), 2, true);// Multiple brackets - iStat += EqnTest(_T("sqrt((3)+a)"), 2, true);// Multiple brackets - iStat += EqnTest(_T("order(1,2)"), 1, true); // May not cause name collision with operator "or" - iStat += EqnTest(_T("(2+"), 0, false); // missing closing bracket - iStat += EqnTest(_T("2++4"), 0, false); // unexpected operator - iStat += EqnTest(_T("2+-4"), 0, false); // unexpected operator - iStat += EqnTest(_T("(2+)"), 0, false); // unexpected closing bracket - iStat += EqnTest(_T("--2"), 0, false); // double sign - iStat += EqnTest(_T("ksdfj"), 0, false); // unknown token - iStat += EqnTest(_T("()"), 0, false); // empty bracket without a function - iStat += EqnTest(_T("5+()"), 0, false); // empty bracket without a function - iStat += EqnTest(_T("sin(cos)"), 0, false); // unexpected function - iStat += EqnTest(_T("5t6"), 0, false); // unknown token - iStat += EqnTest(_T("5 t 6"), 0, false); // unknown token - iStat += EqnTest(_T("8*"), 0, false); // unexpected end of formula - iStat += EqnTest(_T(",3"), 0, false); // unexpected comma - iStat += EqnTest(_T("3,5"), 0, false); // unexpected comma - iStat += EqnTest(_T("sin(8,8)"), 0, false); // too many function args - iStat += EqnTest(_T("(7,8)"), 0, false); // too many function args - iStat += EqnTest(_T("sin)"), 0, false); // unexpected closing bracket - iStat += EqnTest(_T("a)"), 0, false); // unexpected closing bracket - iStat += EqnTest(_T("pi)"), 0, false); // unexpected closing bracket - iStat += EqnTest(_T("sin(())"), 0, false); // unexpected closing bracket - iStat += EqnTest(_T("sin()"), 0, false); // unexpected closing bracket - - if (iStat==0) - mu::console() << _T("passed") << endl; - else - mu::console() << _T("\n failed with ") << iStat << _T(" errors") << endl; - - return iStat; - } - - //--------------------------------------------------------------------------- - int ParserTester::TestVarConst() - { - int iStat = 0; - mu::console() << _T("testing variable/constant detection..."); - - // Test if the result changes when a variable changes - iStat += EqnTestWithVarChange( _T("a"), 1, 1, 2, 2 ); - iStat += EqnTestWithVarChange( _T("2*a"), 2, 4, 3, 6 ); - - // distinguish constants with same basename - iStat += EqnTest( _T("const"), 1, true); - iStat += EqnTest( _T("const1"), 2, true); - iStat += EqnTest( _T("const2"), 3, true); - iStat += EqnTest( _T("2*const"), 2, true); - iStat += EqnTest( _T("2*const1"), 4, true); - iStat += EqnTest( _T("2*const2"), 6, true); - iStat += EqnTest( _T("2*const+1"), 3, true); - iStat += EqnTest( _T("2*const1+1"), 5, true); - iStat += EqnTest( _T("2*const2+1"), 7, true); - iStat += EqnTest( _T("const"), 0, false); - iStat += EqnTest( _T("const1"), 0, false); - iStat += EqnTest( _T("const2"), 0, false); - - // distinguish variables with same basename - iStat += EqnTest( _T("a"), 1, true); - iStat += EqnTest( _T("aa"), 2, true); - iStat += EqnTest( _T("2*a"), 2, true); - iStat += EqnTest( _T("2*aa"), 4, true); - iStat += EqnTest( _T("2*a-1"), 1, true); - iStat += EqnTest( _T("2*aa-1"), 3, true); - - // custom value recognition - iStat += EqnTest( _T("0xff"), 255, true); - iStat += EqnTest( _T("0x97 + 0xff"), 406, true); - - // Finally test querying of used variables - try - { - int idx; - mu::Parser p; - mu::value_type vVarVal[] = { 1, 2, 3, 4, 5}; - p.DefineVar( _T("a"), &vVarVal[0]); - p.DefineVar( _T("b"), &vVarVal[1]); - p.DefineVar( _T("c"), &vVarVal[2]); - p.DefineVar( _T("d"), &vVarVal[3]); - p.DefineVar( _T("e"), &vVarVal[4]); - - // Test lookup of defined variables - // 4 used variables - p.SetExpr( _T("a+b+c+d") ); - mu::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; - - mu::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( _T("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( _T("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) - mu::console() << _T("passed") << endl; - else - mu::console() << _T("\n failed with ") << iStat << _T(" errors") << endl; - - return iStat; - } - - //--------------------------------------------------------------------------- - int ParserTester::TestMultiArg() - { - int iStat = 0; - mu::console() << _T("testing multiarg functions..."); - - // Compound expressions - iStat += EqnTest( _T("1,2,3"), 3, true); - iStat += EqnTest( _T("a,b,c"), 3, true); - iStat += EqnTest( _T("a=10,b=20,c=a*b"), 200, true); - iStat += EqnTest( _T("1,\n2,\n3"), 3, true); - iStat += EqnTest( _T("a,\nb,\nc"), 3, true); - iStat += EqnTest( _T("a=10,\nb=20,\nc=a*b"), 200, true); - iStat += EqnTest( _T("1,\r\n2,\r\n3"), 3, true); - iStat += EqnTest( _T("a,\r\nb,\r\nc"), 3, true); - iStat += EqnTest( _T("a=10,\r\nb=20,\r\nc=a*b"), 200, true); - - // picking the right argument - iStat += EqnTest( _T("f1of1(1)"), 1, true); - iStat += EqnTest( _T("f1of2(1, 2)"), 1, true); - iStat += EqnTest( _T("f2of2(1, 2)"), 2, true); - iStat += EqnTest( _T("f1of3(1, 2, 3)"), 1, true); - iStat += EqnTest( _T("f2of3(1, 2, 3)"), 2, true); - iStat += EqnTest( _T("f3of3(1, 2, 3)"), 3, true); - iStat += EqnTest( _T("f1of4(1, 2, 3, 4)"), 1, true); - iStat += EqnTest( _T("f2of4(1, 2, 3, 4)"), 2, true); - iStat += EqnTest( _T("f3of4(1, 2, 3, 4)"), 3, true); - iStat += EqnTest( _T("f4of4(1, 2, 3, 4)"), 4, true); - iStat += EqnTest( _T("f1of5(1, 2, 3, 4, 5)"), 1, true); - iStat += EqnTest( _T("f2of5(1, 2, 3, 4, 5)"), 2, true); - iStat += EqnTest( _T("f3of5(1, 2, 3, 4, 5)"), 3, true); - iStat += EqnTest( _T("f4of5(1, 2, 3, 4, 5)"), 4, true); - iStat += EqnTest( _T("f5of5(1, 2, 3, 4, 5)"), 5, true); - // Too few arguments / Too many arguments - iStat += EqnTest( _T("1+ping()"), 11, true); - iStat += EqnTest( _T("ping()+1"), 11, true); - iStat += EqnTest( _T("2*ping()"), 20, true); - iStat += EqnTest( _T("ping()*2"), 20, true); - iStat += EqnTest( _T("ping(1,2)"), 0, false); - iStat += EqnTest( _T("1+ping(1,2)"), 0, false); - iStat += EqnTest( _T("f1of1(1,2)"), 0, false); - iStat += EqnTest( _T("f1of1()"), 0, false); - iStat += EqnTest( _T("f1of2(1, 2, 3)"), 0, false); - iStat += EqnTest( _T("f1of2(1)"), 0, false); - iStat += EqnTest( _T("f1of3(1, 2, 3, 4)"), 0, false); - iStat += EqnTest( _T("f1of3(1)"), 0, false); - iStat += EqnTest( _T("f1of4(1, 2, 3, 4, 5)"), 0, false); - iStat += EqnTest( _T("f1of4(1)"), 0, false); - iStat += EqnTest( _T("(1,2,3)"), 0, false); - iStat += EqnTest( _T("1,2,3"), 0, false); - iStat += EqnTest( _T("(1*a,2,3)"), 0, false); - iStat += EqnTest( _T("1,2*a,3"), 0, false); - - // correct calculation of arguments - iStat += EqnTest( _T("min(a, 1)"), 1, true); - iStat += EqnTest( _T("min(3*2, 1)"), 1, true); - iStat += EqnTest( _T("min(3*2, 1)"), 6, false); - iStat += EqnTest( _T("firstArg(2,3,4)"), 2, true); - iStat += EqnTest( _T("lastArg(2,3,4)"), 4, true); - iStat += EqnTest( _T("min(3*a+1, 1)"), 1, true); - iStat += EqnTest( _T("max(3*a+1, 1)"), 4, true); - iStat += EqnTest( _T("max(3*a+1, 1)*2"), 8, true); - iStat += EqnTest( _T("2*max(3*a+1, 1)+2"), 10, true); - - // functions with Variable argument count - iStat += EqnTest( _T("sum(a)"), 1, true); - iStat += EqnTest( _T("sum(1,2,3)"), 6, true); - iStat += EqnTest( _T("sum(a,b,c)"), 6, true); - iStat += EqnTest( _T("sum(1,-max(1,2),3)*2"), 4, true); - iStat += EqnTest( _T("2*sum(1,2,3)"), 12, true); - iStat += EqnTest( _T("2*sum(1,2,3)+2"), 14, true); - iStat += EqnTest( _T("2*sum(-1,2,3)+2"), 10, true); - iStat += EqnTest( _T("2*sum(-1,2,-(-a))+2"), 6, true); - iStat += EqnTest( _T("2*sum(-1,10,-a)+2"), 18, true); - iStat += EqnTest( _T("2*sum(1,2,3)*2"), 24, true); - iStat += EqnTest( _T("sum(1,-max(1,2),3)*2"), 4, true); - iStat += EqnTest( _T("sum(1*3, 4, a+2)"), 10, true); - iStat += EqnTest( _T("sum(1*3, 2*sum(1,2,2), a+2)"), 16, true); - iStat += EqnTest( _T("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( _T("sum()"), 0, false); - iStat += EqnTest( _T("sum(,)"), 0, false); - iStat += EqnTest( _T("sum(1,2,)"), 0, false); - iStat += EqnTest( _T("sum(,1,2)"), 0, false); - - if (iStat==0) - mu::console() << _T("passed") << endl; - else - mu::console() << _T("\n failed with ") << iStat << _T(" errors") << endl; - - return iStat; - } - - - //--------------------------------------------------------------------------- - int ParserTester::TestInfixOprt() - { - int iStat(0); - mu::console() << "testing infix operators..."; - - iStat += EqnTest( _T("+1"), +1, true); - iStat += EqnTest( _T("-(+1)"), -1, true); - iStat += EqnTest( _T("-(+1)*2"), -2, true); - iStat += EqnTest( _T("-(+2)*sqrt(4)"), -4, true); - iStat += EqnTest( _T("3-+a"), 2, true); - iStat += EqnTest( _T("+1*3"), 3, true); - - iStat += EqnTest( _T("-1"), -1, true); - iStat += EqnTest( _T("-(-1)"), 1, true); - iStat += EqnTest( _T("-(-1)*2"), 2, true); - iStat += EqnTest( _T("-(-2)*sqrt(4)"), 4, true); - iStat += EqnTest( _T("-_pi"), -PARSER_CONST_PI, true); - iStat += EqnTest( _T("-a"), -1, true); - iStat += EqnTest( _T("-(a)"), -1, true); - iStat += EqnTest( _T("-(-a)"), 1, true); - iStat += EqnTest( _T("-(-a)*2"), 2, true); - iStat += EqnTest( _T("-(8)"), -8, true); - iStat += EqnTest( _T("-8"), -8, true); - iStat += EqnTest( _T("-(2+1)"), -3, true); - iStat += EqnTest( _T("-(f1of1(1+2*3)+1*2)"), -9, true); - iStat += EqnTest( _T("-(-f1of1(1+2*3)+1*2)"), 5, true); - iStat += EqnTest( _T("-sin(8)"), -0.989358, true); - iStat += EqnTest( _T("3-(-a)"), 4, true); - iStat += EqnTest( _T("3--a"), 4, true); - iStat += EqnTest( _T("-1*3"), -3, true); - - // Postfix / infix priorities - iStat += EqnTest( _T("~2#"), 8, true); - iStat += EqnTest( _T("~f1of1(2)#"), 8, true); - iStat += EqnTest( _T("~(b)#"), 8, true); - iStat += EqnTest( _T("(~b)#"), 12, true); - iStat += EqnTest( _T("~(2#)"), 8, true); - iStat += EqnTest( _T("~(f1of1(2)#)"), 8, true); - // - iStat += EqnTest( _T("-2^2"),-4, true); - iStat += EqnTest( _T("-(a+b)^2"),-9, true); - iStat += EqnTest( _T("(-3)^2"),9, true); - iStat += EqnTest( _T("-(-2^2)"),4, true); - iStat += EqnTest( _T("3+-3^2"),-6, true); - // The following assumes use of sqr as postfix operator ("§") together - // with a sign operator of low priority: - iStat += EqnTest( _T("-2'"), -4, true); - iStat += EqnTest( _T("-(1+1)'"),-4, true); - iStat += EqnTest( _T("2+-(1+1)'"),-2, true); - iStat += EqnTest( _T("2+-2'"), -2, true); - // This is the classic behaviour of the infix sign operator (here: "$") which is - // now deprecated: - iStat += EqnTest( _T("$2^2"),4, true); - iStat += EqnTest( _T("$(a+b)^2"),9, true); - iStat += EqnTest( _T("($3)^2"),9, true); - iStat += EqnTest( _T("$($2^2)"),-4, true); - iStat += EqnTest( _T("3+$3^2"),12, true); - - // infix operators sharing the first few characters - iStat += EqnTest( _T("~ 123"), 123+2, true); - iStat += EqnTest( _T("~~ 123"), 123+2, true); - - if (iStat==0) - mu::console() << _T("passed") << endl; - else - mu::console() << _T("\n failed with ") << iStat << _T(" errors") << endl; - - return iStat; - } - - - //--------------------------------------------------------------------------- - int ParserTester::TestPostFix() - { - int iStat = 0; - mu::console() << _T("testing postfix operators..."); - - // application - iStat += EqnTest( _T("3{m}+5"), 5.003, true); - iStat += EqnTest( _T("1000{m}"), 1, true); - iStat += EqnTest( _T("1000 {m}"), 1, true); - iStat += EqnTest( _T("(a){m}"), 1e-3, true); - iStat += EqnTest( _T("a{m}"), 1e-3, true); - iStat += EqnTest( _T("a {m}"), 1e-3, true); - iStat += EqnTest( _T("-(a){m}"), -1e-3, true); - iStat += EqnTest( _T("-2{m}"), -2e-3, true); - iStat += EqnTest( _T("-2 {m}"), -2e-3, true); - iStat += EqnTest( _T("f1of1(1000){m}"), 1, true); - iStat += EqnTest( _T("-f1of1(1000){m}"), -1, true); - iStat += EqnTest( _T("-f1of1(-1000){m}"), 1, true); - iStat += EqnTest( _T("f4of4(0,0,0,1000){m}"), 1, true); - iStat += EqnTest( _T("2+(a*1000){m}"), 3, true); - - // can postfix operators "m" und "meg" be told apart properly? - iStat += EqnTest( _T("2*3000meg+2"), 2*3e9+2, true); - - // some incorrect results - iStat += EqnTest( _T("1000{m}"), 0.1, false); - iStat += EqnTest( _T("(a){m}"), 2, false); - // failure due to syntax checking - iStat += ThrowTest(_T("0x"), ecUNASSIGNABLE_TOKEN); // incomplete hex definition - iStat += ThrowTest(_T("3+"), ecUNEXPECTED_EOF); - iStat += ThrowTest( _T("4 + {m}"), ecUNASSIGNABLE_TOKEN); - iStat += ThrowTest( _T("{m}4"), ecUNASSIGNABLE_TOKEN); - iStat += ThrowTest( _T("sin({m})"), ecUNASSIGNABLE_TOKEN); - iStat += ThrowTest( _T("{m} {m}"), ecUNASSIGNABLE_TOKEN); - iStat += ThrowTest( _T("{m}(8)"), ecUNASSIGNABLE_TOKEN); - iStat += ThrowTest( _T("4,{m}"), ecUNASSIGNABLE_TOKEN); - iStat += ThrowTest( _T("-{m}"), ecUNASSIGNABLE_TOKEN); - iStat += ThrowTest( _T("2(-{m})"), ecUNEXPECTED_PARENS); - iStat += ThrowTest( _T("2({m})"), ecUNEXPECTED_PARENS); - - iStat += ThrowTest( _T("multi*1.0"), ecUNASSIGNABLE_TOKEN); - - if (iStat==0) - mu::console() << _T("passed") << endl; - else - mu::console() << _T("\n failed with ") << iStat << _T(" errors") << endl; - - return iStat; - } - - //--------------------------------------------------------------------------- - int ParserTester::TestExpression() - { - int iStat = 0; - mu::console() << _T("testing expression samples..."); - - value_type b = 2; - - // Optimization - iStat += EqnTest( _T("2*b*5"), 20, true); - iStat += EqnTest( _T("2*b*5 + 4*b"), 28, true); - iStat += EqnTest( _T("2*a/3"), 2.0/3.0, true); - - // Addition auf cmVARMUL - iStat += EqnTest( _T("3+b"), b+3, true); - iStat += EqnTest( _T("b+3"), b+3, true); - iStat += EqnTest( _T("b*3+2"), b*3+2, true); - iStat += EqnTest( _T("3*b+2"), b*3+2, true); - iStat += EqnTest( _T("2+b*3"), b*3+2, true); - iStat += EqnTest( _T("2+3*b"), b*3+2, true); - iStat += EqnTest( _T("b+3*b"), b+3*b, true); - iStat += EqnTest( _T("3*b+b"), b+3*b, true); - - iStat += EqnTest( _T("2+b*3+b"), 2+b*3+b, true); - iStat += EqnTest( _T("b+2+b*3"), b+2+b*3, true); - - iStat += EqnTest( _T("(2*b+1)*4"), (2*b+1)*4, true); - iStat += EqnTest( _T("4*(2*b+1)"), (2*b+1)*4, true); - - // operator precedencs - iStat += EqnTest( _T("1+2-3*4/5^6"), 2.99923, true); - iStat += EqnTest( _T("1^2/3*4-5+6"), 2.33333333, true); - iStat += EqnTest( _T("1+2*3"), 7, true); - iStat += EqnTest( _T("1+2*3"), 7, true); - iStat += EqnTest( _T("(1+2)*3"), 9, true); - iStat += EqnTest( _T("(1+2)*(-3)"), -9, true); - iStat += EqnTest( _T("2/4"), 0.5, true); - - iStat += EqnTest( _T("exp(ln(7))"), 7, true); - iStat += EqnTest( _T("e^ln(7)"), 7, true); - iStat += EqnTest( _T("e^(ln(7))"), 7, true); - iStat += EqnTest( _T("(e^(ln(7)))"), 7, true); - iStat += EqnTest( _T("1-(e^(ln(7)))"), -6, true); - iStat += EqnTest( _T("2*(e^(ln(7)))"), 14, true); - iStat += EqnTest( _T("10^log(5)"), pow(10.0, log(5.0)), true); - iStat += EqnTest( _T("10^log10(5)"), 5, true); - iStat += EqnTest( _T("2^log2(4)"), 4, true); - iStat += EqnTest( _T("-(sin(0)+1)"), -1, true); - iStat += EqnTest( _T("-(2^1.1)"), -2.14354692, true); - - iStat += EqnTest( _T("(cos(2.41)/b)"), -0.372056, true); - iStat += EqnTest( _T("(1*(2*(3*(4*(5*(6*(a+b)))))))"), 2160, true); - iStat += EqnTest( _T("(1*(2*(3*(4*(5*(6*(7*(a+b))))))))"), 15120, true); - iStat += EqnTest( _T("(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( - _T("(((-9))-e/(((((((pi-(((-7)+(-3)/4/e))))/(((-5))-2)-((pi+(-0))*(sqrt((e+e))*(-8))*(((-pi)+(-pi)-(-9)*(6*5))") - _T("/(-e)-e))/2)/((((sqrt(2/(-e)+6)-(4-2))+((5/(-2))/(1*(-pi)+3))/8)*pi*((pi/((-2)/(-6)*1*(-1))*(-6)+(-e)))))/") - _T("((e+(-2)+(-e)*((((-3)*9+(-e)))+(-9)))))))-((((e-7+(((5/pi-(3/1+pi)))))/e)/(-5))/(sqrt((((((1+(-7))))+((((-") - _T("e)*(-e)))-8))*(-5)/((-e)))*(-6)-((((((-2)-(-9)-(-e)-1)/3))))/(sqrt((8+(e-((-6))+(9*(-9))))*(((3+2-8))*(7+6") - _T("+(-5))+((0/(-e)*(-pi))+7)))+(((((-e)/e/e)+((-6)*5)*e+(3+(-5)/pi))))+pi))/sqrt((((9))+((((pi))-8+2))+pi))/e") - _T("*4)*((-5)/(((-pi))*(sqrt(e)))))-(((((((-e)*(e)-pi))/4+(pi)*(-9)))))))+(-pi)"), -12.23016549, true); - - // long formula (Reference: Matlab) - iStat += EqnTest( - _T("(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") - _T(")+a)))*2.77)"), -2.16995656, true); - - // long formula (Reference: Matlab) - iStat += EqnTest( _T("1+2-3*4/5^6*(2*(1-5+(3*7^9)*(4+6*7-3)))+12"), -7995810.09926, true); - - if (iStat==0) - mu::console() << _T("passed") << endl; - else - mu::console() << _T("\n failed with ") << iStat << _T(" errors") << endl; - - return iStat; - } - - - - //--------------------------------------------------------------------------- - int ParserTester::TestIfThenElse() - { - int iStat = 0; - mu::console() << _T("testing if-then-else operator..."); - - // Test error detection - iStat += ThrowTest(_T(":3"), ecUNEXPECTED_CONDITIONAL); - iStat += ThrowTest(_T("? 1 : 2"), ecUNEXPECTED_CONDITIONAL); - iStat += ThrowTest(_T("(ab) ? 10 : 11"), 11, true); - iStat += EqnTest(_T("(ab) ? c : d"), -2, true); - - iStat += EqnTest(_T("(a>b) ? 1 : 0"), 0, true); - iStat += EqnTest(_T("((a>b) ? 1 : 0) ? 1 : 2"), 2, true); - iStat += EqnTest(_T("((a>b) ? 1 : 0) ? 1 : sum((a>b) ? 1 : 2)"), 2, true); - iStat += EqnTest(_T("((a>b) ? 0 : 1) ? 1 : sum((a>b) ? 1 : 2)"), 1, true); - - iStat += EqnTest(_T("sum((a>b) ? 1 : 2)"), 2, true); - iStat += EqnTest(_T("sum((1) ? 1 : 2)"), 1, true); - iStat += EqnTest(_T("sum((a>b) ? 1 : 2, 100)"), 102, true); - iStat += EqnTest(_T("sum((1) ? 1 : 2, 100)"), 101, true); - iStat += EqnTest(_T("sum(3, (a>b) ? 3 : 10)"), 13, true); - iStat += EqnTest(_T("sum(3, (ab) ? 3 : 10)"), 130, true); - iStat += EqnTest(_T("10*sum(3, (ab) ? 3 : 10)*10"), 130, true); - iStat += EqnTest(_T("sum(3, (ab) ? sum(3, (ab) ? sum(3, (ab) ? sum(3, (ab)&&(a2)&&(1<2) ? 128 : 255"), 255, true); - iStat += EqnTest(_T("((1<2)&&(1<2)) ? 128 : 255"), 128, true); - iStat += EqnTest(_T("((1>2)&&(1<2)) ? 128 : 255"), 255, true); - iStat += EqnTest(_T("((ab)&&(a0 ? 1>2 ? 128 : 255 : 1>0 ? 32 : 64"), 255, true); - iStat += EqnTest(_T("1>0 ? 1>2 ? 128 : 255 :(1>0 ? 32 : 64)"), 255, true); - iStat += EqnTest(_T("1>0 ? 1>0 ? 128 : 255 : 1>2 ? 32 : 64"), 128, true); - iStat += EqnTest(_T("1>0 ? 1>0 ? 128 : 255 :(1>2 ? 32 : 64)"), 128, true); - iStat += EqnTest(_T("1>2 ? 1>2 ? 128 : 255 : 1>0 ? 32 : 64"), 32, true); - iStat += EqnTest(_T("1>2 ? 1>0 ? 128 : 255 : 1>2 ? 32 : 64"), 64, true); - iStat += EqnTest(_T("1>0 ? 50 : 1>0 ? 128 : 255"), 50, true); - iStat += EqnTest(_T("1>0 ? 50 : (1>0 ? 128 : 255)"), 50, true); - iStat += EqnTest(_T("1>0 ? 1>0 ? 128 : 255 : 50"), 128, true); - iStat += EqnTest(_T("1>2 ? 1>2 ? 128 : 255 : 1>0 ? 32 : 1>2 ? 64 : 16"), 32, true); - iStat += EqnTest(_T("1>2 ? 1>2 ? 128 : 255 : 1>0 ? 32 :(1>2 ? 64 : 16)"), 32, true); - iStat += EqnTest(_T("1>0 ? 1>2 ? 128 : 255 : 1>0 ? 32 :1>2 ? 64 : 16"), 255, true); - iStat += EqnTest(_T("1>0 ? 1>2 ? 128 : 255 : (1>0 ? 32 :1>2 ? 64 : 16)"), 255, true); - iStat += EqnTest(_T("1 ? 0 ? 128 : 255 : 1 ? 32 : 64"), 255, true); - - // assignment operators - iStat += EqnTest(_T("a= 0 ? 128 : 255, a"), 255, true); - iStat += EqnTest(_T("a=((a>b)&&(a - // this is now legal, for reference see: - // https://sourceforge.net/forum/message.php?msg_id=7411373 - // iStat += ThrowTest( _T("sin=9"), ecUNEXPECTED_OPERATOR); - //
- - iStat += ThrowTest( _T("(8)=5"), ecUNEXPECTED_OPERATOR); - iStat += ThrowTest( _T("(a)=5"), ecUNEXPECTED_OPERATOR); - iStat += ThrowTest( _T("a=\"tttt\""), ecOPRT_TYPE_CONFLICT); - - if (iStat==0) - mu::console() << _T("passed") << endl; - else - mu::console() << _T("\n failed with ") << iStat << _T(" errors") << endl; - - return iStat; - } - - - //--------------------------------------------------------------------------- - void ParserTester::AddTest(testfun_type a_pFun) - { - m_vTestFun.push_back(a_pFun); - } - - //--------------------------------------------------------------------------- - void ParserTester::Run() - { - int iStat = 0; - try - { - for (int i=0; i<(int)m_vTestFun.size(); ++i) - iStat += (this->*m_vTestFun[i])(); - } - catch(Parser::exception_type &e) - { - mu::console() << "\n" << e.GetMsg() << endl; - mu::console() << e.GetToken() << endl; - Abort(); - } - catch(std::exception &e) - { - mu::console() << e.what() << endl; - Abort(); - } - catch(...) - { - mu::console() << "Internal error"; - Abort(); - } - - if (iStat==0) - { - mu::console() << "Test passed (" << ParserTester::c_iCount << " expressions)" << endl; - } - else - { - mu::console() << "Test failed with " << iStat - << " errors (" << ParserTester::c_iCount - << " expressions)" << endl; - } - ParserTester::c_iCount = 0; - } - - - //--------------------------------------------------------------------------- - int ParserTester::ThrowTest(const string_type &a_str, int a_iErrc, bool a_bFail) - { - ParserTester::c_iCount++; - - try - { - value_type fVal[] = {1,1,1}; - Parser p; - - p.DefineVar( _T("a"), &fVal[0]); - p.DefineVar( _T("b"), &fVal[1]); - p.DefineVar( _T("c"), &fVal[2]); - p.DefinePostfixOprt( _T("{m}"), Milli); - p.DefinePostfixOprt( _T("m"), Milli); - p.DefineFun( _T("ping"), Ping); - p.DefineFun( _T("valueof"), ValueOf); - p.DefineFun( _T("strfun1"), StrFun1); - p.DefineFun( _T("strfun2"), StrFun2); - p.DefineFun( _T("strfun3"), StrFun3); - p.SetExpr(a_str); - p.Eval(); - } - catch(ParserError &e) - { - // output the formula in case of an failed test - if (a_bFail==false || (a_bFail==true && a_iErrc!=e.GetCode()) ) - { - mu::console() << _T("\n ") - << _T("Expression: ") << a_str - << _T(" Code:") << e.GetCode() << _T("(") << e.GetMsg() << _T(")") - << _T(" 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) - { - mu::console() << _T("\n ") - << _T("Expression: ") << a_str - << _T(" did evaluate; Expected error:") << a_iErrc; - } - - return bRet; - } - - //--------------------------------------------------------------------------- - /** \brief Evaluate a tet expression. - - \return 1 in case of a failure, 0 otherwise. - */ - int ParserTester::EqnTestWithVarChange(const string_type &a_str, - double a_fVar1, - double a_fRes1, - double a_fVar2, - double a_fRes2) - { - ParserTester::c_iCount++; - - try - { - value_type fVal[2] = {-999, -999 }; // should be equalinitially - - Parser p; - value_type var = 0; - - // variable - p.DefineVar( _T("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(Parser::exception_type &e) - { - mu::console() << _T("\n fail: ") << a_str.c_str() << _T(" (") << e.GetMsg() << _T(")"); - return 1; - } - catch(std::exception &e) - { - mu::console() << _T("\n fail: ") << a_str.c_str() << _T(" (") << e.what() << _T(")"); - return 1; // always return a failure since this exception is not expected - } - catch(...) - { - mu::console() << _T("\n fail: ") << a_str.c_str() << _T(" (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 ParserTester::EqnTest(const string_type &a_str, double a_fRes, bool a_fPass) - { - ParserTester::c_iCount++; - int iRet(0); - value_type fVal[5] = {-999, -998, -997, -996, -995}; // initially should be different - - try - { - std::unique_ptr p1; - Parser 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 mu::Parser()); - // Add constants - p1->DefineConst( _T("pi"), (value_type)PARSER_CONST_PI); - p1->DefineConst( _T("e"), (value_type)PARSER_CONST_E); - p1->DefineConst( _T("const"), 1); - p1->DefineConst( _T("const1"), 2); - p1->DefineConst( _T("const2"), 3); - // string constants - p1->DefineStrConst( _T("str1"), _T("1.11")); - p1->DefineStrConst( _T("str2"), _T("2.22")); - // variables - value_type vVarVal[] = { 1, 2, 3, -2}; - p1->DefineVar( _T("a"), &vVarVal[0]); - p1->DefineVar( _T("aa"), &vVarVal[1]); - p1->DefineVar( _T("b"), &vVarVal[1]); - p1->DefineVar( _T("c"), &vVarVal[2]); - p1->DefineVar( _T("d"), &vVarVal[3]); - - // custom value ident functions - p1->AddValIdent(&ParserTester::IsHexVal); - - // functions - p1->DefineFun( _T("ping"), Ping); - p1->DefineFun( _T("f1of1"), f1of1); // one parameter - p1->DefineFun( _T("f1of2"), f1of2); // two parameter - p1->DefineFun( _T("f2of2"), f2of2); - p1->DefineFun( _T("f1of3"), f1of3); // three parameter - p1->DefineFun( _T("f2of3"), f2of3); - p1->DefineFun( _T("f3of3"), f3of3); - p1->DefineFun( _T("f1of4"), f1of4); // four parameter - p1->DefineFun( _T("f2of4"), f2of4); - p1->DefineFun( _T("f3of4"), f3of4); - p1->DefineFun( _T("f4of4"), f4of4); - p1->DefineFun( _T("f1of5"), f1of5); // five parameter - p1->DefineFun( _T("f2of5"), f2of5); - p1->DefineFun( _T("f3of5"), f3of5); - p1->DefineFun( _T("f4of5"), f4of5); - p1->DefineFun( _T("f5of5"), f5of5); - - // binary operators - p1->DefineOprt( _T("add"), add, 0); - p1->DefineOprt( _T("++"), add, 0); - p1->DefineOprt( _T("&"), land, prLAND); - - // sample functions - p1->DefineFun( _T("min"), Min); - p1->DefineFun( _T("max"), Max); - p1->DefineFun( _T("sum"), Sum); - p1->DefineFun( _T("valueof"), ValueOf); - p1->DefineFun( _T("atof"), StrToFloat); - p1->DefineFun( _T("strfun1"), StrFun1); - p1->DefineFun( _T("strfun2"), StrFun2); - p1->DefineFun( _T("strfun3"), StrFun3); - p1->DefineFun( _T("lastArg"), LastArg); - p1->DefineFun( _T("firstArg"), FirstArg); - p1->DefineFun( _T("order"), FirstArg); - - // infix / postfix operator - // Note: Identifiers used here do not have any meaning - // they are mere placeholders to test certain features. - p1->DefineInfixOprt( _T("$"), sign, prPOW+1); // sign with high priority - p1->DefineInfixOprt( _T("~"), plus2); // high priority - p1->DefineInfixOprt( _T("~~"), plus2); - p1->DefinePostfixOprt( _T("{m}"), Milli); - p1->DefinePostfixOprt( _T("{M}"), Mega); - p1->DefinePostfixOprt( _T("m"), Milli); - p1->DefinePostfixOprt( _T("meg"), Mega); - p1->DefinePostfixOprt( _T("#"), times3); - p1->DefinePostfixOprt( _T("'"), 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 Parser::exception_type( _T("Bytecode / string parsing mismatch.") ); - - // Test copy and assignement operators - try - { - // Test copy constructor - std::vector vParser; - vParser.push_back(*(p1.get())); - mu::Parser 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 - mu::Parser 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; - value_type *v = p2.Eval(nNum); - fVal[4] = v[nNum-1]; - } - catch(std::exception &e) - { - mu::console() << _T("\n ") << e.what() << _T("\n"); - } - - // limited floating point accuracy requires the following test - bool bCloseEnough(true); - for (unsigned i=0; i::has_infinity) - #pragma warning(pop) - { - bCloseEnough &= (fabs(fVal[i]) != numeric_limits::infinity()); - } - } - - iRet = ((bCloseEnough && a_fPass) || (!bCloseEnough && !a_fPass)) ? 0 : 1; - - - if (iRet==1) - { - mu::console() << _T("\n fail: ") << a_str.c_str() - << _T(" (incorrect result; expected: ") << a_fRes - << _T(" ;calculated: ") << fVal[0] << _T(",") - << fVal[1] << _T(",") - << fVal[2] << _T(",") - << fVal[3] << _T(",") - << fVal[4] << _T(")."); - } - } - catch(Parser::exception_type &e) - { - if (a_fPass) - { - if (fVal[0]!=fVal[2] && fVal[0]!=-999 && fVal[1]!=-998) - mu::console() << _T("\n fail: ") << a_str.c_str() << _T(" (copy construction)"); - else - mu::console() << _T("\n fail: ") << a_str.c_str() << _T(" (") << e.GetMsg() << _T(")"); - return 1; - } - } - catch(std::exception &e) - { - mu::console() << _T("\n fail: ") << a_str.c_str() << _T(" (") << e.what() << _T(")"); - return 1; // always return a failure since this exception is not expected - } - catch(...) - { - mu::console() << _T("\n fail: ") << a_str.c_str() << _T(" (unexpected exception)"); - return 1; // exceptions other than ParserException are not allowed - } - - return iRet; - } - - //--------------------------------------------------------------------------- - int ParserTester::EqnTestInt(const string_type &a_str, double a_fRes, bool a_fPass) - { - ParserTester::c_iCount++; - - value_type vVarVal[] = {1, 2, 3}; // variable values - int iRet(0); - - try - { - value_type fVal[2] = {-99, -999}; // results: initially should be different - ParserInt p; - p.DefineConst( _T("const1"), 1); - p.DefineConst( _T("const2"), 2); - p.DefineVar( _T("a"), &vVarVal[0]); - p.DefineVar( _T("b"), &vVarVal[1]); - p.DefineVar( _T("c"), &vVarVal[2]); - - p.SetExpr(a_str); - fVal[0] = p.Eval(); // result from stringparsing - fVal[1] = p.Eval(); // result from bytecode - - if (fVal[0]!=fVal[1]) - throw Parser::exception_type( _T("Bytecode corrupt.") ); - - iRet = ( (a_fRes==fVal[0] && a_fPass) || - (a_fRes!=fVal[0] && !a_fPass) ) ? 0 : 1; - if (iRet==1) - { - mu::console() << _T("\n fail: ") << a_str.c_str() - << _T(" (incorrect result; expected: ") << a_fRes - << _T(" ;calculated: ") << fVal[0]<< _T(")."); - } - } - catch(Parser::exception_type &e) - { - if (a_fPass) - { - mu::console() << _T("\n fail: ") << e.GetExpr() << _T(" : ") << e.GetMsg(); - iRet = 1; - } - } - catch(...) - { - mu::console() << _T("\n fail: ") << a_str.c_str() << _T(" (unexpected exception)"); - iRet = 1; // exceptions other than ParserException are not allowed - } - - return iRet; - } - - //--------------------------------------------------------------------------- - /** \brief Test an expression in Bulk Mode. */ - int ParserTester::EqnTestBulk(const string_type &a_str, double a_fRes[4], bool a_fPass) - { - ParserTester::c_iCount++; - - // Define Bulk Variables - int nBulkSize = 4; - value_type vVariableA[] = { 1, 2, 3, 4 }; // variable values - value_type vVariableB[] = { 2, 2, 2, 2 }; // variable values - value_type vVariableC[] = { 3, 3, 3, 3 }; // variable values - value_type vResults[] = { 0, 0, 0, 0 }; // variable values - int iRet(0); - - try - { - Parser p; - p.DefineConst(_T("const1"), 1); - p.DefineConst(_T("const2"), 2); - p.DefineVar(_T("a"), vVariableA); - p.DefineVar(_T("b"), vVariableB); - p.DefineVar(_T("c"), vVariableC); - - p.SetExpr(a_str); - p.Eval(vResults, nBulkSize); - - bool bCloseEnough(true); - for (int i = 0; i < nBulkSize; ++i) - { - bCloseEnough &= (fabs(a_fRes[i] - vResults[i]) <= fabs(a_fRes[i] * 0.00001)); - } - - iRet = ((bCloseEnough && a_fPass) || (!bCloseEnough && !a_fPass)) ? 0 : 1; - if (iRet == 1) - { - mu::console() << _T("\n fail: ") << a_str.c_str() - << _T(" (incorrect result; expected: {") << a_fRes[0] << _T(",") << a_fRes[1] << _T(",") << a_fRes[2] << _T(",") << a_fRes[3] << _T("}") - << _T(" ;calculated: ") << vResults[0] << _T(",") << vResults[1] << _T(",") << vResults[2] << _T(",") << vResults[3] << _T("}"); - } - } - catch (Parser::exception_type &e) - { - if (a_fPass) - { - mu::console() << _T("\n fail: ") << e.GetExpr() << _T(" : ") << e.GetMsg(); - iRet = 1; - } - } - catch (...) - { - mu::console() << _T("\n fail: ") << a_str.c_str() << _T(" (unexpected exception)"); - iRet = 1; // exceptions other than ParserException are not allowed - } - - return iRet; - } - - //--------------------------------------------------------------------------- - /** \brief Internal error in test class Test is going to be aborted. */ - void ParserTester::Abort() const - { - mu::console() << _T("Test failed (internal error in test class)") << endl; - while (!getchar()); - exit(-1); - } - } // namespace test -} // namespace mu diff --git a/bundled/muparser_v2_2_4/src/muParserTokenReader.cpp b/bundled/muparser_v2_2_4/src/muParserTokenReader.cpp deleted file mode 100644 index 55daf8e1a6..0000000000 --- a/bundled/muparser_v2_2_4/src/muParserTokenReader.cpp +++ /dev/null @@ -1,958 +0,0 @@ -/* - __________ - _____ __ __\______ \_____ _______ ______ ____ _______ - / \ | | \| ___/\__ \ \_ __ \/ ___/_/ __ \\_ __ \ - | Y Y \| | /| | / __ \_| | \/\___ \ \ ___/ | | \/ - |__|_| /|____/ |____| (____ /|__| /____ > \___ >|__| - \/ \/ \/ \/ - Copyright (C) 2013 Ingo Berg - - 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 "muParserTokenReader.h" -#include "muParserBase.h" - -/** \file - \brief This file contains the parser token reader implementation. -*/ - - -namespace mu -{ - - // Forward declaration - class ParserBase; - - //--------------------------------------------------------------------------- - /** \brief Copy constructor. - - \sa Assign - \throw nothrow - */ - ParserTokenReader::ParserTokenReader(const ParserTokenReader &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 - */ - ParserTokenReader& ParserTokenReader::operator=(const ParserTokenReader &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 ParserTokenReader::Assign(const ParserTokenReader &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; - m_fZero = a_Reader.m_fZero; - m_lastTok = a_Reader.m_lastTok; - } - - //--------------------------------------------------------------------------- - /** \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. - */ - ParserTokenReader::ParserTokenReader(ParserBase *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 ParserTokenReader 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 ParserTokenReader object. - \throw nothrow - */ - ParserTokenReader* ParserTokenReader::Clone(ParserBase *a_pParent) const - { - std::unique_ptr ptr(new ParserTokenReader(*this)); - ptr->SetParent(a_pParent); - return ptr.release(); - } - - //--------------------------------------------------------------------------- - ParserTokenReader::token_type& ParserTokenReader::SaveBeforeReturn(const token_type &tok) - { - m_lastTok = tok; - return m_lastTok; - } - - //--------------------------------------------------------------------------- - void ParserTokenReader::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 ParserTokenReader::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 ParserTokenReader::GetPos() const - { - return m_iPos; - } - - //--------------------------------------------------------------------------- - /** \brief Return a reference to the formula. - - \return #m_strFormula - \throw nothrow - */ - const string_type& ParserTokenReader::GetExpr() const - { - return m_strFormula; - } - - //--------------------------------------------------------------------------- - /** \brief Return a map containing the used variables only. */ - varmap_type& ParserTokenReader::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 ParserTokenReader::SetFormula(const string_type &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 ParserTokenReader::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 ParserTokenReader::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. */ - ParserTokenReader::token_type ParserTokenReader::ReadNextToken() - { - assert(m_pParser); - - const char_type *szFormula = m_strFormula.c_str(); - 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... - // - string_type 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.substr(m_iPos)); - return token_type(); // never reached - } - - //--------------------------------------------------------------------------- - void ParserTokenReader::SetParent(ParserBase *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 ParserTokenReader::ExtractToken(const char_type *a_szCharSet, - string_type &a_sTok, - int a_iPos) const - { - int iEnd = (int)m_strFormula.find_first_not_of(a_szCharSet, a_iPos); - - if (iEnd==(int)string_type::npos) - iEnd = (int)m_strFormula.length(); - - // Assign token string if there was something found - if (a_iPos!=iEnd) - a_sTok = string_type( m_strFormula.begin()+a_iPos, m_strFormula.begin()+iEnd); - - 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 ParserTokenReader::ExtractOperatorToken(string_type &a_sTok, - int a_iPos) const - { - // Changed as per Issue 6: https://code.google.com/p/muparser/issues/detail?id=6 - int iEnd = (int)m_strFormula.find_first_not_of(m_pParser->ValidOprtChars(), a_iPos); - if (iEnd==(int)string_type::npos) - iEnd = (int)m_strFormula.length(); - - // Assign token string if there was something found - if (a_iPos!=iEnd) - { - a_sTok = string_type( m_strFormula.begin() + a_iPos, m_strFormula.begin() + iEnd); - return iEnd; - } - else - { - // There is still the chance of having to deal with an operator consisting exclusively - // of alphabetic characters. - return ExtractToken(MUP_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 ParserTokenReader::IsBuiltIn(token_type &a_Tok) - { - const char_type **const pOprtDef = m_pParser->GetOprtDef(), - *const szFormula = m_strFormula.c_str(); - - // Compare token with function and operator strings - // check string for operator/function - for (int i=0; pOprtDef[i]; i++) - { - std::size_t len( std::char_traits::length(pOprtDef[i]) ); - if ( string_type(pOprtDef[i]) == string_type(szFormula + m_iPos, szFormula + 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[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[i]); - } - - m_iSynFlags = noBC | noOPT | noARG_SEP | noPOSTOP | noASSIGN | noIF | noELSE | noEND; - break; - - case cmBO: - if (m_iSynFlags & noBO) - Error(ecUNEXPECTED_PARENS, m_iPos, pOprtDef[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[i]); - - m_iSynFlags = noBO | noVAR | noVAL | noFUN | noINFIXOP | noSTR | noASSIGN; - - if (--m_iBrackets<0) - Error(ecUNEXPECTED_PARENS, m_iPos, pOprtDef[i]); - break; - - case cmELSE: - if (m_iSynFlags & noELSE) - Error(ecUNEXPECTED_CONDITIONAL, m_iPos, pOprtDef[i]); - - m_iSynFlags = noBC | noPOSTOP | noEND | noOPT | noIF | noELSE; - break; - - case cmIF: - if (m_iSynFlags & noIF) - Error(ecUNEXPECTED_CONDITIONAL, m_iPos, pOprtDef[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 += (int)len; - a_Tok.Set( (ECmdCode)i, pOprtDef[i] ); - return true; - } // if operator string found - } // end of for all operator strings - - return false; - } - - //--------------------------------------------------------------------------- - bool ParserTokenReader::IsArgSep(token_type &a_Tok) - { - const char_type* szFormula = m_strFormula.c_str(); - - if (szFormula[m_iPos]==m_cArgSep) - { - // copy the separator into null terminated string - char_type szSep[2]; - 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 ParserTokenReader::IsEOF(token_type &a_Tok) - { - const char_type* szFormula = m_strFormula.c_str(); - - // 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, _T(")")); - - 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 ParserTokenReader::IsInfixOpTok(token_type &a_Tok) - { - string_type 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.find(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 ParserTokenReader::IsFunTok(token_type &a_Tok) - { - string_type 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 - const char_type *szFormula = m_strFormula.c_str(); - if (szFormula[iEnd]!='(') - return false; - - a_Tok.Set(item->second, strTok); - - m_iPos = (int)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 ParserTokenReader::IsOprt(token_type &a_Tok) - { - const char_type *const szExpr = m_strFormula.c_str(); - string_type 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 char_type **const pOprtDef = m_pParser->GetOprtDef(); - for (int i=0; m_pParser->HasBuiltInOprt() && pOprtDef[i]; ++i) - { - if (string_type(pOprtDef[i])==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 string_type &sID = it->first; - if ( sID == string_type(szExpr + m_iPos, szExpr + 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 | noASSIGN; - return true; - } - } - - return false; - } - - //--------------------------------------------------------------------------- - /** \brief Check if a string position contains a unary post value operator. */ - bool ParserTokenReader::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 - string_type 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.find(it->first)!=0) - continue; - - a_Tok.Set(it->second, sTok); - m_iPos += (int)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 ParserTokenReader::IsValTok(token_type &a_Tok) - { - assert(m_pConstDef); - assert(m_pParser); - - string_type strTok; - value_type 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 - (int)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.c_str() + m_iPos, &m_iPos, &fVal)==1 ) - { - // 2013-11-27 Issue 2: https://code.google.com/p/muparser/issues/detail?id=2 - strTok.assign(m_strFormula.c_str(), iStart, m_iPos-iStart); - - if (m_iSynFlags & noVAL) - Error(ecUNEXPECTED_VAL, m_iPos - (int)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 ParserTokenReader::IsVarTok(token_type &a_Tok) - { - if (m_pVarDef->empty()) - return false; - - string_type 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 ParserTokenReader::IsStrVarTok(token_type &a_Tok) - { - if (!m_pStrVarDef || m_pStrVarDef->empty()) - return false; - - string_type 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 ParserTokenReader::IsUndefVarTok(token_type &a_Tok) - { - string_type 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) - { - value_type *fVar = m_pFactory(strTok.c_str(), 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((value_type*)&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 ParserTokenReader::IsString(token_type &a_Tok) - { - if (m_strFormula[m_iPos]!='"') - return false; - - string_type strBuf(&m_strFormula[m_iPos+1]); - std::size_t iEnd(0), iSkip(0); - - // parser over escaped '\"' end replace them with '"' - for(iEnd=(int)strBuf.find( _T('\"') ); iEnd!=0 && iEnd!=string_type::npos; iEnd=(int)strBuf.find( _T('\"'), iEnd)) - { - if (strBuf[iEnd-1]!='\\') break; - strBuf.replace(iEnd-1, 2, _T("\"") ); - iSkip++; - } - - if (iEnd==string_type::npos) - Error(ecUNTERMINATED_STRING, m_iPos, _T("\"") ); - - string_type strTok(strBuf.begin(), strBuf.begin()+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 + (int)iSkip; // +2 wg AnfĂ¼hrungszeichen; +iSkip fĂ¼r 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 ParserTokenReader::Error( EErrorCodes a_iErrc, - int a_iPos, - const string_type &a_sTok) const - { - m_pParser->Error(a_iErrc, a_iPos, a_sTok); - } - - //--------------------------------------------------------------------------- - void ParserTokenReader::SetArgSep(char_type cArgSep) - { - m_cArgSep = cArgSep; - } - - //--------------------------------------------------------------------------- - char_type ParserTokenReader::GetArgSep() const - { - return m_cArgSep; - } -} // namespace mu - diff --git a/bundled/muparser_v2_2_4/Changes.txt b/bundled/muparser_v2_3_3/CHANGELOG similarity index 82% rename from bundled/muparser_v2_2_4/Changes.txt rename to bundled/muparser_v2_3_3/CHANGELOG index 08d4dc7b3b..3ad1916840 100644 --- a/bundled/muparser_v2_2_4/Changes.txt +++ b/bundled/muparser_v2_3_3/CHANGELOG @@ -1,547 +1,616 @@ -####################################################################### -# # -# # -# __________ # -# _____ __ __\______ \_____ _______ ______ ____ _______ # -# / \ | | \| ___/\__ \ \_ __ \/ ___/_/ __ \\_ __ \ # -# | Y Y \| | /| | / __ \_| | \/\___ \ \ ___/ | | \/ # -# |__|_| /|____/ |____| (____ /|__| /____ > \___ >|__| # -# \/ \/ \/ \/ # -# Fast math parser Library # -# # -# Copyright (C) 2014 Ingo Berg # -# # -# Web: muparser.beltoforion.de # -# e-mail: muparser@beltoforion.de # -# # -# # -####################################################################### - - -History: --------- - -Rev 2.2.4: 02.10.2014 ---------------------- - Changes: - * explicit positive sign allowed - - Bugfixes: - * Fix for Issue 6 (https://code.google.com/p/muparser/issues/detail?id=6) - * String constants did not work properly. Using more than a single one - was impossible. - * Project Files for VS2008 and VS2010 removed from the repository - * Fix for Issue 4 (https://code.google.com/p/muparser/issues/detail?id=4) - * Fix for VS2013 64 bit build option - * return type of ParserError::GetPos changed to int - * OpenMP support enabled in the VS2013 project files and precompiled windows DLL's - * Bulkmode did not evaluate properly if "=" and "," operator was used in the expression - -Rev 2.2.3: 22.12.2012 ---------------------- - - Removed features: - * build files for msvc2005, borland and watcom compiler were removed - - Bugfixes: - * Bugfix for Intel Compilers added: The power operator did not work properly - with Intel C++ composer XE 2011. - (see https://sourceforge.net/projects/muparser/forums/forum/462843/topic/5117983/index/page/1) - * Issue 3509860: Callbacks of functions with string parameters called twice - (see http://sourceforge.net/tracker/?func=detail&aid=3509860&group_id=137191&atid=737979) - * Issue 3570423: example1 shows slot number in hexadecimal - (see https://sourceforge.net/tracker/?func=detail&aid=3570423&group_id=137191&atid=737979) - * Fixes for compiling with the "MUP_MATH_EXCEPTIONS" macro definition: - - division by zero in constant expressions was reported with the code "ec_GENERIC" - instead of "ecDIV_BY_ZERO" - - added throwing of "ecDOMAIN_ERROR" to sqrt and log functions - - -Rev 2.2.2: 18.02.2012 ---------------------- - Bugfixes: - * Optimizer did'nt work properly for division: - (see https://sourceforge.net/projects/muparser/forums/forum/462843/topic/5037825) - -Rev 2.2.1: 22.01.2012 ---------------------- - Bugfixes: - * Optimizer bug in 64 bit systems fixed - (see https://sourceforge.net/projects/muparser/forums/forum/462843/topic/4977977/index/page/1) - -Rev 2.2.0: 22.01.2012 ---------------------- - Improvements: - * Optimizer rewritten and improved. In general: more optimizations are - now applied to the bytecode. The downside is that callback Functions - can no longer be flagged as non-optimizable. (The flag is still present - but ignored) This is necessary since the optimizer had to call the - functions in order to precalculate the result (see Bugfixes). These calls - posed a problems for callback functions with side effects and if-then-else - clauses in general since they undermined the shortcut evaluation prinziple. - - Bugfixes: - * Infix operators where not properly detected in the presence of a constant - name starting with an underscore which is a valid character for infix - operators too (i.e. "-_pi"). - * Issue 3463353: Callback functions are called twice during the first call to eval. - * Issue 3447007: GetUsedVar unnecessaryly executes callback functions. - - -Rev 2.1.0: 19.11.2011 ---------------------- - New feature: - * Function atan2 added - - Bugfixes: - * Issue 3438380: Changed behaviour of tellg with GCC >4.6 led to failures - in value detection callbacks. - * Issue 3438715: only "double" is a valid MUP_BASETYPE - MUP_BASETYPE can now be any of: - float, - double, - long double, - short, - unsigned short, - unsigned int, - long, - unsigned long. - Previousely only floating point types were allowed. - Using "int" is still not allowed! - * Compiler issues with GCC 4.6 fixed - * Custom value recognition callbacks added with AddValIdent had lower - priority than built in functions. This was causing problems with - hex value recognition since detection of non hex values had priority - over the detection of hex values. The "0" in the hex prefix "0x" would - be read as a separate non-hex number leaving the rest of the expression - unparseable. - -Rev 2.0.0: 04.09.2011 ---------------------- -This release introduces a new version numbering scheme in order to make -future changes in the ABI apparent to users of the library. The number is -now based on the SONAME property as used by GNU/Linux. - - Changes: - * Beginning with this version all version numbers will be SONAME compliant - * Project files for MSVC2010 added - * Project files for MSVC2003 removed - * Bytecode parsing engine cleaned up and rewritten - * Retrieving all results of expressions made up of comma separate - subexpressions is now possible with a new Eval overload. - * Callback functions with fixed number of arguments can now have up to 10 - Parameters (previous limit was 5) - - New features: - * ternary if-then-else operator added (C++ like; "(...) ? ... : ..." ) - * new intrinsic binary operators: "&&", "||" (logical and, or) - * A new bulkmode allows submitting large arrays as variables to compute large - numbers of expressions with a single call. This can drastically improve - parsing performance when interfacing the library from managed languages like - C#. (It doesn't bring any performance benefit for C++ users though...) - - Removed features: - * intrinsic "and", "or" and "xor" operators have been removed. I'd like to let - users the freedom of defining them on their own versions (either as logical or bitwise - operators). - * Implementation for complex numbers removed. This was merely a hack. If you - need complex numbers try muParserX which provides native support for them. - (see: http://beltoforion.de/muparserx/math_expression_parser_en.html) - - Bugfixes: - * User defined operators could collide with built in operators that entirely - contained their identifier. i.e. user defined "&" would not work with the built - in "&&" operator since the user defined operator was detected with a higher - priority resulting in a syntax error. - * Detection of unknown variables did not work properly in case a postfix operator - was defined which was part of the undefined variable. - i.e. If a postfix operator "m" was defined expressions like "multi*1.0" did - not detect "multi" as an undefined variable. - (Reference: http://sourceforge.net/tracker/index.php?func=detail&aid=3343891&group_id=137191&atid=737979) - * Postfix operators sharing the first few characters were causing bogus parsing exception. - (Reference: https://sourceforge.net/tracker/?func=detail&aid=3358571&group_id=137191&atid=737979) - -Rev 1.34: 04.09.2010 --------------------- - Changes: - * The prefix needed for parsing hex values is now "0x" and no longer "$". - * AddValIdent reintroduced into the DLL interface - - New features: - * The associativity of binary operators can now be changed. The pow operator - is now right associative. (This is what Mathematica is using) - * Seperator can now be used outside of functions. This allows compound - expressions like: - "a=10,b=20,c=a*b" The last "argument" will be taken as the return value - - Bugfixes: - * The copy constructor did not copy binary operator definitions. Those were lost - in the copied parser instance. - * Mixing special characters and alphabetic characters in binary operator names - led to inconsistent parsing behaviour when parsing expressions like "a ++ b" - and "a++b" when "++" is defined as a binary operator. Binary operators must - now consist entirely of special characters or of alphabetic ones. - (original bug report: https://sourceforge.net/projects/muparser/forums/forum/462843/topic/3696881/index/page/1) - * User defined operators were not exactly handled like built in operators. This - led to inconsistencies in expression evaluation when using them. The results - differed due to slightly different precedence rules. - * Using empty string arguments ("") would cause a crash of muParser - - -Rev 1.32: 29.01.2010 --------------------- - - Changes: - * "example3" renamed to "example2" - * Project/Makefiles files are now provided for: - - msvc2003 - - msvc2005 - - msvc2008 - - watcom (makefile) - - mingw (makefile) - - bcc (makefile) - * Project files for borland cpp builder were removed - - - New features: - * Added function returning muparsers version number - * Added function for resetting the locale - - - Bugfixes: - * Changes example1 in order to fix issues with irritating memory leak reports. - Added conditional code for memory leak detection with MSVC in example1. - (see: http://www.codeproject.com/KB/recipes/FastMathParser.aspx?msg=3286367#xx3286367xx) - * Fixed some warnings for gcc - - - -Rev 1.31cp: 15.01.2010 (Maintainance release for CodeProject) ----------------------- - - Changes: - * Archive structure changed - * C# wrapper added - * Fixed issued that prevented compiling with VS2010 Beta2 - - -Rev 1.30: 09.06.2008 --------------------- - - Changes: - * Epsilon of the numerical differentiation algorithm changed to allow greater accuracy. - - New features: - * Setting thousands separator and decimal separator is now possible - - Bugfixes: - * The dll interface did not provide a callback for functions without any arguments. - - -Rev 1.29: Januar 2008 ---------------------- - - Unrelease Version available only via SVN. - - -Rev 1.28: 02. July, 2007 ---------------------------- - - Library changes: - * Interface for the dynamic library changed and extended to create an interface - using pure C functions only. - * mupInit() removed - - Build system: - * MSVC7 Project files removed in favor of MSVC8 - - Bugfixes: - * The dynamic library did not build on other systems than linux due to a misplaced - preprocessor definition. This is fixed now. - - -Rev 1.27: ---------------------------- - - Build system: - * Modified build\ directory layout introducing some subfolders - for the various IDE supported - * Project files for BCB and MSVC7 added - * Switched to use bakefile 0.2.1 which now correctly creates the - "make uninstall" target for autoconf's Makefile.in - * Now the library debug builds are named "muparserd" instead of "muparser" - to allow multiple mixed release/debug builds to coexist; so e.g. on Windows - when building with DEBUG=1, you'll get "muparserd.lib" instead of "muparser.lib" - - New Features: - * Factory functions can now take a user defined pointer - * String functions can now be used with up to two additional - double parameters - * Support for UNICODE character types added - * Infix operator priority can now be changed by the user - - Bugfixes: - * An internal error was raised when evaluating an empty - expressions - * The error message raised in case of name collisions between - implicitely defined variables and postfix operators did contain - misleading data. - - -Rev 1.26: (unofficial release) ------------------------------- - - New Features: - * Unary operator precedence can now be changed by the user. - - -Rev 1.25: 5. February, 2006 ---------------------------- - - Build system: (special thanks to Francesco Montorsi for implementing it!) - * created a bakefile-based build system which adds support for the following win32 compilers: - -> MS visual C++ (6 and .NET) - -> BorlandC++ (5 or greater) - -> Mingw32 (tested with gcc 3.2) - -> Watcom (not tested) - and for GCC on Unix (using a standard autoconf's configure script). - - Compatibility improvements: - * fixed some small warnings when using -Wall with GCC on Unix - * added inclusion guards for win32-specific portions of code - * added fixes that remove compiler warnings on Intel C++ and the Solaris C++ compiler. - - -Rev 1.24: 29. October, 2005 ---------------------------- - -Changes: - - Compatibility improvements: - * parser now works on 64 bit compilers - * (bytecode base datatype can now be changed freely) - - -Rev 1.23: 19. October, 2005 ---------------------------- - -Changes: - - Bugfixes: - * Variable factory examples in Example1.cpp and Example3.cpp contained a subtle bug. - - New features: - * Added a MSVC6 project file and introduced muParserFixes.h in order to make it compile with MSVC6 - - -Rev 1.22: October, 2005 ------------------------ - -Release notes: - -All features of Version 1.22 are similar to Version 1.21. Version 1.22 fixes a compilation issue with -gcc 4.0. In order to fix this issue I rewrote part of the library to remove some unnecessary templates. -This should make the code cleaner. The Borland Project files were removed. If you want to use it -with Borland either use the dll version or create your own project files. I can't support it since I don't -have this compiler at hand. - -Changes: - - Project Changes: - * Borland project files removed - (The code should still compile with BCB but I cant provide you with project files) - - Internal Changes: - * unnecessary template files have been removed: - - new files: muParserError.cpp, muParserTokenReader.cpp, muParserCallback.cpp - - removed Files: muIParserTypes.h - - -Rev 1.2 / 1.21: April, 2005 ---------------------------- - -Release Notes: -First of all the interface has changed so this version is not backwards compatible. -After receiving a couple of questions about it, this version features support for -user defined binary operators. Consequently the built in operators can now be -turned off, thus you can deactivate them and write complete customized parser -subclasses that only contain the functionality you want. Another new feature is -the introduction of callback functions taking string arguments, implicit -generation of variables and the Assignement operator. - - Functionality - * New built in operator: xor; Logical xor. - * New built in operator: Assignement operator; Defining variables in terms of - other variables/constants - * New feature: Strings as arguments for callback functions - * New feature: User defined binary operators - * New feature: ParserInt a class with a sample implementation for - integer numbers. - * New feature: Callbacks to value regognition functions. - - * Removed: all predefined postfix operators have been removed. - * New project file: Now comes with a ready to use windows DLL. - * New project file: Makefile for cygwin now included. - * New example: Example3 shows usage of the DLL. - - Interface changes - * New member function: DefineOprt For adding user defined binary operators. - * New member function: EnableBuiltInOprt(bool) Enables/Disables built in - binary operators. - * New member function: AddValIdent(...) to add callbacks for custom value - recognition functions. - * Removed: SetVar(), SetConst(). - * Renamed: Most interface functions have been renamed - * Changed: The type for multiargument callbacks multfun_type has changed. - It no longer takes a std::vector as input. - - Internal changes - * new class muParserTokenReader.h encapsulates the token identification - and token assignement. - * Internal handling of function callbacks unified as a result the performance - of the bytecode evaluation increased. - - -Rev 1.10 : December 30, 2004 ----------------------------- - -Release Notes: -This version does not contain major new feature compared to V1.07 but its internal structure has -changed significantly. The String parsing routine is slower than the one of V1.07 but bytecode -parsing is equally fast. On the other hand the error messages of V1.09 are more flexible and you -can change its value datatype. It should work on 64-bit systems. For this reason I supply both -versions for download. If you use V1.07 and are happy with it there is no need for updating -your version. - - * New example program: Archive now contains two demo programs: One for standard C++ and one for - managed C++. - * New member function: RemoveVar(...) can be used for removing a single variable from the internal - storage. - * New member function: GetVar() can be used for querying the variable names and pointers of all - variables defined in the parser. - * New member function: GetConst() can be used for querying all defined constants and their values. - * New member function: GetFunDef() can be used for querying all defined functions and the number of - arguments they expect. - * Internal structure changed; hanging base datatype at compile time is now possible. - * Bugfix: Postfix operator parsing could fail in certain cases; This has been fixed now. - * Bugfix: Variable names must will now be tested if they conflict with constant or function names. - * Internal change: Removed most dependencies from the C-string libraries. - * Internal change: Bytecode is now stored in a separate class: ParserByteCode.h - * Internal change: GetUsedVar() does no longer require that variables are defined at time of call. - * Internal change: Error treatment changed. ParserException is no longer derived from - std::runtime_error; Internal treatment of Error messages changed. - * New functions in Parser interface: ValidNameChars(), ValidOprtChars() and ValidPrefixOprtChars() - they are used for defining the charset allowed for variable-, operator- and - function names. - - -Rev 1.09 : November 20, 2004 ----------------------------- - - * New member function: RemoveVar(...) can be used for removing a single variable from the internal - storage. - * Internal structure changed; changing base datatype at compile time is now possible. - * Bug fix: Postfix operator parsing could fail in certain cases; This has been fixed now. - * Internal change: Removed most dependencies from the C-string libraries. - * Internal change: Bytecode is now stored in a seperate class: ParserByteCode.h. - * Internal change: GetUsedVar() does no longer require that variables are defined at time of call. - * Internal change: Error treatment changed. ParserException is no longer derived from - std::runtime_error; Internal treatment of Error messages changed. - * New functions in Parser interface; ValidNameChars(), ValidOprtChars() and ValidPrefixOprtChars() - they are used for defining the charset allowed for variable-, operator- and function names. - - -Rev 1.08 : November, 2004 -------------------------- - - * unpublished; experimental template version with respect to data type and underlying string - type (string <-> widestring). The idea was dropped... - - -Rev 1.07 : September 4 2004 ---------------------------- - - * Improved portability; Changes to make life for MSVC 6 user easier, there are probably still some - issues left. - * Improved portability; Changes in order to allow compiling on BCB. - * New function; value_type Diff(value_type *a_Var, value_type a_fPos) 4th order Differentiation with - respect to a certain variable; added in muParser.h. - - -Rev 1.06 : August 20 2004 -------------------------- - - * Volatile functions added; All overloaded AddFun(...) functions can now take a third parameter - indicating that the function can not be optimized. - * Internal changes: muParserStack.h simplified; refactorings - * Parser is now distributed under the MIT License; all comments changed accordingly. - - -Rev 1.05 : August 20 2004 -------------------------- - - * Variable/constant names will now be checked for invalid characters. - * Querying the names of all variables used in an expression is now possible; new function: GetUsedVar(). - * Disabling bytecode parsing is now possible; new function: EnableByteCode(bool bStat). - * Predefined functions with variable number of arguments added: sum, avg, min, max. - * Unary prefix operators added; new functions: AddPrefixOp(...), ClearPrefixOp(). - * Postfix operator interface names changed; new function names: AddPostfixOp(...), ClearPostfixOp(). - * Hardcoded sign operators removed in favor of prefix operators; bytecode format changed accordingly. - * Internal changes: static array removed in Command code calculation routine; misc. changes. - - -Rev 1.04 : August 16 2004 -------------------------- - - * Support for functions with variable number of arguments added. - * Internal structure changed; new: ParserBase.h, ParserBase.cpp; removed: ParserException.h; - changed: Parser.h, Parser.cpp. - * Bug in the bytecode calculation function fixed (affected the unary minus operator). - * Optimizer can be deactivated; new function: EnableOptimizer(bool bStat). - - -Rev 1.03 : August 10 2004 -------------------------- - - * Support for user-defined unary postfix operators added; new functions: AddPostOp(), InitPostOp(), - ClearPostOp(). - * Minor changes to the bytecode parsing routine. - * User defined functions can now have up to four parameters. - * Performance optimized: simple formula optimization added; (precalculation of constant parts of the - expression). - * Bug fixes: Multi-arg function parameters, constant name lookup and unary minus did not work properly. - - -Rev 1.02 : July 30 2004 ------------------------ - - * Support for user defined constants added; new functions: InitConst(), AddConst(), SetConst(), - ClearConst(). - * Single variables can now be added using AddVar(); you have now the choice of adding them either - one by one or all at the same time using SetVar(const varmap_type &a_vVar). - * Internal handling of variables changed, is now similar to function handling. - * Virtual destructor added; InitFun(), InitConst() are now virtual too thus making it possible to - derive new parsers with a modified set of default functions and constants. - * Support for user defined functions with 2 or 3 parameters added; bytecode format changed to hold - function parameter count. - - -Rev 1.01 : July 23 2004 ------------------------ - - * Support for user defined functions has been added; new functions: AddFun(), ClearFun(), - InitFunctions(). - * Built in constants have been removed; the parser contained undocumented built in - constants pi, e. - There was the possibility of name conflicts with user defined variables. - * Setting multiple variables with SetVar can now be done with a map of names and pointers as the only - argument. For this reason, a new type Parser::varmap_type was added. The old version that took 3 - arguments (array of names, array of pointers, and array length) is now marked as deprecated. - * The names of logarithm functions have changed. The new names are: log2 for base 2, log10 or log for - base 10, and ln for base e. - - -Rev 1.00 : July 21 2004 ------------------------ - - * Initial release + + _____ __ _____________ _______ ______ ___________ + / \| | \____ \__ \\_ __ \/ ___// __ \_ __ \ + | Y Y \ | / |_> > __ \| | \/\___ \\ ___/| | \/ + |__|_| /____/| __(____ /__| /____ >\___ >__| + \/ |__| \/ \/ \/ + Copyright (C) 2004 - 2020 Ingo Berg + +======================================================================= + https://beltoforion.de/en/muparser +======================================================================= + +History: +-------- + +GitHub Master +--------------------- + Build System Changes (CMake): + * Added a new option "-DENABLE_WIDE_CHAR" to CMake for building muparser with wide character support + + Compiler Warnings fixed/disabled (Visual Studio): + * Disabled compiler warning 26812 (Prefer 'enum class' over 'enum') + I consider this a bogus warning. Use of plain old enums has not been deprecated and only MSVC is complaining. + * Disabled compiler warning 4251 (... needs to have dll-interface to be used by clients of class ...) + When the build system was changed to CMake Linux and Windows builds were unified. Each dynamic library contains the + class interface as well as the C-interface. Before the linux shared library was using the class interface and the + windows dll was using the C-interface. + + Only the C-Interface is safe to use when you intent to bring an executable to another linux distribution or windows version! + This is up to the client software. I cannot change this because on linux the shared library was always using the class + interface. Usually this is not a problem since distributions compile all applications from scratch. + + If you use the class interface you can not take for granted that your software will run with a muparser + version compiled for another operating system or linux distribution! You must either use the C-Interface if you + want this or use a static library build of muparser! + + Security Fixes: (The issues were present in all prior stable releases) + * https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=24167 + * https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=24355 + + Bugfixes: + * fixed a couple of issues for building the C-Interface (muParserDLL.cpp/.h) with wide character support. + +Rev 2.3.2: 17.06.2020 +--------------------- + API-Change Revertion (to 2.3.0): + * removed final keyword from Parser class (added in 2.3.0) as this was breaking existing Applications + + Changes: + * made Parser class final + * using OpenMP is now the default settings for cmake based builds + * added optimization for trivial expressions. (Expressions whose RPN only has a single entry) + * introduced a maximum length for expressions (5000 Character) + * introduced a maximum length for identifiers (100 Characters) + * removed the MUP_MATH_EXCEPTION macro and related functionality. (C++ exceptions for divide by zero or sqrt of a negative number are no longer supported) + * removed ParserStack.h (replaced with std::stack) + * removed macros for defining E and PI (replaced with a static constants) + * source code is now aimed at C++17 + * the MUP_ASSERT macro is no longer removed in release builds for better protection against segmentation faults + + Security Fixes: (The issues were present in all prior stable releases) + * Prevented multiple access violations for malformed expressions with if then else and functions taking multiple arguments like "sum(0?1,2,3,4:5)" + * https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=23330 + * https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=22922 + * https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=22938 + * https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=23330 + * https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=23410 + * Added additional runtime checks for release builds to prevent segmentation faults for invalid expressions + + Bugfixes: + * Fixed an issue where the bulk mode could hang on GCC/CLANG builds due to OpenMP chunksize dropping below 1. + +Rev 2.3.0 - 2.3.1: +------------------ +Short lived releases or prereleases that were replaced by 2.3.2 almost instantly due to API breaking changes. Version 2.3.2 is the successor to version 2.2.6 + +Rev 2.2.6: 04.10.2018 +--------------------- + Changes: + * Build system is now based on cmake + * several compiler warnings fixed + +Rev 2.2.5: 27.04.2015 +--------------------- + Changes: + * example2 extended to work with UNICODE character set + * Applied patch from Issue 9 + + Bugfixes: + * muChar_t in muParserDLL.h was not set properly when UNICODE was used + * muparser.dll did not build on UNICODE systems + +Rev 2.2.4: 02.10.2014 +--------------------- + Changes: + * explicit positive sign allowed + + Bugfixes: + * Fix for Issue 6 (https://code.google.com/p/muparser/issues/detail?id=6) + * String constants did not work properly. Using more than a single one + was impossible. + * Project Files for VS2008 and VS2010 removed from the repository + * Fix for Issue 4 (https://code.google.com/p/muparser/issues/detail?id=4) + * Fix for VS2013 64 bit build option + * return type of ParserError::GetPos changed to int + * OpenMP support enabled in the VS2013 project files and precompiled windows DLL's + * Bulkmode did not evaluate properly if "=" and "," operator was used in the expression + +Rev 2.2.3: 22.12.2012 +--------------------- + + Removed features: + * build files for msvc2005, borland and watcom compiler were removed + + Bugfixes: + * Bugfix for Intel Compilers added: The power operator did not work properly + with Intel C++ composer XE 2011. + (see https://sourceforge.net/projects/muparser/forums/forum/462843/topic/5117983/index/page/1) + * Issue 3509860: Callbacks of functions with string parameters called twice + (see http://sourceforge.net/tracker/?func=detail&aid=3509860&group_id=137191&atid=737979) + * Issue 3570423: example1 shows slot number in hexadecimal + (see https://sourceforge.net/tracker/?func=detail&aid=3570423&group_id=137191&atid=737979) + * Fixes for compiling with the "MUP_MATH_EXCEPTIONS" macro definition: + - division by zero in constant expressions was reported with the code "ec_GENERIC" + instead of "ecDIV_BY_ZERO" + - added throwing of "ecDOMAIN_ERROR" to sqrt and log functions + + +Rev 2.2.2: 18.02.2012 +--------------------- + Bugfixes: + * Optimizer did'nt work properly for division: + (see https://sourceforge.net/projects/muparser/forums/forum/462843/topic/5037825) + +Rev 2.2.1: 22.01.2012 +--------------------- + Bugfixes: + * Optimizer bug in 64 bit systems fixed + (see https://sourceforge.net/projects/muparser/forums/forum/462843/topic/4977977/index/page/1) + +Rev 2.2.0: 22.01.2012 +--------------------- + Improvements: + * Optimizer rewritten and improved. In general: more optimizations are + now applied to the bytecode. The downside is that callback Functions + can no longer be flagged as non-optimizable. (The flag is still present + but ignored) This is necessary since the optimizer had to call the + functions in order to precalculate the result (see Bugfixes). These calls + posed a problems for callback functions with side effects and if-then-else + clauses in general since they undermined the shortcut evaluation prinziple. + + Bugfixes: + * Infix operators where not properly detected in the presence of a constant + name starting with an underscore which is a valid character for infix + operators too (i.e. "-_pi"). + * Issue 3463353: Callback functions are called twice during the first call to eval. + * Issue 3447007: GetUsedVar unnecessaryly executes callback functions. + + +Rev 2.1.0: 19.11.2011 +--------------------- + New feature: + * Function atan2 added + + Bugfixes: + * Issue 3438380: Changed behaviour of tellg with GCC >4.6 led to failures + in value detection callbacks. + * Issue 3438715: only "double" is a valid MUP_BASETYPE + MUP_BASETYPE can now be any of: + float, + double, + long double, + short, + unsigned short, + unsigned int, + long, + unsigned long. + Previousely only floating point types were allowed. + Using "int" is still not allowed! + * Compiler issues with GCC 4.6 fixed + * Custom value recognition callbacks added with AddValIdent had lower + priority than built in functions. This was causing problems with + hex value recognition since detection of non hex values had priority + over the detection of hex values. The "0" in the hex prefix "0x" would + be read as a separate non-hex number leaving the rest of the expression + unparseable. + +Rev 2.0.0: 04.09.2011 +--------------------- +This release introduces a new version numbering scheme in order to make +future changes in the ABI apparent to users of the library. The number is +now based on the SONAME property as used by GNU/Linux. + + Changes: + * Beginning with this version all version numbers will be SONAME compliant + * Project files for MSVC2010 added + * Project files for MSVC2003 removed + * Bytecode parsing engine cleaned up and rewritten + * Retrieving all results of expressions made up of comma separate + subexpressions is now possible with a new Eval overload. + * Callback functions with fixed number of arguments can now have up to 10 + Parameters (previous limit was 5) + + New features: + * ternary if-then-else operator added (C++ like; "(...) ? ... : ..." ) + * new intrinsic binary operators: "&&", "||" (logical and, or) + * A new bulkmode allows submitting large arrays as variables to compute large + numbers of expressions with a single call. This can drastically improve + parsing performance when interfacing the library from managed languages like + C#. (It doesn't bring any performance benefit for C++ users though...) + + Removed features: + * intrinsic "and", "or" and "xor" operators have been removed. I'd like to let + users the freedom of defining them on their own versions (either as logical or bitwise + operators). + * Implementation for complex numbers removed. This was merely a hack. If you + need complex numbers try muParserX which provides native support for them. + (see: http://beltoforion.de/muparserx/math_expression_parser_en.html) + + Bugfixes: + * User defined operators could collide with built in operators that entirely + contained their identifier. i.e. user defined "&" would not work with the built + in "&&" operator since the user defined operator was detected with a higher + priority resulting in a syntax error. + * Detection of unknown variables did not work properly in case a postfix operator + was defined which was part of the undefined variable. + i.e. If a postfix operator "m" was defined expressions like "multi*1.0" did + not detect "multi" as an undefined variable. + (Reference: http://sourceforge.net/tracker/index.php?func=detail&aid=3343891&group_id=137191&atid=737979) + * Postfix operators sharing the first few characters were causing bogus parsing exception. + (Reference: https://sourceforge.net/tracker/?func=detail&aid=3358571&group_id=137191&atid=737979) + +Rev 1.34: 04.09.2010 +-------------------- + Changes: + * The prefix needed for parsing hex values is now "0x" and no longer "$". + * AddValIdent reintroduced into the DLL interface + + New features: + * The associativity of binary operators can now be changed. The pow operator + is now right associative. (This is what Mathematica is using) + * Separator can now be used outside of functions. This allows compound + expressions like: + "a=10,b=20,c=a*b" The last "argument" will be taken as the return value + + Bugfixes: + * The copy constructor did not copy binary operator definitions. Those were lost + in the copied parser instance. + * Mixing special characters and alphabetic characters in binary operator names + led to inconsistent parsing behaviour when parsing expressions like "a ++ b" + and "a++b" when "++" is defined as a binary operator. Binary operators must + now consist entirely of special characters or of alphabetic ones. + (original bug report: https://sourceforge.net/projects/muparser/forums/forum/462843/topic/3696881/index/page/1) + * User defined operators were not exactly handled like built in operators. This + led to inconsistencies in expression evaluation when using them. The results + differed due to slightly different precedence rules. + * Using empty string arguments ("") would cause a crash of muParser + + +Rev 1.32: 29.01.2010 +-------------------- + + Changes: + * "example3" renamed to "example2" + * Project/Makefiles files are now provided for: + - msvc2003 + - msvc2005 + - msvc2008 + - watcom (makefile) + - mingw (makefile) + - bcc (makefile) + * Project files for borland cpp builder were removed + + + New features: + * Added function returning muparsers version number + * Added function for resetting the locale + + + Bugfixes: + * Changes example1 in order to fix issues with irritating memory leak reports. + Added conditional code for memory leak detection with MSVC in example1. + (see: http://www.codeproject.com/KB/recipes/FastMathParser.aspx?msg=3286367#xx3286367xx) + * Fixed some warnings for gcc + + + +Rev 1.31cp: 15.01.2010 (Maintenance release for CodeProject) +---------------------- + + Changes: + * Archive structure changed + * C# wrapper added + * Fixed issued that prevented compiling with VS2010 Beta2 + + +Rev 1.30: 09.06.2008 +-------------------- + + Changes: + * Epsilon of the numerical differentiation algorithm changed to allow greater accuracy. + + New features: + * Setting thousands separator and decimal separator is now possible + + Bugfixes: + * The dll interface did not provide a callback for functions without any arguments. + + +Rev 1.29: Januar 2008 +--------------------- + + Unrelease Version available only via SVN. + + +Rev 1.28: 02. July, 2007 +--------------------------- + + Library changes: + * Interface for the dynamic library changed and extended to create an interface + using pure C functions only. + * mupInit() removed + + Build system: + * MSVC7 Project files removed in favor of MSVC8 + + Bugfixes: + * The dynamic library did not build on other systems than linux due to a misplaced + preprocessor definition. This is fixed now. + + +Rev 1.27: +--------------------------- + + Build system: + * Modified build\ directory layout introducing some subfolders + for the various IDE supported + * Project files for BCB and MSVC7 added + * Switched to use bakefile 0.2.1 which now correctly creates the + "make uninstall" target for autoconf's Makefile.in + * Now the library debug builds are named "muparserd" instead of "muparser" + to allow multiple mixed release/debug builds to coexist; so e.g. on Windows + when building with DEBUG=1, you'll get "muparserd.lib" instead of "muparser.lib" + + New Features: + * Factory functions can now take a user defined pointer + * String functions can now be used with up to two additional + double parameters + * Support for UNICODE character types added + * Infix operator priority can now be changed by the user + + Bugfixes: + * An internal error was raised when evaluating an empty + expressions + * The error message raised in case of name collisions between + implicitely defined variables and postfix operators did contain + misleading data. + + +Rev 1.26: (unofficial release) +------------------------------ + + New Features: + * Unary operator precedence can now be changed by the user. + + +Rev 1.25: 5. February, 2006 +--------------------------- + + Build system: (special thanks to Francesco Montorsi for implementing it!) + * created a bakefile-based build system which adds support for the following win32 compilers: + -> MS visual C++ (6 and .NET) + -> BorlandC++ (5 or greater) + -> Mingw32 (tested with gcc 3.2) + -> Watcom (not tested) + and for GCC on Unix (using a standard autoconf's configure script). + + Compatibility improvements: + * fixed some small warnings when using -Wall with GCC on Unix + * added inclusion guards for win32-specific portions of code + * added fixes that remove compiler warnings on Intel C++ and the Solaris C++ compiler. + + +Rev 1.24: 29. October, 2005 +--------------------------- + +Changes: + + Compatibility improvements: + * parser now works on 64 bit compilers + * (bytecode base datatype can now be changed freely) + + +Rev 1.23: 19. October, 2005 +--------------------------- + +Changes: + + Bugfixes: + * Variable factory examples in Example1.cpp and Example3.cpp contained a subtle bug. + + New features: + * Added a MSVC6 project file and introduced muParserFixes.h in order to make it compile with MSVC6 + + +Rev 1.22: October, 2005 +----------------------- + +Release notes: + +All features of Version 1.22 are similar to Version 1.21. Version 1.22 fixes a compilation issue with +gcc 4.0. In order to fix this issue I rewrote part of the library to remove some unnecessary templates. +This should make the code cleaner. The Borland Project files were removed. If you want to use it +with Borland either use the dll version or create your own project files. I can't support it since I don't +have this compiler at hand. + +Changes: + + Project Changes: + * Borland project files removed + (The code should still compile with BCB but I can't provide you with project files) + + Internal Changes: + * unnecessary template files have been removed: + - new files: muParserError.cpp, muParserTokenReader.cpp, muParserCallback.cpp + - removed Files: muIParserTypes.h + + +Rev 1.2 / 1.21: April, 2005 +--------------------------- + +Release Notes: +First of all the interface has changed so this version is not backwards compatible. +After receiving a couple of questions about it, this version features support for +user defined binary operators. Consequently the built in operators can now be +turned off, thus you can deactivate them and write complete customized parser +subclasses that only contain the functionality you want. Another new feature is +the introduction of callback functions taking string arguments, implicit +generation of variables and the Assignment operator. + + Functionality + * New built in operator: xor; Logical xor. + * New built in operator: Assignment operator; Defining variables in terms of + other variables/constants + * New feature: Strings as arguments for callback functions + * New feature: User defined binary operators + * New feature: ParserInt a class with a sample implementation for + integer numbers. + * New feature: Callbacks to value regognition functions. + + * Removed: all predefined postfix operators have been removed. + * New project file: Now comes with a ready to use windows DLL. + * New project file: Makefile for cygwin now included. + * New example: Example3 shows usage of the DLL. + + Interface changes + * New member function: DefineOprt For adding user defined binary operators. + * New member function: EnableBuiltInOprt(bool) Enables/Disables built in + binary operators. + * New member function: AddValIdent(...) to add callbacks for custom value + recognition functions. + * Removed: SetVar(), SetConst(). + * Renamed: Most interface functions have been renamed + * Changed: The type for multiargument callbacks multfun_type has changed. + It no longer takes a std::vector as input. + + Internal changes + * new class muParserTokenReader.h encapsulates the token identification + and token assignment. + * Internal handling of function callbacks unified as a result the performance + of the bytecode evaluation increased. + + +Rev 1.10 : December 30, 2004 +---------------------------- + +Release Notes: +This version does not contain major new feature compared to V1.07 but its internal structure has +changed significantly. The String parsing routine is slower than the one of V1.07 but bytecode +parsing is equally fast. On the other hand the error messages of V1.09 are more flexible and you +can change its value datatype. It should work on 64-bit systems. For this reason I supply both +versions for download. If you use V1.07 and are happy with it there is no need for updating +your version. + + * New example program: Archive now contains two demo programs: One for standard C++ and one for + managed C++. + * New member function: RemoveVar(...) can be used for removing a single variable from the internal + storage. + * New member function: GetVar() can be used for querying the variable names and pointers of all + variables defined in the parser. + * New member function: GetConst() can be used for querying all defined constants and their values. + * New member function: GetFunDef() can be used for querying all defined functions and the number of + arguments they expect. + * Internal structure changed; hanging base datatype at compile time is now possible. + * Bugfix: Postfix operator parsing could fail in certain cases; This has been fixed now. + * Bugfix: Variable names must will now be tested if they conflict with constant or function names. + * Internal change: Removed most dependencies from the C-string libraries. + * Internal change: Bytecode is now stored in a separate class: ParserByteCode.h + * Internal change: GetUsedVar() does no longer require that variables are defined at time of call. + * Internal change: Error treatment changed. ParserException is no longer derived from + std::runtime_error; Internal treatment of Error messages changed. + * New functions in Parser interface: ValidNameChars(), ValidOprtChars() and ValidPrefixOprtChars() + they are used for defining the charset allowed for variable-, operator- and + function names. + + +Rev 1.09 : November 20, 2004 +---------------------------- + + * New member function: RemoveVar(...) can be used for removing a single variable from the internal + storage. + * Internal structure changed; changing base datatype at compile time is now possible. + * Bug fix: Postfix operator parsing could fail in certain cases; This has been fixed now. + * Internal change: Removed most dependencies from the C-string libraries. + * Internal change: Bytecode is now stored in a separate class: ParserByteCode.h. + * Internal change: GetUsedVar() does no longer require that variables are defined at time of call. + * Internal change: Error treatment changed. ParserException is no longer derived from + std::runtime_error; Internal treatment of Error messages changed. + * New functions in Parser interface; ValidNameChars(), ValidOprtChars() and ValidPrefixOprtChars() + they are used for defining the charset allowed for variable-, operator- and function names. + + +Rev 1.08 : November, 2004 +------------------------- + + * unpublished; experimental template version with respect to data type and underlying string + type (string <-> widestring). The idea was dropped... + + +Rev 1.07 : September 4 2004 +--------------------------- + + * Improved portability; Changes to make life for MSVC 6 user easier, there are probably still some + issues left. + * Improved portability; Changes in order to allow compiling on BCB. + * New function; value_type Diff(value_type *a_Var, value_type a_fPos) 4th order Differentiation with + respect to a certain variable; added in muParser.h. + + +Rev 1.06 : August 20 2004 +------------------------- + + * Volatile functions added; All overloaded AddFun(...) functions can now take a third parameter + indicating that the function can not be optimized. + * Internal changes: muParserStack.h simplified; refactorings + * Parser is now distributed under the MIT License; all comments changed accordingly. + + +Rev 1.05 : August 20 2004 +------------------------- + + * Variable/constant names will now be checked for invalid characters. + * Querying the names of all variables used in an expression is now possible; new function: GetUsedVar(). + * Disabling bytecode parsing is now possible; new function: EnableByteCode(bool bStat). + * Predefined functions with variable number of arguments added: sum, avg, min, max. + * Unary prefix operators added; new functions: AddPrefixOp(...), ClearPrefixOp(). + * Postfix operator interface names changed; new function names: AddPostfixOp(...), ClearPostfixOp(). + * Hardcoded sign operators removed in favor of prefix operators; bytecode format changed accordingly. + * Internal changes: static array removed in Command code calculation routine; misc. changes. + + +Rev 1.04 : August 16 2004 +------------------------- + + * Support for functions with variable number of arguments added. + * Internal structure changed; new: ParserBase.h, ParserBase.cpp; removed: ParserException.h; + changed: Parser.h, Parser.cpp. + * Bug in the bytecode calculation function fixed (affected the unary minus operator). + * Optimizer can be deactivated; new function: EnableOptimizer(bool bStat). + + +Rev 1.03 : August 10 2004 +------------------------- + + * Support for user-defined unary postfix operators added; new functions: AddPostOp(), InitPostOp(), + ClearPostOp(). + * Minor changes to the bytecode parsing routine. + * User defined functions can now have up to four parameters. + * Performance optimized: simple formula optimization added; (precalculation of constant parts of the + expression). + * Bug fixes: Multi-arg function parameters, constant name lookup and unary minus did not work properly. + + +Rev 1.02 : July 30 2004 +----------------------- + + * Support for user defined constants added; new functions: InitConst(), AddConst(), SetConst(), + ClearConst(). + * Single variables can now be added using AddVar(); you have now the choice of adding them either + one by one or all at the same time using SetVar(const varmap_type &a_vVar). + * Internal handling of variables changed, is now similar to function handling. + * Virtual destructor added; InitFun(), InitConst() are now virtual too thus making it possible to + derive new parsers with a modified set of default functions and constants. + * Support for user defined functions with 2 or 3 parameters added; bytecode format changed to hold + function parameter count. + + +Rev 1.01 : July 23 2004 +----------------------- + + * Support for user defined functions has been added; new functions: AddFun(), ClearFun(), + InitFunctions(). + * Built in constants have been removed; the parser contained undocumented built in + constants pi, e. + There was the possibility of name conflicts with user defined variables. + * Setting multiple variables with SetVar can now be done with a map of names and pointers as the only + argument. For this reason, a new type Parser::varmap_type was added. The old version that took 3 + arguments (array of names, array of pointers, and array length) is now marked as deprecated. + * The names of logarithm functions have changed. The new names are: log2 for base 2, log10 or log for + base 10, and ln for base e. + + +Rev 1.00 : July 21 2004 +----------------------- + + * Initial release diff --git a/bundled/muparser_v2_2_4/CMakeLists.txt b/bundled/muparser_v2_3_3/CMakeLists.txt similarity index 95% rename from bundled/muparser_v2_2_4/CMakeLists.txt rename to bundled/muparser_v2_3_3/CMakeLists.txt index 7edf1d4a67..1fcf61cf51 100644 --- a/bundled/muparser_v2_2_4/CMakeLists.txt +++ b/bundled/muparser_v2_3_3/CMakeLists.txt @@ -22,6 +22,7 @@ ENABLE_IF_SUPPORTED(DEAL_II_CXX_FLAGS -Wno-switch) ENABLE_IF_SUPPORTED(DEAL_II_CXX_FLAGS -Wno-nested-anon-types) ENABLE_IF_SUPPORTED(DEAL_II_CXX_FLAGS -Wno-implicit-fallthrough) ENABLE_IF_SUPPORTED(DEAL_II_CXX_FLAGS -Wno-cast-function-type) +ENABLE_IF_SUPPORTED(DEAL_II_CXX_FLAGS -Wno-float-conversion) INCLUDE_DIRECTORIES( include/ diff --git a/bundled/muparser_v2_3_3/LICENSE b/bundled/muparser_v2_3_3/LICENSE new file mode 100644 index 0000000000..e0a01f1bc6 --- /dev/null +++ b/bundled/muparser_v2_3_3/LICENSE @@ -0,0 +1,25 @@ +Copyright 2020 Ingo Berg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/bundled/muparser_v2_3_3/include/muParser.h b/bundled/muparser_v2_3_3/include/muParser.h new file mode 100644 index 0000000000..62032aa1e9 --- /dev/null +++ b/bundled/muparser_v2_3_3/include/muParser.h @@ -0,0 +1,71 @@ +/* + + _____ __ _____________ _______ ______ ___________ + / \| | \____ \__ \\_ __ \/ ___// __ \_ __ \ + | Y Y \ | / |_> > __ \| | \/\___ \\ ___/| | \/ + |__|_| /____/| __(____ /__| /____ >\___ >__| + \/ |__| \/ \/ \/ + Copyright (C) 2004 - 2021 Ingo Berg + + Redistribution and use in source and binary forms, with or without modification, are permitted + provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this list of + conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of + conditions and the following disclaimer in the documentation and/or other materials provided + with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef MU_PARSER_H +#define MU_PARSER_H + +//--- Standard includes ------------------------------------------------------------------------ +#include + +//--- Parser includes -------------------------------------------------------------------------- +#include "muParserBase.h" +#include "muParserTemplateMagic.h" + +/** \file + \brief Definition of the standard floating point parser. +*/ + +namespace mu +{ + /** \brief Mathematical expressions parser. + + Standard implementation of the mathematical expressions parser. + Can be used as a reference implementation for subclassing the parser. + */ + class API_EXPORT_CXX Parser : public ParserBase + { + public: + + Parser(); + + void InitCharSets() override; + void InitFun() override; + void InitConst() override; + void InitOprt() override; + void OnDetectVar(string_type* pExpr, int& nStart, int& nEnd) override; + + value_type Diff(value_type* a_Var, value_type a_fPos, value_type a_fEpsilon = 0) const; + + protected: + + static int IsVal(const char_type* a_szExpr, int* a_iPos, value_type* a_fVal); + }; +} // namespace mu + +#endif + diff --git a/bundled/muparser_v2_3_3/include/muParserBase.h b/bundled/muparser_v2_3_3/include/muParserBase.h new file mode 100644 index 0000000000..6d32f2f4d7 --- /dev/null +++ b/bundled/muparser_v2_3_3/include/muParserBase.h @@ -0,0 +1,317 @@ +/* + + _____ __ _____________ _______ ______ ___________ + / \| | \____ \__ \\_ __ \/ ___// __ \_ __ \ + | Y Y \ | / |_> > __ \| | \/\___ \\ ___/| | \/ + |__|_| /____/| __(____ /__| /____ >\___ >__| + \/ |__| \/ \/ \/ + Copyright (C) 2004 - 2021 Ingo Berg + + Redistribution and use in source and binary forms, with or without modification, are permitted + provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this list of + conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of + conditions and the following disclaimer in the documentation and/or other materials provided + with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef MU_PARSER_BASE_H +#define MU_PARSER_BASE_H + +//--- Standard includes ------------------------------------------------------------------------ +#include +#include +#include +#include +#include +#include +#include + +//--- Parser includes -------------------------------------------------------------------------- +#include "muParserDef.h" +#include "muParserTokenReader.h" +#include "muParserBytecode.h" +#include "muParserError.h" + +#if defined(_MSC_VER) + #pragma warning(push) + #pragma warning(disable : 4251) // ...needs to have dll-interface to be used by clients of class ... +#endif + + +namespace mu +{ + /** \file + \brief This file contains the class definition of the muparser engine. + */ + + /** \brief Mathematical expressions parser (base parser engine). + + 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 API_EXPORT_CXX ParserBase + { + friend class ParserTokenReader; + + 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 value_type(ParserBase::* ParseFunction)() const; + + /** \brief Type used for storing an array of values. */ + typedef std::vector valbuf_type; + + /** \brief Type for a vector of strings. */ + typedef std::vector stringbuf_type; + + /** \brief Typedef for the token reader. */ + typedef ParserTokenReader token_reader_type; + + /** \brief Type used for parser tokens. */ + typedef ParserToken token_type; + + /** \brief Maximum number of threads spawned by OpenMP when using the bulk mode. */ + static const int s_MaxNumOpenMPThreads; + + public: + + /** \brief Type of the error class. + + Included for backwards compatibility. + */ + typedef ParserError exception_type; + + static void EnableDebugDump(bool bDumpCmd, bool bDumpStack); + + ParserBase(); + ParserBase(const ParserBase& a_Parser); + ParserBase& operator=(const ParserBase& a_Parser); + + virtual ~ParserBase(); + + value_type Eval() const; + value_type* Eval(int& nStackSize) const; + void Eval(value_type* results, int nBulkSize); + + int GetNumResults() const; + + void SetExpr(const string_type& a_sExpr); + void SetVarFactory(facfun_type a_pFactory, void* pUserData = nullptr); + + 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 mu::ParserBase::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 string_type& a_strName, T a_pFun, bool a_bAllowOpt = true) + { + AddCallback(a_strName, ParserCallback(a_pFun, a_bAllowOpt), m_FunDef, ValidNameChars()); + } + + /** \fn void mu::ParserBase::DefineFunUserData + \brief Define a parser function with user data (not null). + \param a_strName Name of the function + \param a_pFun Pointer to the callback function + \param a_pUserData Pointer that will be passed back to callback (shall not be nullptr) + \param a_bAllowOpt A flag indicating this function may be optimized + */ + template + void DefineFunUserData(const string_type& a_strName, T a_pFun, void* a_pUserData, bool a_bAllowOpt = true) + { + AddCallback(a_strName, ParserCallback(a_pFun, a_pUserData, a_bAllowOpt), m_FunDef, ValidNameChars()); + } + + void DefineOprt(const string_type& a_strName, fun_type2 a_pFun, unsigned a_iPri = 0, EOprtAssociativity a_eAssociativity = oaLEFT, bool a_bAllowOpt = false); + void DefineConst(const string_type& a_sName, value_type a_fVal); + void DefineStrConst(const string_type& a_sName, const string_type& a_strVal); + void DefineVar(const string_type& a_sName, value_type* a_fVar); + void DefinePostfixOprt(const string_type& a_strFun, fun_type1 a_pOprt, bool a_bAllowOpt = true); + void DefineInfixOprt(const string_type& 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 string_type& a_strVarName); + const varmap_type& GetUsedVar() const; + const varmap_type& GetVar() const; + const valmap_type& GetConst() const; + const string_type& GetExpr() const; + const funmap_type& GetFunDef() const; + string_type GetVersion(EParserVersionInfo eInfo = pviFULL) const; + const ParserByteCode& GetByteCode() const; + + const char_type** GetOprtDef() const; + void DefineNameChars(const char_type* a_szCharset); + void DefineOprtChars(const char_type* a_szCharset); + void DefineInfixOprtChars(const char_type* a_szCharset); + + const char_type* ValidNameChars() const; + const char_type* ValidOprtChars() const; + const char_type* ValidInfixOprtChars() const; + + void SetArgSep(char_type cArgSep); + char_type GetArgSep() const; + + protected: + + void Init(); + void Error(EErrorCodes a_iErrc, int a_iPos = static_cast(mu::string_type::npos), const string_type& a_strTok = string_type()) const; + + virtual void InitCharSets() = 0; + virtual void InitFun() = 0; + virtual void InitConst() = 0; + virtual void InitOprt() = 0; + + virtual void OnDetectVar(string_type* pExpr, int& nStart, int& nEnd); + + static const char_type* 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 + { + 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: + + char_type do_decimal_point() const override + { + return m_cDecPoint; + } + + char_type do_thousands_sep() const override + { + return m_cThousandsSep; + } + + std::string do_grouping() const override + { + // fix for issue 4: https://code.google.com/p/muparser/issues/detail?id=4 + // courtesy of Jens Bartsch + // original code: + // return std::string(1, (char)m_nGroup); + // new code: + return std::string(1, (char)(m_cThousandsSep > 0 ? m_nGroup : CHAR_MAX)); + } + + private: + + int m_nGroup; + char_type m_cDecPoint; + char_type m_cThousandsSep; + }; + + private: + + void Assign(const ParserBase& a_Parser); + void InitTokenReader(); + void ReInit() const; + + void AddCallback(const string_type& a_strName, const ParserCallback& a_Callback, funmap_type& a_Storage, const char_type* a_szCharSet); + void ApplyRemainingOprt(std::stack& a_stOpt, std::stack& a_stVal) const; + void ApplyBinOprt(std::stack& a_stOpt, std::stack& a_stVal) const; + void ApplyIfElse(std::stack& a_stOpt, std::stack& a_stVal) const; + void ApplyFunc(std::stack& a_stOpt, std::stack& a_stVal, int iArgCount) const; + + token_type ApplyStrFunc(const token_type& a_FunTok, const std::vector& a_vArg) const; + + int GetOprtPrecedence(const token_type& a_Tok) const; + EOprtAssociativity GetOprtAssociativity(const token_type& a_Tok) const; + + void CreateRPN() const; + + value_type ParseString() const; + value_type ParseCmdCode() const; + value_type ParseCmdCodeShort() const; + value_type ParseCmdCodeBulk(int nOffset, int nThreadID) const; + + void CheckName(const string_type& a_strName, const string_type& a_CharSet) const; + void CheckOprt(const string_type& a_sName, const ParserCallback& a_Callback, const string_type& a_szCharSet) const; + + void StackDump(const std::stack& a_stVal, const std::stack& a_stOprt) const; + + /** \brief Pointer to the parser function. + + Eval() calls the function whose address is stored there. + */ + mutable ParseFunction m_pParseFormula; + mutable ParserByteCode m_vRPN; ///< The Bytecode class. + mutable stringbuf_type m_vStringBuf; ///< String buffer, used for storing string function arguments + stringbuf_type m_vStringVarBuf; + + std::unique_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 + + string_type m_sNameChars; ///< Charset for names + string_type m_sOprtChars; ///< Charset for postfix/ binary operator tokens + string_type m_sInfixOprtChars; ///< Charset for infix operator tokens + + // 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 mu + +#if defined(_MSC_VER) + #pragma warning(pop) +#endif + +#endif diff --git a/bundled/muparser_v2_3_3/include/muParserBytecode.h b/bundled/muparser_v2_3_3/include/muParserBytecode.h new file mode 100644 index 0000000000..cf16e20687 --- /dev/null +++ b/bundled/muparser_v2_3_3/include/muParserBytecode.h @@ -0,0 +1,152 @@ +/* + + _____ __ _____________ _______ ______ ___________ + / \| | \____ \__ \\_ __ \/ ___// __ \_ __ \ + | Y Y \ | / |_> > __ \| | \/\___ \\ ___/| | \/ + |__|_| /____/| __(____ /__| /____ >\___ >__| + \/ |__| \/ \/ \/ + Copyright (C) 2004 - 2021 Ingo Berg + + Redistribution and use in source and binary forms, with or without modification, are permitted + provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this list of + conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of + conditions and the following disclaimer in the documentation and/or other materials provided + with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef MU_PARSER_BYTECODE_H +#define MU_PARSER_BYTECODE_H + +#include +#include +#include + +#include "muParserDef.h" +#include "muParserError.h" +#include "muParserToken.h" + +/** \file + \brief Definition of the parser bytecode class. +*/ + + +namespace mu +{ + struct SToken + { + ECmdCode Cmd; + + union + { + struct // SValData + { + value_type* ptr; + value_type data; + value_type data2; + } Val; + + struct // SFunData + { + // Note: the type is erased in generic_callable_type and the signature of the + // function to call is tracked elsewhere in regard with the number of + // parameters (args) and the general kind of function (Cmd: cmFUNC, + // cmFUNC_STR, or cmFUNC_BULK) + generic_callable_type cb; + int argc; + int idx; + } Fun; + + struct // SOprtData + { + value_type* 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. + */ + class ParserByteCode final + { + private: + + /** \brief Token type for internal use only. */ + typedef ParserToken token_type; + + /** \brief Token vector for storing the RPN. */ + typedef std::vector rpn_type; + + /** \brief Position in the Calculation array. */ + unsigned m_iStackPos; + + /** \brief Maximum size needed for the stack. */ + std::size_t m_iMaxStackSize; + + /** \brief The actual rpn storage. */ + rpn_type m_vRPN; + + bool m_bEnableOptimizer; + + void ConstantFolding(ECmdCode a_Oprt); + + public: + + ParserByteCode(); + ParserByteCode(const ParserByteCode& a_ByteCode); + ParserByteCode& operator=(const ParserByteCode& a_ByteCode); + void Assign(const ParserByteCode& a_ByteCode); + + void AddVar(value_type* a_pVar); + void AddVal(value_type a_fVal); + void AddOp(ECmdCode a_Oprt); + void AddIfElse(ECmdCode a_Oprt); + void AddAssignOp(value_type* a_pVar); + void AddFun(generic_callable_type a_pFun, int a_iArgc, bool isOptimizable); + void AddBulkFun(generic_callable_type a_pFun, int a_iArgc); + void AddStrFun(generic_callable_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 + { + return m_vRPN.size(); + } + + inline const SToken* GetBase() const + { + if (m_vRPN.size() == 0) + throw ParserError(ecINTERNAL_ERROR); + else + return &m_vRPN[0]; + } + + void AsciiDump(); + }; + +} // namespace mu + +#endif + + diff --git a/bundled/muparser_v2_3_3/include/muParserCallback.h b/bundled/muparser_v2_3_3/include/muParserCallback.h new file mode 100644 index 0000000000..1e53f14c52 --- /dev/null +++ b/bundled/muparser_v2_3_3/include/muParserCallback.h @@ -0,0 +1,161 @@ +/* + + _____ __ _____________ _______ ______ ___________ + / \| | \____ \__ \\_ __ \/ ___// __ \_ __ \ + | Y Y \ | / |_> > __ \| | \/\___ \\ ___/| | \/ + |__|_| /____/| __(____ /__| /____ >\___ >__| + \/ |__| \/ \/ \/ + Copyright (C) 2022 Ingo Berg + + Redistribution and use in source and binary forms, with or without modification, are permitted + provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this list of + conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of + conditions and the following disclaimer in the documentation and/or other materials provided + with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + +#ifndef MU_PARSER_CALLBACK_H +#define MU_PARSER_CALLBACK_H + +#include "muParserDef.h" + +/** \file + \brief Definition of the parser callback class. +*/ + +namespace mu +{ + + /** \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. + */ + class API_EXPORT_CXX ParserCallback final + { + public: + ParserCallback(fun_type0 a_pFun, bool a_bAllowOpti); + ParserCallback(fun_type1 a_pFun, bool a_bAllowOpti, int a_iPrec, ECmdCode a_iCode); + ParserCallback(fun_type1 a_pFun, bool a_bAllowOpti); + ParserCallback(fun_type2 a_pFun, bool a_bAllowOpti, int a_iPrec, EOprtAssociativity a_eAssociativity); + ParserCallback(fun_type2 a_pFun, bool a_bAllowOpti); + ParserCallback(fun_type3 a_pFun, bool a_bAllowOpti); + ParserCallback(fun_type4 a_pFun, bool a_bAllowOpti); + ParserCallback(fun_type5 a_pFun, bool a_bAllowOpti); + ParserCallback(fun_type6 a_pFun, bool a_bAllowOpti); + ParserCallback(fun_type7 a_pFun, bool a_bAllowOpti); + ParserCallback(fun_type8 a_pFun, bool a_bAllowOpti); + ParserCallback(fun_type9 a_pFun, bool a_bAllowOpti); + ParserCallback(fun_type10 a_pFun, bool a_bAllowOpti); + + ParserCallback(bulkfun_type0 a_pFun, bool a_bAllowOpti); + ParserCallback(bulkfun_type1 a_pFun, bool a_bAllowOpti); + ParserCallback(bulkfun_type2 a_pFun, bool a_bAllowOpti); + ParserCallback(bulkfun_type3 a_pFun, bool a_bAllowOpti); + ParserCallback(bulkfun_type4 a_pFun, bool a_bAllowOpti); + ParserCallback(bulkfun_type5 a_pFun, bool a_bAllowOpti); + ParserCallback(bulkfun_type6 a_pFun, bool a_bAllowOpti); + ParserCallback(bulkfun_type7 a_pFun, bool a_bAllowOpti); + ParserCallback(bulkfun_type8 a_pFun, bool a_bAllowOpti); + ParserCallback(bulkfun_type9 a_pFun, bool a_bAllowOpti); + ParserCallback(bulkfun_type10 a_pFun, bool a_bAllowOpti); + + ParserCallback(multfun_type a_pFun, bool a_bAllowOpti); + + ParserCallback(strfun_type1 a_pFun, bool a_bAllowOpti); + ParserCallback(strfun_type2 a_pFun, bool a_bAllowOpti); + ParserCallback(strfun_type3 a_pFun, bool a_bAllowOpti); + ParserCallback(strfun_type4 a_pFun, bool a_bAllowOpti); + ParserCallback(strfun_type5 a_pFun, bool a_bAllowOpti); + ParserCallback(strfun_type6 a_pFun, bool a_bAllowOpti); + + // note: a_pUserData shall not be nullptr + ParserCallback(fun_userdata_type0 a_pFun, void* a_pUserData, bool a_bAllowOpti); + ParserCallback(fun_userdata_type1 a_pFun, void* a_pUserData, bool a_bAllowOpti); + ParserCallback(fun_userdata_type2 a_pFun, void* a_pUserData, bool a_bAllowOpti); + ParserCallback(fun_userdata_type3 a_pFun, void* a_pUserData, bool a_bAllowOpti); + ParserCallback(fun_userdata_type4 a_pFun, void* a_pUserData, bool a_bAllowOpti); + ParserCallback(fun_userdata_type5 a_pFun, void* a_pUserData, bool a_bAllowOpti); + ParserCallback(fun_userdata_type6 a_pFun, void* a_pUserData, bool a_bAllowOpti); + ParserCallback(fun_userdata_type7 a_pFun, void* a_pUserData, bool a_bAllowOpti); + ParserCallback(fun_userdata_type8 a_pFun, void* a_pUserData, bool a_bAllowOpti); + ParserCallback(fun_userdata_type9 a_pFun, void* a_pUserData, bool a_bAllowOpti); + ParserCallback(fun_userdata_type10 a_pFun, void* a_pUserData, bool a_bAllowOpti); + + ParserCallback(bulkfun_userdata_type0 a_pFun, void* a_pUserData, bool a_bAllowOpti); + ParserCallback(bulkfun_userdata_type1 a_pFun, void* a_pUserData, bool a_bAllowOpti); + ParserCallback(bulkfun_userdata_type2 a_pFun, void* a_pUserData, bool a_bAllowOpti); + ParserCallback(bulkfun_userdata_type3 a_pFun, void* a_pUserData, bool a_bAllowOpti); + ParserCallback(bulkfun_userdata_type4 a_pFun, void* a_pUserData, bool a_bAllowOpti); + ParserCallback(bulkfun_userdata_type5 a_pFun, void* a_pUserData, bool a_bAllowOpti); + ParserCallback(bulkfun_userdata_type6 a_pFun, void* a_pUserData, bool a_bAllowOpti); + ParserCallback(bulkfun_userdata_type7 a_pFun, void* a_pUserData, bool a_bAllowOpti); + ParserCallback(bulkfun_userdata_type8 a_pFun, void* a_pUserData, bool a_bAllowOpti); + ParserCallback(bulkfun_userdata_type9 a_pFun, void* a_pUserData, bool a_bAllowOpti); + ParserCallback(bulkfun_userdata_type10 a_pFun, void* a_pUserData, bool a_bAllowOpti); + + ParserCallback(multfun_userdata_type a_pFun, void* a_pUserData, bool a_bAllowOpti); + + ParserCallback(strfun_userdata_type1 a_pFun, void* a_pUserData, bool a_bAllowOpti); + ParserCallback(strfun_userdata_type2 a_pFun, void* a_pUserData, bool a_bAllowOpti); + ParserCallback(strfun_userdata_type3 a_pFun, void* a_pUserData, bool a_bAllowOpti); + ParserCallback(strfun_userdata_type4 a_pFun, void* a_pUserData, bool a_bAllowOpti); + ParserCallback(strfun_userdata_type5 a_pFun, void* a_pUserData, bool a_bAllowOpti); + ParserCallback(strfun_userdata_type6 a_pFun, void* a_pUserData, bool a_bAllowOpti); + + ParserCallback(); + ParserCallback(const ParserCallback& a_Fun); + ParserCallback & operator=(const ParserCallback& a_Fun); + ~ParserCallback(); + + ParserCallback* Clone() const; + + bool IsOptimizable() const; + bool IsValid() const; + void* GetAddr() const; + void* GetUserData() const; + ECmdCode GetCode() const; + ETypeCode GetType() const; + int GetPri() const; + EOprtAssociativity GetAssociativity() const; + int GetArgc() const; + + private: + void Assign(const ParserCallback& ref); + + void* m_pFun; ///< Pointer to the callback function or internal data, casted to void + + int m_iArgc; ///< Internal representation of number of numeric function arguments + int m_iPri; ///< Valid only for binary and infix operators; Operator precedence. + EOprtAssociativity m_eOprtAsct; ///< Operator associativity; Valid only for binary operators + ECmdCode m_iCode; + ETypeCode m_iType; + bool m_bAllowOpti; ///< Flag indication optimizeability + }; + + + /** \brief Container for Callback objects. */ + typedef std::map funmap_type; + +} // namespace mu + +#endif + diff --git a/bundled/muparser_v2_3_3/include/muParserDef.h b/bundled/muparser_v2_3_3/include/muParserDef.h new file mode 100644 index 0000000000..13a15c8789 --- /dev/null +++ b/bundled/muparser_v2_3_3/include/muParserDef.h @@ -0,0 +1,518 @@ +/* + + _____ __ _____________ _______ ______ ___________ + / \| | \____ \__ \\_ __ \/ ___// __ \_ __ \ + | Y Y \ | / |_> > __ \| | \/\___ \\ ___/| | \/ + |__|_| /____/| __(____ /__| /____ >\___ >__| + \/ |__| \/ \/ \/ + Copyright (C) 2004 - 2021 Ingo Berg + + Redistribution and use in source and binary forms, with or without modification, are permitted + provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this list of + conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of + conditions and the following disclaimer in the documentation and/or other materials provided + with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef MUP_DEF_H +#define MUP_DEF_H + +#include +#include +#include +#include + +#include "muParserFixes.h" + +/** \file + \brief This file contains standard definitions used by the parser. +*/ + +/** \brief Define the base datatype for values. + + This datatype must be a built in value type. You can not use custom classes. + It should be working with all types except "int"! +*/ +#define MUP_BASETYPE double + +/** \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. + + !!! DO NOT ACTIVATE THIS MACRO HERE IF YOU USE CMAKE FOR BUILDING !!! + + use the cmake option instead! +*/ +//#define MUP_USE_OPENMP + +#if defined(_UNICODE) + /** \brief Definition of the basic parser string type. */ + #define MUP_STRING_TYPE std::wstring + + #if !defined(_T) + #define _T(x) L##x + #endif // not defined _T +#else + #ifndef _T + #define _T(x) x + #endif + + /** \brief Definition of the basic parser string type. */ + #define MUP_STRING_TYPE std::string +#endif + +/** \brief An assertion that does not kill the program. */ +#define MUP_ASSERT(COND) \ + if (!(COND)) \ + { \ + stringstream_type ss; \ + ss << _T("Assertion \"") _T(#COND) _T("\" failed: ") \ + << __FILE__ << _T(" line ") \ + << __LINE__ << _T("."); \ + throw ParserError( ecINTERNAL_ERROR, -1, ss.str()); \ + } + +#if defined(_MSC_VER) + #pragma warning(push) + #pragma warning(disable : 26812) +#endif + + +namespace mu +{ +#if defined(_UNICODE) + + /** \brief Encapsulate wcout. */ + inline std::wostream& console() + { + return std::wcout; + } + + /** \brief Encapsulate cin. */ + inline std::wistream& console_in() + { + return std::wcin; + } + +#else + + /** \brief Encapsulate cout. + + Used for supporting UNICODE more easily. + */ + inline std::ostream& console() + { + return std::cout; + } + + /** \brief Encapsulate cin. + + Used for supporting UNICODE more easily. + */ + inline std::istream& console_in() + { + return std::cin; + } + +#endif + + /** \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 = 22, + cmVARPOW3 = 23, + cmVARPOW4 = 24, + cmVARMUL = 25, + + // operators and functions + cmFUNC = 26, ///< 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) + }; + + + /** \brief Error codes. */ + enum EErrorCodes + { + // Formula syntax errors + ecUNEXPECTED_OPERATOR = 0, ///< Unexpected binary operator found + ecUNASSIGNABLE_TOKEN = 1, ///< Token can't 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, + + ecUNREASONABLE_NUMBER_OF_COMPUTATIONS = 35, + + ecIDENTIFIER_TOO_LONG = 36, ///< Thrown when an identifier with more then 255 characters is used. + + ecEXPRESSION_TOO_LONG = 37, ///< Throw an exception if the expression has more than 10000 characters. (an arbitrary limit) + + ecINVALID_CHARACTERS_FOUND = 38,///< The expression or identifier contains invalid non printable characters + + // internal errors + ecINTERNAL_ERROR = 39, ///< 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 + }; + + //------------------------------------------------------------------------------ + // Basic Types + //------------------------------------------------------------------------------ + + /** \brief The numeric datatype used by the parser. + + Normally this is a floating point type either single or double precision. + */ + typedef MUP_BASETYPE value_type; + + /** \brief The stringtype used by the parser. + + Depends on whether UNICODE is used or not. + */ + typedef MUP_STRING_TYPE string_type; + + /** \brief The character type used by the parser. + + Depends on whether 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 Function type used to erase type. Voluntarily needs explicit cast with all other *fun_type*. */ + typedef void(*erased_fun_type)(); + + /** \brief Callback type used for functions without arguments. */ + typedef value_type(*fun_type0)(); + + /** \brief Callback type used for functions with a single arguments. */ + typedef value_type(*fun_type1)(value_type); + + /** \brief Callback type used for functions with two arguments. */ + typedef value_type(*fun_type2)(value_type, value_type); + + /** \brief Callback type used for functions with three arguments. */ + typedef value_type(*fun_type3)(value_type, value_type, value_type); + + /** \brief Callback type used for functions with four arguments. */ + typedef value_type(*fun_type4)(value_type, value_type, value_type, value_type); + + /** \brief Callback type used for functions with five arguments. */ + typedef value_type(*fun_type5)(value_type, value_type, value_type, value_type, value_type); + + /** \brief Callback type used for functions with six arguments. */ + typedef value_type(*fun_type6)(value_type, value_type, value_type, value_type, value_type, value_type); + + /** \brief Callback type used for functions with seven arguments. */ + typedef value_type(*fun_type7)(value_type, value_type, value_type, value_type, value_type, value_type, value_type); + + /** \brief Callback type used for functions with eight arguments. */ + typedef value_type(*fun_type8)(value_type, value_type, value_type, value_type, value_type, value_type, value_type, value_type); + + /** \brief Callback type used for functions with nine arguments. */ + typedef value_type(*fun_type9)(value_type, value_type, value_type, value_type, value_type, value_type, value_type, value_type, value_type); + + /** \brief Callback type used for functions with ten arguments. */ + typedef value_type(*fun_type10)(value_type, value_type, value_type, value_type, value_type, value_type, value_type, value_type, value_type, value_type); + + /** \brief Callback type with user data (not null) used for functions without arguments. */ + typedef value_type(*fun_userdata_type0)(void*); + + /** \brief Callback type with user data (not null) used for functions with a single arguments. */ + typedef value_type(*fun_userdata_type1)(void*, value_type); + + /** \brief Callback type with user data (not null) used for functions with two arguments. */ + typedef value_type(*fun_userdata_type2)(void*, value_type, value_type); + + /** \brief Callback type with user data (not null) used for functions with three arguments. */ + typedef value_type(*fun_userdata_type3)(void*, value_type, value_type, value_type); + + /** \brief Callback type with user data (not null) used for functions with four arguments. */ + typedef value_type(*fun_userdata_type4)(void*, value_type, value_type, value_type, value_type); + + /** \brief Callback type with user data (not null) used for functions with five arguments. */ + typedef value_type(*fun_userdata_type5)(void*, value_type, value_type, value_type, value_type, value_type); + + /** \brief Callback type with user data (not null) used for functions with six arguments. */ + typedef value_type(*fun_userdata_type6)(void*, value_type, value_type, value_type, value_type, value_type, value_type); + + /** \brief Callback type with user data (not null) used for functions with seven arguments. */ + typedef value_type(*fun_userdata_type7)(void*, value_type, value_type, value_type, value_type, value_type, value_type, value_type); + + /** \brief Callback type with user data (not null) used for functions with eight arguments. */ + typedef value_type(*fun_userdata_type8)(void*, value_type, value_type, value_type, value_type, value_type, value_type, value_type, value_type); + + /** \brief Callback type with user data (not null) used for functions with nine arguments. */ + typedef value_type(*fun_userdata_type9)(void*, value_type, value_type, value_type, value_type, value_type, value_type, value_type, value_type, value_type); + + /** \brief Callback type with user data (not null) used for functions with ten arguments. */ + typedef value_type(*fun_userdata_type10)(void*, value_type, value_type, value_type, value_type, value_type, value_type, value_type, value_type, value_type, value_type); + + /** \brief Callback type used for functions without arguments. */ + typedef value_type(*bulkfun_type0)(int, int); + + /** \brief Callback type used for functions with a single arguments. */ + typedef value_type(*bulkfun_type1)(int, int, value_type); + + /** \brief Callback type used for functions with two arguments. */ + typedef value_type(*bulkfun_type2)(int, int, value_type, value_type); + + /** \brief Callback type used for functions with three arguments. */ + typedef value_type(*bulkfun_type3)(int, int, value_type, value_type, value_type); + + /** \brief Callback type used for functions with four arguments. */ + typedef value_type(*bulkfun_type4)(int, int, value_type, value_type, value_type, value_type); + + /** \brief Callback type used for functions with five arguments. */ + typedef value_type(*bulkfun_type5)(int, int, value_type, value_type, value_type, value_type, value_type); + + /** \brief Callback type used for functions with six arguments. */ + typedef value_type(*bulkfun_type6)(int, int, value_type, value_type, value_type, value_type, value_type, value_type); + + /** \brief Callback type used for functions with seven arguments. */ + typedef value_type(*bulkfun_type7)(int, int, value_type, value_type, value_type, value_type, value_type, value_type, value_type); + + /** \brief Callback type used for functions with eight arguments. */ + typedef value_type(*bulkfun_type8)(int, int, value_type, value_type, value_type, value_type, value_type, value_type, value_type, value_type); + + /** \brief Callback type used for functions with nine arguments. */ + typedef value_type(*bulkfun_type9)(int, int, value_type, value_type, value_type, value_type, value_type, value_type, value_type, value_type, value_type); + + /** \brief Callback type used for functions with ten arguments. */ + typedef value_type(*bulkfun_type10)(int, int, value_type, value_type, value_type, value_type, value_type, value_type, value_type, value_type, value_type, value_type); + + /** \brief Callback type with user data (not null) used for functions without arguments. */ + typedef value_type(*bulkfun_userdata_type0)(void*, int, int); + + /** \brief Callback type with user data (not null) used for functions with a single arguments. */ + typedef value_type(*bulkfun_userdata_type1)(void*, int, int, value_type); + + /** \brief Callback type with user data (not null) used for functions with two arguments. */ + typedef value_type(*bulkfun_userdata_type2)(void*, int, int, value_type, value_type); + + /** \brief Callback type with user data (not null) used for functions with three arguments. */ + typedef value_type(*bulkfun_userdata_type3)(void*, int, int, value_type, value_type, value_type); + + /** \brief Callback type with user data (not null) used for functions with four arguments. */ + typedef value_type(*bulkfun_userdata_type4)(void*, int, int, value_type, value_type, value_type, value_type); + + /** \brief Callback type with user data (not null) used for functions with five arguments. */ + typedef value_type(*bulkfun_userdata_type5)(void*, int, int, value_type, value_type, value_type, value_type, value_type); + + /** \brief Callback type with user data (not null) used for functions with six arguments. */ + typedef value_type(*bulkfun_userdata_type6)(void*, int, int, value_type, value_type, value_type, value_type, value_type, value_type); + + /** \brief Callback type with user data (not null) used for functions with seven arguments. */ + typedef value_type(*bulkfun_userdata_type7)(void*, int, int, value_type, value_type, value_type, value_type, value_type, value_type, value_type); + + /** \brief Callback type with user data (not null) used for functions with eight arguments. */ + typedef value_type(*bulkfun_userdata_type8)(void*, int, int, value_type, value_type, value_type, value_type, value_type, value_type, value_type, value_type); + + /** \brief Callback type with user data (not null) used for functions with nine arguments. */ + typedef value_type(*bulkfun_userdata_type9)(void*, int, int, value_type, value_type, value_type, value_type, value_type, value_type, value_type, value_type, value_type); + + /** \brief Callback type with user data (not null) used for functions with ten arguments. */ + typedef value_type(*bulkfun_userdata_type10)(void*, int, int, value_type, value_type, value_type, value_type, value_type, value_type, value_type, value_type, value_type, value_type); + + /** \brief Callback type used for functions with a variable argument list. */ + typedef value_type(*multfun_type)(const value_type*, int); + + /** \brief Callback type with user data (not null) used for functions and a variable argument list. */ + typedef value_type(*multfun_userdata_type)(void*, const value_type*, int); + + /** \brief Callback type used for functions taking a string as an argument. */ + typedef value_type(*strfun_type1)(const char_type*); + + /** \brief Callback type used for functions taking a string and a value as arguments. */ + typedef value_type(*strfun_type2)(const char_type*, value_type); + + /** \brief Callback type used for functions taking a string and two values as arguments. */ + typedef value_type(*strfun_type3)(const char_type*, value_type, value_type); + + /** \brief Callback type used for functions taking a string and three values as arguments. */ + typedef value_type(*strfun_type4)(const char_type*, value_type, value_type, value_type); + + /** \brief Callback type used for functions taking a string and four values as arguments. */ + typedef value_type(*strfun_type5)(const char_type*, value_type, value_type, value_type, value_type); + + /** \brief Callback type used for functions taking a string and five values as arguments. */ + typedef value_type(*strfun_type6)(const char_type*, value_type, value_type, value_type, value_type, value_type); + + /** \brief Callback type with user data (not null) used for functions taking a string as an argument. */ + typedef value_type(*strfun_userdata_type1)(void*, const char_type*); + + /** \brief Callback type with user data (not null) used for functions taking a string and a value as arguments. */ + typedef value_type(*strfun_userdata_type2)(void*, const char_type*, value_type); + + /** \brief Callback type with user data (not null) used for functions taking a string and two values as arguments. */ + typedef value_type(*strfun_userdata_type3)(void*, const char_type*, value_type, value_type); + + /** \brief Callback type with user data (not null) used for functions taking a string and a value as arguments. */ + typedef value_type(*strfun_userdata_type4)(void*, const char_type*, value_type, value_type, value_type); + + /** \brief Callback type with user data (not null) used for functions taking a string and two values as arguments. */ + typedef value_type(*strfun_userdata_type5)(void*, const char_type*, value_type, value_type, value_type, value_type); + + /** \brief Callback type with user data (not null) used for functions taking a string and five values as arguments. */ + typedef value_type(*strfun_userdata_type6)(void*, const char_type*, value_type, value_type, value_type, value_type, value_type); + + /** \brief Callback used for functions that identify values in a string. */ + typedef int (*identfun_type)(const char_type* sExpr, int* nPos, value_type* fVal); + + /** \brief Callback used for variable creation factory functions. */ + typedef value_type* (*facfun_type)(const char_type*, void*); + + static const int MaxLenExpression = 20000; + static const int MaxLenIdentifier = 100; + static const string_type ParserVersion = string_type(_T("2.3.3 (Release)")); + static const string_type ParserVersionDate = string_type(_T("20220122")); +} // end of namespace + +#if defined(_MSC_VER) + #pragma warning(pop) +#endif + +#endif + diff --git a/bundled/muparser_v2_3_3/include/muParserError.h b/bundled/muparser_v2_3_3/include/muParserError.h new file mode 100644 index 0000000000..90fbfdfaa8 --- /dev/null +++ b/bundled/muparser_v2_3_3/include/muParserError.h @@ -0,0 +1,117 @@ +/* + + _____ __ _____________ _______ ______ ___________ + / \| | \____ \__ \\_ __ \/ ___// __ \_ __ \ + | Y Y \ | / |_> > __ \| | \/\___ \\ ___/| | \/ + |__|_| /____/| __(____ /__| /____ >\___ >__| + \/ |__| \/ \/ \/ + Copyright (C) 2004 - 2021 Ingo Berg + + Redistribution and use in source and binary forms, with or without modification, are permitted + provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this list of + conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of + conditions and the following disclaimer in the documentation and/or other materials provided + with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef MU_PARSER_ERROR_H +#define MU_PARSER_ERROR_H + +#include +#include +#include +#include +#include + +#include "muParserDef.h" + +#if defined(_MSC_VER) + #pragma warning(push) + #pragma warning(disable : 4251) // ...needs to have dll-interface to be used by clients of class ... +#endif + + +/** \file + \brief This file defines the error class used by the parser. +*/ + +namespace mu +{ + /** \brief A class that handles the error messages. */ + class ParserErrorMsg final + { + public: + static const ParserErrorMsg& Instance(); + string_type operator[](unsigned a_iIdx) const; + + private: + ParserErrorMsg& operator=(const ParserErrorMsg&) = delete; + ParserErrorMsg(const ParserErrorMsg&) = delete; + ParserErrorMsg(); + + ~ParserErrorMsg() = default; + + std::vector m_vErrMsg; ///< A vector with the predefined error messages + }; + + + /** \brief Error class of the parser. + + Part of the math parser package. + */ + class API_EXPORT_CXX ParserError + { + private: + + /** \brief Replace all ocuurences of a substring with another string. */ + void ReplaceSubString(string_type& strSource, const string_type& strFind, const string_type& strReplaceWith); + void Reset(); + + public: + + ParserError(); + explicit ParserError(EErrorCodes a_iErrc); + explicit ParserError(const string_type& sMsg); + ParserError(EErrorCodes a_iErrc, const string_type& sTok, const string_type& sFormula = string_type(), int a_iPos = -1); + ParserError(EErrorCodes a_iErrc, int a_iPos, const string_type& sTok); + ParserError(const char_type* a_szMsg, int a_iPos = -1, const string_type& sTok = string_type()); + ParserError(const ParserError& a_Obj); + + ParserError& operator=(const ParserError& a_Obj); + ~ParserError(); + + void SetFormula(const string_type& a_strFormula); + const string_type& GetExpr() const; + const string_type& GetMsg() const; + int GetPos() const; + const string_type& GetToken() const; + EErrorCodes GetCode() const; + + private: + string_type m_strMsg; ///< The message string + string_type m_strFormula; ///< Formula string + string_type m_strTok; ///< Token related with the error + int m_iPos; ///< Formula position related to the error + EErrorCodes m_iErrc; ///< Error code + const ParserErrorMsg& m_ErrMsg; + }; +} // namespace mu + +#if defined(_MSC_VER) + #pragma warning(pop) +#endif + +#endif + diff --git a/bundled/muparser_v2_3_3/include/muParserFixes.h b/bundled/muparser_v2_3_3/include/muParserFixes.h new file mode 100644 index 0000000000..8042d281d3 --- /dev/null +++ b/bundled/muparser_v2_3_3/include/muParserFixes.h @@ -0,0 +1,90 @@ +/* + + _____ __ _____________ _______ ______ ___________ + / \| | \____ \__ \\_ __ \/ ___// __ \_ __ \ + | Y Y \ | / |_> > __ \| | \/\___ \\ ___/| | \/ + |__|_| /____/| __(____ /__| /____ >\___ >__| + \/ |__| \/ \/ \/ + Copyright (C) 2004 - 2021 Ingo Berg + + Redistribution and use in source and binary forms, with or without modification, are permitted + provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this list of + conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of + conditions and the following disclaimer in the documentation and/or other materials provided + with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef MU_PARSER_FIXES_H +#define MU_PARSER_FIXES_H + +/** \file + \brief This file contains compatibility fixes for some platforms. +*/ + +// +// Compatibility fixes +// + +/* From http://gcc.gnu.org/wiki/Visibility */ +/* Generic helper definitions for shared library support */ +#if defined _WIN32 || defined __CYGWIN__ + #define MUPARSER_HELPER_DLL_IMPORT __declspec(dllimport) + #define MUPARSER_HELPER_DLL_EXPORT __declspec(dllexport) + #define MUPARSER_HELPER_DLL_LOCAL +#else + #if __GNUC__ >= 4 + #define MUPARSER_HELPER_DLL_IMPORT __attribute__ ((visibility ("default"))) + #define MUPARSER_HELPER_DLL_EXPORT __attribute__ ((visibility ("default"))) + #define MUPARSER_HELPER_DLL_LOCAL __attribute__ ((visibility ("hidden"))) + #else + #define MUPARSER_HELPER_DLL_IMPORT + #define MUPARSER_HELPER_DLL_EXPORT + #define MUPARSER_HELPER_DLL_LOCAL + #endif +#endif + +/* + Now we use the generic helper definitions above to define API_EXPORT_CXX and MUPARSER_LOCAL. + API_EXPORT_CXX is used for the public API symbols. It either DLL imports or DLL exports (or does nothing for static build) + MUPARSER_LOCAL is used for non-api symbols. +*/ + +#ifndef MUPARSER_STATIC /* defined if muParser is compiled as a DLL */ + + #ifdef MUPARSERLIB_EXPORTS /* defined if we are building the muParser DLL (instead of using it) */ + #define API_EXPORT_CXX MUPARSER_HELPER_DLL_EXPORT + #else + #define API_EXPORT_CXX MUPARSER_HELPER_DLL_IMPORT + #endif /* MUPARSER_DLL_EXPORTS */ + #define MUPARSER_LOCAL MUPARSER_HELPER_DLL_LOCAL + +#else /* MUPARSER_STATIC is defined: this means muParser is a static lib. */ + + #define API_EXPORT_CXX + #define MUPARSER_LOCAL + +#endif /* !MUPARSER_STATIC */ + + +#ifdef _WIN32 + #define API_EXPORT(TYPE) API_EXPORT_CXX TYPE __cdecl +#else + #define API_EXPORT(TYPE) TYPE +#endif + + +#endif // include guard + + diff --git a/bundled/muparser_v2_3_3/include/muParserInt.h b/bundled/muparser_v2_3_3/include/muParserInt.h new file mode 100644 index 0000000000..513f0926b1 --- /dev/null +++ b/bundled/muparser_v2_3_3/include/muParserInt.h @@ -0,0 +1,143 @@ +/* + + _____ __ _____________ _______ ______ ___________ + / \| | \____ \__ \\_ __ \/ ___// __ \_ __ \ + | Y Y \ | / |_> > __ \| | \/\___ \\ ___/| | \/ + |__|_| /____/| __(____ /__| /____ >\___ >__| + \/ |__| \/ \/ \/ + Copyright (C) 2004 - 2021 Ingo Berg + + Redistribution and use in source and binary forms, with or without modification, are permitted + provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this list of + conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of + conditions and the following disclaimer in the documentation and/or other materials provided + with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef MU_PARSER_INT_H +#define MU_PARSER_INT_H + +#include "muParserBase.h" +#include + + +/** \file + \brief Definition of a parser using integer value. +*/ + + +namespace mu +{ + + /** \brief Mathematical expressions parser. + + This version of the parser handles only integer numbers. It disables the built in operators thus it is + slower than muParser. Integer values are stored in the double value_type and converted if needed. + */ + class ParserInt : public ParserBase + { + private: + static int Round(value_type v) { return (int)(v + ((v >= 0) ? 0.5 : -0.5)); }; + + static value_type Abs(value_type); + static value_type Sign(value_type); + static value_type Ite(value_type, value_type, value_type); + // !! The unary Minus is a MUST, otherwise you can't use negative signs !! + static value_type UnaryMinus(value_type); + // Functions with variable number of arguments + static value_type Sum(const value_type* a_afArg, int a_iArgc); // sum + static value_type Min(const value_type* a_afArg, int a_iArgc); // minimum + static value_type Max(const value_type* a_afArg, int a_iArgc); // maximum + // binary operator callbacks + static value_type Add(value_type v1, value_type v2); + static value_type Sub(value_type v1, value_type v2); + static value_type Mul(value_type v1, value_type v2); + static value_type Div(value_type v1, value_type v2); + static value_type Mod(value_type v1, value_type v2); + static value_type Pow(value_type v1, value_type v2); + static value_type Shr(value_type v1, value_type v2); + static value_type Shl(value_type v1, value_type v2); + static value_type LogAnd(value_type v1, value_type v2); + static value_type LogOr(value_type v1, value_type v2); + static value_type And(value_type v1, value_type v2); + static value_type Or(value_type v1, value_type v2); + static value_type Xor(value_type v1, value_type v2); + static value_type Less(value_type v1, value_type v2); + static value_type Greater(value_type v1, value_type v2); + static value_type LessEq(value_type v1, value_type v2); + static value_type GreaterEq(value_type v1, value_type v2); + static value_type Equal(value_type v1, value_type v2); + static value_type NotEqual(value_type v1, value_type v2); + static value_type Not(value_type v1); + + static int IsHexVal(const char_type* a_szExpr, int* a_iPos, value_type* a_iVal); + static int IsBinVal(const char_type* a_szExpr, int* a_iPos, value_type* a_iVal); + static int IsVal(const char_type* a_szExpr, int* a_iPos, value_type* a_iVal); + + /** \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_cDecPoint(cDecSep) + , m_cThousandsSep(cThousandsSep) + , m_nGroup(nGroup) + {} + + 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 + { + // fix for issue 4: https://code.google.com/p/muparser/issues/detail?id=4 + // courtesy of Jens Bartsch + // original code: + // return std::string(1, (char)m_nGroup); + // new code: + return std::string(1, (char)(m_cThousandsSep > 0 ? m_nGroup : CHAR_MAX)); + } + + private: + + int m_nGroup; + char_type m_cDecPoint; + char_type m_cThousandsSep; + }; + + public: + ParserInt(); + + void InitFun() override; + void InitOprt() override; + void InitConst() override; + void InitCharSets() override; + }; + +} // namespace mu + +#endif + diff --git a/bundled/muparser_v2_2_4/include/muParserStack.h b/bundled/muparser_v2_3_3/include/muParserStack.h similarity index 100% rename from bundled/muparser_v2_2_4/include/muParserStack.h rename to bundled/muparser_v2_3_3/include/muParserStack.h diff --git a/bundled/muparser_v2_3_3/include/muParserTemplateMagic.h b/bundled/muparser_v2_3_3/include/muParserTemplateMagic.h new file mode 100644 index 0000000000..77fe54935d --- /dev/null +++ b/bundled/muparser_v2_3_3/include/muParserTemplateMagic.h @@ -0,0 +1,198 @@ +/* + + _____ __ _____________ _______ ______ ___________ + / \| | \____ \__ \\_ __ \/ ___// __ \_ __ \ + | Y Y \ | / |_> > __ \| | \/\___ \\ ___/| | \/ + |__|_| /____/| __(____ /__| /____ >\___ >__| + \/ |__| \/ \/ \/ + Copyright (C) 2004 - 2021 Ingo Berg + + Redistribution and use in source and binary forms, with or without modification, are permitted + provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this list of + conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of + conditions and the following disclaimer in the documentation and/or other materials provided + with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef MU_PARSER_TEMPLATE_MAGIC_H +#define MU_PARSER_TEMPLATE_MAGIC_H + +#include +#include "muParserError.h" + + +namespace mu +{ + //----------------------------------------------------------------------------------------------- + // + // Compile time type detection + // + //----------------------------------------------------------------------------------------------- + + /** \brief A class singling out integer types at compile time using + template meta programming. + */ + template + struct TypeInfo + { + static bool IsInteger() { return false; } + }; + + template<> + struct TypeInfo + { + static bool IsInteger() { return true; } + }; + + template<> + struct TypeInfo + { + static bool IsInteger() { return true; } + }; + + template<> + struct TypeInfo + { + static bool IsInteger() { return true; } + }; + + template<> + struct TypeInfo + { + static bool IsInteger() { return true; } + }; + + template<> + struct TypeInfo + { + static bool IsInteger() { return true; } + }; + + template<> + struct TypeInfo + { + static bool IsInteger() { return true; } + }; + + template<> + struct TypeInfo + { + static bool IsInteger() { return true; } + }; + + template<> + struct TypeInfo + { + static bool IsInteger() { return true; } + }; + + + //----------------------------------------------------------------------------------------------- + // + // Standard math functions with dummy overload for integer types + // + //----------------------------------------------------------------------------------------------- + + /** \brief A template class for providing wrappers for essential math functions. + + This template is spezialized for several types in order to provide a unified interface + for parser internal math function calls regardless of the data type. + */ + template + struct MathImpl + { + static T Sin(T v) { return sin(v); } + static T Cos(T v) { return cos(v); } + static T Tan(T v) { return tan(v); } + static T ASin(T v) { return asin(v); } + static T ACos(T v) { return acos(v); } + static T ATan(T v) { return atan(v); } + static T ATan2(T v1, T v2) { return atan2(v1, v2); } + static T Sinh(T v) { return sinh(v); } + static T Cosh(T v) { return cosh(v); } + static T Tanh(T v) { return tanh(v); } + static T ASinh(T v) { return log(v + sqrt(v * v + 1)); } + static T ACosh(T v) { return log(v + sqrt(v * v - 1)); } + static T ATanh(T v) { return ((T)0.5 * log((1 + v) / (1 - v))); } + static T Log(T v) { return log(v); } + static T Log2(T v) { return log(v) / log((T)2); } // Logarithm base 2 + static T Log10(T v) { return log10(v); } // Logarithm base 10 + static T Exp(T v) { return exp(v); } + static T Abs(T v) { return (v >= 0) ? v : -v; } + static T Sqrt(T v) { return sqrt(v); } + static T Rint(T v) { return floor(v + (T)0.5); } + static T Sign(T v) { return (T)((v < 0) ? -1 : (v > 0) ? 1 : 0); } + static T Pow(T v1, T v2) { return std::pow(v1, v2); } + + static T UnaryMinus(T v) { return -v; } + static T UnaryPlus(T v) { return v; } + + static T Sum(const T *a_afArg, int a_iArgc) + { + if (!a_iArgc) + throw ParserError(_T("too few arguments for function sum.")); + + T fRes = 0; + for (int i = 0; i < a_iArgc; ++i) fRes += a_afArg[i]; + return fRes; + } + + static T Avg(const T *a_afArg, int a_iArgc) + { + if (!a_iArgc) + throw ParserError(_T("too few arguments for function avg.")); + + T fRes = 0; + for (int i = 0; i < a_iArgc; ++i) fRes += a_afArg[i]; + return fRes / (T)a_iArgc; + } + + static T Min(const T *a_afArg, int a_iArgc) + { + if (!a_iArgc) + throw ParserError(_T("too few arguments for function min.")); + + T fRes = a_afArg[0]; + for (int i = 0; i < a_iArgc; ++i) + fRes = std::min(fRes, a_afArg[i]); + + return fRes; + } + + static T Max(const T *a_afArg, int a_iArgc) + { + if (!a_iArgc) + throw ParserError(_T("too few arguments for function max.")); + + T fRes = a_afArg[0]; + for (int i = 0; i < a_iArgc; ++i) fRes = std::max(fRes, a_afArg[i]); + + return fRes; + } + + +#if defined (__GNUG__) + // Bei zu genauer definition von pi kann die Berechnung von + // sin(pi*a) mit a=1 10 x langsamer sein! + static constexpr T CONST_PI = (T)3.141592653589; +#else + static constexpr T CONST_PI = (T)3.141592653589793238462643; +#endif + + static constexpr T CONST_E = (T)2.718281828459045235360287; + }; +} + +#endif diff --git a/bundled/muparser_v2_3_3/include/muParserTest.h b/bundled/muparser_v2_3_3/include/muParserTest.h new file mode 100644 index 0000000000..22d2075f9f --- /dev/null +++ b/bundled/muparser_v2_3_3/include/muParserTest.h @@ -0,0 +1,296 @@ +/* + + _____ __ _____________ _______ ______ ___________ + / \| | \____ \__ \\_ __ \/ ___// __ \_ __ \ + | Y Y \ | / |_> > __ \| | \/\___ \\ ___/| | \/ + |__|_| /____/| __(____ /__| /____ >\___ >__| + \/ |__| \/ \/ \/ + Copyright (C) 2004 - 2021 Ingo Berg + + Redistribution and use in source and binary forms, with or without modification, are permitted + provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this list of + conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of + conditions and the following disclaimer in the documentation and/or other materials provided + with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef MU_PARSER_TEST_H +#define MU_PARSER_TEST_H + +#include +#include +#include +#include // for accumulate +#include "muParser.h" +#include "muParserInt.h" + +#if defined(_MSC_VER) + #pragma warning(push) + #pragma warning(disable : 4251) // ...needs to have dll-interface to be used by clients of class ... +#endif + +/** \file + \brief This file contains the parser test class. +*/ + +namespace mu +{ + /** \brief Namespace for test cases. */ + namespace Test + { + /** \brief Test cases for unit testing. */ + class API_EXPORT_CXX ParserTester final + { + private: + static int c_iCount; + + static value_type f0() { return 42; }; + + // Multiarg callbacks + static value_type f1of1(value_type v) { return v; }; + + static value_type f1of2(value_type v, value_type) { return v; }; + static value_type f2of2(value_type, value_type v) { return v; }; + + static value_type f1of3(value_type v, value_type, value_type) { return v; }; + static value_type f2of3(value_type, value_type v, value_type) { return v; }; + static value_type f3of3(value_type, value_type, value_type v) { return v; }; + + static value_type f1of4(value_type v, value_type, value_type, value_type) { return v; } + static value_type f2of4(value_type, value_type v, value_type, value_type) { return v; } + static value_type f3of4(value_type, value_type, value_type v, value_type) { return v; } + static value_type f4of4(value_type, value_type, value_type, value_type v) { return v; } + + static value_type f1of5(value_type v, value_type, value_type, value_type, value_type) { return v; } + static value_type f2of5(value_type, value_type v, value_type, value_type, value_type) { return v; } + static value_type f3of5(value_type, value_type, value_type v, value_type, value_type) { return v; } + static value_type f4of5(value_type, value_type, value_type, value_type v, value_type) { return v; } + static value_type f5of5(value_type, value_type, value_type, value_type, value_type v) { return v; } + + static value_type Min(value_type a_fVal1, value_type a_fVal2) { return (a_fVal1 < a_fVal2) ? a_fVal1 : a_fVal2; } + static value_type Max(value_type a_fVal1, value_type a_fVal2) { return (a_fVal1 > a_fVal2) ? a_fVal1 : a_fVal2; } + + static value_type plus2(value_type v1) { return v1 + 2; } + static value_type times3(value_type v1) { return v1 * 3; } + static value_type sqr(value_type v1) { return v1 * v1; } + static value_type sign(value_type v) { return -v; } + static value_type add(value_type v1, value_type v2) { return v1 + v2; } + static value_type land(value_type v1, value_type v2) { return (int)v1 & (int)v2; } + + + static value_type FirstArg(const value_type* a_afArg, int a_iArgc) + { + if (!a_iArgc) + throw mu::Parser::exception_type(_T("too few arguments for function FirstArg.")); + + return a_afArg[0]; + } + + static value_type LastArg(const value_type* a_afArg, int a_iArgc) + { + if (!a_iArgc) + throw mu::Parser::exception_type(_T("too few arguments for function LastArg.")); + + return a_afArg[a_iArgc - 1]; + } + + static value_type Sum(const value_type* a_afArg, int a_iArgc) + { + if (!a_iArgc) + throw mu::Parser::exception_type(_T("too few arguments for function sum.")); + + value_type fRes = 0; + for (int i = 0; i < a_iArgc; ++i) fRes += a_afArg[i]; + return fRes; + } + + static value_type Rnd(value_type v) + { + return (value_type)(1 + (v * std::rand() / (RAND_MAX + 1.0))); + } + + static value_type RndWithString(const char_type*) + { + return (value_type)(1.0 + (1000.0 * std::rand() / (RAND_MAX + 1.0))); + } + + static value_type Ping() + { + return 10; + } + + static value_type ValueOf(const char_type*) + { + return 123; + } + + static value_type StrFun1(const char_type* v1) + { + int val(0); + stringstream_type(v1) >> val; + return (value_type)val; + } + + static value_type StrFun2(const char_type* v1, value_type v2) + { + int val(0); + stringstream_type(v1) >> val; + return (value_type)(val + v2); + } + + static value_type StrFun3(const char_type* v1, value_type v2, value_type v3) + { + int val(0); + stringstream_type(v1) >> val; + return val + v2 + v3; + } + + static value_type StrFun4(const char_type* v1, value_type v2, value_type v3, value_type v4) + { + int val(0); + stringstream_type(v1) >> val; + return val + v2 + v3 + v4; + } + + static value_type StrFun5(const char_type* v1, value_type v2, value_type v3, value_type v4, value_type v5) + { + int val(0); + stringstream_type(v1) >> val; + return val + v2 + v3 + v4 + v5; + } + + static value_type StrFun6(const char_type* v1, value_type v2, value_type v3, value_type v4, value_type v5, value_type v6) + { + int val(0); + stringstream_type(v1) >> val; + return val + v2 + v3 + v4 + v5 + v6; + } + + static value_type StrToFloat(const char_type* a_szMsg) + { + value_type val(0); + stringstream_type(a_szMsg) >> val; + return val; + } + + // postfix operator callback + static value_type Mega(value_type a_fVal) + { + return a_fVal * (value_type)1e6; + } + + static value_type Micro(value_type a_fVal) + { + return a_fVal * (value_type)1e-6; + } + + static value_type Milli(value_type a_fVal) + { + return a_fVal / (value_type)1e3; + } + + // Custom value recognition + static int IsHexVal(const char_type* a_szExpr, int* a_iPos, value_type* a_fVal); + + // With user data + static value_type FunUd0(void* data) + { + return reinterpret_cast(data); + } + + static value_type FunUd1(void* data, value_type v) + { + return reinterpret_cast(data) + v; + } + + static value_type FunUd2(void* data, value_type v1, value_type v2) + { + return reinterpret_cast(data) + v1 + v2; + } + + static value_type FunUd10(void* data, value_type v1, value_type v2, value_type v3, value_type v4, value_type v5, value_type v6, value_type v7, value_type v8, value_type v9, value_type v10) + { + return reinterpret_cast(data) + v1 + v2 + v3 + v4 + v5 + v6 + v7 + v8 + v9 + v10; + } + + static value_type StrFunUd3(void* data, const char_type* v1, value_type v2, value_type v3) + { + int val(0); + stringstream_type(v1) >> val; + return reinterpret_cast(data) + val + v2 + v3; + } + + static value_type SumUd(void* data, const value_type* a_afArg, int a_iArgc) + { + if (!a_iArgc) + throw mu::Parser::exception_type(_T("too few arguments for function sum.")); + + value_type fRes = 0; + for (int i = 0; i < a_iArgc; ++i) + fRes += a_afArg[i]; + + return reinterpret_cast(data) + fRes; + } + + int TestNames(); + int TestSyntax(); + int TestMultiArg(); + int TestPostFix(); + int TestExpression(); + int TestInfixOprt(); + int TestBinOprt(); + int TestVarConst(); + int TestInterface(); + int TestException(); + int TestStrArg(); + int TestIfThenElse(); + int TestBulkMode(); + int TestOssFuzzTestCases(); + int TestOptimizer(); + + void Abort() const; + + public: + typedef int (ParserTester::* testfun_type)(); + + ParserTester(); + int Run(); + + private: + std::vector m_vTestFun; + void AddTest(testfun_type a_pFun); + + // Test Double Parser + int EqnTest(const string_type& a_str, double a_fRes, bool a_fPass); + int EqnTestWithVarChange(const string_type& a_str, double a_fRes1, double a_fVar1, double a_fRes2, double a_fVar2); + int ThrowTest(const string_type& a_str, int a_iErrc, bool a_bFail = true); + + // Test Int Parser + int EqnTestInt(const string_type& a_str, double a_fRes, bool a_fPass); + + // Test Bulkmode + int EqnTestBulk(const string_type& a_str, double a_fRes[4], bool a_fPass); + + }; + } // namespace Test +} // namespace mu + + +#if defined(_MSC_VER) + #pragma warning(pop) +#endif + +#endif + diff --git a/bundled/muparser_v2_3_3/include/muParserToken.h b/bundled/muparser_v2_3_3/include/muParserToken.h new file mode 100644 index 0000000000..7410a65e81 --- /dev/null +++ b/bundled/muparser_v2_3_3/include/muParserToken.h @@ -0,0 +1,524 @@ +/* + + _____ __ _____________ _______ ______ ___________ + / \| | \____ \__ \\_ __ \/ ___// __ \_ __ \ + | Y Y \ | / |_> > __ \| | \/\___ \\ ___/| | \/ + |__|_| /____/| __(____ /__| /____ >\___ >__| + \/ |__| \/ \/ \/ + Copyright (C) 2004 - 2021 Ingo Berg + + Redistribution and use in source and binary forms, with or without modification, are permitted + provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this list of + conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of + conditions and the following disclaimer in the documentation and/or other materials provided + with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef MU_PARSER_TOKEN_H +#define MU_PARSER_TOKEN_H + +#include +#include +#include +#include +#include +#include +#include + +#if defined(_MSC_VER) + #pragma warning(push) + #pragma warning(disable : 26812) +#endif + +#include "muParserError.h" +#include "muParserCallback.h" + +/** \file + \brief This file contains the parser token definition. +*/ + +namespace mu +{ + template struct TplCallType; + template <> struct TplCallType<0> { using fun_type = fun_type0; using fun_userdata_type = fun_userdata_type0; using bulkfun_type = bulkfun_type0; using bulkfun_userdata_type = bulkfun_userdata_type0; }; + template <> struct TplCallType<1> { using fun_type = fun_type1; using fun_userdata_type = fun_userdata_type1; using bulkfun_type = bulkfun_type1; using bulkfun_userdata_type = bulkfun_userdata_type1; using strfun_type = strfun_type1; using strfun_userdata_type = strfun_userdata_type1; }; + template <> struct TplCallType<2> { using fun_type = fun_type2; using fun_userdata_type = fun_userdata_type2; using bulkfun_type = bulkfun_type2; using bulkfun_userdata_type = bulkfun_userdata_type2; using strfun_type = strfun_type2; using strfun_userdata_type = strfun_userdata_type2; }; + template <> struct TplCallType<3> { using fun_type = fun_type3; using fun_userdata_type = fun_userdata_type3; using bulkfun_type = bulkfun_type3; using bulkfun_userdata_type = bulkfun_userdata_type3; using strfun_type = strfun_type3; using strfun_userdata_type = strfun_userdata_type3; }; + template <> struct TplCallType<4> { using fun_type = fun_type4; using fun_userdata_type = fun_userdata_type4; using bulkfun_type = bulkfun_type4; using bulkfun_userdata_type = bulkfun_userdata_type4; using strfun_type = strfun_type4; using strfun_userdata_type = strfun_userdata_type4; }; + template <> struct TplCallType<5> { using fun_type = fun_type5; using fun_userdata_type = fun_userdata_type5; using bulkfun_type = bulkfun_type5; using bulkfun_userdata_type = bulkfun_userdata_type5; using strfun_type = strfun_type5; using strfun_userdata_type = strfun_userdata_type5; }; + template <> struct TplCallType<6> { using fun_type = fun_type6; using fun_userdata_type = fun_userdata_type6; using bulkfun_type = bulkfun_type6; using bulkfun_userdata_type = bulkfun_userdata_type6; using strfun_type = strfun_type6; using strfun_userdata_type = strfun_userdata_type6; }; + template <> struct TplCallType<7> { using fun_type = fun_type7; using fun_userdata_type = fun_userdata_type7; using bulkfun_type = bulkfun_type7; using bulkfun_userdata_type = bulkfun_userdata_type7; }; + template <> struct TplCallType<8> { using fun_type = fun_type8; using fun_userdata_type = fun_userdata_type8; using bulkfun_type = bulkfun_type8; using bulkfun_userdata_type = bulkfun_userdata_type8; }; + template <> struct TplCallType<9> { using fun_type = fun_type9; using fun_userdata_type = fun_userdata_type9; using bulkfun_type = bulkfun_type9; using bulkfun_userdata_type = bulkfun_userdata_type9; }; + template <> struct TplCallType<10> { using fun_type = fun_type10; using fun_userdata_type = fun_userdata_type10; using bulkfun_type = bulkfun_type10; using bulkfun_userdata_type = bulkfun_userdata_type10; }; + + struct generic_callable_type + { + // Note: we keep generic_callable_type a pod for the purpose of layout + + erased_fun_type _pRawFun; + void* _pUserData; + + template + value_type call_fun(Args&&... args) const + { + static_assert(NbParams == sizeof...(Args), "mismatch between NbParams and Args"); + if (_pUserData == nullptr) + { + auto fun_typed_ptr = reinterpret_cast::fun_type>(_pRawFun); + return (*fun_typed_ptr)(std::forward(args)...); + } + else + { + auto fun_userdata_typed_ptr = reinterpret_cast::fun_userdata_type>(_pRawFun); + return (*fun_userdata_typed_ptr)(_pUserData, std::forward(args)...); + } + } + + template + value_type call_bulkfun(Args&&... args) const + { + static_assert(NbParams == sizeof...(Args) - 2, "mismatch between NbParams and Args"); + if (_pUserData == nullptr) { + auto bulkfun_typed_ptr = reinterpret_cast::bulkfun_type>(_pRawFun); + return (*bulkfun_typed_ptr)(std::forward(args)...); + } else { + auto bulkfun_userdata_typed_ptr = reinterpret_cast::bulkfun_userdata_type>(_pRawFun); + return (*bulkfun_userdata_typed_ptr)(_pUserData, std::forward(args)...); + } + } + + value_type call_multfun(const value_type* a_afArg, int a_iArgc) const + { + if (_pUserData == nullptr) { + auto multfun_typed_ptr = reinterpret_cast(_pRawFun); + return (*multfun_typed_ptr)(a_afArg, a_iArgc); + } else { + auto multfun_userdata_typed_ptr = reinterpret_cast(_pRawFun); + return (*multfun_userdata_typed_ptr)(_pUserData, a_afArg, a_iArgc); + } + } + + template + value_type call_strfun(Args&&... args) const + { + static_assert(NbParams == sizeof...(Args), "mismatch between NbParams and Args"); + if (_pUserData == nullptr) + { + auto strfun_typed_ptr = reinterpret_cast::strfun_type>(_pRawFun); + return (*strfun_typed_ptr)(std::forward(args)...); + } + else + { + auto strfun_userdata_typed_ptr = reinterpret_cast::strfun_userdata_type>(_pRawFun); + return (*strfun_userdata_typed_ptr)(_pUserData, std::forward(args)...); + } + } + + bool operator==(generic_callable_type other) const + { + return _pRawFun == other._pRawFun && _pUserData == other._pUserData; + } + + explicit operator bool() const + { + return _pRawFun != nullptr; + } + + bool operator==(std::nullptr_t) const + { + return _pRawFun == nullptr; + } + + bool operator!=(std::nullptr_t) const + { + return _pRawFun != nullptr; + } + }; + + static_assert(std::is_trivial::value, "generic_callable_type shall be trivial"); + static_assert(std::is_standard_layout::value, "generic_callable_type shall have standard layout"); + // C++17: static_assert(std::is_aggregate::value, "generic_callable_type shall be an aggregate"); + + /** \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: +
    +
  • value
  • +
  • variable
  • +
  • function with numerical arguments
  • +
  • functions with a string as argument
  • +
  • prefix operators
  • +
  • infix operators
  • +
  • binary operator
  • +
+ */ + template + class ParserToken final + { + private: + + ECmdCode m_iCode; ///< Type of the token; The token type is a constant of type #ECmdCode. + ETypeCode m_iType; + void* m_pTok; ///< Stores Token pointer; not applicable for all tokens + int m_iIdx; ///< An otional index to an external buffer storing the token data + TString m_strTok; ///< Token string + TString m_strVal; ///< Value for string variables + value_type m_fVal; ///< the value + std::unique_ptr m_pCallback; + + public: + + /** \brief Constructor (default). + + Sets token to an neutral state of type cmUNKNOWN. + \throw nothrow + \sa ECmdCode + */ + ParserToken() + :m_iCode(cmUNKNOWN) + , m_iType(tpVOID) + , m_pTok(0) + , m_iIdx(-1) + , m_strTok() + , m_strVal() + , m_fVal(0) + , m_pCallback() + {} + + //------------------------------------------------------------------------------ + /** \brief Create token from another one. + + Implemented by calling Assign(...) + \throw nothrow + \post m_iType==cmUNKNOWN + \sa #Assign + */ + ParserToken(const ParserToken& a_Tok) + { + Assign(a_Tok); + } + + + /** \brief Assignment operator. + + Copy token state from another token and return this. + Implemented by calling Assign(...). + \throw nothrow + */ + ParserToken& operator=(const ParserToken& a_Tok) + { + Assign(a_Tok); + return *this; + } + + + /** \brief Copy token information from argument. + + \throw nothrow + */ + void Assign(const ParserToken& a_Tok) + { + m_iCode = a_Tok.m_iCode; + m_pTok = a_Tok.m_pTok; + m_strTok = a_Tok.m_strTok; + m_iIdx = a_Tok.m_iIdx; + m_strVal = a_Tok.m_strVal; + m_iType = a_Tok.m_iType; + m_fVal = a_Tok.m_fVal; + // create new callback object if a_Tok has one + m_pCallback.reset(a_Tok.m_pCallback.get() ? a_Tok.m_pCallback->Clone() : 0); + } + + //------------------------------------------------------------------------------ + /** \brief Assign a token type. + + Token may not be of type value, variable or function. Those have separate 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 + */ + ParserToken& Set(ECmdCode a_iType, const TString& a_strTok = TString()) + { + // The following types can't be set this way, they have special Set functions + MUP_ASSERT(a_iType != cmVAR); + MUP_ASSERT(a_iType != cmVAL); + MUP_ASSERT(a_iType != cmFUNC); + + m_iCode = a_iType; + m_iType = tpVOID; + m_pTok = 0; + m_strTok = a_strTok; + m_iIdx = -1; + + return *this; + } + + //------------------------------------------------------------------------------ + /** \brief Set Callback type. */ + ParserToken& Set(const ParserCallback& a_pCallback, const TString& a_sTok) + { + MUP_ASSERT(a_pCallback.IsValid()); + + m_iCode = a_pCallback.GetCode(); + m_iType = tpVOID; + m_strTok = a_sTok; + m_pCallback.reset(new ParserCallback(a_pCallback)); + + m_pTok = 0; + m_iIdx = -1; + + return *this; + } + + //------------------------------------------------------------------------------ + /** \brief Make this token a value token. + + Member variables not necessary for value tokens will be invalidated. + \throw nothrow + */ + ParserToken& SetVal(TBase a_fVal, const TString& a_strTok = TString()) + { + m_iCode = cmVAL; + m_iType = tpDBL; + m_fVal = a_fVal; + m_strTok = a_strTok; + m_iIdx = -1; + + m_pTok = 0; + m_pCallback.reset(0); + + return *this; + } + + //------------------------------------------------------------------------------ + /** \brief make this token a variable token. + + Member variables not necessary for variable tokens will be invalidated. + \throw nothrow + */ + ParserToken& SetVar(TBase* a_pVar, const TString& a_strTok) + { + m_iCode = cmVAR; + m_iType = tpDBL; + m_strTok = a_strTok; + m_iIdx = -1; + m_pTok = (void*)a_pVar; + m_pCallback.reset(0); + return *this; + } + + //------------------------------------------------------------------------------ + /** \brief Make this token a variable token. + + Member variables not necessary for variable tokens will be invalidated. + \throw nothrow + */ + ParserToken& SetString(const TString& a_strTok, std::size_t a_iSize) + { + m_iCode = cmSTRING; + m_iType = tpSTR; + m_strTok = a_strTok; + m_iIdx = static_cast(a_iSize); + + m_pTok = 0; + m_pCallback.reset(0); + return *this; + } + + //------------------------------------------------------------------------------ + /** \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 + */ + void SetIdx(int a_iIdx) + { + if (m_iCode != cmSTRING || a_iIdx < 0) + throw ParserError(ecINTERNAL_ERROR); + + m_iIdx = a_iIdx; + } + + //------------------------------------------------------------------------------ + /** \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). + */ + int GetIdx() const + { + if (m_iIdx < 0 || m_iCode != cmSTRING) + throw ParserError(ecINTERNAL_ERROR); + + return m_iIdx; + } + + //------------------------------------------------------------------------------ + /** \brief Return the token type. + + \return #m_iType + \throw nothrow + */ + ECmdCode GetCode() const + { + if (m_pCallback.get()) + { + return m_pCallback->GetCode(); + } + else + { + return m_iCode; + } + } + + //------------------------------------------------------------------------------ + ETypeCode GetType() const + { + if (m_pCallback.get()) + { + return m_pCallback->GetType(); + } + else + { + return m_iType; + } + } + + //------------------------------------------------------------------------------ + int GetPri() const + { + if (!m_pCallback.get()) + throw ParserError(ecINTERNAL_ERROR); + + if (m_pCallback->GetCode() != cmOPRT_BIN && m_pCallback->GetCode() != cmOPRT_INFIX) + throw ParserError(ecINTERNAL_ERROR); + + return m_pCallback->GetPri(); + } + + //------------------------------------------------------------------------------ + EOprtAssociativity GetAssociativity() const + { + if (m_pCallback.get() == nullptr || m_pCallback->GetCode() != cmOPRT_BIN) + throw ParserError(ecINTERNAL_ERROR); + + return m_pCallback->GetAssociativity(); + } + + //------------------------------------------------------------------------------ + /** \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: +
    +
  • cmFUNC
  • +
  • cmSTRFUNC
  • +
  • cmPOSTOP
  • +
  • cmINFIXOP
  • +
  • cmOPRT_BIN
  • +
+ \sa ECmdCode + */ + generic_callable_type GetFuncAddr() const + { + return (m_pCallback.get()) + ? generic_callable_type{(erased_fun_type)m_pCallback->GetAddr(), + m_pCallback->GetUserData()} + : generic_callable_type{}; + } + + //------------------------------------------------------------------------------ + /** \biref Get value of the token. + + Only applicable to variable and value tokens. + \throw exception_type if token is no value/variable token. + */ + TBase GetVal() const + { + switch (m_iCode) + { + case cmVAL: return m_fVal; + case cmVAR: return *((TBase*)m_pTok); + default: throw ParserError(ecVAL_EXPECTED); + } + } + + //------------------------------------------------------------------------------ + /** \brief Get address of a variable token. + + Valid only if m_iType==CmdVar. + \throw exception_type if token is no variable token. + */ + TBase* GetVar() const + { + if (m_iCode != cmVAR) + throw ParserError(ecINTERNAL_ERROR); + + return (TBase*)m_pTok; + } + + //------------------------------------------------------------------------------ + /** \brief Return the number of function arguments. + + Valid only if m_iType==CmdFUNC. + */ + int GetArgCount() const + { + MUP_ASSERT(m_pCallback.get()); + + if (!m_pCallback->IsValid()) + throw ParserError(ecINTERNAL_ERROR); + + return m_pCallback->GetArgc(); + } + + //------------------------------------------------------------------------------ + /** \brief Return true if the token is a function token that can be optimized. + */ + bool IsOptimizable() const + { + return m_pCallback->IsValid() && m_pCallback->IsOptimizable(); + } + + //------------------------------------------------------------------------------ + /** \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 + */ + const TString& GetAsString() const + { + return m_strTok; + } + }; +} // namespace mu + +#if defined(_MSC_VER) + #pragma warning(pop) +#endif + +#endif diff --git a/bundled/muparser_v2_3_3/include/muParserTokenReader.h b/bundled/muparser_v2_3_3/include/muParserTokenReader.h new file mode 100644 index 0000000000..0ea19f533a --- /dev/null +++ b/bundled/muparser_v2_3_3/include/muParserTokenReader.h @@ -0,0 +1,160 @@ +/* + + _____ __ _____________ _______ ______ ___________ + / \| | \____ \__ \\_ __ \/ ___// __ \_ __ \ + | Y Y \ | / |_> > __ \| | \/\___ \\ ___/| | \/ + |__|_| /____/| __(____ /__| /____ >\___ >__| + \/ |__| \/ \/ \/ + Copyright (C) 2004 - 2021 Ingo Berg + + Redistribution and use in source and binary forms, with or without modification, are permitted + provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this list of + conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of + conditions and the following disclaimer in the documentation and/or other materials provided + with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef MU_PARSER_TOKEN_READER_H +#define MU_PARSER_TOKEN_READER_H + +#include +#include +#include +#include +#include +#include +#include + +#include "muParserDef.h" +#include "muParserToken.h" + +/** \file + \brief This file contains the parser token reader definition. +*/ + + +namespace mu +{ + // Forward declaration + class ParserBase; + + /** \brief Token reader for the ParserBase class. */ + class ParserTokenReader final + { + private: + + typedef ParserToken token_type; + + public: + + ParserTokenReader(ParserBase* a_pParent); + ParserTokenReader* Clone(ParserBase* a_pParent) const; + + void AddValIdent(identfun_type a_pCallback); + void SetVarCreator(facfun_type a_pFactory, void* pUserData); + void SetFormula(const string_type& a_strFormula); + void SetArgSep(char_type cArgSep); + + int GetPos() const; + const string_type& GetExpr() const; + varmap_type& GetUsedVar(); + char_type GetArgSep() const; + + void IgnoreUndefVar(bool bIgnore); + void ReInit(); + token_type ReadNextToken(); + + private: + + /** \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 + if certain tokens are identified. + */ + enum ESynCodes + { + noBO = 1 << 0, ///< to avoid i.e. "cos(7)(" + noBC = 1 << 1, ///< to avoid i.e. "sin)" or "()" + noVAL = 1 << 2, ///< to avoid i.e. "tan 2" or "sin(8)3.14" + noVAR = 1 << 3, ///< to avoid i.e. "sin a" or "sin(8)a" + noARG_SEP = 1 << 4, ///< to avoid i.e. ",," or "+," ... + noFUN = 1 << 5, ///< to avoid i.e. "sqrt cos" or "(1)sin" + noOPT = 1 << 6, ///< to avoid i.e. "(+)" + noPOSTOP = 1 << 7, ///< to avoid i.e. "(5!!)" "sin!" + noINFIXOP = 1 << 8, ///< to avoid i.e. "++4" "!!4" + noEND = 1 << 9, ///< to avoid unexpected end of formula + noSTR = 1 << 10, ///< to block numeric arguments on string functions + noASSIGN = 1 << 11, ///< to block assignment to constant i.e. "4=7" + noIF = 1 << 12, + noELSE = 1 << 13, + sfSTART_OF_LINE = noOPT | noBC | noPOSTOP | noASSIGN | noIF | noELSE | noARG_SEP, + noANY = ~0 ///< All of he above flags set + }; + + ParserTokenReader(const ParserTokenReader& a_Reader); + ParserTokenReader& operator=(const ParserTokenReader& a_Reader); + void Assign(const ParserTokenReader& a_Reader); + + void SetParent(ParserBase* a_pParent); + int ExtractToken(const char_type* a_szCharSet, string_type& a_strTok, std::size_t a_iPos) const; + int ExtractOperatorToken(string_type& a_sTok, std::size_t a_iPos) const; + + bool IsBuiltIn(token_type& a_Tok); + bool IsArgSep(token_type& a_Tok); + bool IsEOF(token_type& a_Tok); + bool IsInfixOpTok(token_type& a_Tok); + bool IsFunTok(token_type& a_Tok); + bool IsPostOpTok(token_type& a_Tok); + bool IsOprt(token_type& a_Tok); + bool IsValTok(token_type& a_Tok); + bool IsVarTok(token_type& a_Tok); + bool IsStrVarTok(token_type& a_Tok); + bool IsUndefVarTok(token_type& a_Tok); + bool IsString(token_type& a_Tok); + void Error(EErrorCodes a_iErrc, int a_iPos = -1, const string_type& a_sTok = string_type()) const; + + token_type& SaveBeforeReturn(const token_type& tok); + + ParserBase* m_pParser; + string_type m_strFormula; + int m_iPos; + int m_iSynFlags; + bool m_bIgnoreUndefVar; + + const funmap_type* m_pFunDef; + const funmap_type* m_pPostOprtDef; + const funmap_type* m_pInfixOprtDef; + const funmap_type* m_pOprtDef; + const valmap_type* m_pConstDef; + const strmap_type* m_pStrVarDef; + + varmap_type* m_pVarDef; ///< The only non const pointer to parser internals + facfun_type m_pFactory; + void* m_pFactoryData; + std::list m_vIdentFun; ///< Value token identification function + varmap_type m_UsedVar; + value_type m_fZero; ///< Dummy value of zero, referenced by undefined variables + + std::stack m_bracketStack; + + token_type m_lastTok; + char_type m_cArgSep; ///< The character used for separating function arguments + }; +} // namespace mu + +#endif + + diff --git a/bundled/muparser_v2_3_3/src/muParser.cpp b/bundled/muparser_v2_3_3/src/muParser.cpp new file mode 100644 index 0000000000..0ebef2a833 --- /dev/null +++ b/bundled/muparser_v2_3_3/src/muParser.cpp @@ -0,0 +1,233 @@ +/* + + _____ __ _____________ _______ ______ ___________ + / \| | \____ \__ \\_ __ \/ ___// __ \_ __ \ + | Y Y \ | / |_> > __ \| | \/\___ \\ ___/| | \/ + |__|_| /____/| __(____ /__| /____ >\___ >__| + \/ |__| \/ \/ \/ + Copyright (C) 2004 - 2021 Ingo Berg + + Redistribution and use in source and binary forms, with or without modification, are permitted + provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this list of + conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of + conditions and the following disclaimer in the documentation and/or other materials provided + with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "muParser.h" +#include "muParserTemplateMagic.h" + +//--- Standard includes ------------------------------------------------------------------------ +#include +#include +#include + +using namespace std; + +/** \file + \brief Implementation of the standard floating point parser. +*/ + + + +/** \brief Namespace for mathematical applications. */ +namespace mu +{ + //--------------------------------------------------------------------------- + /** \brief Default value recognition callback. + \param [in] a_szExpr Pointer to the expression + \param [in, out] a_iPos Pointer to an index storing the current position within the expression + \param [out] a_fVal Pointer where the value should be stored in case one is found. + \return 1 if a value was found 0 otherwise. + */ + int Parser::IsVal(const char_type* a_szExpr, int* a_iPos, value_type* a_fVal) + { + value_type fVal(0); + + stringstream_type stream(a_szExpr); + stream.seekg(0); // todo: check if this really is necessary + stream.imbue(Parser::s_locale); + stream >> fVal; + stringstream_type::pos_type iEnd = stream.tellg(); // Position after reading + + if (iEnd == (stringstream_type::pos_type) - 1) + return 0; + + *a_iPos += (int)iEnd; + *a_fVal = fVal; + return 1; + } + + + //--------------------------------------------------------------------------- + /** \brief Constructor. + + Call ParserBase class constructor and trigger Function, Operator and Constant initialization. + */ + Parser::Parser() + :ParserBase() + { + 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 Parser::InitCharSets() + { + DefineNameChars(_T("0123456789_abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")); + DefineOprtChars(_T("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ+-*^/?<>=#!$%&|~'_{}")); + DefineInfixOprtChars(_T("/+-*^?<>=#!$%&|~'_")); + } + + //--------------------------------------------------------------------------- + /** \brief Initialize the default functions. */ + void Parser::InitFun() + { + if (mu::TypeInfo::IsInteger()) + { + // When setting MUP_BASETYPE to an integer type + // Place functions for dealing with integer values here + // ... + // ... + // ... + } + else + { + // trigonometric functions + DefineFun(_T("sin"), MathImpl::Sin); + DefineFun(_T("cos"), MathImpl::Cos); + DefineFun(_T("tan"), MathImpl::Tan); + // arcus functions + DefineFun(_T("asin"), MathImpl::ASin); + DefineFun(_T("acos"), MathImpl::ACos); + DefineFun(_T("atan"), MathImpl::ATan); + DefineFun(_T("atan2"), MathImpl::ATan2); + // hyperbolic functions + DefineFun(_T("sinh"), MathImpl::Sinh); + DefineFun(_T("cosh"), MathImpl::Cosh); + DefineFun(_T("tanh"), MathImpl::Tanh); + // arcus hyperbolic functions + DefineFun(_T("asinh"), MathImpl::ASinh); + DefineFun(_T("acosh"), MathImpl::ACosh); + DefineFun(_T("atanh"), MathImpl::ATanh); + // Logarithm functions + DefineFun(_T("log2"), MathImpl::Log2); + DefineFun(_T("log10"), MathImpl::Log10); + DefineFun(_T("log"), MathImpl::Log); + DefineFun(_T("ln"), MathImpl::Log); + // misc + DefineFun(_T("exp"), MathImpl::Exp); + DefineFun(_T("sqrt"), MathImpl::Sqrt); + DefineFun(_T("sign"), MathImpl::Sign); + DefineFun(_T("rint"), MathImpl::Rint); + DefineFun(_T("abs"), MathImpl::Abs); + // Functions with variable number of arguments + DefineFun(_T("sum"), MathImpl::Sum); + DefineFun(_T("avg"), MathImpl::Avg); + DefineFun(_T("min"), MathImpl::Min); + DefineFun(_T("max"), MathImpl::Max); + } + } + + //--------------------------------------------------------------------------- + /** \brief Initialize constants. + + By default the parser recognizes two constants. Pi ("pi") and the Eulerian + number ("_e"). + */ + void Parser::InitConst() + { + DefineConst(_T("_pi"), MathImpl::CONST_PI); + DefineConst(_T("_e"), MathImpl::CONST_E); + } + + //--------------------------------------------------------------------------- + /** \brief Initialize operators. + + By default only the unary minus operator is added. + */ + void Parser::InitOprt() + { + DefineInfixOprt(_T("-"), MathImpl::UnaryMinus); + DefineInfixOprt(_T("+"), MathImpl::UnaryPlus); + } + + //--------------------------------------------------------------------------- + void Parser::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 + "_"; + + 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. + + 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). + */ + value_type Parser::Diff(value_type* a_Var, value_type a_fPos, value_type a_fEpsilon) const + { + value_type fRes(0); + value_type fBuf(*a_Var); + value_type f[4] = { 0,0,0,0 }; + value_type fEpsilon(a_fEpsilon); + + // Backwards compatible calculation of epsilon inc case the user doesn't provide + // his own epsilon + if (fEpsilon == 0) + fEpsilon = (a_fPos == 0) ? (value_type)1e-10 : (value_type)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; + } +} // namespace mu diff --git a/bundled/muparser_v2_3_3/src/muParserBase.cpp b/bundled/muparser_v2_3_3/src/muParserBase.cpp new file mode 100644 index 0000000000..a28a7fc176 --- /dev/null +++ b/bundled/muparser_v2_3_3/src/muParserBase.cpp @@ -0,0 +1,1922 @@ +/* + + _____ __ _____________ _______ ______ ___________ + / \| | \____ \__ \\_ __ \/ ___// __ \_ __ \ + | Y Y \ | / |_> > __ \| | \/\___ \\ ___/| | \/ + |__|_| /____/| __(____ /__| /____ >\___ >__| + \/ |__| \/ \/ \/ + Copyright (C) 2022 Ingo Berg + + Redistribution and use in source and binary forms, with or without modification, are permitted + provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this list of + conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of + conditions and the following disclaimer in the documentation and/or other materials provided + with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "muParserBase.h" +#include "muParserTemplateMagic.h" + +//--- Standard includes ------------------------------------------------------------------------ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef MUP_USE_OPENMP + +#include + +#endif + +#if defined(_MSC_VER) + #pragma warning(push) + #pragma warning(disable : 26812) +#endif + +using namespace std; + +/** \file + \brief This file contains the basic implementation of the muparser engine. +*/ + +namespace mu +{ + std::locale ParserBase::s_locale = std::locale(std::locale::classic(), new change_dec_sep('.')); + + bool ParserBase::g_DbgDumpCmdCode = false; + bool ParserBase::g_DbgDumpStack = false; + + //------------------------------------------------------------------------------ + /** \brief Identifiers for built in binary operators. + + When defining custom binary operators with #AddOprt(...) make sure not to choose + names conflicting with these definitions. + */ + const char_type* ParserBase::c_DefaultOprt[] = + { + _T("<="), _T(">="), _T("!="), + _T("=="), _T("<"), _T(">"), + _T("+"), _T("-"), _T("*"), + _T("/"), _T("^"), _T("&&"), + _T("||"), _T("="), _T("("), + _T(")"), _T("?"), _T(":"), 0 + }; + + const int ParserBase::s_MaxNumOpenMPThreads = 16; + + //------------------------------------------------------------------------------ + /** \brief Constructor. + \param a_szFormula the formula to interpret. + \throw ParserException if a_szFormula is nullptr. + */ + ParserBase::ParserBase() + : m_pParseFormula(&ParserBase::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_vStackBuffer() + , m_nFinalResultIdx(0) + { + InitTokenReader(); + } + + //--------------------------------------------------------------------------- + /** \brief Copy constructor. + + The parser can be safely copy constructed but the bytecode is reset during + copy construction. + */ + ParserBase::ParserBase(const ParserBase& a_Parser) + : m_pParseFormula(&ParserBase::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_pTokenReader.reset(new token_reader_type(this)); + Assign(a_Parser); + } + + //--------------------------------------------------------------------------- + ParserBase::~ParserBase() + {} + + //--------------------------------------------------------------------------- + /** \brief Assignment operator. + + Implemented by calling Assign(a_Parser). Self assignment is suppressed. + \param a_Parser Object to copy to this. + \return *this + \throw nothrow + */ + ParserBase& ParserBase::operator=(const ParserBase& a_Parser) + { + Assign(a_Parser); + return *this; + } + + //--------------------------------------------------------------------------- + /** \brief Copy state of a parser object to this. + + Clears Variables and Functions of this parser. + Copies the states of all internal variables. + Resets parse function to string parse mode. + + \param a_Parser the source object. + */ + void ParserBase::Assign(const ParserBase& a_Parser) + { + if (&a_Parser == this) + return; + + // Don't copy bytecode instead cause the parser to create new bytecode + // by resetting the parse function. + ReInit(); + + m_ConstDef = a_Parser.m_ConstDef; // Copy user define constants + m_VarDef = a_Parser.m_VarDef; // Copy user defined variables + m_bBuiltInOp = a_Parser.m_bBuiltInOp; + m_vStringBuf = a_Parser.m_vStringBuf; + m_vStackBuffer = a_Parser.m_vStackBuffer; + m_nFinalResultIdx = a_Parser.m_nFinalResultIdx; + m_StrVarDef = a_Parser.m_StrVarDef; + m_vStringVarBuf = a_Parser.m_vStringVarBuf; + m_pTokenReader.reset(a_Parser.m_pTokenReader->Clone(this)); + + // Copy function and operator callbacks + m_FunDef = a_Parser.m_FunDef; // Copy function definitions + m_PostOprtDef = a_Parser.m_PostOprtDef; // post value unary operators + m_InfixOprtDef = a_Parser.m_InfixOprtDef; // unary operators for infix notation + m_OprtDef = a_Parser.m_OprtDef; // binary operators + + m_sNameChars = a_Parser.m_sNameChars; + m_sOprtChars = a_Parser.m_sOprtChars; + m_sInfixOprtChars = a_Parser.m_sInfixOprtChars; + } + + //--------------------------------------------------------------------------- + /** \brief Set the decimal separator. + \param cDecSep Decimal separator as a character value. + \sa SetThousandsSep + + By default muparser uses the "C" locale. The decimal separator of this + locale is overwritten by the one provided here. + */ + void ParserBase::SetDecSep(char_type cDecSep) + { + char_type cThousandsSep = std::use_facet< change_dec_sep >(s_locale).thousands_sep(); + s_locale = std::locale(std::locale("C"), new change_dec_sep(cDecSep, cThousandsSep)); + } + + //--------------------------------------------------------------------------- + /** \brief Sets the thousands operator. + \param cThousandsSep The thousands separator as a character + \sa SetDecSep + + By default muparser uses the "C" locale. The thousands separator of this + locale is overwritten by the one provided here. + */ + void ParserBase::SetThousandsSep(char_type cThousandsSep) + { + char_type cDecSep = std::use_facet< change_dec_sep >(s_locale).decimal_point(); + s_locale = std::locale(std::locale("C"), new change_dec_sep(cDecSep, cThousandsSep)); + } + + //--------------------------------------------------------------------------- + /** \brief Resets the locale. + + The default locale used "." as decimal separator, no thousands separator and + "," as function argument separator. + */ + void ParserBase::ResetLocale() + { + s_locale = std::locale(std::locale("C"), new change_dec_sep('.')); + SetArgSep(','); + } + + //--------------------------------------------------------------------------- + /** \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 ParserBase::InitTokenReader() + { + m_pTokenReader.reset(new token_reader_type(this)); + } + + //--------------------------------------------------------------------------- + /** \brief Reset parser to string parsing mode and clear internal buffers. + + Clear bytecode, reset the token reader. + \throw nothrow + */ + void ParserBase::ReInit() const + { + m_pParseFormula = &ParserBase::ParseString; + m_vStringBuf.clear(); + m_vRPN.clear(); + m_pTokenReader->ReInit(); + } + + //--------------------------------------------------------------------------- + void ParserBase::OnDetectVar(string_type* /*pExpr*/, int& /*nStart*/, int& /*nEnd*/) + {} + + //--------------------------------------------------------------------------- + /** \brief Returns the bytecode of the current expression. + */ + const ParserByteCode& ParserBase::GetByteCode() const + { + return m_vRPN; + } + + //--------------------------------------------------------------------------- + /** \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. + */ + string_type ParserBase::GetVersion(EParserVersionInfo eInfo) const + { + stringstream_type ss; + + ss << ParserVersion; + + if (eInfo == pviFULL) + { + ss << _T(" (") << ParserVersionDate; + ss << std::dec << _T("; ") << sizeof(void*) * 8 << _T("BIT"); + +#ifdef _DEBUG + ss << _T("; DEBUG"); +#else + ss << _T("; RELEASE"); +#endif + +#ifdef _UNICODE + ss << _T("; UNICODE"); +#else +#ifdef _MBCS + ss << _T("; MBCS"); +#else + ss << _T("; ASCII"); +#endif +#endif + +#ifdef MUP_USE_OPENMP + ss << _T("; OPENMP"); +#endif + + ss << _T(")"); + } + + 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. Thus it's possible to parse + for hex values, binary values and floating point values. + */ + void ParserBase::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 ParserBase::SetVarFactory(facfun_type a_pFactory, void* pUserData) + { + m_pTokenReader->SetVarCreator(a_pFactory, pUserData); + } + + //--------------------------------------------------------------------------- + /** \brief Add a function or operator callback to the parser. */ + void ParserBase::AddCallback( + const string_type& a_strName, + const ParserCallback& a_Callback, + funmap_type& a_Storage, + const char_type* a_szCharSet) + { + if (!a_Callback.IsValid()) + 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 characters. + */ + void ParserBase::CheckOprt(const string_type& a_sName, + const ParserCallback& a_Callback, + const string_type& a_szCharSet) const + { + if (!a_sName.length() || + (a_sName.find_first_not_of(a_szCharSet) != string_type::npos) || + (a_sName[0] >= '0' && a_sName[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); + } + } + } + + + /** \brief Check if a name contains invalid characters. + \throw ParserException if the name contains invalid characters. + */ + void ParserBase::CheckName(const string_type& a_sName, const string_type& a_szCharSet) const + { + if (!a_sName.length() || + (a_sName.find_first_not_of(a_szCharSet) != string_type::npos) || + (a_sName[0] >= '0' && a_sName[0] <= '9')) + { + Error(ecINVALID_NAME); + } + } + + /** \brief Set the formula. + \param a_strFormula Formula as string_type + \throw ParserException in case of syntax errors. + + Triggers first time calculation thus the creation of the bytecode and + scanning of used variables. + */ + void ParserBase::SetExpr(const string_type& a_sExpr) + { + // Check locale compatibility + if (m_pTokenReader->GetArgSep() == std::use_facet >(s_locale).decimal_point()) + Error(ecLOCALE); + + // Check maximum allowed expression length. An arbitrary value small enough so i can debug expressions sent to me + if (a_sExpr.length() >= MaxLenExpression) + Error(ecEXPRESSION_TOO_LONG, 0, a_sExpr); + + m_pTokenReader->SetFormula(a_sExpr + _T(" ")); + ReInit(); + } + + //--------------------------------------------------------------------------- + /** \brief Get the default symbols used for the built in operators. + \sa c_DefaultOprt + */ + const char_type** ParserBase::GetOprtDef() const + { + return (const char_type**)(&c_DefaultOprt[0]); + } + + //--------------------------------------------------------------------------- + /** \brief Define the set of valid characters to be used in names of + functions, variables, constants. + */ + void ParserBase::DefineNameChars(const char_type* 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 ParserBase::DefineOprtChars(const char_type* a_szCharset) + { + m_sOprtChars = a_szCharset; + } + + //--------------------------------------------------------------------------- + /** \brief Define the set of valid characters to be used in names of + infix operators. + */ + void ParserBase::DefineInfixOprtChars(const char_type* a_szCharset) + { + m_sInfixOprtChars = a_szCharset; + } + + //--------------------------------------------------------------------------- + /** \brief Virtual function that defines the characters allowed in name identifiers. + \sa #ValidOprtChars, #ValidPrefixOprtChars + */ + const char_type* ParserBase::ValidNameChars() const + { + MUP_ASSERT(m_sNameChars.size()); + return m_sNameChars.c_str(); + } + + //--------------------------------------------------------------------------- + /** \brief Virtual function that defines the characters allowed in operator definitions. + \sa #ValidNameChars, #ValidPrefixOprtChars + */ + const char_type* ParserBase::ValidOprtChars() const + { + MUP_ASSERT(m_sOprtChars.size()); + return m_sOprtChars.c_str(); + } + + //--------------------------------------------------------------------------- + /** \brief Virtual function that defines the characters allowed in infix operator definitions. + \sa #ValidNameChars, #ValidOprtChars + */ + const char_type* ParserBase::ValidInfixOprtChars() const + { + MUP_ASSERT(m_sInfixOprtChars.size()); + return m_sInfixOprtChars.c_str(); + } + + //--------------------------------------------------------------------------- + /** \brief Add a user defined operator. + \post Will reset the Parser to string parsing mode. + */ + void ParserBase::DefinePostfixOprt(const string_type& a_sName, fun_type1 a_pFun, bool a_bAllowOpt) + { + if (a_sName.length() > MaxLenIdentifier) + Error(ecIDENTIFIER_TOO_LONG); + + AddCallback(a_sName, ParserCallback(a_pFun, a_bAllowOpt, prPOSTFIX, cmOPRT_POSTFIX), m_PostOprtDef, ValidOprtChars()); + } + + //--------------------------------------------------------------------------- + /** \brief Initialize user defined functions. + + Calls the virtual functions InitFun(), InitConst() and InitOprt(). + */ + void ParserBase::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 ParserBase::DefineInfixOprt(const string_type& a_sName, fun_type1 a_pFun, int a_iPrec, bool a_bAllowOpt) + { + if (a_sName.length() > MaxLenIdentifier) + Error(ecIDENTIFIER_TOO_LONG); + + AddCallback(a_sName, ParserCallback(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 ParserBase::DefineOprt(const string_type& a_sName, fun_type2 a_pFun, unsigned a_iPrec, EOprtAssociativity a_eAssociativity, bool a_bAllowOpt) + { + if (a_sName.length() > MaxLenIdentifier) + Error(ecIDENTIFIER_TOO_LONG); + + // Check for conflicts with built in operator names + for (int i = 0; m_bBuiltInOp && i < cmENDIF; ++i) + { + if (a_sName == string_type(c_DefaultOprt[i])) + { + Error(ecBUILTIN_OVERLOAD, -1, a_sName); + } + } + + AddCallback(a_sName, ParserCallback(a_pFun, a_bAllowOpt, a_iPrec, a_eAssociativity), m_OprtDef, ValidOprtChars()); + } + + //--------------------------------------------------------------------------- + /** \brief Define a new string constant. + \param [in] a_strName The name of the constant. + \param [in] a_strVal the value of the constant. + */ + void ParserBase::DefineStrConst(const string_type& a_strName, const string_type& a_strVal) + { + // Test if a constant with that names already exists + if (m_StrVarDef.find(a_strName) != m_StrVarDef.end()) + Error(ecNAME_CONFLICT); + + CheckName(a_strName, ValidNameChars()); + + m_vStringVarBuf.push_back(a_strVal); // Store variable string in internal buffer + m_StrVarDef[a_strName] = m_vStringVarBuf.size() - 1; // bind buffer index to variable name + + ReInit(); + } + + //--------------------------------------------------------------------------- + /** \brief Add a user defined variable. + \param [in] a_sName the variable name + \param [in] a_pVar A pointer to the variable value. + \post Will reset the Parser to string parsing mode. + \throw ParserException in case the name contains invalid signs or a_pVar is nullptr. + */ + void ParserBase::DefineVar(const string_type& a_sName, value_type* a_pVar) + { + if (a_pVar == 0) + Error(ecINVALID_VAR_PTR); + + if (a_sName.length() > MaxLenIdentifier) + Error(ecIDENTIFIER_TOO_LONG); + + // 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 ParserBase::DefineConst(const string_type& a_sName, value_type a_fVal) + { + if (a_sName.length() > MaxLenIdentifier) + Error(ecIDENTIFIER_TOO_LONG); + + 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 ParserBase::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: + throw exception_type(ecINTERNAL_ERROR, 5, _T("")); + } + } + + //--------------------------------------------------------------------------- + /** \brief Get operator priority. + \throw ParserException if a_Oprt is no operator code + */ + EOprtAssociativity ParserBase::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& ParserBase::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 = &ParserBase::ParseString; + m_pTokenReader->IgnoreUndefVar(false); + } + catch (exception_type& /*e*/) + { + // Make sure to stay in string parse mode, don't call ReInit() + // because it deletes the array with the used variables + m_pParseFormula = &ParserBase::ParseString; + m_pTokenReader->IgnoreUndefVar(false); + throw; + } + + return m_pTokenReader->GetUsedVar(); + } + + //--------------------------------------------------------------------------- + /** \brief Return a map containing the used variables only. */ + const varmap_type& ParserBase::GetVar() const + { + return m_VarDef; + } + + //--------------------------------------------------------------------------- + /** \brief Return a map containing all parser constants. */ + const valmap_type& ParserBase::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& ParserBase::GetFunDef() const + { + return m_FunDef; + } + + //--------------------------------------------------------------------------- + /** \brief Retrieve the formula. */ + const string_type& ParserBase::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 + */ + ParserBase::token_type ParserBase::ApplyStrFunc( + const token_type& a_FunTok, + const std::vector& a_vArg) const + { + if (a_vArg.back().GetCode() != cmSTRING) + Error(ecSTRING_EXPECTED, m_pTokenReader->GetPos(), a_FunTok.GetAsString()); + + token_type valTok; + generic_callable_type pFunc = a_FunTok.GetFuncAddr(); + MUP_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; + case 3: valTok.SetVal(1); a_vArg[3].GetAsString(); a_vArg[2].GetVal(); a_vArg[1].GetVal(); a_vArg[0].GetVal(); break; + case 4: valTok.SetVal(1); a_vArg[4].GetAsString(); a_vArg[3].GetVal(); a_vArg[2].GetVal(); a_vArg[1].GetVal(); a_vArg[0].GetVal(); break; + case 5: valTok.SetVal(1); a_vArg[5].GetAsString(); a_vArg[4].GetVal(); a_vArg[3].GetVal(); a_vArg[2].GetVal(); a_vArg[1].GetVal(); a_vArg[0].GetVal(); break; + default: Error(ecINTERNAL_ERROR); + } + } + catch (ParserError&) + { + 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 match function requirements. + */ + void ParserBase::ApplyFunc(std::stack& a_stOpt, std::stack& a_stVal, int a_iArgCount) const + { + MUP_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.top(); + a_stOpt.pop(); + MUP_ASSERT(funTok.GetFuncAddr() != nullptr); + + // 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); + + // That's 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 && iArgCount < iArgRequired) + Error(ecTOO_FEW_PARAMS, m_pTokenReader->GetPos() - 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 + std::vector stArg; + for (int i = 0; i < iArgNumerical; ++i) + { + if (a_stVal.empty()) + Error(ecINTERNAL_ERROR, m_pTokenReader->GetPos(), funTok.GetAsString()); + + stArg.push_back(a_stVal.top()); + a_stVal.pop(); + + if (stArg.back().GetType() == tpSTR && funTok.GetType() != tpSTR) + Error(ecVAL_EXPECTED, m_pTokenReader->GetPos(), funTok.GetAsString()); + } + + switch (funTok.GetCode()) + { + case cmFUNC_STR: + if (a_stVal.empty()) + Error(ecINTERNAL_ERROR, m_pTokenReader->GetPos(), funTok.GetAsString()); + + stArg.push_back(a_stVal.top()); + 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, funTok.IsOptimizable()); + break; + default: + break; + } + + // Push dummy value representing the function result to the stack + token_type token; + token.SetVal(1); + a_stVal.push(token); + } + + //--------------------------------------------------------------------------- + void ParserBase::ApplyIfElse(std::stack& a_stOpt, std::stack& a_stVal) const + { + // Check if there is an if Else clause to be calculated + while (a_stOpt.size() && a_stOpt.top().GetCode() == cmELSE) + { + MUP_ASSERT(!a_stOpt.empty()) + token_type opElse = a_stOpt.top(); + a_stOpt.pop(); + + // Take the value associated with the else branch from the value stack + MUP_ASSERT(!a_stVal.empty()); + token_type vVal2 = a_stVal.top(); + if (vVal2.GetType() != tpDBL) + Error(ecUNEXPECTED_STR, m_pTokenReader->GetPos()); + + a_stVal.pop(); + + // it then else is a ternary operator Pop all three values from the value s + // tack and just return the right value + MUP_ASSERT(!a_stVal.empty()); + token_type vVal1 = a_stVal.top(); + if (vVal1.GetType() != tpDBL) + Error(ecUNEXPECTED_STR, m_pTokenReader->GetPos()); + + a_stVal.pop(); + + MUP_ASSERT(!a_stVal.empty()); + token_type vExpr = a_stVal.top(); + a_stVal.pop(); + + a_stVal.push((vExpr.GetVal() != 0) ? vVal1 : vVal2); + + token_type opIf = a_stOpt.top(); + a_stOpt.pop(); + + MUP_ASSERT(opElse.GetCode() == cmELSE); + + if (opIf.GetCode() != cmIF) + Error(ecMISPLACED_COLON, m_pTokenReader->GetPos()); + + 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 ParserBase::ApplyBinOprt(std::stack& a_stOpt, std::stack& a_stVal) const + { + // is it a user defined binary operator? + if (a_stOpt.top().GetCode() == cmOPRT_BIN) + { + ApplyFunc(a_stOpt, a_stVal, 2); + } + else + { + if (a_stVal.size() < 2) + Error(ecINTERNAL_ERROR, m_pTokenReader->GetPos(), _T("ApplyBinOprt: not enough values in value stack!")); + + token_type valTok1 = a_stVal.top(); + a_stVal.pop(); + + token_type valTok2 = a_stVal.top(); + a_stVal.pop(); + + token_type optTok = a_stOpt.top(); + a_stOpt.pop(); + + token_type 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, _T("=")); + + 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 ParserBase::ApplyRemainingOprt(std::stack& stOpt, std::stack& 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. + */ + value_type ParserBase::ParseCmdCode() const + { + return ParseCmdCodeBulk(0, 0); + } + + value_type ParserBase::ParseCmdCodeShort() const + { + const SToken *const tok = m_vRPN.GetBase(); + value_type buf; + + switch (tok->Cmd) + { + case cmVAL: + return tok->Val.data2; + + case cmVAR: + return *tok->Val.ptr; + + case cmVARMUL: + return *tok->Val.ptr * tok->Val.data + tok->Val.data2; + + case cmVARPOW2: + buf = *(tok->Val.ptr); + return buf * buf; + + case cmVARPOW3: + buf = *(tok->Val.ptr); + return buf * buf * buf; + + case cmVARPOW4: + buf = *(tok->Val.ptr); + return buf * buf * buf * buf; + + // numerical function without any argument + case cmFUNC: + return tok->Fun.cb.call_fun<0>(); + + // String function without a numerical argument + case cmFUNC_STR: + return tok->Fun.cb.call_strfun<1>(m_vStringBuf[0].c_str()); + + default: + throw ParserError(ecINTERNAL_ERROR); + } + } + + //--------------------------------------------------------------------------- + /** \brief Evaluate the RPN. + \param nOffset The offset added to variable addresses (for bulk mode) + \param nThreadID OpenMP Thread id of the calling thread + */ + value_type ParserBase::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. + value_type *stack = ((nOffset == 0) && (nThreadID == 0)) ? &m_vStackBuffer[0] : &m_vStackBuffer[nThreadID * (m_vStackBuffer.size() / s_MaxNumOpenMPThreads)]; + value_type 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; + stack[sidx] /= stack[1 + sidx]; + continue; + + case cmPOW: + --sidx; stack[sidx] = MathImpl::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: + // Bugfix for Bulkmode: + // for details see: + // https://groups.google.com/forum/embed/?place=forum/muparser-dev&showsearch=true&showpopout=true&showtabs=false&parenturl=http://muparser.beltoforion.de/mup_forum.html&afterlogin&pli=1#!topic/muparser-dev/szgatgoHTws + --sidx; + stack[sidx] = *(pTok->Oprt.ptr + nOffset) = stack[sidx + 1]; + continue; + // original code: + //--sidx; Stack[sidx] = *pTok->Oprt.ptr = Stack[sidx+1]; continue; + + case cmIF: + if (stack[sidx--] == 0) + { + MUP_ASSERT(sidx >= 0); + pTok += pTok->Oprt.offset; + } + continue; + + case cmELSE: + pTok += pTok->Oprt.offset; + continue; + + case cmENDIF: + continue; + + // value and variable tokens + case cmVAR: stack[++sidx] = *(pTok->Val.ptr + nOffset); continue; + case cmVAL: stack[++sidx] = pTok->Val.data2; continue; + + case cmVARPOW2: buf = *(pTok->Val.ptr + nOffset); + stack[++sidx] = buf * buf; + continue; + + case cmVARPOW3: buf = *(pTok->Val.ptr + nOffset); + stack[++sidx] = buf * buf * buf; + continue; + + case cmVARPOW4: buf = *(pTok->Val.ptr + nOffset); + stack[++sidx] = buf * buf * buf * buf; + continue; + + case cmVARMUL: + stack[++sidx] = *(pTok->Val.ptr + nOffset) * pTok->Val.data + pTok->Val.data2; + continue; + + // Next is treatment of numeric functions + case cmFUNC: + { + int iArgCount = pTok->Fun.argc; + + // switch according to argument count + switch (iArgCount) + { + case 0: sidx += 1; stack[sidx] = pTok->Fun.cb.call_fun<0 >(); continue; + case 1: stack[sidx] = pTok->Fun.cb.call_fun<1 >(stack[sidx]); continue; + case 2: sidx -= 1; stack[sidx] = pTok->Fun.cb.call_fun<2 >(stack[sidx], stack[sidx + 1]); continue; + case 3: sidx -= 2; stack[sidx] = pTok->Fun.cb.call_fun<3 >(stack[sidx], stack[sidx + 1], stack[sidx + 2]); continue; + case 4: sidx -= 3; stack[sidx] = pTok->Fun.cb.call_fun<4 >(stack[sidx], stack[sidx + 1], stack[sidx + 2], stack[sidx + 3]); continue; + case 5: sidx -= 4; stack[sidx] = pTok->Fun.cb.call_fun<5 >(stack[sidx], stack[sidx + 1], stack[sidx + 2], stack[sidx + 3], stack[sidx + 4]); continue; + case 6: sidx -= 5; stack[sidx] = pTok->Fun.cb.call_fun<6 >(stack[sidx], stack[sidx + 1], stack[sidx + 2], stack[sidx + 3], stack[sidx + 4], stack[sidx + 5]); continue; + case 7: sidx -= 6; stack[sidx] = pTok->Fun.cb.call_fun<7 >(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] = pTok->Fun.cb.call_fun<8 >(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] = pTok->Fun.cb.call_fun<9 >(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] = pTok->Fun.cb.call_fun<10>(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: + // function with variable arguments store the number as a negative value + if (iArgCount > 0) + Error(ecINTERNAL_ERROR, -1); + + sidx -= -iArgCount - 1; + + // From oss-fuzz. Happend when Multiarg functions and if-then-else are used incorrectly. + // Expressions where this was observed: + // sum(0?1,2,3,4,5:6) -> fixed + // avg(0>3?4:(""),0^3?4:("")) + // + // The final result normally lieas at position 1. If sixd is smaller there is something wrong. + if (sidx <= 0) + Error(ecINTERNAL_ERROR, -1); + // + + stack[sidx] = pTok->Fun.cb.call_multfun(&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; + if (iIdxStack < 0 || iIdxStack >= (int)m_vStringBuf.size()) + Error(ecINTERNAL_ERROR, m_pTokenReader->GetPos()); + + switch (pTok->Fun.argc) // switch according to argument count + { + case 0: stack[sidx] = pTok->Fun.cb.call_strfun<1>(m_vStringBuf[iIdxStack].c_str()); continue; + case 1: stack[sidx] = pTok->Fun.cb.call_strfun<2>(m_vStringBuf[iIdxStack].c_str(), stack[sidx]); continue; + case 2: stack[sidx] = pTok->Fun.cb.call_strfun<3>(m_vStringBuf[iIdxStack].c_str(), stack[sidx], stack[sidx + 1]); continue; + case 3: stack[sidx] = pTok->Fun.cb.call_strfun<4>(m_vStringBuf[iIdxStack].c_str(), stack[sidx], stack[sidx + 1], stack[sidx + 2]); continue; + case 4: stack[sidx] = pTok->Fun.cb.call_strfun<5>(m_vStringBuf[iIdxStack].c_str(), stack[sidx], stack[sidx + 1], stack[sidx + 2], stack[sidx + 3]); continue; + case 5: stack[sidx] = pTok->Fun.cb.call_strfun<6>(m_vStringBuf[iIdxStack].c_str(), stack[sidx], stack[sidx + 1], stack[sidx + 2], stack[sidx + 3], stack[sidx + 4]); continue; + } + + continue; + } + + case cmFUNC_BULK: + { + int iArgCount = pTok->Fun.argc; + + // switch according to argument count + switch (iArgCount) + { + case 0: sidx += 1; stack[sidx] = pTok->Fun.cb.call_bulkfun<0 >(nOffset, nThreadID); continue; + case 1: stack[sidx] = pTok->Fun.cb.call_bulkfun<1 >(nOffset, nThreadID, stack[sidx]); continue; + case 2: sidx -= 1; stack[sidx] = pTok->Fun.cb.call_bulkfun<2 >(nOffset, nThreadID, stack[sidx], stack[sidx + 1]); continue; + case 3: sidx -= 2; stack[sidx] = pTok->Fun.cb.call_bulkfun<3 >(nOffset, nThreadID, stack[sidx], stack[sidx + 1], stack[sidx + 2]); continue; + case 4: sidx -= 3; stack[sidx] = pTok->Fun.cb.call_bulkfun<4 >(nOffset, nThreadID, stack[sidx], stack[sidx + 1], stack[sidx + 2], stack[sidx + 3]); continue; + case 5: sidx -= 4; stack[sidx] = pTok->Fun.cb.call_bulkfun<5 >(nOffset, nThreadID, stack[sidx], stack[sidx + 1], stack[sidx + 2], stack[sidx + 3], stack[sidx + 4]); continue; + case 6: sidx -= 5; stack[sidx] = pTok->Fun.cb.call_bulkfun<6 >(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] = pTok->Fun.cb.call_bulkfun<7 >(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] = pTok->Fun.cb.call_bulkfun<8 >(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] = pTok->Fun.cb.call_bulkfun<9 >(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] = pTok->Fun.cb.call_bulkfun<10>(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: + throw exception_type(ecINTERNAL_ERROR, 2, _T("")); + } + } + + default: + throw exception_type(ecINTERNAL_ERROR, 3, _T("")); + } // switch CmdCode + } // for all bytecode tokens + + return stack[m_nFinalResultIdx]; + } + + //--------------------------------------------------------------------------- + void ParserBase::CreateRPN() const + { + if (!m_pTokenReader->GetExpr().length()) + Error(ecUNEXPECTED_EOF, 0); + + std::stack stOpt, stVal; + std::stack stArgCount; + token_type opta, opt; // for storing operators + token_type val, tval; // for storing value + int ifElseCounter = 0; + + ReInit(); + + // The outermost counter counts the number of separated 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: + if (stOpt.empty()) + Error(ecSTR_RESULT, m_pTokenReader->GetPos(), opt.GetAsString()); + + 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: + if (stArgCount.empty()) + Error(ecMISPLACED_COLON, m_pTokenReader->GetPos()); + + if (stArgCount.top() > 1) + Error(ecUNEXPECTED_ARG_SEP, m_pTokenReader->GetPos()); + + stArgCount.pop(); + + ifElseCounter--; + if (ifElseCounter < 0) + Error(ecMISPLACED_COLON, m_pTokenReader->GetPos()); + + ApplyRemainingOprt(stOpt, stVal); + m_vRPN.AddIfElse(cmELSE); + stOpt.push(opt); + break; + + case cmARG_SEP: + if (!stOpt.empty() && stOpt.top().GetCode() == cmIF) + Error(ecUNEXPECTED_ARG_SEP, m_pTokenReader->GetPos()); + + if (stArgCount.empty()) + Error(ecUNEXPECTED_ARG_SEP, m_pTokenReader->GetPos()); + + ++stArgCount.top(); + // Falls through. + // 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 + MUP_ASSERT(stArgCount.size()); + int iArgCount = stArgCount.top(); + 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 cmIF: + ifElseCounter++; + stArgCount.push(1); + // Falls through. + // 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 implicitly 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 (ParserBase::g_DbgDumpStack) + { + StackDump(stVal, stOpt); + m_vRPN.AsciiDump(); + } + +// if (ParserBase::g_DbgDumpCmdCode) + //m_vRPN.AsciiDump(); + } // while (true) + + if (ParserBase::g_DbgDumpCmdCode) + m_vRPN.AsciiDump(); + + if (ifElseCounter > 0) + Error(ecMISSING_ELSE_CLAUSE); + + // get the last value (= final result) from the stack + MUP_ASSERT(stArgCount.size() == 1); + m_nFinalResultIdx = stArgCount.top(); + if (m_nFinalResultIdx == 0) + Error(ecINTERNAL_ERROR, 9); + + if (stVal.size() == 0) + Error(ecEMPTY_EXPRESSION); + + // 2020-09-17; fix for https://oss-fuzz.com/testcase-detail/5758791700971520 + // I don't need the value stack any more. Destructively check if all values in the value + // stack represent floating point values + while (stVal.size()) + { + if (stVal.top().GetType() != tpDBL) + Error(ecSTR_RESULT); + + stVal.pop(); + } + + 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. + */ + value_type ParserBase::ParseString() const + { + try + { + CreateRPN(); + + if (m_vRPN.GetSize() == 2) + { + m_pParseFormula = &ParserBase::ParseCmdCodeShort; + m_vStackBuffer[1] = (this->*m_pParseFormula)(); + return m_vStackBuffer[1]; + } + else + { + m_pParseFormula = &ParserBase::ParseCmdCode; + return (this->*m_pParseFormula)(); + } + } + catch (ParserError& 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 that's the only purpose of this function. + */ + void ParserBase::Error(EErrorCodes a_iErrc, int a_iPos, const string_type& 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 ParserBase::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 ParserBase::RemoveVar(const string_type& 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 ParserBase::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 ParserBase::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 ParserBase::ClearPostfixOprt() + { + m_PostOprtDef.clear(); + ReInit(); + } + + //------------------------------------------------------------------------------ + /** \brief Clear all user defined binary operators. + \post Resets the parser to string parsing mode. + \throw nothrow + */ + void ParserBase::ClearOprt() + { + m_OprtDef.clear(); + ReInit(); + } + + //------------------------------------------------------------------------------ + /** \brief Clear the user defined Prefix operators. + \post Resets the parser to string parser mode. + \throw nothrow + */ + void ParserBase::ClearInfixOprt() + { + m_InfixOprtDef.clear(); + ReInit(); + } + + //------------------------------------------------------------------------------ + /** \brief Enable or disable the formula optimization feature. + \post Resets the parser to string parser mode. + \throw nothrow + */ + void ParserBase::EnableOptimizer(bool a_bIsOn) + { + m_vRPN.EnableOptimizer(a_bIsOn); + ReInit(); + } + + //--------------------------------------------------------------------------- + /** \brief Enable the dumping of bytecode and 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 ParserBase::EnableDebugDump(bool bDumpCmd, bool bDumpStack) + { + ParserBase::g_DbgDumpCmdCode = bDumpCmd; + ParserBase::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 ParserBase::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 ParserBase::HasBuiltInOprt() const + { + return m_bBuiltInOp; + } + + //------------------------------------------------------------------------------ + /** \brief Get the argument separator character. + */ + char_type ParserBase::GetArgSep() const + { + return m_pTokenReader->GetArgSep(); + } + + //------------------------------------------------------------------------------ + /** \brief Set argument separator. + \param cArgSep the argument separator character. + */ + void ParserBase::SetArgSep(char_type cArgSep) + { + m_pTokenReader->SetArgSep(cArgSep); + } + + //------------------------------------------------------------------------------ + /** \brief Dump stack content. + + This function is used for debugging only. + */ + void ParserBase::StackDump(const std::stack& a_stVal, const std::stack& a_stOprt) const + { + std::stack stOprt(a_stOprt); + std::stack stVal(a_stVal); + + mu::console() << _T("\nValue stack:\n"); + while (!stVal.empty()) + { + token_type val = stVal.top(); + stVal.pop(); + + if (val.GetType() == tpSTR) + mu::console() << _T(" \"") << val.GetAsString() << _T("\" "); + else + mu::console() << _T(" ") << val.GetVal() << _T(" "); + } + mu::console() << "\nOperator stack:\n"; + + while (!stOprt.empty()) + { + if (stOprt.top().GetCode() <= cmASSIGN) + { + mu::console() << _T("OPRT_INTRNL \"") + << ParserBase::c_DefaultOprt[stOprt.top().GetCode()] + << _T("\" \n"); + } + else + { + switch (stOprt.top().GetCode()) + { + case cmVAR: mu::console() << _T("VAR\n"); break; + case cmVAL: mu::console() << _T("VAL\n"); break; + case cmFUNC: + mu::console() + << _T("FUNC \"") + << stOprt.top().GetAsString() + << _T("\"\n"); + break; + + case cmFUNC_BULK: + mu::console() + << _T("FUNC_BULK \"") + << stOprt.top().GetAsString() + << _T("\"\n"); + break; + + case cmOPRT_INFIX: + mu::console() << _T("OPRT_INFIX \"") + << stOprt.top().GetAsString() + << _T("\"\n"); + break; + + case cmOPRT_BIN: + mu::console() << _T("OPRT_BIN \"") + << stOprt.top().GetAsString() + << _T("\"\n"); + break; + + case cmFUNC_STR: mu::console() << _T("FUNC_STR\n"); break; + case cmEND: mu::console() << _T("END\n"); break; + case cmUNKNOWN: mu::console() << _T("UNKNOWN\n"); break; + case cmBO: mu::console() << _T("BRACKET \"(\"\n"); break; + case cmBC: mu::console() << _T("BRACKET \")\"\n"); break; + case cmIF: mu::console() << _T("IF\n"); break; + case cmELSE: mu::console() << _T("ELSE\n"); break; + case cmENDIF: mu::console() << _T("ENDIF\n"); break; + default: mu::console() << stOprt.top().GetCode() << _T(" "); break; + } + } + stOprt.pop(); + } + + mu::console() << dec << endl; + } + + /** \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 explicitly 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. + */ + value_type ParserBase::Eval() const + { + return (this->*m_pParseFormula)(); + } + + //------------------------------------------------------------------------------ + /** \brief Evaluate an expression containing comma separated 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 retrieve all results of an expression + made up of multiple comma separated subexpressions (i.e. "x+y,sin(x),cos(y)") + */ + value_type* ParserBase::Eval(int& nStackSize) const + { + if (m_vRPN.GetSize() > 0) + { + ParseCmdCode(); + } + else + { + ParseString(); + } + + 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 separated subexpressions (i.e. "sin(y), x+y"). + There may be more than one return value. This function returns the number of + available results. + */ + int ParserBase::GetNumResults() const + { + return m_nFinalResultIdx; + } + + //--------------------------------------------------------------------------- + void ParserBase::Eval(value_type* 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 = 0; + +#ifdef DEBUG_OMP_STUFF + int ct = 0; +#endif + omp_set_num_threads(nMaxThreads); + +#pragma omp parallel for schedule(static, std::max(nBulkSize/nMaxThreads, 1)) private(nThreadID) + for (i = 0; i < nBulkSize; ++i) + { + nThreadID = omp_get_thread_num(); + results[i] = ParseCmdCodeBulk(i, nThreadID); + +#ifdef DEBUG_OMP_STUFF +#pragma omp critical + { + pThread[ct] = nThreadID; + pIdx[ct] = i; + ct++; + } +#endif + } + +#ifdef DEBUG_OMP_STUFF + FILE* pFile = fopen("bulk_dbg.txt", "w"); + for (i = 0; i < nBulkSize; ++i) + { + fprintf(pFile, "idx: %d thread: %d \n", pIdx[i], pThread[i]); + } + + delete[] pIdx; + delete[] pThread; + + fclose(pFile); +#endif + +#else + for (i = 0; i < nBulkSize; ++i) + { + results[i] = ParseCmdCodeBulk(i, 0); + } +#endif + + } +} // namespace mu + +#if defined(_MSC_VER) + #pragma warning(pop) +#endif + diff --git a/bundled/muparser_v2_3_3/src/muParserBytecode.cpp b/bundled/muparser_v2_3_3/src/muParserBytecode.cpp new file mode 100644 index 0000000000..54427b9486 --- /dev/null +++ b/bundled/muparser_v2_3_3/src/muParserBytecode.cpp @@ -0,0 +1,644 @@ +/* + + _____ __ _____________ _______ ______ ___________ + / \| | \____ \__ \\_ __ \/ ___// __ \_ __ \ + | Y Y \ | / |_> > __ \| | \/\___ \\ ___/| | \/ + |__|_| /____/| __(____ /__| /____ >\___ >__| + \/ |__| \/ \/ \/ + Copyright (C) 2004 - 2021 Ingo Berg + + Redistribution and use in source and binary forms, with or without modification, are permitted + provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this list of + conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of + conditions and the following disclaimer in the documentation and/or other materials provided + with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "muParserBytecode.h" + +#include +#include +#include +#include +#include + +#include "muParserDef.h" +#include "muParserError.h" +#include "muParserToken.h" +#include "muParserTemplateMagic.h" + +#if defined(_MSC_VER) + #pragma warning(push) + #pragma warning(disable : 26812) +#endif + + +namespace mu +{ + /** \brief Bytecode default constructor. */ + ParserByteCode::ParserByteCode() + :m_iStackPos(0) + , m_iMaxStackSize(0) + , m_vRPN() + , m_bEnableOptimizer(true) + { + m_vRPN.reserve(50); + } + + + /** \brief Copy constructor. + + Implemented in Terms of Assign(const ParserByteCode &a_ByteCode) + */ + ParserByteCode::ParserByteCode(const ParserByteCode& a_ByteCode) + { + Assign(a_ByteCode); + } + + + /** \brief Assignment operator. + + Implemented in Terms of Assign(const ParserByteCode &a_ByteCode) + */ + ParserByteCode& ParserByteCode::operator=(const ParserByteCode& a_ByteCode) + { + Assign(a_ByteCode); + return *this; + } + + + void ParserByteCode::EnableOptimizer(bool bStat) + { + m_bEnableOptimizer = bStat; + } + + + /** \brief Copy state of another object to this. + + \throw nowthrow + */ + void ParserByteCode::Assign(const ParserByteCode& 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; + m_bEnableOptimizer = a_ByteCode.m_bEnableOptimizer; + } + + + /** \brief Add a Variable pointer to bytecode. + \param a_pVar Pointer to be added. + \throw nothrow + */ + void ParserByteCode::AddVar(value_type* 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 ParserByteCode::AddVal(value_type 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 = nullptr; + tok.Val.data = 0; + tok.Val.data2 = a_fVal; + m_vRPN.push_back(tok); + } + + + void ParserByteCode::ConstantFolding(ECmdCode a_Oprt) + { + std::size_t sz = m_vRPN.size(); + value_type& x = m_vRPN[sz - 2].Val.data2; + value_type& 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: + x = x / y; + m_vRPN.pop_back(); + break; + + case cmPOW: x = MathImpl::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 ParserByteCode::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 polynomials of low order + if (m_vRPN[sz - 2].Cmd == cmVAR && m_vRPN[sz - 1].Cmd == cmVAL) + { + if (m_vRPN[sz - 1].Val.data2 == 0) + { + m_vRPN[sz - 2].Cmd = cmVAL; + m_vRPN[sz - 2].Val.ptr = nullptr; + m_vRPN[sz - 2].Val.data = 0; + m_vRPN[sz - 2].Val.data2 = 1; + } + else if (m_vRPN[sz - 1].Val.data2 == 1) + m_vRPN[sz - 2].Cmd = cmVAR; + else 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)) + { + MUP_ASSERT( + (m_vRPN[sz - 2].Val.ptr == nullptr && m_vRPN[sz - 1].Val.ptr != nullptr) || + (m_vRPN[sz - 2].Val.ptr != nullptr && m_vRPN[sz - 1].Val.ptr == nullptr) || + (m_vRPN[sz - 2].Val.ptr == m_vRPN[sz - 1].Val.ptr)); + + m_vRPN[sz - 2].Cmd = cmVARMUL; + m_vRPN[sz - 2].Val.ptr = (value_type*)((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; // multiplicand + 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 = (value_type*)((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 = (value_type*)((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; + + // no optimization for other opcodes + default: + 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 ParserByteCode::AddIfElse(ECmdCode a_Oprt) + { + SToken tok; + tok.Cmd = a_Oprt; + m_vRPN.push_back(tok); + } + + + /** \brief Add an assignment operator + + Operator entries in byte code consist of: +
    +
  • cmASSIGN code
  • +
  • the pointer of the destination variable
  • +
+ + \sa ParserToken::ECmdCode + */ + void ParserByteCode::AddAssignOp(value_type* a_pVar) + { + --m_iStackPos; + + SToken tok; + tok.Cmd = cmASSIGN; + tok.Oprt.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 ParserByteCode::AddFun(generic_callable_type a_pFun, int a_iArgc, bool isFunctionOptimizable) + { + std::size_t sz = m_vRPN.size(); + bool optimize = false; + + // only optimize functions with fixed number of more than a single arguments + if (isFunctionOptimizable && m_bEnableOptimizer && a_iArgc > 0) + { + // Unary Plus is a no-op + if (a_pFun == generic_callable_type{(erased_fun_type)&MathImpl::UnaryPlus, nullptr}) + return; + + optimize = true; + + for (int i = 0; i < std::abs(a_iArgc); ++i) + { + if (m_vRPN[sz - i - 1].Cmd != cmVAL) + { + optimize = false; + break; + } + } + } + + if (optimize) + { + value_type val = 0; + switch (a_iArgc) + { + case 1: val = a_pFun.call_fun<1>(m_vRPN[sz - 1].Val.data2); break; + case 2: val = a_pFun.call_fun<2>(m_vRPN[sz - 2].Val.data2, m_vRPN[sz - 1].Val.data2); break; + case 3: val = a_pFun.call_fun<3>(m_vRPN[sz - 3].Val.data2, m_vRPN[sz - 2].Val.data2, m_vRPN[sz - 1].Val.data2); break; + case 4: val = a_pFun.call_fun<4>(m_vRPN[sz - 4].Val.data2, m_vRPN[sz - 3].Val.data2, m_vRPN[sz - 2].Val.data2, m_vRPN[sz - 1].Val.data2); break; + case 5: val = a_pFun.call_fun<5>(m_vRPN[sz - 5].Val.data2, m_vRPN[sz - 4].Val.data2, m_vRPN[sz - 3].Val.data2, m_vRPN[sz - 2].Val.data2, m_vRPN[sz - 1].Val.data2); break; + case 6: val = a_pFun.call_fun<6>(m_vRPN[sz - 6].Val.data2, m_vRPN[sz - 5].Val.data2, m_vRPN[sz - 4].Val.data2, m_vRPN[sz - 3].Val.data2, m_vRPN[sz - 2].Val.data2, m_vRPN[sz - 1].Val.data2); break; + case 7: val = a_pFun.call_fun<7>(m_vRPN[sz - 7].Val.data2, m_vRPN[sz - 6].Val.data2, m_vRPN[sz - 5].Val.data2, m_vRPN[sz - 4].Val.data2, m_vRPN[sz - 3].Val.data2, m_vRPN[sz - 2].Val.data2, m_vRPN[sz - 1].Val.data2); break; + case 8: val = a_pFun.call_fun<8>(m_vRPN[sz - 8].Val.data2, m_vRPN[sz - 7].Val.data2, m_vRPN[sz - 6].Val.data2, m_vRPN[sz - 5].Val.data2, m_vRPN[sz - 4].Val.data2, m_vRPN[sz - 3].Val.data2, m_vRPN[sz - 2].Val.data2, m_vRPN[sz - 1].Val.data2); break; + case 9: val = a_pFun.call_fun<9>(m_vRPN[sz - 9].Val.data2, m_vRPN[sz - 8].Val.data2, m_vRPN[sz - 7].Val.data2, m_vRPN[sz - 6].Val.data2, m_vRPN[sz - 5].Val.data2, m_vRPN[sz - 4].Val.data2, m_vRPN[sz - 3].Val.data2, m_vRPN[sz - 2].Val.data2, m_vRPN[sz - 1].Val.data2); break; + case 10: val = a_pFun.call_fun<10>(m_vRPN[sz - 10].Val.data2, m_vRPN[sz - 9].Val.data2, m_vRPN[sz - 8].Val.data2, m_vRPN[sz - 7].Val.data2, m_vRPN[sz - 6].Val.data2, m_vRPN[sz - 5].Val.data2, m_vRPN[sz - 4].Val.data2, m_vRPN[sz - 3].Val.data2, m_vRPN[sz - 2].Val.data2, m_vRPN[sz - 1].Val.data2); break; + default: + // For now functions with unlimited number of arguments are not optimized + throw ParserError(ecINTERNAL_ERROR); + } + + // remove the folded values + m_vRPN.erase(m_vRPN.end() - a_iArgc, m_vRPN.end()); + + SToken tok; + tok.Cmd = cmVAL; + tok.Val.data = 0; + tok.Val.data2 = val; + tok.Val.ptr = nullptr; + m_vRPN.push_back(tok); + } + else + { + SToken tok; + tok.Cmd = cmFUNC; + tok.Fun.argc = a_iArgc; + tok.Fun.cb = a_pFun; + m_vRPN.push_back(tok); + } + + m_iStackPos = m_iStackPos - std::abs(a_iArgc) + 1; + m_iMaxStackSize = std::max(m_iMaxStackSize, (size_t)m_iStackPos); + + } + + + /** \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 ParserByteCode::AddBulkFun(generic_callable_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.cb = 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 ParserByteCode::AddStrFun(generic_callable_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.cb = 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 ParserByteCode::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 + std::stack 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.top(); + stIf.pop(); + m_vRPN[idx].Oprt.offset = i - idx; + break; + + case cmENDIF: + idx = stElse.top(); + stElse.pop(); + m_vRPN[idx].Oprt.offset = i - idx; + break; + + default: + break; + } + } + } + + + std::size_t ParserByteCode::GetMaxStackSize() const + { + return m_iMaxStackSize + 1; + } + + + /** \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 ParserByteCode::clear() + { + m_vRPN.clear(); + m_iStackPos = 0; + m_iMaxStackSize = 0; + } + + + /** \brief Dump bytecode (for debugging only!). */ + void ParserByteCode::AsciiDump() + { + if (!m_vRPN.size()) + { + mu::console() << _T("No bytecode available\n"); + return; + } + + mu::console() << _T("Number of RPN tokens:") << (int)m_vRPN.size() << _T("\n"); + for (std::size_t i = 0; i < m_vRPN.size() && m_vRPN[i].Cmd != cmEND; ++i) + { + mu::console() << std::dec << i << _T(" : \t"); + switch (m_vRPN[i].Cmd) + { + case cmVAL: mu::console() << _T("VAL \t"); + mu::console() << _T("[") << m_vRPN[i].Val.data2 << _T("]\n"); + break; + + case cmVAR: mu::console() << _T("VAR \t"); + mu::console() << _T("[ADDR: 0x") << std::hex << m_vRPN[i].Val.ptr << _T("]\n"); + break; + + case cmVARPOW2: mu::console() << _T("VARPOW2 \t"); + mu::console() << _T("[ADDR: 0x") << std::hex << m_vRPN[i].Val.ptr << _T("]\n"); + break; + + case cmVARPOW3: mu::console() << _T("VARPOW3 \t"); + mu::console() << _T("[ADDR: 0x") << std::hex << m_vRPN[i].Val.ptr << _T("]\n"); + break; + + case cmVARPOW4: mu::console() << _T("VARPOW4 \t"); + mu::console() << _T("[ADDR: 0x") << std::hex << m_vRPN[i].Val.ptr << _T("]\n"); + break; + + case cmVARMUL: mu::console() << _T("VARMUL \t"); + mu::console() << _T("[ADDR: 0x") << std::hex << m_vRPN[i].Val.ptr << _T("]"); + mu::console() << _T(" * [") << m_vRPN[i].Val.data << _T("]"); + mu::console() << _T(" + [") << m_vRPN[i].Val.data2 << _T("]\n"); + break; + + case cmFUNC: mu::console() << _T("CALL\t"); + mu::console() << _T("[ARG:") << std::dec << m_vRPN[i].Fun.argc << _T("]"); + mu::console() << _T("[ADDR: 0x") << std::hex << reinterpret_cast(m_vRPN[i].Fun.cb._pRawFun) << _T("]"); + mu::console() << _T("[USERDATA: 0x") << std::hex << reinterpret_cast(m_vRPN[i].Fun.cb._pUserData) << _T("]"); + mu::console() << _T("\n"); + break; + + case cmFUNC_STR: + mu::console() << _T("CALL STRFUNC\t"); + mu::console() << _T("[ARG:") << std::dec << m_vRPN[i].Fun.argc << _T("]"); + mu::console() << _T("[IDX:") << std::dec << m_vRPN[i].Fun.idx << _T("]"); + mu::console() << _T("[ADDR: 0x") << std::hex << reinterpret_cast(m_vRPN[i].Fun.cb._pRawFun) << _T("]"); + mu::console() << _T("[USERDATA: 0x") << std::hex << reinterpret_cast(m_vRPN[i].Fun.cb._pUserData) << _T("]"); + mu::console() << _T("\n"); + break; + + case cmLT: mu::console() << _T("LT\n"); break; + case cmGT: mu::console() << _T("GT\n"); break; + case cmLE: mu::console() << _T("LE\n"); break; + case cmGE: mu::console() << _T("GE\n"); break; + case cmEQ: mu::console() << _T("EQ\n"); break; + case cmNEQ: mu::console() << _T("NEQ\n"); break; + case cmADD: mu::console() << _T("ADD\n"); break; + case cmLAND: mu::console() << _T("&&\n"); break; + case cmLOR: mu::console() << _T("||\n"); break; + case cmSUB: mu::console() << _T("SUB\n"); break; + case cmMUL: mu::console() << _T("MUL\n"); break; + case cmDIV: mu::console() << _T("DIV\n"); break; + case cmPOW: mu::console() << _T("POW\n"); break; + + case cmIF: mu::console() << _T("IF\t"); + mu::console() << _T("[OFFSET:") << std::dec << m_vRPN[i].Oprt.offset << _T("]\n"); + break; + + case cmELSE: mu::console() << _T("ELSE\t"); + mu::console() << _T("[OFFSET:") << std::dec << m_vRPN[i].Oprt.offset << _T("]\n"); + break; + + case cmENDIF: mu::console() << _T("ENDIF\n"); break; + + case cmASSIGN: + mu::console() << _T("ASSIGN\t"); + mu::console() << _T("[ADDR: 0x") << m_vRPN[i].Oprt.ptr << _T("]\n"); + break; + + default: mu::console() << _T("(unknown code: ") << m_vRPN[i].Cmd << _T(")\n"); + break; + } // switch cmdCode + } // while bytecode + + mu::console() << _T("END") << std::endl; + } +} // namespace mu + +#if defined(_MSC_VER) + #pragma warning(pop) +#endif diff --git a/bundled/muparser_v2_3_3/src/muParserCallback.cpp b/bundled/muparser_v2_3_3/src/muParserCallback.cpp new file mode 100644 index 0000000000..8f2265b28d --- /dev/null +++ b/bundled/muparser_v2_3_3/src/muParserCallback.cpp @@ -0,0 +1,894 @@ +/* + + _____ __ _____________ _______ ______ ___________ + / \| | \____ \__ \\_ __ \/ ___// __ \_ __ \ + | Y Y \ | / |_> > __ \| | \/\___ \\ ___/| | \/ + |__|_| /____/| __(____ /__| /____ >\___ >__| + \/ |__| \/ \/ \/ + Copyright (C) 2022 Ingo Berg + + Redistribution and use in source and binary forms, with or without modification, are permitted + provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this list of + conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of + conditions and the following disclaimer in the documentation and/or other materials provided + with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "muParserCallback.h" + +#if defined(_MSC_VER) + #pragma warning(push) + #pragma warning(disable : 26812) +#endif + +/** \file + \brief Implementation of the parser callback class. +*/ + + +namespace mu +{ + static constexpr int CALLBACK_INTERNAL_VAR_ARGS = 1 << 14; + static constexpr int CALLBACK_INTERNAL_FIXED_ARGS_MASK = 0xf; + static constexpr int CALLBACK_INTERNAL_WITH_USER_DATA = 1 << 13; + + struct CbWithUserData + { + void* pFun; + void* pUserData; + }; + + + ParserCallback::ParserCallback(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) + {} + + + ParserCallback::ParserCallback(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) + {} + + + ParserCallback::ParserCallback(fun_type1 a_pFun, bool a_bAllowOpti) + : ParserCallback(a_pFun, a_bAllowOpti, -1, cmFUNC) + {} + + + /** \brief Constructor for constructing function callbacks taking two arguments. + \throw nothrow + */ + ParserCallback::ParserCallback(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 function can be optimized + \param a_iPrec The operator precedence + \param a_eOprtAsct The operators associativity + \throw nothrow + */ + ParserCallback::ParserCallback(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) + {} + + + ParserCallback::ParserCallback(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) + {} + + + ParserCallback::ParserCallback(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) + {} + + + ParserCallback::ParserCallback(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) + {} + + + ParserCallback::ParserCallback(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) + {} + + + ParserCallback::ParserCallback(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) + {} + + + ParserCallback::ParserCallback(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) + {} + + + ParserCallback::ParserCallback(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) + {} + + + ParserCallback::ParserCallback(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) + {} + + + ParserCallback::ParserCallback(fun_userdata_type0 a_pFun, void* a_pUserData, bool a_bAllowOpti) + :m_pFun(new CbWithUserData{reinterpret_cast(a_pFun), a_pUserData}) + , m_iArgc(0 | CALLBACK_INTERNAL_WITH_USER_DATA) + , m_iPri(-1) + , m_eOprtAsct(oaNONE) + , m_iCode(cmFUNC) + , m_iType(tpDBL) + , m_bAllowOpti(a_bAllowOpti) + {} + + + ParserCallback::ParserCallback(fun_userdata_type1 a_pFun, void* a_pUserData, bool a_bAllowOpti) + :m_pFun(new CbWithUserData{reinterpret_cast(a_pFun), a_pUserData}) + , m_iArgc(1 | CALLBACK_INTERNAL_WITH_USER_DATA) + , m_iPri(-1) + , m_eOprtAsct(oaNONE) + , m_iCode(cmFUNC) + , m_iType(tpDBL) + , m_bAllowOpti(a_bAllowOpti) + {} + + + ParserCallback::ParserCallback(fun_userdata_type2 a_pFun, void* a_pUserData, bool a_bAllowOpti) + :m_pFun(new CbWithUserData{reinterpret_cast(a_pFun), a_pUserData}) + , m_iArgc(2 | CALLBACK_INTERNAL_WITH_USER_DATA) + , m_iPri(-1) + , m_eOprtAsct(oaNONE) + , m_iCode(cmFUNC) + , m_iType(tpDBL) + , m_bAllowOpti(a_bAllowOpti) + {} + + + ParserCallback::ParserCallback(fun_userdata_type3 a_pFun, void* a_pUserData, bool a_bAllowOpti) + :m_pFun(new CbWithUserData{reinterpret_cast(a_pFun), a_pUserData}) + , m_iArgc(3 | CALLBACK_INTERNAL_WITH_USER_DATA) + , m_iPri(-1) + , m_eOprtAsct(oaNONE) + , m_iCode(cmFUNC) + , m_iType(tpDBL) + , m_bAllowOpti(a_bAllowOpti) + {} + + + ParserCallback::ParserCallback(fun_userdata_type4 a_pFun, void* a_pUserData, bool a_bAllowOpti) + :m_pFun(new CbWithUserData{reinterpret_cast(a_pFun), a_pUserData}) + , m_iArgc(4 | CALLBACK_INTERNAL_WITH_USER_DATA) + , m_iPri(-1) + , m_eOprtAsct(oaNONE) + , m_iCode(cmFUNC) + , m_iType(tpDBL) + , m_bAllowOpti(a_bAllowOpti) + {} + + + ParserCallback::ParserCallback(fun_userdata_type5 a_pFun, void* a_pUserData, bool a_bAllowOpti) + :m_pFun(new CbWithUserData{reinterpret_cast(a_pFun), a_pUserData}) + , m_iArgc(5 | CALLBACK_INTERNAL_WITH_USER_DATA) + , m_iPri(-1) + , m_eOprtAsct(oaNONE) + , m_iCode(cmFUNC) + , m_iType(tpDBL) + , m_bAllowOpti(a_bAllowOpti) + {} + + + ParserCallback::ParserCallback(fun_userdata_type6 a_pFun, void* a_pUserData, bool a_bAllowOpti) + :m_pFun(new CbWithUserData{reinterpret_cast(a_pFun), a_pUserData}) + , m_iArgc(6 | CALLBACK_INTERNAL_WITH_USER_DATA) + , m_iPri(-1) + , m_eOprtAsct(oaNONE) + , m_iCode(cmFUNC) + , m_iType(tpDBL) + , m_bAllowOpti(a_bAllowOpti) + {} + + + ParserCallback::ParserCallback(fun_userdata_type7 a_pFun, void* a_pUserData, bool a_bAllowOpti) + :m_pFun(new CbWithUserData{reinterpret_cast(a_pFun), a_pUserData}) + , m_iArgc(7 | CALLBACK_INTERNAL_WITH_USER_DATA) + , m_iPri(-1) + , m_eOprtAsct(oaNONE) + , m_iCode(cmFUNC) + , m_iType(tpDBL) + , m_bAllowOpti(a_bAllowOpti) + {} + + + ParserCallback::ParserCallback(fun_userdata_type8 a_pFun, void* a_pUserData, bool a_bAllowOpti) + :m_pFun(new CbWithUserData{reinterpret_cast(a_pFun), a_pUserData}) + , m_iArgc(8 | CALLBACK_INTERNAL_WITH_USER_DATA) + , m_iPri(-1) + , m_eOprtAsct(oaNONE) + , m_iCode(cmFUNC) + , m_iType(tpDBL) + , m_bAllowOpti(a_bAllowOpti) + {} + + + ParserCallback::ParserCallback(fun_userdata_type9 a_pFun, void* a_pUserData, bool a_bAllowOpti) + :m_pFun(new CbWithUserData{reinterpret_cast(a_pFun), a_pUserData}) + , m_iArgc(9 | CALLBACK_INTERNAL_WITH_USER_DATA) + , m_iPri(-1) + , m_eOprtAsct(oaNONE) + , m_iCode(cmFUNC) + , m_iType(tpDBL) + , m_bAllowOpti(a_bAllowOpti) + {} + + + ParserCallback::ParserCallback(fun_userdata_type10 a_pFun, void* a_pUserData, bool a_bAllowOpti) + :m_pFun(new CbWithUserData{reinterpret_cast(a_pFun), a_pUserData}) + , m_iArgc(10 | CALLBACK_INTERNAL_WITH_USER_DATA) + , m_iPri(-1) + , m_eOprtAsct(oaNONE) + , m_iCode(cmFUNC) + , m_iType(tpDBL) + , m_bAllowOpti(a_bAllowOpti) + {} + + + ParserCallback::ParserCallback(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) + {} + + + ParserCallback::ParserCallback(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 function callbacks taking two arguments. + \throw nothrow + */ + ParserCallback::ParserCallback(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) + {} + + + ParserCallback::ParserCallback(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) + {} + + + ParserCallback::ParserCallback(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) + {} + + + ParserCallback::ParserCallback(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) + {} + + + ParserCallback::ParserCallback(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) + {} + + + ParserCallback::ParserCallback(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) + {} + + + ParserCallback::ParserCallback(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) + {} + + + ParserCallback::ParserCallback(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) + {} + + + ParserCallback::ParserCallback(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) + {} + + + ParserCallback::ParserCallback(bulkfun_userdata_type0 a_pFun, void* a_pUserData, bool a_bAllowOpti) + :m_pFun(new CbWithUserData{reinterpret_cast(a_pFun), a_pUserData}) + , m_iArgc(0 | CALLBACK_INTERNAL_WITH_USER_DATA) + , m_iPri(-1) + , m_eOprtAsct(oaNONE) + , m_iCode(cmFUNC_BULK) + , m_iType(tpDBL) + , m_bAllowOpti(a_bAllowOpti) + {} + + + ParserCallback::ParserCallback(bulkfun_userdata_type1 a_pFun, void* a_pUserData, bool a_bAllowOpti) + :m_pFun(new CbWithUserData{reinterpret_cast(a_pFun), a_pUserData}) + , m_iArgc(1 | CALLBACK_INTERNAL_WITH_USER_DATA) + , m_iPri(-1) + , m_eOprtAsct(oaNONE) + , m_iCode(cmFUNC_BULK) + , m_iType(tpDBL) + , m_bAllowOpti(a_bAllowOpti) + {} + + + ParserCallback::ParserCallback(bulkfun_userdata_type2 a_pFun, void* a_pUserData, bool a_bAllowOpti) + :m_pFun(new CbWithUserData{reinterpret_cast(a_pFun), a_pUserData}) + , m_iArgc(2 | CALLBACK_INTERNAL_WITH_USER_DATA) + , m_iPri(-1) + , m_eOprtAsct(oaNONE) + , m_iCode(cmFUNC_BULK) + , m_iType(tpDBL) + , m_bAllowOpti(a_bAllowOpti) + {} + + + ParserCallback::ParserCallback(bulkfun_userdata_type3 a_pFun, void* a_pUserData, bool a_bAllowOpti) + :m_pFun(new CbWithUserData{reinterpret_cast(a_pFun), a_pUserData}) + , m_iArgc(3 | CALLBACK_INTERNAL_WITH_USER_DATA) + , m_iPri(-1) + , m_eOprtAsct(oaNONE) + , m_iCode(cmFUNC_BULK) + , m_iType(tpDBL) + , m_bAllowOpti(a_bAllowOpti) + {} + + + ParserCallback::ParserCallback(bulkfun_userdata_type4 a_pFun, void* a_pUserData, bool a_bAllowOpti) + :m_pFun(new CbWithUserData{reinterpret_cast(a_pFun), a_pUserData}) + , m_iArgc(4 | CALLBACK_INTERNAL_WITH_USER_DATA) + , m_iPri(-1) + , m_eOprtAsct(oaNONE) + , m_iCode(cmFUNC_BULK) + , m_iType(tpDBL) + , m_bAllowOpti(a_bAllowOpti) + {} + + + ParserCallback::ParserCallback(bulkfun_userdata_type5 a_pFun, void* a_pUserData, bool a_bAllowOpti) + :m_pFun(new CbWithUserData{reinterpret_cast(a_pFun), a_pUserData}) + , m_iArgc(5 | CALLBACK_INTERNAL_WITH_USER_DATA) + , m_iPri(-1) + , m_eOprtAsct(oaNONE) + , m_iCode(cmFUNC_BULK) + , m_iType(tpDBL) + , m_bAllowOpti(a_bAllowOpti) + {} + + + ParserCallback::ParserCallback(bulkfun_userdata_type6 a_pFun, void* a_pUserData, bool a_bAllowOpti) + :m_pFun(new CbWithUserData{reinterpret_cast(a_pFun), a_pUserData}) + , m_iArgc(6 | CALLBACK_INTERNAL_WITH_USER_DATA) + , m_iPri(-1) + , m_eOprtAsct(oaNONE) + , m_iCode(cmFUNC_BULK) + , m_iType(tpDBL) + , m_bAllowOpti(a_bAllowOpti) + {} + + + ParserCallback::ParserCallback(bulkfun_userdata_type7 a_pFun, void* a_pUserData, bool a_bAllowOpti) + :m_pFun(new CbWithUserData{reinterpret_cast(a_pFun), a_pUserData}) + , m_iArgc(7 | CALLBACK_INTERNAL_WITH_USER_DATA) + , m_iPri(-1) + , m_eOprtAsct(oaNONE) + , m_iCode(cmFUNC_BULK) + , m_iType(tpDBL) + , m_bAllowOpti(a_bAllowOpti) + {} + + + ParserCallback::ParserCallback(bulkfun_userdata_type8 a_pFun, void* a_pUserData, bool a_bAllowOpti) + :m_pFun(new CbWithUserData{reinterpret_cast(a_pFun), a_pUserData}) + , m_iArgc(8 | CALLBACK_INTERNAL_WITH_USER_DATA) + , m_iPri(-1) + , m_eOprtAsct(oaNONE) + , m_iCode(cmFUNC_BULK) + , m_iType(tpDBL) + , m_bAllowOpti(a_bAllowOpti) + {} + + + ParserCallback::ParserCallback(bulkfun_userdata_type9 a_pFun, void* a_pUserData, bool a_bAllowOpti) + :m_pFun(new CbWithUserData{reinterpret_cast(a_pFun), a_pUserData}) + , m_iArgc(9 | CALLBACK_INTERNAL_WITH_USER_DATA) + , m_iPri(-1) + , m_eOprtAsct(oaNONE) + , m_iCode(cmFUNC_BULK) + , m_iType(tpDBL) + , m_bAllowOpti(a_bAllowOpti) + {} + + + ParserCallback::ParserCallback(bulkfun_userdata_type10 a_pFun, void* a_pUserData, bool a_bAllowOpti) + :m_pFun(new CbWithUserData{reinterpret_cast(a_pFun), a_pUserData}) + , m_iArgc(10 | CALLBACK_INTERNAL_WITH_USER_DATA) + , m_iPri(-1) + , m_eOprtAsct(oaNONE) + , m_iCode(cmFUNC_BULK) + , m_iType(tpDBL) + , m_bAllowOpti(a_bAllowOpti) + {} + + + ParserCallback::ParserCallback(multfun_type a_pFun, bool a_bAllowOpti) + :m_pFun((void*)a_pFun) + , m_iArgc(CALLBACK_INTERNAL_VAR_ARGS) + , m_iPri(-1) + , m_eOprtAsct(oaNONE) + , m_iCode(cmFUNC) + , m_iType(tpDBL) + , m_bAllowOpti(a_bAllowOpti) + {} + + + ParserCallback::ParserCallback(multfun_userdata_type a_pFun, void* a_pUserData, bool a_bAllowOpti) + :m_pFun(new CbWithUserData{reinterpret_cast(a_pFun), a_pUserData}) + , m_iArgc(CALLBACK_INTERNAL_VAR_ARGS | CALLBACK_INTERNAL_WITH_USER_DATA) + , m_iPri(-1) + , m_eOprtAsct(oaNONE) + , m_iCode(cmFUNC) + , m_iType(tpDBL) + , m_bAllowOpti(a_bAllowOpti) + {} + + + ParserCallback::ParserCallback(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) + {} + + + ParserCallback::ParserCallback(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) + {} + + + ParserCallback::ParserCallback(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) + {} + + + ParserCallback::ParserCallback(strfun_type4 a_pFun, bool a_bAllowOpti) + :m_pFun((void*)a_pFun) + , m_iArgc(3) + , m_iPri(-1) + , m_eOprtAsct(oaNONE) + , m_iCode(cmFUNC_STR) + , m_iType(tpSTR) + , m_bAllowOpti(a_bAllowOpti) + {} + + + ParserCallback::ParserCallback(strfun_type5 a_pFun, bool a_bAllowOpti) + :m_pFun((void*)a_pFun) + , m_iArgc(4) + , m_iPri(-1) + , m_eOprtAsct(oaNONE) + , m_iCode(cmFUNC_STR) + , m_iType(tpSTR) + , m_bAllowOpti(a_bAllowOpti) + {} + + ParserCallback::ParserCallback(strfun_type6 a_pFun, bool a_bAllowOpti) + :m_pFun((void*)a_pFun) + , m_iArgc(5) + , m_iPri(-1) + , m_eOprtAsct(oaNONE) + , m_iCode(cmFUNC_STR) + , m_iType(tpSTR) + , m_bAllowOpti(a_bAllowOpti) + {} + + + ParserCallback::ParserCallback(strfun_userdata_type1 a_pFun, void* a_pUserData, bool a_bAllowOpti) + :m_pFun(new CbWithUserData{reinterpret_cast(a_pFun), a_pUserData}) + , m_iArgc(0 | CALLBACK_INTERNAL_WITH_USER_DATA) + , m_iPri(-1) + , m_eOprtAsct(oaNONE) + , m_iCode(cmFUNC_STR) + , m_iType(tpSTR) + , m_bAllowOpti(a_bAllowOpti) + {} + + + ParserCallback::ParserCallback(strfun_userdata_type2 a_pFun, void* a_pUserData, bool a_bAllowOpti) + :m_pFun(new CbWithUserData{reinterpret_cast(a_pFun), a_pUserData}) + , m_iArgc(1 | CALLBACK_INTERNAL_WITH_USER_DATA) + , m_iPri(-1) + , m_eOprtAsct(oaNONE) + , m_iCode(cmFUNC_STR) + , m_iType(tpSTR) + , m_bAllowOpti(a_bAllowOpti) + {} + + + ParserCallback::ParserCallback(strfun_userdata_type3 a_pFun, void* a_pUserData, bool a_bAllowOpti) + :m_pFun(new CbWithUserData{reinterpret_cast(a_pFun), a_pUserData}) + , m_iArgc(2 | CALLBACK_INTERNAL_WITH_USER_DATA) + , m_iPri(-1) + , m_eOprtAsct(oaNONE) + , m_iCode(cmFUNC_STR) + , m_iType(tpSTR) + , m_bAllowOpti(a_bAllowOpti) + {} + + + ParserCallback::ParserCallback(strfun_userdata_type4 a_pFun, void* a_pUserData, bool a_bAllowOpti) + :m_pFun(new CbWithUserData{reinterpret_cast(a_pFun), a_pUserData}) + , m_iArgc(3 | CALLBACK_INTERNAL_WITH_USER_DATA) + , m_iPri(-1) + , m_eOprtAsct(oaNONE) + , m_iCode(cmFUNC_STR) + , m_iType(tpSTR) + , m_bAllowOpti(a_bAllowOpti) + {} + + + ParserCallback::ParserCallback(strfun_userdata_type5 a_pFun, void* a_pUserData, bool a_bAllowOpti) + :m_pFun(new CbWithUserData{reinterpret_cast(a_pFun), a_pUserData}) + , m_iArgc(4 | CALLBACK_INTERNAL_WITH_USER_DATA) + , m_iPri(-1) + , m_eOprtAsct(oaNONE) + , m_iCode(cmFUNC_STR) + , m_iType(tpSTR) + , m_bAllowOpti(a_bAllowOpti) + {} + + + ParserCallback::ParserCallback(strfun_userdata_type6 a_pFun, void* a_pUserData, bool a_bAllowOpti) + :m_pFun(new CbWithUserData{ reinterpret_cast(a_pFun), a_pUserData }) + , m_iArgc(5 | CALLBACK_INTERNAL_WITH_USER_DATA) + , m_iPri(-1) + , m_eOprtAsct(oaNONE) + , m_iCode(cmFUNC_STR) + , m_iType(tpSTR) + , m_bAllowOpti(a_bAllowOpti) + {} + + /** \brief Default constructor. + \throw nothrow + */ + ParserCallback::ParserCallback() + :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 + */ + ParserCallback::ParserCallback(const ParserCallback& ref) + :ParserCallback() + { + Assign(ref); + } + + ParserCallback & ParserCallback::operator=(const ParserCallback & ref) + { + Assign(ref); + return *this; + } + + + ParserCallback::~ParserCallback() + { + if (m_iArgc & CALLBACK_INTERNAL_WITH_USER_DATA) + delete reinterpret_cast(m_pFun); + } + + + /** \brief Copy callback from argument. + + \throw nothrow + */ + void ParserCallback::Assign(const ParserCallback& ref) + { + if (this == &ref) + return; + + if (m_iArgc & CALLBACK_INTERNAL_WITH_USER_DATA) { + delete reinterpret_cast(m_pFun); + m_pFun = nullptr; + } + + if (ref.m_iArgc & CALLBACK_INTERNAL_WITH_USER_DATA) + m_pFun = new CbWithUserData(*reinterpret_cast(ref.m_pFun)); + else + 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. */ + ParserCallback* ParserCallback::Clone() const + { + return new ParserCallback(*this); + } + + + /** \brief Return tru if the function is conservative. + + Conservative functions return always the same result for the same argument. + \throw nothrow + */ + bool ParserCallback::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 + */ + void* ParserCallback::GetAddr() const + { + if (m_iArgc & CALLBACK_INTERNAL_WITH_USER_DATA) + return reinterpret_cast(m_pFun)->pFun; + else + return m_pFun; + } + + + /** \brief Get the user data if present, else nullptr + + \throw nothrow + */ + void* ParserCallback::GetUserData() const + { + if (m_iArgc & CALLBACK_INTERNAL_WITH_USER_DATA) + return reinterpret_cast(m_pFun)->pUserData; + else + return nullptr; + } + + + /** \brief Check that the callback looks valid + \throw nothrow + + Check that the function pointer is not null, + and if there are user data that they are not null. + */ + bool ParserCallback::IsValid() const + { + return GetAddr() != nullptr + && !((m_iArgc & CALLBACK_INTERNAL_WITH_USER_DATA) + && GetUserData() == nullptr); + } + + + /** \brief Return the callback code. */ + ECmdCode ParserCallback::GetCode() const + { + return m_iCode; + } + + + ETypeCode ParserCallback::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 ParserCallback::GetPri() const + { + return m_iPri; + } + + + /** \brief Return the operators associativity. + \throw nothrown + + Only valid if the callback token is a binary operator token. + */ + EOprtAssociativity ParserCallback::GetAssociativity() const + { + return m_eOprtAsct; + } + + + /** \brief Returns the number of numeric function Arguments. + + This number is negative for functions with variable number of arguments. + */ + int ParserCallback::GetArgc() const + { + return (m_iArgc & CALLBACK_INTERNAL_VAR_ARGS) ? -1 : (m_iArgc & CALLBACK_INTERNAL_FIXED_ARGS_MASK); + } +} // namespace mu + +#if defined(_MSC_VER) + #pragma warning(pop) +#endif diff --git a/bundled/muparser_v2_3_3/src/muParserError.cpp b/bundled/muparser_v2_3_3/src/muParserError.cpp new file mode 100644 index 0000000000..960e48ab4d --- /dev/null +++ b/bundled/muparser_v2_3_3/src/muParserError.cpp @@ -0,0 +1,335 @@ +/* + + _____ __ _____________ _______ ______ ___________ + / \| | \____ \__ \\_ __ \/ ___// __ \_ __ \ + | Y Y \ | / |_> > __ \| | \/\___ \\ ___/| | \/ + |__|_| /____/| __(____ /__| /____ >\___ >__| + \/ |__| \/ \/ \/ + Copyright (C) 2004 - 2021 Ingo Berg + + Redistribution and use in source and binary forms, with or without modification, are permitted + provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this list of + conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of + conditions and the following disclaimer in the documentation and/or other materials provided + with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "muParserError.h" +#include + +#if defined(_MSC_VER) + #pragma warning(push) + #pragma warning(disable : 26812) // MSVC wants to force me te use enum classes or bother me with pointless warnings +#endif + +namespace mu +{ + //------------------------------------------------------------------------------ + const ParserErrorMsg& ParserErrorMsg::Instance() + { + static const ParserErrorMsg instance; + return instance; + } + + //------------------------------------------------------------------------------ + string_type ParserErrorMsg::operator[](unsigned a_iIdx) const + { + return (a_iIdx < m_vErrMsg.size()) ? m_vErrMsg[a_iIdx] : string_type(); + } + + //--------------------------------------------------------------------------- + ParserErrorMsg::ParserErrorMsg() + :m_vErrMsg(0) + { + m_vErrMsg.resize(ecCOUNT); + + m_vErrMsg[ecUNASSIGNABLE_TOKEN] = _T("Unexpected token \"$TOK$\" found at position $POS$."); + m_vErrMsg[ecINTERNAL_ERROR] = _T("Internal error"); + m_vErrMsg[ecINVALID_NAME] = _T("Invalid function-, variable- or constant name: \"$TOK$\"."); + m_vErrMsg[ecINVALID_BINOP_IDENT] = _T("Invalid binary operator identifier: \"$TOK$\"."); + m_vErrMsg[ecINVALID_INFIX_IDENT] = _T("Invalid infix operator identifier: \"$TOK$\"."); + m_vErrMsg[ecINVALID_POSTFIX_IDENT] = _T("Invalid postfix operator identifier: \"$TOK$\"."); + m_vErrMsg[ecINVALID_FUN_PTR] = _T("Invalid pointer to callback function."); + m_vErrMsg[ecEMPTY_EXPRESSION] = _T("Expression is empty."); + m_vErrMsg[ecINVALID_VAR_PTR] = _T("Invalid pointer to variable."); + m_vErrMsg[ecUNEXPECTED_OPERATOR] = _T("Unexpected operator \"$TOK$\" found at position $POS$"); + m_vErrMsg[ecUNEXPECTED_EOF] = _T("Unexpected end of expression at position $POS$"); + m_vErrMsg[ecUNEXPECTED_ARG_SEP] = _T("Unexpected argument separator at position $POS$"); + m_vErrMsg[ecUNEXPECTED_PARENS] = _T("Unexpected parenthesis \"$TOK$\" at position $POS$"); + m_vErrMsg[ecUNEXPECTED_FUN] = _T("Unexpected function \"$TOK$\" at position $POS$"); + m_vErrMsg[ecUNEXPECTED_VAL] = _T("Unexpected value \"$TOK$\" found at position $POS$"); + m_vErrMsg[ecUNEXPECTED_VAR] = _T("Unexpected variable \"$TOK$\" found at position $POS$"); + m_vErrMsg[ecUNEXPECTED_ARG] = _T("Function arguments used without a function (position: $POS$)"); + m_vErrMsg[ecMISSING_PARENS] = _T("Missing parenthesis"); + m_vErrMsg[ecTOO_MANY_PARAMS] = _T("Too many parameters for function \"$TOK$\" at expression position $POS$"); + m_vErrMsg[ecTOO_FEW_PARAMS] = _T("Too few parameters for function \"$TOK$\" at expression position $POS$"); + m_vErrMsg[ecDIV_BY_ZERO] = _T("Divide by zero"); + m_vErrMsg[ecDOMAIN_ERROR] = _T("Domain error"); + m_vErrMsg[ecNAME_CONFLICT] = _T("Name conflict"); + m_vErrMsg[ecOPT_PRI] = _T("Invalid value for operator priority (must be greater or equal to zero)."); + m_vErrMsg[ecBUILTIN_OVERLOAD] = _T("user defined binary operator \"$TOK$\" conflicts with a built in operator."); + m_vErrMsg[ecUNEXPECTED_STR] = _T("Unexpected string token found at position $POS$."); + m_vErrMsg[ecUNTERMINATED_STRING] = _T("Unterminated string starting at position $POS$."); + m_vErrMsg[ecSTRING_EXPECTED] = _T("String function called with a non string type of argument."); + m_vErrMsg[ecVAL_EXPECTED] = _T("String value used where a numerical argument is expected."); + m_vErrMsg[ecOPRT_TYPE_CONFLICT] = _T("No suitable overload for operator \"$TOK$\" at position $POS$."); + m_vErrMsg[ecSTR_RESULT] = _T("Strings must only be used as function arguments!"); + m_vErrMsg[ecGENERIC] = _T("Parser error."); + m_vErrMsg[ecLOCALE] = _T("Decimal separator is identic to function argument separator."); + m_vErrMsg[ecUNEXPECTED_CONDITIONAL] = _T("The \"$TOK$\" operator must be preceded by a closing bracket."); + m_vErrMsg[ecMISSING_ELSE_CLAUSE] = _T("If-then-else operator is missing an else clause"); + m_vErrMsg[ecMISPLACED_COLON] = _T("Misplaced colon at position $POS$"); + m_vErrMsg[ecUNREASONABLE_NUMBER_OF_COMPUTATIONS] = _T("Number of computations to small for bulk mode. (Vectorisation overhead too costly)"); + m_vErrMsg[ecIDENTIFIER_TOO_LONG] = _T("Identifier too long."); + m_vErrMsg[ecEXPRESSION_TOO_LONG] = _T("Expression too long."); + m_vErrMsg[ecINVALID_CHARACTERS_FOUND] = _T("Invalid non printable characters found in expression/identifer!"); + + for (int i = 0; i < ecCOUNT; ++i) + { + if (!m_vErrMsg[i].length()) + throw std::runtime_error("Error definitions are incomplete!"); + } + } + + //--------------------------------------------------------------------------- + // + // ParserError class + // + //--------------------------------------------------------------------------- + + /** \brief Default constructor. */ + ParserError::ParserError() + :m_strMsg() + , m_strFormula() + , m_strTok() + , m_iPos(-1) + , m_iErrc(ecUNDEFINED) + , m_ErrMsg(ParserErrorMsg::Instance()) + { + } + + //------------------------------------------------------------------------------ + /** \brief This Constructor is used for internal exceptions only. + + It does not contain any information but the error code. + */ + ParserError::ParserError(EErrorCodes a_iErrc) + :m_strMsg() + , m_strFormula() + , m_strTok() + , m_iPos(-1) + , m_iErrc(a_iErrc) + , m_ErrMsg(ParserErrorMsg::Instance()) + { + m_strMsg = m_ErrMsg[m_iErrc]; + stringstream_type stream; + stream << (int)m_iPos; + ReplaceSubString(m_strMsg, _T("$POS$"), stream.str()); + ReplaceSubString(m_strMsg, _T("$TOK$"), m_strTok); + } + + //------------------------------------------------------------------------------ + /** \brief Construct an error from a message text. */ + ParserError::ParserError(const string_type& sMsg) + :m_ErrMsg(ParserErrorMsg::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 occurred. + */ + ParserError::ParserError(EErrorCodes iErrc, + const string_type& sTok, + const string_type& sExpr, + int iPos) + :m_strMsg() + , m_strFormula(sExpr) + , m_strTok(sTok) + , m_iPos(iPos) + , m_iErrc(iErrc) + , m_ErrMsg(ParserErrorMsg::Instance()) + { + m_strMsg = m_ErrMsg[m_iErrc]; + stringstream_type stream; + stream << (int)m_iPos; + ReplaceSubString(m_strMsg, _T("$POS$"), stream.str()); + ReplaceSubString(m_strMsg, _T("$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 occurred. + \param [in] sTok The token string related to this error. + */ + ParserError::ParserError(EErrorCodes iErrc, int iPos, const string_type& sTok) + :m_strMsg() + , m_strFormula() + , m_strTok(sTok) + , m_iPos(iPos) + , m_iErrc(iErrc) + , m_ErrMsg(ParserErrorMsg::Instance()) + { + m_strMsg = m_ErrMsg[m_iErrc]; + stringstream_type stream; + stream << (int)m_iPos; + ReplaceSubString(m_strMsg, _T("$POS$"), stream.str()); + ReplaceSubString(m_strMsg, _T("$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. + */ + ParserError::ParserError(const char_type* szMsg, int iPos, const string_type& sTok) + :m_strMsg(szMsg) + , m_strFormula() + , m_strTok(sTok) + , m_iPos(iPos) + , m_iErrc(ecGENERIC) + , m_ErrMsg(ParserErrorMsg::Instance()) + { + stringstream_type stream; + stream << (int)m_iPos; + ReplaceSubString(m_strMsg, _T("$POS$"), stream.str()); + ReplaceSubString(m_strMsg, _T("$TOK$"), m_strTok); + } + + //------------------------------------------------------------------------------ + /** \brief Copy constructor. */ + ParserError::ParserError(const ParserError& 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(ParserErrorMsg::Instance()) + { + } + + //------------------------------------------------------------------------------ + /** \brief Assignment operator. */ + ParserError& ParserError::operator=(const ParserError& 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; + } + + //------------------------------------------------------------------------------ + ParserError::~ParserError() + {} + + //------------------------------------------------------------------------------ + /** \brief Replace all occurrences 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 ParserError::ReplaceSubString(string_type& strSource, + const string_type& strFind, + const string_type& strReplaceWith) + { + string_type strResult; + string_type::size_type iPos(0), iNext(0); + + for (;;) + { + iNext = strSource.find(strFind, iPos); + strResult.append(strSource, iPos, iNext - iPos); + + if (iNext == string_type::npos) + break; + + strResult.append(strReplaceWith); + iPos = iNext + strFind.length(); + } + + strSource.swap(strResult); + } + + //------------------------------------------------------------------------------ + /** \brief Reset the error object. */ + void ParserError::Reset() + { + m_strMsg = _T(""); + m_strFormula = _T(""); + m_strTok = _T(""); + m_iPos = -1; + m_iErrc = ecUNDEFINED; + } + + //------------------------------------------------------------------------------ + /** \brief Set the expression related to this error. */ + void ParserError::SetFormula(const string_type& a_strFormula) + { + m_strFormula = a_strFormula; + } + + //------------------------------------------------------------------------------ + /** \brief gets the expression related tp this error.*/ + const string_type& ParserError::GetExpr() const + { + return m_strFormula; + } + + //------------------------------------------------------------------------------ + /** \brief Returns the message string for this error. */ + const string_type& ParserError::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 + */ + int ParserError::GetPos() const + { + return m_iPos; + } + + //------------------------------------------------------------------------------ + /** \brief Return string related with this token (if available). */ + const string_type& ParserError::GetToken() const + { + return m_strTok; + } + + //------------------------------------------------------------------------------ + /** \brief Return the error code. */ + EErrorCodes ParserError::GetCode() const + { + return m_iErrc; + } +} // namespace mu + +#if defined(_MSC_VER) + #pragma warning(pop) +#endif diff --git a/bundled/muparser_v2_3_3/src/muParserInt.cpp b/bundled/muparser_v2_3_3/src/muParserInt.cpp new file mode 100644 index 0000000000..76189e9198 --- /dev/null +++ b/bundled/muparser_v2_3_3/src/muParserInt.cpp @@ -0,0 +1,279 @@ +/* + + _____ __ _____________ _______ ______ ___________ + / \| | \____ \__ \\_ __ \/ ___// __ \_ __ \ + | Y Y \ | / |_> > __ \| | \/\___ \\ ___/| | \/ + |__|_| /____/| __(____ /__| /____ >\___ >__| + \/ |__| \/ \/ \/ + Copyright (C) 2004 - 2021 Ingo Berg + + Redistribution and use in source and binary forms, with or without modification, are permitted + provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this list of + conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of + conditions and the following disclaimer in the documentation and/or other materials provided + with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "muParserInt.h" + +#include +#include +#include + +using namespace std; + +/** \file + \brief Implementation of a parser using integer value. +*/ + +/** \brief Namespace for mathematical applications. */ +namespace mu +{ + value_type ParserInt::Abs(value_type v) { return (value_type)Round(fabs((double)v)); } + value_type ParserInt::Sign(value_type v) { return (Round(v) < 0) ? -1 : (Round(v) > 0) ? 1 : 0; } + value_type ParserInt::Ite(value_type v1, value_type v2, value_type v3) { return (Round(v1) == 1) ? Round(v2) : Round(v3); } + value_type ParserInt::Add(value_type v1, value_type v2) { return Round(v1) + Round(v2); } + value_type ParserInt::Sub(value_type v1, value_type v2) { return Round(v1) - Round(v2); } + value_type ParserInt::Mul(value_type v1, value_type v2) { return Round(v1) * Round(v2); } + value_type ParserInt::Div(value_type v1, value_type v2) { return Round(v1) / Round(v2); } + value_type ParserInt::Mod(value_type v1, value_type v2) { return Round(v1) % Round(v2); } + value_type ParserInt::Shr(value_type v1, value_type v2) { return Round(v1) >> Round(v2); } + value_type ParserInt::Shl(value_type v1, value_type v2) { return Round(v1) << Round(v2); } + value_type ParserInt::LogAnd(value_type v1, value_type v2) { return Round(v1) & Round(v2); } + value_type ParserInt::LogOr(value_type v1, value_type v2) { return Round(v1) | Round(v2); } + value_type ParserInt::And(value_type v1, value_type v2) { return Round(v1) && Round(v2); } + value_type ParserInt::Or(value_type v1, value_type v2) { return Round(v1) || Round(v2); } + value_type ParserInt::Less(value_type v1, value_type v2) { return Round(v1) < Round(v2); } + value_type ParserInt::Greater(value_type v1, value_type v2) { return Round(v1) > Round(v2); } + value_type ParserInt::LessEq(value_type v1, value_type v2) { return Round(v1) <= Round(v2); } + value_type ParserInt::GreaterEq(value_type v1, value_type v2) { return Round(v1) >= Round(v2); } + value_type ParserInt::Equal(value_type v1, value_type v2) { return Round(v1) == Round(v2); } + value_type ParserInt::NotEqual(value_type v1, value_type v2) { return Round(v1) != Round(v2); } + value_type ParserInt::Not(value_type v) { return !Round(v); } + + value_type ParserInt::Pow(value_type v1, value_type v2) + { + return std::pow((double)Round(v1), (double)Round(v2)); + } + + + value_type ParserInt::UnaryMinus(value_type v) + { + return -Round(v); + } + + + value_type ParserInt::Sum(const value_type* a_afArg, int a_iArgc) + { + if (!a_iArgc) + throw ParserError(_T("too few arguments for function sum.")); + + value_type fRes = 0; + for (int i = 0; i < a_iArgc; ++i) + fRes += a_afArg[i]; + + return fRes; + } + + + value_type ParserInt::Min(const value_type* a_afArg, int a_iArgc) + { + if (!a_iArgc) + throw ParserError(_T("too few arguments for function min.")); + + value_type fRes = a_afArg[0]; + for (int i = 0; i < a_iArgc; ++i) + fRes = std::min(fRes, a_afArg[i]); + + return fRes; + } + + + value_type ParserInt::Max(const value_type* a_afArg, int a_iArgc) + { + if (!a_iArgc) + throw ParserError(_T("too few arguments for function min.")); + + value_type fRes = a_afArg[0]; + for (int i = 0; i < a_iArgc; ++i) + fRes = std::max(fRes, a_afArg[i]); + + return fRes; + } + + + int ParserInt::IsVal(const char_type* a_szExpr, int* a_iPos, value_type* a_fVal) + { + string_type buf(a_szExpr); + std::size_t pos = buf.find_first_not_of(_T("0123456789")); + + if (pos == std::string::npos) + return 0; + + stringstream_type stream(buf.substr(0, pos)); + int iVal(0); + + stream >> iVal; + if (stream.fail()) + return 0; + + stringstream_type::pos_type iEnd = stream.tellg(); // Position after reading + if (stream.fail()) + iEnd = stream.str().length(); + + if (iEnd == (stringstream_type::pos_type) - 1) + return 0; + + *a_iPos += (int)iEnd; + *a_fVal = (value_type)iVal; + return 1; + } + + + /** \brief Check a given position in the expression for the presence of + a hex value. + \param a_szExpr Pointer to the expression string + \param [in/out] a_iPos Pointer to an integer value holding the current parsing + position in the expression. + \param [out] a_fVal Pointer to the position where the detected value shall be stored. + + Hey values must be prefixed with "0x" in order to be detected properly. + */ + int ParserInt::IsHexVal(const char_type* a_szExpr, int* a_iPos, value_type* a_fVal) + { + if (a_szExpr[1] == 0 || (a_szExpr[0] != '0' || a_szExpr[1] != 'x')) + return 0; + + unsigned iVal(0); + + // New code based on streams for UNICODE compliance: + stringstream_type::pos_type nPos(0); + stringstream_type ss(a_szExpr + 2); + ss >> std::hex >> iVal; + nPos = ss.tellg(); + + if (nPos == (stringstream_type::pos_type)0) + return 1; + + *a_iPos += (int)(2 + nPos); + *a_fVal = (value_type)iVal; + return 1; + } + + + int ParserInt::IsBinVal(const char_type* a_szExpr, int* a_iPos, value_type* a_fVal) + { + if (a_szExpr[0] != '#') + return 0; + + unsigned iVal(0), + iBits(sizeof(iVal) * 8), + i(0); + + for (i = 0; (a_szExpr[i + 1] == '0' || a_szExpr[i + 1] == '1') && i < iBits; ++i) + iVal |= (int)(a_szExpr[i + 1] == '1') << ((iBits - 1) - i); + + if (i == 0) + return 0; + + if (i == iBits) + throw exception_type(_T("Binary to integer conversion error (overflow).")); + + *a_fVal = (unsigned)(iVal >> (iBits - i)); + *a_iPos += i + 1; + + return 1; + } + + + /** \brief Constructor. + + Call ParserBase class constructor and trigger Function, Operator and Constant initialization. + */ + ParserInt::ParserInt() + :ParserBase() + { + AddValIdent(IsVal); // lowest priority + AddValIdent(IsBinVal); + AddValIdent(IsHexVal); // highest priority + + InitCharSets(); + InitFun(); + InitOprt(); + } + + + void ParserInt::InitConst() + { + } + + + void ParserInt::InitCharSets() + { + DefineNameChars(_T("0123456789_abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")); + DefineOprtChars(_T("+-*^/?<>=!%&|~'_")); + DefineInfixOprtChars(_T("/+-*^?<>=!%&|~'_")); + } + + + /** \brief Initialize the default functions. */ + void ParserInt::InitFun() + { + DefineFun(_T("sign"), Sign); + DefineFun(_T("abs"), Abs); + DefineFun(_T("if"), Ite); + DefineFun(_T("sum"), Sum); + DefineFun(_T("min"), Min); + DefineFun(_T("max"), Max); + } + + + /** \brief Initialize operators. */ + void ParserInt::InitOprt() + { + // disable all built in operators, not all of them useful for integer numbers + // (they don't do rounding of values) + EnableBuiltInOprt(false); + + // Disable all built in operators, they won't work with integer numbers + // since they are designed for floating point numbers + DefineInfixOprt(_T("-"), UnaryMinus); + DefineInfixOprt(_T("!"), Not); + + DefineOprt(_T("&"), LogAnd, prLOGIC); + DefineOprt(_T("|"), LogOr, prLOGIC); + DefineOprt(_T("&&"), And, prLOGIC); + DefineOprt(_T("||"), Or, prLOGIC); + + DefineOprt(_T("<"), Less, prCMP); + DefineOprt(_T(">"), Greater, prCMP); + DefineOprt(_T("<="), LessEq, prCMP); + DefineOprt(_T(">="), GreaterEq, prCMP); + DefineOprt(_T("=="), Equal, prCMP); + DefineOprt(_T("!="), NotEqual, prCMP); + + DefineOprt(_T("+"), Add, prADD_SUB); + DefineOprt(_T("-"), Sub, prADD_SUB); + + DefineOprt(_T("*"), Mul, prMUL_DIV); + DefineOprt(_T("/"), Div, prMUL_DIV); + DefineOprt(_T("%"), Mod, prMUL_DIV); + + DefineOprt(_T("^"), Pow, prPOW, oaRIGHT); + DefineOprt(_T(">>"), Shr, prMUL_DIV + 1); + DefineOprt(_T("<<"), Shl, prMUL_DIV + 1); + } + +} // namespace mu diff --git a/bundled/muparser_v2_3_3/src/muParserTest.cpp b/bundled/muparser_v2_3_3/src/muParserTest.cpp new file mode 100644 index 0000000000..46e0af7ddb --- /dev/null +++ b/bundled/muparser_v2_3_3/src/muParserTest.cpp @@ -0,0 +1,1717 @@ +/* + + _____ __ _____________ _______ ______ ___________ + / \| | \____ \__ \\_ __ \/ ___// __ \_ __ \ + | Y Y \ | / |_> > __ \| | \/\___ \\ ___/| | \/ + |__|_| /____/| __(____ /__| /____ >\___ >__| + \/ |__| \/ \/ \/ + Copyright (C) 2022 Ingo Berg + + Redistribution and use in source and binary forms, with or without modification, are permitted + provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this list of + conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of + conditions and the following disclaimer in the documentation and/or other materials provided + with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "muParserTest.h" + +#include +#include +#include +#include + +using namespace std; + +/** \file + \brief This file contains the implementation of parser test cases. +*/ + +namespace mu +{ + namespace Test + { + int ParserTester::c_iCount = 0; + + //--------------------------------------------------------------------------------------------- + ParserTester::ParserTester() + :m_vTestFun() + { + AddTest(&ParserTester::TestNames); + AddTest(&ParserTester::TestSyntax); + AddTest(&ParserTester::TestPostFix); + AddTest(&ParserTester::TestInfixOprt); + AddTest(&ParserTester::TestVarConst); + AddTest(&ParserTester::TestMultiArg); + AddTest(&ParserTester::TestExpression); + AddTest(&ParserTester::TestIfThenElse); + AddTest(&ParserTester::TestInterface); + AddTest(&ParserTester::TestBinOprt); + AddTest(&ParserTester::TestException); + AddTest(&ParserTester::TestStrArg); + AddTest(&ParserTester::TestBulkMode); + AddTest(&ParserTester::TestOptimizer); + + ParserTester::c_iCount = 0; + } + + //--------------------------------------------------------------------------------------------- + int ParserTester::IsHexVal(const char_type* a_szExpr, int* a_iPos, value_type* a_fVal) + { + if (a_szExpr[1] == 0 || (a_szExpr[0] != '0' || a_szExpr[1] != 'x')) + return 0; + + unsigned iVal(0); + + // New code based on streams for UNICODE compliance: + stringstream_type::pos_type nPos(0); + stringstream_type ss(a_szExpr + 2); + ss >> std::hex >> iVal; + nPos = ss.tellg(); + + if (nPos == (stringstream_type::pos_type)0) + return 1; + + *a_iPos += (int)(2 + nPos); + *a_fVal = (value_type)iVal; + return 1; + } + + //--------------------------------------------------------------------------------------------- + int ParserTester::TestInterface() + { + int iStat = 0; + mu::console() << _T("testing member functions..."); + + // Test RemoveVar + value_type afVal[3] = { 1,2,3 }; + Parser p; + + try + { + p.DefineVar(_T("a"), &afVal[0]); + p.DefineVar(_T("b"), &afVal[1]); + p.DefineVar(_T("c"), &afVal[2]); + p.SetExpr(_T("a+b+c")); + p.Eval(); + } + catch (...) + { + iStat += 1; // this is not supposed to happen + } + + try + { + p.RemoveVar(_T("c")); + p.Eval(); + iStat += 1; // not supposed to reach this, nonexisting variable "c" deleted... + } + catch (...) + { + // failure is expected... + } + + if (iStat == 0) + mu::console() << _T("passed") << endl; + else + mu::console() << _T("\n failed with ") << iStat << _T(" errors") << endl; + + return iStat; + } + + //--------------------------------------------------------------------------------------------- + int ParserTester::TestOptimizer() + { + int iStat = 0; + mu::console() << _T("testing optimizer..."); + + // Test RemoveVar + Parser p; + try + { + // test for #93 (https://github.com/beltoforion/muparser/issues/93) + // expected bytecode is: + // VAL, FUN + { + p.DefineFun(_T("unoptimizable"), f1of1, false); + p.SetExpr(_T("unoptimizable(1)")); + p.Eval(); + + auto& bc = p.GetByteCode(); + const SToken* tok = bc.GetBase(); + if (bc.GetSize() != 2 && tok[1].Cmd != cmFUNC) + { + mu::console() << _T("#93 an unoptimizable expression was optimized!") << endl; + iStat += 1; + } + } + + { + p.ClearFun(); + p.DefineFun(_T("unoptimizable"), f1of1, true); + p.SetExpr(_T("unoptimizable(1)")); + p.Eval(); + + auto& bc = p.GetByteCode(); + const SToken* tok = bc.GetBase(); + if (bc.GetSize() != 1 && tok[0].Cmd != cmVAL) + { + mu::console() << _T("#93 optimizer error") << endl; + iStat += 1; + } + } + } + catch (...) + { + iStat += 1; // this is not supposed to happen + } + + if (iStat == 0) + mu::console() << _T("passed") << endl; + else + mu::console() << _T("\n failed with ") << iStat << _T(" errors") << endl; + + return iStat; + } + + //--------------------------------------------------------------------------------------------- + int ParserTester::TestStrArg() + { + int iStat = 0; + mu::console() << _T("testing string arguments..."); + + // from oss-fuzz: https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=23410 + iStat += ThrowTest(_T(R"(6 - 6 ? 4 : "", ? 4 : "", ? 4 : "")"), ecUNEXPECTED_STR, true); + // variations: + iStat += ThrowTest(_T(R"(avg(0?4:(""),1))"), ecUNEXPECTED_STR); + iStat += ThrowTest(_T(R"(1 ? 4 : "")"), ecUNEXPECTED_STR); + iStat += ThrowTest(_T(R"(1 ? "" : 4)"), ecUNEXPECTED_STR); + iStat += ThrowTest(_T(R"(1 ? "" : "")"), ecUNEXPECTED_STR); + iStat += ThrowTest(_T(R"(0 ? 4 : "")"), ecUNEXPECTED_STR); + iStat += ThrowTest(_T(R"(0 ? 4 : (""))"), ecUNEXPECTED_STR); + iStat += ThrowTest(_T(R"(1 ? 4 : "")"), ecUNEXPECTED_STR); + + // from oss-fuzz: https://oss-fuzz.com/testcase-detail/5106868061208576 + iStat += ThrowTest(_T(R"("","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",8)"), ecSTR_RESULT); + + // derived from oss-fuzz: https://oss-fuzz.com/testcase-detail/5758791700971520 + iStat += ThrowTest(_T("(\"\"), 7"), ecSTR_RESULT); + iStat += ThrowTest(_T("((\"\")), 7"), ecSTR_RESULT); + //iStat += ThrowTest(_T("(\"\"),(\" \"), 7, (3)"), ecSTR_RESULT); + //iStat += ThrowTest(_T("(\"\"),(\"\"), 7, (3)"), ecSTR_RESULT); + //iStat += ThrowTest(_T("(\"\"),(\"\"), (3)"), ecSTR_RESULT); + //iStat += ThrowTest(_T("(\"\"),(\"\"), 7"), ecSTR_RESULT); + + + // variations: + iStat += ThrowTest(_T(R"("","",9)"), ecSTR_RESULT); + + iStat += EqnTest(_T("valueof(\"\")"), 123, true); // empty string arguments caused a crash + iStat += EqnTest(_T("valueof(\"aaa\")+valueof(\"bbb\") "), 246, true); + iStat += EqnTest(_T("2*(valueof(\"aaa\")-23)+valueof(\"bbb\")"), 323, true); + + // use in expressions with variables + iStat += EqnTest(_T("a*(atof(\"10\")-b)"), 8, true); + iStat += EqnTest(_T("a-(atof(\"10\")*b)"), -19, true); + + // string + numeric arguments + iStat += EqnTest(_T("strfun1(\"100\")"), 100, true); + iStat += EqnTest(_T("strfun2(\"100\",1)"), 101, true); + iStat += EqnTest(_T("strfun3(\"99\",1,2)"), 102, true); + iStat += EqnTest(_T("strfun4(\"99\",1,2,3)"), 105, true); + iStat += EqnTest(_T("strfun5(\"99\",1,2,3,4)"), 109, true); + iStat += EqnTest(_T("strfun6(\"99\",1,2,3,4,5)"), 114, true); + + // string constants + iStat += EqnTest(_T("atof(str1)+atof(str2)"), 3.33, true); + + // user data + iStat += EqnTest(_T("strfunud3_10(\"99\",1,2)"), 112, true); + + if (iStat == 0) + mu::console() << _T("passed") << endl; + else + mu::console() << _T("\n failed with ") << iStat << _T(" errors") << endl; + + return iStat; + } + + //--------------------------------------------------------------------------------------------- + int ParserTester::TestBulkMode() + { + int iStat = 0; + mu::console() << _T("testing bulkmode..."); + +#define EQN_TEST_BULK(EXPR, R1, R2, R3, R4, PASS) \ + { \ + double res[] = { R1, R2, R3, R4 }; \ + iStat += EqnTestBulk(_T(EXPR), res, (PASS)); \ + } + + // Bulk Variables for the test: + // a: 1,2,3,4 + // b: 2,2,2,2 + // c: 3,3,3,3 + // d: 5,4,3,2 + EQN_TEST_BULK("a", 1, 1, 1, 1, false) + EQN_TEST_BULK("a", 1, 2, 3, 4, true) + EQN_TEST_BULK("b=a", 1, 2, 3, 4, true) + EQN_TEST_BULK("b=a, b*10", 10, 20, 30, 40, true) + EQN_TEST_BULK("b=a, b*10, a", 1, 2, 3, 4, true) + EQN_TEST_BULK("a+b", 3, 4, 5, 6, true) + EQN_TEST_BULK("c*(a+b)", 9, 12, 15, 18, true) +#undef EQN_TEST_BULK + + if (iStat == 0) + mu::console() << _T("passed") << endl; + else + mu::console() << _T("\n failed with ") << iStat << _T(" errors") << endl; + + return iStat; + } + + //--------------------------------------------------------------------------------------------- + int ParserTester::TestBinOprt() + { + int iStat = 0; + mu::console() << _T("testing binary operators..."); + + // built in operators + // xor operator + + iStat += EqnTest(_T("a++b"), 3, true); + iStat += EqnTest(_T("a ++ b"), 3, true); + iStat += EqnTest(_T("1++2"), 3, true); + iStat += EqnTest(_T("1 ++ 2"), 3, true); + iStat += EqnTest(_T("a add b"), 3, true); + iStat += EqnTest(_T("1 add 2"), 3, true); + iStat += EqnTest(_T("aa"), 1, true); + iStat += EqnTest(_T("a>a"), 0, true); + iStat += EqnTest(_T("aa"), 0, true); + iStat += EqnTest(_T("a<=a"), 1, true); + iStat += EqnTest(_T("a<=b"), 1, true); + iStat += EqnTest(_T("b<=a"), 0, true); + iStat += EqnTest(_T("a>=a"), 1, true); + iStat += EqnTest(_T("b>=a"), 1, true); + iStat += EqnTest(_T("a>=b"), 0, true); + + // Test logical operators, especially if user defined "&" and the internal "&&" collide + iStat += EqnTest(_T("1 && 1"), 1, true); + iStat += EqnTest(_T("1 && 0"), 0, true); + iStat += EqnTest(_T("(aa)"), 1, true); + iStat += EqnTest(_T("(ab)"), 0, true); + //iStat += EqnTest(_T("12 and 255"), 12, true); + //iStat += EqnTest(_T("12 and 0"), 0, true); + iStat += EqnTest(_T("12 & 255"), 12, true); + iStat += EqnTest(_T("12 & 0"), 0, true); + iStat += EqnTest(_T("12&255"), 12, true); + iStat += EqnTest(_T("12&0"), 0, true); + + // Assignment operator + iStat += EqnTest(_T("a = b"), 2, true); + iStat += EqnTest(_T("a = sin(b)"), 0.909297, true); + iStat += EqnTest(_T("a = 1+sin(b)"), 1.909297, true); + iStat += EqnTest(_T("(a=b)*2"), 4, true); + iStat += EqnTest(_T("2*(a=b)"), 4, true); + iStat += EqnTest(_T("2*(a=b+1)"), 6, true); + iStat += EqnTest(_T("(a=b+1)*2"), 6, true); + iStat += EqnTest(_T("a=c, a*10"), 30, true); + + iStat += EqnTest(_T("2^2^3"), 256, true); + iStat += EqnTest(_T("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(_T("3+4*2/(1-5)^2^3"), 3.0001220703125, true); + + // Test user defined binary operators + iStat += EqnTestInt(_T("1 | 2"), 3, true); + iStat += EqnTestInt(_T("1 || 2"), 1, true); + iStat += EqnTestInt(_T("123 & 456"), 72, true); + iStat += EqnTestInt(_T("(123 & 456) % 10"), 2, true); + iStat += EqnTestInt(_T("1 && 0"), 0, true); + iStat += EqnTestInt(_T("123 && 456"), 1, true); + iStat += EqnTestInt(_T("1 << 3"), 8, true); + iStat += EqnTestInt(_T("8 >> 3"), 1, true); + iStat += EqnTestInt(_T("9 / 4"), 2, true); + iStat += EqnTestInt(_T("9 % 4"), 1, true); + iStat += EqnTestInt(_T("if(5%2,1,0)"), 1, true); + iStat += EqnTestInt(_T("if(4%2,1,0)"), 0, true); + iStat += EqnTestInt(_T("-10+1"), -9, true); + iStat += EqnTestInt(_T("1+2*3"), 7, true); + iStat += EqnTestInt(_T("const1 != const2"), 1, true); + iStat += EqnTestInt(_T("const1 != const2"), 0, false); + iStat += EqnTestInt(_T("const1 == const2"), 0, true); + iStat += EqnTestInt(_T("const1 == 1"), 1, true); + iStat += EqnTestInt(_T("10*(const1 == 1)"), 10, true); + iStat += EqnTestInt(_T("2*(const1 | const2)"), 6, true); + iStat += EqnTestInt(_T("2*(const1 | const2)"), 7, false); + iStat += EqnTestInt(_T("const1 < const2"), 1, true); + iStat += EqnTestInt(_T("const2 > const1"), 1, true); + iStat += EqnTestInt(_T("const1 <= 1"), 1, true); + iStat += EqnTestInt(_T("const2 >= 2"), 1, true); + iStat += EqnTestInt(_T("2*(const1 + const2)"), 6, true); + iStat += EqnTestInt(_T("2*(const1 - const2)"), -2, true); + iStat += EqnTestInt(_T("a != b"), 1, true); + iStat += EqnTestInt(_T("a != b"), 0, false); + iStat += EqnTestInt(_T("a == b"), 0, true); + iStat += EqnTestInt(_T("a == 1"), 1, true); + iStat += EqnTestInt(_T("10*(a == 1)"), 10, true); + iStat += EqnTestInt(_T("2*(a | b)"), 6, true); + iStat += EqnTestInt(_T("2*(a | b)"), 7, false); + iStat += EqnTestInt(_T("a < b"), 1, true); + iStat += EqnTestInt(_T("b > a"), 1, true); + iStat += EqnTestInt(_T("a <= 1"), 1, true); + iStat += EqnTestInt(_T("b >= 2"), 1, true); + iStat += EqnTestInt(_T("2*(a + b)"), 6, true); + iStat += EqnTestInt(_T("2*(a - b)"), -2, true); + iStat += EqnTestInt(_T("a + (a << b)"), 5, true); + iStat += EqnTestInt(_T("-2^2"), -4, true); + iStat += EqnTestInt(_T("3--a"), 4, true); + iStat += EqnTestInt(_T("3+-3^2"), -6, true); + + // Test reading of hex values: + iStat += EqnTestInt(_T("0xff"), 255, true); + iStat += EqnTestInt(_T("10+0xff"), 265, true); + iStat += EqnTestInt(_T("0xff+10"), 265, true); + iStat += EqnTestInt(_T("10*0xff"), 2550, true); + iStat += EqnTestInt(_T("0xff*10"), 2550, true); + iStat += EqnTestInt(_T("10+0xff+1"), 266, true); + iStat += EqnTestInt(_T("1+0xff+10"), 266, true); + + // incorrect: '^' is yor here, not power + // iStat += EqnTestInt("-(1+2)^2", -9, true); + // iStat += EqnTestInt("-1^3", -1, true); + + // Test precedence + // a=1, b=2, c=3 + iStat += EqnTestInt(_T("a + b * c"), 7, true); + iStat += EqnTestInt(_T("a * b + c"), 5, true); + iStat += EqnTestInt(_T("a10"), 0, true); + iStat += EqnTestInt(_T("a"), f1of1) + PARSER_THROWCHECK(PostfixOprt, true, _T("?<"), f1of1) + PARSER_THROWCHECK(PostfixOprt, true, _T("**"), f1of1) + PARSER_THROWCHECK(PostfixOprt, true, _T("xor"), f1of1) + PARSER_THROWCHECK(PostfixOprt, true, _T("and"), f1of1) + PARSER_THROWCHECK(PostfixOprt, true, _T("or"), f1of1) + PARSER_THROWCHECK(PostfixOprt, true, _T("not"), f1of1) + PARSER_THROWCHECK(PostfixOprt, true, _T("!"), f1of1) + + // Binary operator + // The following must fail with builtin operators activated + // p.EnableBuiltInOp(true); -> this is the default + p.ClearPostfixOprt(); + PARSER_THROWCHECK(Oprt, false, _T("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"), f1of2); + PARSER_THROWCHECK(Oprt, false, _T("+"), f1of2) + PARSER_THROWCHECK(Oprt, false, _T("-"), f1of2) + PARSER_THROWCHECK(Oprt, false, _T("*"), f1of2) + PARSER_THROWCHECK(Oprt, false, _T("/"), f1of2) + PARSER_THROWCHECK(Oprt, false, _T("^"), f1of2) + PARSER_THROWCHECK(Oprt, false, _T("&&"), f1of2) + PARSER_THROWCHECK(Oprt, false, _T("||"), f1of2) + + // without activated built in operators it should work + p.EnableBuiltInOprt(false); + PARSER_THROWCHECK(Oprt, true, _T("+"), f1of2) + PARSER_THROWCHECK(Oprt, true, _T("-"), f1of2) + PARSER_THROWCHECK(Oprt, true, _T("*"), f1of2) + PARSER_THROWCHECK(Oprt, true, _T("/"), f1of2) + PARSER_THROWCHECK(Oprt, true, _T("^"), f1of2) + PARSER_THROWCHECK(Oprt, true, _T("&&"), f1of2) + PARSER_THROWCHECK(Oprt, true, _T("||"), f1of2) +#undef PARSER_THROWCHECK + + if (iStat == 0) + mu::console() << _T("passed") << endl; + else + mu::console() << _T("\n failed with ") << iStat << _T(" errors") << endl; + + return iStat; + } + + //--------------------------------------------------------------------------- + int ParserTester::TestSyntax() + { + int iStat = 0; + mu::console() << _T("testing syntax engine..."); + + iStat += ThrowTest(_T("1,"), ecUNEXPECTED_EOF); // incomplete hex definition + iStat += ThrowTest(_T("a,"), ecUNEXPECTED_EOF); // incomplete hex definition + iStat += ThrowTest(_T("sin(8),"), ecUNEXPECTED_EOF); // incomplete hex definition + iStat += ThrowTest(_T("(sin(8)),"), ecUNEXPECTED_EOF); // incomplete hex definition + iStat += ThrowTest(_T("a{m},"), ecUNEXPECTED_EOF); // incomplete hex definition + + iStat += EqnTest(_T("(1+ 2*a)"), 3, true); // Spaces within formula + iStat += EqnTest(_T("sqrt((4))"), 2, true); // Multiple brackets + iStat += EqnTest(_T("sqrt((2)+2)"), 2, true);// Multiple brackets + iStat += EqnTest(_T("sqrt(2+(2))"), 2, true);// Multiple brackets + iStat += EqnTest(_T("sqrt(a+(3))"), 2, true);// Multiple brackets + iStat += EqnTest(_T("sqrt((3)+a)"), 2, true);// Multiple brackets + iStat += EqnTest(_T("order(1,2)"), 1, true); // May not cause name collision with operator "or" + iStat += EqnTest(_T("(2+"), 0, false); // missing closing bracket + iStat += EqnTest(_T("2++4"), 0, false); // unexpected operator + iStat += EqnTest(_T("2+-4"), 0, false); // unexpected operator + iStat += EqnTest(_T("(2+)"), 0, false); // unexpected closing bracket + iStat += EqnTest(_T("--2"), 0, false); // double sign + iStat += EqnTest(_T("ksdfj"), 0, false); // unknown token + iStat += EqnTest(_T("()"), 0, false); // empty bracket without a function + iStat += EqnTest(_T("5+()"), 0, false); // empty bracket without a function + iStat += EqnTest(_T("sin(cos)"), 0, false); // unexpected function + iStat += EqnTest(_T("5t6"), 0, false); // unknown token + iStat += EqnTest(_T("5 t 6"), 0, false); // unknown token + iStat += EqnTest(_T("8*"), 0, false); // unexpected end of formula + iStat += EqnTest(_T(",3"), 0, false); // unexpected comma + iStat += EqnTest(_T("3,5"), 0, false); // unexpected comma + iStat += EqnTest(_T("sin(8,8)"), 0, false); // too many function args + iStat += EqnTest(_T("(7,8)"), 0, false); // too many function args + iStat += EqnTest(_T("sin)"), 0, false); // unexpected closing bracket + iStat += EqnTest(_T("a)"), 0, false); // unexpected closing bracket + iStat += EqnTest(_T("pi)"), 0, false); // unexpected closing bracket + iStat += EqnTest(_T("sin(())"), 0, false); // unexpected closing bracket + iStat += EqnTest(_T("sin()"), 0, false); // unexpected closing bracket + + if (iStat == 0) + mu::console() << _T("passed") << endl; + else + mu::console() << _T("\n failed with ") << iStat << _T(" errors") << endl; + + return iStat; + } + + //--------------------------------------------------------------------------- + int ParserTester::TestVarConst() + { + int iStat = 0; + mu::console() << _T("testing variable/constant detection..."); + + // Test if the result changes when a variable changes + iStat += EqnTestWithVarChange(_T("a"), 1, 1, 2, 2); + iStat += EqnTestWithVarChange(_T("2*a"), 2, 4, 3, 6); + + // distinguish constants with same basename + iStat += EqnTest(_T("const"), 1, true); + iStat += EqnTest(_T("const1"), 2, true); + iStat += EqnTest(_T("const2"), 3, true); + iStat += EqnTest(_T("2*const"), 2, true); + iStat += EqnTest(_T("2*const1"), 4, true); + iStat += EqnTest(_T("2*const2"), 6, true); + iStat += EqnTest(_T("2*const+1"), 3, true); + iStat += EqnTest(_T("2*const1+1"), 5, true); + iStat += EqnTest(_T("2*const2+1"), 7, true); + iStat += EqnTest(_T("const"), 0, false); + iStat += EqnTest(_T("const1"), 0, false); + iStat += EqnTest(_T("const2"), 0, false); + + // distinguish variables with same basename + iStat += EqnTest(_T("a"), 1, true); + iStat += EqnTest(_T("aa"), 2, true); + iStat += EqnTest(_T("2*a"), 2, true); + iStat += EqnTest(_T("2*aa"), 4, true); + iStat += EqnTest(_T("2*a-1"), 1, true); + iStat += EqnTest(_T("2*aa-1"), 3, true); + + // custom value recognition + iStat += EqnTest(_T("0xff"), 255, true); + iStat += EqnTest(_T("0x97 + 0xff"), 406, true); + + // Finally test querying of used variables + try + { + int idx; + mu::Parser p; + mu::value_type vVarVal[] = { 1, 2, 3, 4, 5 }; + p.DefineVar(_T("a"), &vVarVal[0]); + p.DefineVar(_T("b"), &vVarVal[1]); + p.DefineVar(_T("c"), &vVarVal[2]); + p.DefineVar(_T("d"), &vVarVal[3]); + p.DefineVar(_T("e"), &vVarVal[4]); + + // Test lookup of defined variables + // 4 used variables + p.SetExpr(_T("a+b+c+d")); + mu::varmap_type UsedVar = p.GetUsedVar(); + int iCount = (int)UsedVar.size(); + if (iCount != 4) + throw false; + + // the next check will fail if the parser + // erroneously creates new variables internally + if (p.GetVar().size() != 5) + throw false; + + mu::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(_T("undef1+undef2+undef3")); + UsedVar = p.GetUsedVar(); + iCount = (int)UsedVar.size(); + if (iCount != 3) + throw false; + + // the next check will fail if the parser + // erroneously 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(_T("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) + mu::console() << _T("passed") << endl; + else + mu::console() << _T("\n failed with ") << iStat << _T(" errors") << endl; + + return iStat; + } + + //--------------------------------------------------------------------------- + int ParserTester::TestMultiArg() + { + int iStat = 0; + mu::console() << _T("testing multiarg functions..."); + + // from oss-fzz.com: UNKNOWN READ; https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=23330#c1 + iStat += ThrowTest(_T("6, +, +, +, +, +, +, +, +, +, +, +, +, +, +, 1, +, +, +, +, +, +, +, +, +, +, +, +, +, +, +, +, +, +, +, +, +, +, +, +, +, +, +, +, +, +, +, +"), ecUNEXPECTED_ARG_SEP, true); + + // misplaced string argument + iStat += ThrowTest(_T(R"(sin(0?4:("")))"), ecUNEXPECTED_STR); + iStat += ThrowTest(_T(R"(avg(0?4:(""),1))"), ecUNEXPECTED_STR); + + // Compound expressions + iStat += EqnTest(_T("1,2,3"), 3, true); + iStat += EqnTest(_T("a,b,c"), 3, true); + iStat += EqnTest(_T("a=10,b=20,c=a*b"), 200, true); + iStat += EqnTest(_T("1,\n2,\n3"), 3, true); + iStat += EqnTest(_T("a,\nb,\nc"), 3, true); + iStat += EqnTest(_T("a=10,\nb=20,\nc=a*b"), 200, true); + iStat += EqnTest(_T("1,\r\n2,\r\n3"), 3, true); + iStat += EqnTest(_T("a,\r\nb,\r\nc"), 3, true); + iStat += EqnTest(_T("a=10,\r\nb=20,\r\nc=a*b"), 200, true); + + // picking the right argument + iStat += EqnTest(_T("f1of1(1)"), 1, true); + iStat += EqnTest(_T("f1of2(1, 2)"), 1, true); + iStat += EqnTest(_T("f2of2(1, 2)"), 2, true); + iStat += EqnTest(_T("f1of3(1, 2, 3)"), 1, true); + iStat += EqnTest(_T("f2of3(1, 2, 3)"), 2, true); + iStat += EqnTest(_T("f3of3(1, 2, 3)"), 3, true); + iStat += EqnTest(_T("f1of4(1, 2, 3, 4)"), 1, true); + iStat += EqnTest(_T("f2of4(1, 2, 3, 4)"), 2, true); + iStat += EqnTest(_T("f3of4(1, 2, 3, 4)"), 3, true); + iStat += EqnTest(_T("f4of4(1, 2, 3, 4)"), 4, true); + iStat += EqnTest(_T("f1of5(1, 2, 3, 4, 5)"), 1, true); + iStat += EqnTest(_T("f2of5(1, 2, 3, 4, 5)"), 2, true); + iStat += EqnTest(_T("f3of5(1, 2, 3, 4, 5)"), 3, true); + iStat += EqnTest(_T("f4of5(1, 2, 3, 4, 5)"), 4, true); + iStat += EqnTest(_T("f5of5(1, 2, 3, 4, 5)"), 5, true); + // Too few arguments / Too many arguments + iStat += EqnTest(_T("1+ping()"), 11, true); + iStat += EqnTest(_T("ping()+1"), 11, true); + iStat += EqnTest(_T("2*ping()"), 20, true); + iStat += EqnTest(_T("ping()*2"), 20, true); + iStat += EqnTest(_T("ping(1,2)"), 0, false); + iStat += EqnTest(_T("1+ping(1,2)"), 0, false); + iStat += EqnTest(_T("f1of1(1,2)"), 0, false); + iStat += EqnTest(_T("f1of1()"), 0, false); + iStat += EqnTest(_T("f1of2(1, 2, 3)"), 0, false); + iStat += EqnTest(_T("f1of2(1)"), 0, false); + iStat += EqnTest(_T("f1of3(1, 2, 3, 4)"), 0, false); + iStat += EqnTest(_T("f1of3(1)"), 0, false); + iStat += EqnTest(_T("f1of4(1, 2, 3, 4, 5)"), 0, false); + iStat += EqnTest(_T("f1of4(1)"), 0, false); + iStat += EqnTest(_T("(1,2,3)"), 0, false); + iStat += EqnTest(_T("1,2,3"), 0, false); + iStat += EqnTest(_T("(1*a,2,3)"), 0, false); + iStat += EqnTest(_T("1,2*a,3"), 0, false); + + // correct calculation of arguments + iStat += EqnTest(_T("min(a, 1)"), 1, true); + iStat += EqnTest(_T("min(3*2, 1)"), 1, true); + iStat += EqnTest(_T("min(3*2, 1)"), 6, false); + iStat += EqnTest(_T("firstArg(2,3,4)"), 2, true); + iStat += EqnTest(_T("lastArg(2,3,4)"), 4, true); + iStat += EqnTest(_T("min(3*a+1, 1)"), 1, true); + iStat += EqnTest(_T("max(3*a+1, 1)"), 4, true); + iStat += EqnTest(_T("max(3*a+1, 1)*2"), 8, true); + iStat += EqnTest(_T("2*max(3*a+1, 1)+2"), 10, true); + + // functions with Variable argument count + iStat += EqnTest(_T("sum(a)"), 1, true); + iStat += EqnTest(_T("sum(1,2,3)"), 6, true); + iStat += EqnTest(_T("sum(a,b,c)"), 6, true); + iStat += EqnTest(_T("sum(1,-max(1,2),3)*2"), 4, true); + iStat += EqnTest(_T("2*sum(1,2,3)"), 12, true); + iStat += EqnTest(_T("2*sum(1,2,3)+2"), 14, true); + iStat += EqnTest(_T("2*sum(-1,2,3)+2"), 10, true); + iStat += EqnTest(_T("2*sum(-1,2,-(-a))+2"), 6, true); + iStat += EqnTest(_T("2*sum(-1,10,-a)+2"), 18, true); + iStat += EqnTest(_T("2*sum(1,2,3)*2"), 24, true); + iStat += EqnTest(_T("sum(1,-max(1,2),3)*2"), 4, true); + iStat += EqnTest(_T("sum(1*3, 4, a+2)"), 10, true); + iStat += EqnTest(_T("sum(1*3, 2*sum(1,2,2), a+2)"), 16, true); + iStat += EqnTest(_T("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(_T("sum()"), 0, false); + iStat += EqnTest(_T("sum(,)"), 0, false); + iStat += EqnTest(_T("sum(1,2,)"), 0, false); + iStat += EqnTest(_T("sum(,1,2)"), 0, false); + + // user data + iStat += EqnTest(_T("funud0_8()"), 8, true); + iStat += EqnTest(_T("funud1_16(10)"), 26, true); + iStat += EqnTest(_T("funud2_24(10, 100)"), 134, true); + iStat += EqnTest(_T("funud10_32(1,2,3,4,5,6,7,8,9,10)"), 87, true); + iStat += EqnTest(_T("funud0_9()"), 9, true); + iStat += EqnTest(_T("funud1_17(10)"), 27, true); + iStat += EqnTest(_T("funud2_25(10, 100)"), 135, true); + iStat += EqnTest(_T("funud10_33(1,2,3,4,5,6,7,8,9,10)"), 88, true); + iStat += EqnTest(_T("sumud_100(1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2)"), 124, true); + iStat += EqnTest(_T("sumud_100()"), 0, false); + + if (iStat == 0) + mu::console() << _T("passed") << endl; + else + mu::console() << _T("\n failed with ") << iStat << _T(" errors") << endl; + + return iStat; + } + + + //--------------------------------------------------------------------------- + int ParserTester::TestInfixOprt() + { + int iStat(0); + mu::console() << "testing infix operators..."; + + iStat += EqnTest(_T("+1"), +1, true); + iStat += EqnTest(_T("-(+1)"), -1, true); + iStat += EqnTest(_T("-(+1)*2"), -2, true); + iStat += EqnTest(_T("-(+2)*sqrt(4)"), -4, true); + iStat += EqnTest(_T("3-+a"), 2, true); + iStat += EqnTest(_T("+1*3"), 3, true); + + iStat += EqnTest(_T("-1"), -1, true); + iStat += EqnTest(_T("-(-1)"), 1, true); + iStat += EqnTest(_T("-(-1)*2"), 2, true); + iStat += EqnTest(_T("-(-2)*sqrt(4)"), 4, true); + iStat += EqnTest(_T("-_pi"), -MathImpl::CONST_PI, true); + iStat += EqnTest(_T("-a"), -1, true); + iStat += EqnTest(_T("-(a)"), -1, true); + iStat += EqnTest(_T("-(-a)"), 1, true); + iStat += EqnTest(_T("-(-a)*2"), 2, true); + iStat += EqnTest(_T("-(8)"), -8, true); + iStat += EqnTest(_T("-8"), -8, true); + iStat += EqnTest(_T("-(2+1)"), -3, true); + iStat += EqnTest(_T("-(f1of1(1+2*3)+1*2)"), -9, true); + iStat += EqnTest(_T("-(-f1of1(1+2*3)+1*2)"), 5, true); + iStat += EqnTest(_T("-sin(8)"), -0.989358, true); + iStat += EqnTest(_T("3-(-a)"), 4, true); + iStat += EqnTest(_T("3--a"), 4, true); + iStat += EqnTest(_T("-1*3"), -3, true); + + // Postfix / infix priorities + iStat += EqnTest(_T("~2#"), 8, true); + iStat += EqnTest(_T("~f1of1(2)#"), 8, true); + iStat += EqnTest(_T("~(b)#"), 8, true); + iStat += EqnTest(_T("(~b)#"), 12, true); + iStat += EqnTest(_T("~(2#)"), 8, true); + iStat += EqnTest(_T("~(f1of1(2)#)"), 8, true); + // + iStat += EqnTest(_T("-2^2"), -4, true); + iStat += EqnTest(_T("-(a+b)^2"), -9, true); + iStat += EqnTest(_T("(-3)^2"), 9, true); + iStat += EqnTest(_T("-(-2^2)"), 4, true); + iStat += EqnTest(_T("3+-3^2"), -6, true); + // The following assumes use of sqr as postfix operator together + // with a sign operator of low priority: + iStat += EqnTest(_T("-2'"), -4, true); + iStat += EqnTest(_T("-(1+1)'"), -4, true); + iStat += EqnTest(_T("2+-(1+1)'"), -2, true); + iStat += EqnTest(_T("2+-2'"), -2, true); + // This is the classic behaviour of the infix sign operator (here: "$") which is + // now deprecated: + iStat += EqnTest(_T("$2^2"), 4, true); + iStat += EqnTest(_T("$(a+b)^2"), 9, true); + iStat += EqnTest(_T("($3)^2"), 9, true); + iStat += EqnTest(_T("$($2^2)"), -4, true); + iStat += EqnTest(_T("3+$3^2"), 12, true); + + // infix operators sharing the first few characters + iStat += EqnTest(_T("~ 123"), (value_type)123.0 + 2, true); + iStat += EqnTest(_T("~~ 123"), (value_type)123.0 + 2, true); + + if (iStat == 0) + mu::console() << _T("passed") << endl; + else + mu::console() << _T("\n failed with ") << iStat << _T(" errors") << endl; + + return iStat; + } + + + //--------------------------------------------------------------------------- + int ParserTester::TestPostFix() + { + int iStat = 0; + mu::console() << _T("testing postfix operators..."); + + // application + iStat += EqnTest(_T("3{m}+5"), 5.003, true); + iStat += EqnTest(_T("1000{m}"), 1, true); + iStat += EqnTest(_T("1000 {m}"), 1, true); + iStat += EqnTest(_T("(a){m}"), 1e-3, true); + iStat += EqnTest(_T("a{m}"), 1e-3, true); + iStat += EqnTest(_T("a {m}"), 1e-3, true); + iStat += EqnTest(_T("-(a){m}"), -1e-3, true); + iStat += EqnTest(_T("-2{m}"), -2e-3, true); + iStat += EqnTest(_T("-2 {m}"), -2e-3, true); + iStat += EqnTest(_T("f1of1(1000){m}"), 1, true); + iStat += EqnTest(_T("-f1of1(1000){m}"), -1, true); + iStat += EqnTest(_T("-f1of1(-1000){m}"), 1, true); + iStat += EqnTest(_T("f4of4(0,0,0,1000){m}"), 1, true); + iStat += EqnTest(_T("2+(a*1000){m}"), 3, true); + + // can postfix operators "m" und "meg" be told apart properly? + iStat += EqnTest(_T("2*3000meg+2"), 2 * 3e9 + 2, true); + + // some incorrect results + iStat += EqnTest(_T("1000{m}"), 0.1, false); + iStat += EqnTest(_T("(a){m}"), 2, false); + // failure due to syntax checking + iStat += ThrowTest(_T("0x"), ecUNASSIGNABLE_TOKEN); // incomplete hex definition + iStat += ThrowTest(_T("3+"), ecUNEXPECTED_EOF); + iStat += ThrowTest(_T("4 + {m}"), ecUNASSIGNABLE_TOKEN); + iStat += ThrowTest(_T("{m}4"), ecUNASSIGNABLE_TOKEN); + iStat += ThrowTest(_T("sin({m})"), ecUNASSIGNABLE_TOKEN); + iStat += ThrowTest(_T("{m} {m}"), ecUNASSIGNABLE_TOKEN); + iStat += ThrowTest(_T("{m}(8)"), ecUNASSIGNABLE_TOKEN); + iStat += ThrowTest(_T("4,{m}"), ecUNASSIGNABLE_TOKEN); + iStat += ThrowTest(_T("-{m}"), ecUNASSIGNABLE_TOKEN); + iStat += ThrowTest(_T("2(-{m})"), ecUNEXPECTED_PARENS); + iStat += ThrowTest(_T("2({m})"), ecUNEXPECTED_PARENS); + + iStat += ThrowTest(_T("multi*1.0"), ecUNASSIGNABLE_TOKEN); + + if (iStat == 0) + mu::console() << _T("passed") << endl; + else + mu::console() << _T("\n failed with ") << iStat << _T(" errors") << endl; + + return iStat; + } + + //--------------------------------------------------------------------------- + int ParserTester::TestExpression() + { + int iStat = 0; + mu::console() << _T("testing expression samples..."); + + value_type b = 2; + + iStat += EqnTest(_T("f0()"), 42, true); + iStat += EqnTest(_T("b^2"), 4, true); + iStat += EqnTest(_T("b^1"), 2, true); + iStat += EqnTest(_T("b^0"), 1, true); + iStat += EqnTest(_T("b^-1"), 0.5, true); + + // Optimization + iStat += EqnTest(_T("2*b*5"), 20, true); + iStat += EqnTest(_T("2*b*5 + 4*b"), 28, true); + iStat += EqnTest(_T("2*a/3"), 2.0 / 3.0, true); + + // Addition auf cmVARMUL + iStat += EqnTest(_T("3+b"), b + 3, true); + iStat += EqnTest(_T("b+3"), b + 3, true); + iStat += EqnTest(_T("b*3+2"), b * 3 + 2, true); + iStat += EqnTest(_T("3*b+2"), b * 3 + 2, true); + iStat += EqnTest(_T("2+b*3"), b * 3 + 2, true); + iStat += EqnTest(_T("2+3*b"), b * 3 + 2, true); + iStat += EqnTest(_T("b+3*b"), b + 3 * b, true); + iStat += EqnTest(_T("3*b+b"), b + 3 * b, true); + + iStat += EqnTest(_T("2+b*3+b"), 2 + b * 3 + b, true); + iStat += EqnTest(_T("b+2+b*3"), b + 2 + b * 3, true); + + iStat += EqnTest(_T("(2*b+1)*4"), (2 * b + 1) * 4, true); + iStat += EqnTest(_T("4*(2*b+1)"), (2 * b + 1) * 4, true); + + // operator precedences + iStat += EqnTest(_T("1+2-3*4/5^6"), 2.99923, true); + iStat += EqnTest(_T("1^2/3*4-5+6"), 2.33333333, true); + iStat += EqnTest(_T("1+2*3"), 7, true); + iStat += EqnTest(_T("1+2*3"), 7, true); + iStat += EqnTest(_T("(1+2)*3"), 9, true); + iStat += EqnTest(_T("(1+2)*(-3)"), -9, true); + iStat += EqnTest(_T("2/4"), 0.5, true); + + iStat += EqnTest(_T("exp(ln(7))"), 7, true); + iStat += EqnTest(_T("e^ln(7)"), 7, true); + iStat += EqnTest(_T("e^(ln(7))"), 7, true); + iStat += EqnTest(_T("(e^(ln(7)))"), 7, true); + iStat += EqnTest(_T("1-(e^(ln(7)))"), -6, true); + iStat += EqnTest(_T("2*(e^(ln(7)))"), 14, true); + iStat += EqnTest(_T("10^log(5)"), pow(10.0, log(5.0)), true); + iStat += EqnTest(_T("10^log10(5)"), 5, true); + iStat += EqnTest(_T("2^log2(4)"), 4, true); + iStat += EqnTest(_T("-(sin(0)+1)"), -1, true); + iStat += EqnTest(_T("-(2^1.1)"), -2.14354692, true); + + iStat += EqnTest(_T("(cos(2.41)/b)"), -0.372056, true); + iStat += EqnTest(_T("(1*(2*(3*(4*(5*(6*(a+b)))))))"), 2160, true); + iStat += EqnTest(_T("(1*(2*(3*(4*(5*(6*(7*(a+b))))))))"), 15120, true); + iStat += EqnTest(_T("(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( + _T("(((-9))-e/(((((((pi-(((-7)+(-3)/4/e))))/(((-5))-2)-((pi+(-0))*(sqrt((e+e))*(-8))*(((-pi)+(-pi)-(-9)*(6*5))") + _T("/(-e)-e))/2)/((((sqrt(2/(-e)+6)-(4-2))+((5/(-2))/(1*(-pi)+3))/8)*pi*((pi/((-2)/(-6)*1*(-1))*(-6)+(-e)))))/") + _T("((e+(-2)+(-e)*((((-3)*9+(-e)))+(-9)))))))-((((e-7+(((5/pi-(3/1+pi)))))/e)/(-5))/(sqrt((((((1+(-7))))+((((-") + _T("e)*(-e)))-8))*(-5)/((-e)))*(-6)-((((((-2)-(-9)-(-e)-1)/3))))/(sqrt((8+(e-((-6))+(9*(-9))))*(((3+2-8))*(7+6") + _T("+(-5))+((0/(-e)*(-pi))+7)))+(((((-e)/e/e)+((-6)*5)*e+(3+(-5)/pi))))+pi))/sqrt((((9))+((((pi))-8+2))+pi))/e") + _T("*4)*((-5)/(((-pi))*(sqrt(e)))))-(((((((-e)*(e)-pi))/4+(pi)*(-9)))))))+(-pi)"), -12.23016549, true); + + // long formula (Reference: Matlab) + iStat += EqnTest( + _T("(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") + _T(")+a)))*2.77)"), -2.16995656, true); + + // long formula (Reference: Matlab) + iStat += EqnTest(_T("1+2-3*4/5^6*(2*(1-5+(3*7^9)*(4+6*7-3)))+12"), -7995810.09926, true); + + if (iStat == 0) + mu::console() << _T("passed") << endl; + else + mu::console() << _T("\n failed with ") << iStat << _T(" errors") << endl; + + return iStat; + } + + + + //--------------------------------------------------------------------------- + int ParserTester::TestIfThenElse() + { + int iStat = 0; + mu::console() << _T("testing if-then-else operator..."); + + // from oss-fuzz.com: https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=24167 + iStat += ThrowTest(_T(R"(0^3^avg(0>3?4:(""),0^3?4:("")))"), ecUNEXPECTED_STR); + // derivatives + iStat += ThrowTest(_T(R"(avg(0?(""):4,1))"), ecUNEXPECTED_STR); + iStat += ThrowTest(_T(R"(avg(0>3?4:(""),0^3?4:("")))"), ecUNEXPECTED_STR); + + iStat += ThrowTest(_T(R"(0?4:(""))"), ecUNEXPECTED_STR); + iStat += ThrowTest(_T(R"((0)?4:(""))"), ecUNEXPECTED_STR); + iStat += ThrowTest(_T(R"((0>3)?4:(""))"), ecUNEXPECTED_STR); + iStat += ThrowTest(_T(R"(0>3?4:(""))"), ecUNEXPECTED_STR); + + // from oss-fuzz.com: https://oss-fuzz.com/testcase-detail/4777121158529024 + iStat += ThrowTest(_T("3!=min(0?2>2,2>5,1:6)"), ecUNEXPECTED_ARG_SEP); + + // Test error detection + iStat += ThrowTest(_T(":3"), ecUNEXPECTED_CONDITIONAL); + iStat += ThrowTest(_T("? 1 : 2"), ecUNEXPECTED_CONDITIONAL); + iStat += ThrowTest(_T("(a3?2,4,2:4)"), ecUNEXPECTED_ARG_SEP); + iStat += ThrowTest(_T("sum(2>3?2,4,sin(2):4)"), ecUNEXPECTED_ARG_SEP); + iStat += ThrowTest(_T("sum(2>3?sin(2),4,2:4)"), ecUNEXPECTED_ARG_SEP); + iStat += ThrowTest(_T("sum(2>3?sin(a),4,2:4)"), ecUNEXPECTED_ARG_SEP); + iStat += ThrowTest(_T("sum(2>3?sin(2),4,2:4)"), ecUNEXPECTED_ARG_SEP); + + iStat += EqnTest(_T("1 ? 128 : 255"), 128, true); + iStat += EqnTest(_T("1<2 ? 128 : 255"), 128, true); + iStat += EqnTest(_T("ab) ? 10 : 11"), 11, true); + iStat += EqnTest(_T("(ab) ? c : d"), -2, true); + + iStat += EqnTest(_T("(a>b) ? 1 : 0"), 0, true); + iStat += EqnTest(_T("((a>b) ? 1 : 0) ? 1 : 2"), 2, true); + iStat += EqnTest(_T("((a>b) ? 1 : 0) ? 1 : sum((a>b) ? 1 : 2)"), 2, true); + iStat += EqnTest(_T("((a>b) ? 0 : 1) ? 1 : sum((a>b) ? 1 : 2)"), 1, true); + + iStat += EqnTest(_T("sum((a>b) ? 1 : 2)"), 2, true); + iStat += EqnTest(_T("sum((1) ? 1 : 2)"), 1, true); + iStat += EqnTest(_T("sum((a>b) ? 1 : 2, 100)"), 102, true); + iStat += EqnTest(_T("sum((1) ? 1 : 2, 100)"), 101, true); + iStat += EqnTest(_T("sum(3, (a>b) ? 3 : 10)"), 13, true); + iStat += EqnTest(_T("sum(3, (ab) ? 3 : 10)"), 130, true); + iStat += EqnTest(_T("10*sum(3, (ab) ? 3 : 10)*10"), 130, true); + iStat += EqnTest(_T("sum(3, (ab) ? sum(3, (ab) ? sum(3, (ab) ? sum(3, (ab)&&(a2)&&(1<2) ? 128 : 255"), 255, true); + iStat += EqnTest(_T("((1<2)&&(1<2)) ? 128 : 255"), 128, true); + iStat += EqnTest(_T("((1>2)&&(1<2)) ? 128 : 255"), 255, true); + iStat += EqnTest(_T("((ab)&&(a0 ? 1>2 ? 128 : 255 : 1>0 ? 32 : 64"), 255, true); + iStat += EqnTest(_T("1>0 ? 1>2 ? 128 : 255 :(1>0 ? 32 : 64)"), 255, true); + iStat += EqnTest(_T("1>0 ? 1>0 ? 128 : 255 : 1>2 ? 32 : 64"), 128, true); + iStat += EqnTest(_T("1>0 ? 1>0 ? 128 : 255 :(1>2 ? 32 : 64)"), 128, true); + iStat += EqnTest(_T("1>2 ? 1>2 ? 128 : 255 : 1>0 ? 32 : 64"), 32, true); + iStat += EqnTest(_T("1>2 ? 1>0 ? 128 : 255 : 1>2 ? 32 : 64"), 64, true); + iStat += EqnTest(_T("1>0 ? 50 : 1>0 ? 128 : 255"), 50, true); + iStat += EqnTest(_T("1>0 ? 50 : (1>0 ? 128 : 255)"), 50, true); + iStat += EqnTest(_T("1>0 ? 1>0 ? 128 : 255 : 50"), 128, true); + iStat += EqnTest(_T("1>2 ? 1>2 ? 128 : 255 : 1>0 ? 32 : 1>2 ? 64 : 16"), 32, true); + iStat += EqnTest(_T("1>2 ? 1>2 ? 128 : 255 : 1>0 ? 32 :(1>2 ? 64 : 16)"), 32, true); + iStat += EqnTest(_T("1>0 ? 1>2 ? 128 : 255 : 1>0 ? 32 :1>2 ? 64 : 16"), 255, true); + iStat += EqnTest(_T("1>0 ? 1>2 ? 128 : 255 : (1>0 ? 32 :1>2 ? 64 : 16)"), 255, true); + iStat += EqnTest(_T("1 ? 0 ? 128 : 255 : 1 ? 32 : 64"), 255, true); + + // assignment operators + iStat += EqnTest(_T("a= 0 ? 128 : 255, a"), 255, true); + iStat += EqnTest(_T("a=((a>b)&&(a + // this is now legal, for reference see: + // https://sourceforge.net/forum/message.php?msg_id=7411373 + // iStat += ThrowTest( _T("sin=9"), ecUNEXPECTED_OPERATOR); + // + + iStat += ThrowTest(_T("(8)=5"), ecUNEXPECTED_OPERATOR); + iStat += ThrowTest(_T("(a)=5"), ecUNEXPECTED_OPERATOR); + iStat += ThrowTest(_T("a=\"tttt\""), ecOPRT_TYPE_CONFLICT); + + if (iStat == 0) + mu::console() << _T("passed") << endl; + else + mu::console() << _T("\n failed with ") << iStat << _T(" errors") << endl; + + return iStat; + } + + + //--------------------------------------------------------------------------- + void ParserTester::AddTest(testfun_type a_pFun) + { + m_vTestFun.push_back(a_pFun); + } + + //--------------------------------------------------------------------------- + int ParserTester::Run() + { + int iStat = 0; + try + { + for (int i = 0; i < (int)m_vTestFun.size(); ++i) + iStat += (this->*m_vTestFun[i])(); + } + catch (Parser::exception_type& e) + { + mu::console() << "\n" << e.GetMsg() << endl; + mu::console() << e.GetToken() << endl; + Abort(); + } + catch (std::exception& e) + { + mu::console() << e.what() << endl; + Abort(); + } + catch (...) + { + mu::console() << "Internal error"; + Abort(); + } + + if (iStat == 0) + { + mu::console() << "Test passed (" << ParserTester::c_iCount << " expressions)" << endl; + } + else + { + mu::console() << "Test failed with " << iStat + << " errors (" << ParserTester::c_iCount + << " expressions)" << endl; + } + ParserTester::c_iCount = 0; + return iStat; + } + + + //--------------------------------------------------------------------------- + int ParserTester::ThrowTest(const string_type& a_str, int a_iErrc, bool a_expectedToFail) + { + ParserTester::c_iCount++; + + try + { + value_type fVal[] = { 1,1,1 }; + Parser p; + + p.DefineVar(_T("a"), &fVal[0]); + p.DefineVar(_T("b"), &fVal[1]); + p.DefineVar(_T("c"), &fVal[2]); + p.DefinePostfixOprt(_T("{m}"), Milli); + p.DefinePostfixOprt(_T("m"), Milli); + p.DefineFun(_T("ping"), Ping); + p.DefineFun(_T("valueof"), ValueOf); + p.DefineFun(_T("strfun1"), StrFun1); + p.DefineFun(_T("strfun2"), StrFun2); + p.DefineFun(_T("strfun3"), StrFun3); + p.DefineFun(_T("strfun4"), StrFun4); + p.DefineFun(_T("strfun5"), StrFun5); + p.DefineFun(_T("strfun6"), StrFun6); + p.SetExpr(a_str); + // p.EnableDebugDump(1, 0); + p.Eval(); + } + catch (ParserError& e) + { + // output the formula in case of an failed test + if (a_expectedToFail == false || (a_expectedToFail == true && a_iErrc != e.GetCode())) + { + mu::console() << _T("\n ") + << _T("Expression: ") << a_str + << _T(" Code:") << e.GetCode() << _T("(") << e.GetMsg() << _T(")") + << _T(" Expected:") << a_iErrc; + } + + return (a_iErrc == e.GetCode()) ? 0 : 1; + } + + // if a_expectedToFail == false no exception is expected + bool bRet((a_expectedToFail == false) ? 0 : 1); + if (bRet == 1) + { + mu::console() << _T("\n ") + << _T("Expression: ") << a_str + << _T(" did evaluate; Expected error:") << a_iErrc; + } + + return bRet; + } + + //--------------------------------------------------------------------------- + /** \brief Evaluate a tet expression. + + \return 1 in case of a failure, 0 otherwise. + */ + int ParserTester::EqnTestWithVarChange(const string_type& a_str, + double a_fVar1, + double a_fRes1, + double a_fVar2, + double a_fRes2) + { + ParserTester::c_iCount++; + + try + { + value_type fVal[2] = { -999, -999 }; // should be equal + + Parser p; + value_type var = 0; + + // variable + p.DefineVar(_T("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 (Parser::exception_type& e) + { + mu::console() << _T("\n fail: ") << a_str.c_str() << _T(" (") << e.GetMsg() << _T(")"); + return 1; + } + catch (std::exception& e) + { + mu::console() << _T("\n fail: ") << a_str.c_str() << _T(" (") << e.what() << _T(")"); + return 1; // always return a failure since this exception is not expected + } + catch (...) + { + mu::console() << _T("\n fail: ") << a_str.c_str() << _T(" (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 ParserTester::EqnTest(const string_type& a_str, double a_fRes, bool a_fPass) + { + ParserTester::c_iCount++; + int iRet(0); + value_type fVal[6] = { -999, -998, -997, -996, -995, -994 }; // initially should be different + + try + { + std::unique_ptr p1; + Parser p2, p3; // three parser objects + // they will be used for testing copy and assignment 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 mu::Parser()); + // Add constants + p1->DefineConst(_T("pi"), MathImpl::CONST_PI); + p1->DefineConst(_T("e"), MathImpl::CONST_E); + p1->DefineConst(_T("const"), 1); + p1->DefineConst(_T("const1"), 2); + p1->DefineConst(_T("const2"), 3); + // string constants + p1->DefineStrConst(_T("str1"), _T("1.11")); + p1->DefineStrConst(_T("str2"), _T("2.22")); + // variables + value_type vVarVal[] = { 1, 2, 3, -2 }; + p1->DefineVar(_T("a"), &vVarVal[0]); + p1->DefineVar(_T("aa"), &vVarVal[1]); + p1->DefineVar(_T("b"), &vVarVal[1]); + p1->DefineVar(_T("c"), &vVarVal[2]); + p1->DefineVar(_T("d"), &vVarVal[3]); + + // custom value ident functions + p1->AddValIdent(&ParserTester::IsHexVal); + + // functions + p1->DefineFun(_T("ping"), Ping); + p1->DefineFun(_T("f0"), f0); // no parameter + p1->DefineFun(_T("f1of1"), f1of1); // one parameter + p1->DefineFun(_T("f1of2"), f1of2); // two parameter + p1->DefineFun(_T("f2of2"), f2of2); + p1->DefineFun(_T("f1of3"), f1of3); // three parameter + p1->DefineFun(_T("f2of3"), f2of3); + p1->DefineFun(_T("f3of3"), f3of3); + p1->DefineFun(_T("f1of4"), f1of4); // four parameter + p1->DefineFun(_T("f2of4"), f2of4); + p1->DefineFun(_T("f3of4"), f3of4); + p1->DefineFun(_T("f4of4"), f4of4); + p1->DefineFun(_T("f1of5"), f1of5); // five parameter + p1->DefineFun(_T("f2of5"), f2of5); + p1->DefineFun(_T("f3of5"), f3of5); + p1->DefineFun(_T("f4of5"), f4of5); + p1->DefineFun(_T("f5of5"), f5of5); + + // binary operators + p1->DefineOprt(_T("add"), add, 0); + p1->DefineOprt(_T("++"), add, 0); + p1->DefineOprt(_T("&"), land, prLAND); + + // sample functions + p1->DefineFun(_T("min"), Min); + p1->DefineFun(_T("max"), Max); + p1->DefineFun(_T("sum"), Sum); + p1->DefineFun(_T("valueof"), ValueOf); + p1->DefineFun(_T("atof"), StrToFloat); + p1->DefineFun(_T("strfun1"), StrFun1); + p1->DefineFun(_T("strfun2"), StrFun2); + p1->DefineFun(_T("strfun3"), StrFun3); + p1->DefineFun(_T("strfun4"), StrFun4); + p1->DefineFun(_T("strfun5"), StrFun5); + p1->DefineFun(_T("strfun6"), StrFun6); + p1->DefineFun(_T("lastArg"), LastArg); + p1->DefineFun(_T("firstArg"), FirstArg); + p1->DefineFun(_T("order"), FirstArg); + + // functions with user data + p1->DefineFunUserData(_T("funud0_8"), FunUd0, reinterpret_cast(8)); + p1->DefineFunUserData(_T("funud1_16"), FunUd1, reinterpret_cast(16)); + p1->DefineFunUserData(_T("funud2_24"), FunUd2, reinterpret_cast(24)); + p1->DefineFunUserData(_T("funud10_32"), FunUd10, reinterpret_cast(32)); + p1->DefineFunUserData(_T("funud0_9"), FunUd0, reinterpret_cast(9)); + p1->DefineFunUserData(_T("funud1_17"), FunUd1, reinterpret_cast(17)); + p1->DefineFunUserData(_T("funud2_25"), FunUd2, reinterpret_cast(25)); + p1->DefineFunUserData(_T("funud10_33"), FunUd10, reinterpret_cast(33)); + p1->DefineFunUserData(_T("strfunud3_10"), StrFunUd3, reinterpret_cast(10)); + p1->DefineFunUserData(_T("sumud_100"), SumUd, reinterpret_cast(100)); + + // infix / postfix operator + // Note: Identifiers used here do not have any meaning + // they are mere placeholders to test certain features. + p1->DefineInfixOprt(_T("$"), sign, prPOW + 1); // sign with high priority + p1->DefineInfixOprt(_T("~"), plus2); // high priority + p1->DefineInfixOprt(_T("~~"), plus2); + p1->DefinePostfixOprt(_T("{m}"), Milli); + p1->DefinePostfixOprt(_T("{M}"), Mega); + p1->DefinePostfixOprt(_T("m"), Milli); + p1->DefinePostfixOprt(_T("meg"), Mega); + p1->DefinePostfixOprt(_T("#"), times3); + p1->DefinePostfixOprt(_T("'"), 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 Parser::exception_type(_T("Bytecode / string parsing mismatch.")); + + // Test copy and assignment operators + try + { + // Test copy constructor + std::vector vParser; + vParser.push_back(*(p1.get())); + mu::Parser p4 = vParser[0]; // take parser from vector + + // destroy the originals from p2 + vParser.clear(); // delete the vector + p1.reset(nullptr); + + fVal[2] = p4.Eval(); + + // Test assignment operator + // additionally disable Optimizer this time + mu::Parser p5; + p5 = p4; + p5.EnableOptimizer(false); + fVal[3] = p5.Eval(); + + // Test Eval function for multiple return values + // use p2 since it has the optimizer enabled! + int nNum; + p4.SetExpr(a_str); // reset bytecode to trigger #94 (https://github.com/beltoforion/muparser/issues/94) + value_type* v = p4.Eval(nNum); + fVal[4] = v[nNum - 1]; + + v = p4.Eval(nNum); + fVal[5] = v[nNum - 1]; + + } + catch (std::exception& e) + { + mu::console() << _T("\n ") << e.what() << _T("\n"); + } + + // limited floating point accuracy requires the following test + bool bCloseEnough(true); + for (unsigned i = 0; i < sizeof(fVal) / sizeof(value_type); ++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 +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable:4127) +#endif + if (std::numeric_limits::has_infinity) +#ifdef _MSC_VER +#pragma warning(pop) +#endif + { + bCloseEnough &= (fabs(fVal[i]) != numeric_limits::infinity()); + } + } + + iRet = ((bCloseEnough && a_fPass) || (!bCloseEnough && !a_fPass)) ? 0 : 1; + + + if (iRet == 1) + { + mu::console() << _T("\n fail: ") << a_str.c_str() + << _T(" (incorrect result; expected: ") << a_fRes + << _T(" ;calculated: ") << fVal[0] << _T(",") + << fVal[1] << _T(",") + << fVal[2] << _T(",") + << fVal[3] << _T(",") + << fVal[4] << _T(",") + << fVal[5] << _T(")."); + } + } + catch (Parser::exception_type& e) + { + if (a_fPass) + { + if (fVal[0] != fVal[2] && fVal[0] != -999 && fVal[1] != -998) + mu::console() << _T("\n fail: ") << a_str.c_str() << _T(" (copy construction)"); + else + mu::console() << _T("\n fail: ") << a_str.c_str() << _T(" (") << e.GetMsg() << _T(")"); + return 1; + } + } + catch (std::exception& e) + { + mu::console() << _T("\n fail: ") << a_str.c_str() << _T(" (") << e.what() << _T(")"); + return 1; // always return a failure since this exception is not expected + } + catch (...) + { + mu::console() << _T("\n fail: ") << a_str.c_str() << _T(" (unexpected exception)"); + return 1; // exceptions other than ParserException are not allowed + } + + return iRet; + } + + //--------------------------------------------------------------------------- + int ParserTester::EqnTestInt(const string_type& a_str, double a_fRes, bool a_fPass) + { + ParserTester::c_iCount++; + + value_type vVarVal[] = { 1, 2, 3 }; // variable values + int iRet(0); + + try + { + value_type fVal[2] = { -99, -999 }; // results: initially should be different + ParserInt p; + p.DefineConst(_T("const1"), 1); + p.DefineConst(_T("const2"), 2); + p.DefineVar(_T("a"), &vVarVal[0]); + p.DefineVar(_T("b"), &vVarVal[1]); + p.DefineVar(_T("c"), &vVarVal[2]); + + p.SetExpr(a_str); + fVal[0] = p.Eval(); // result from stringparsing + fVal[1] = p.Eval(); // result from bytecode + + if (fVal[0] != fVal[1]) + throw Parser::exception_type(_T("Bytecode corrupt.")); + + iRet = ((a_fRes == fVal[0] && a_fPass) || + (a_fRes != fVal[0] && !a_fPass)) ? 0 : 1; + if (iRet == 1) + { + mu::console() << _T("\n fail: ") << a_str.c_str() + << _T(" (incorrect result; expected: ") << a_fRes + << _T(" ;calculated: ") << fVal[0] << _T(")."); + } + } + catch (Parser::exception_type& e) + { + if (a_fPass) + { + mu::console() << _T("\n fail: ") << e.GetExpr() << _T(" : ") << e.GetMsg(); + iRet = 1; + } + } + catch (...) + { + mu::console() << _T("\n fail: ") << a_str.c_str() << _T(" (unexpected exception)"); + iRet = 1; // exceptions other than ParserException are not allowed + } + + return iRet; + } + + //--------------------------------------------------------------------------- + /** \brief Test an expression in Bulk Mode. */ + int ParserTester::EqnTestBulk(const string_type& a_str, double a_fRes[4], bool a_fPass) + { + ParserTester::c_iCount++; + + // Define Bulk Variables + int nBulkSize = 4; + value_type vVariableA[] = { 1, 2, 3, 4 }; // variable values + value_type vVariableB[] = { 2, 2, 2, 2 }; // variable values + value_type vVariableC[] = { 3, 3, 3, 3 }; // variable values + value_type vResults[] = { 0, 0, 0, 0 }; // variable values + int iRet(0); + + try + { + Parser p; + p.DefineConst(_T("const1"), 1); + p.DefineConst(_T("const2"), 2); + p.DefineVar(_T("a"), vVariableA); + p.DefineVar(_T("b"), vVariableB); + p.DefineVar(_T("c"), vVariableC); + + p.SetExpr(a_str); + p.Eval(vResults, nBulkSize); + + bool bCloseEnough(true); + for (int i = 0; i < nBulkSize; ++i) + { + bCloseEnough &= (fabs(a_fRes[i] - vResults[i]) <= fabs(a_fRes[i] * 0.00001)); + } + + iRet = ((bCloseEnough && a_fPass) || (!bCloseEnough && !a_fPass)) ? 0 : 1; + if (iRet == 1) + { + mu::console() << _T("\n fail: ") << a_str.c_str() + << _T(" (incorrect result; expected: {") << a_fRes[0] << _T(",") << a_fRes[1] << _T(",") << a_fRes[2] << _T(",") << a_fRes[3] << _T("}") + << _T(" ;calculated: ") << vResults[0] << _T(",") << vResults[1] << _T(",") << vResults[2] << _T(",") << vResults[3] << _T("}"); + } + } + catch (Parser::exception_type& e) + { + if (a_fPass) + { + mu::console() << _T("\n fail: ") << e.GetExpr() << _T(" : ") << e.GetMsg(); + iRet = 1; + } + } + catch (...) + { + mu::console() << _T("\n fail: ") << a_str.c_str() << _T(" (unexpected exception)"); + iRet = 1; // exceptions other than ParserException are not allowed + } + + return iRet; + } + + //--------------------------------------------------------------------------- + /** \brief Internal error in test class Test is going to be aborted. */ + void ParserTester::Abort() const + { + mu::console() << _T("Test failed (internal error in test class)") << endl; + while (!getchar()); + exit(-1); + } + } // namespace test +} // namespace mu diff --git a/bundled/muparser_v2_3_3/src/muParserTokenReader.cpp b/bundled/muparser_v2_3_3/src/muParserTokenReader.cpp new file mode 100644 index 0000000000..312a0c5ffe --- /dev/null +++ b/bundled/muparser_v2_3_3/src/muParserTokenReader.cpp @@ -0,0 +1,997 @@ +/* + + _____ __ _____________ _______ ______ ___________ + / \| | \____ \__ \\_ __ \/ ___// __ \_ __ \ + | Y Y \ | / |_> > __ \| | \/\___ \\ ___/| | \/ + |__|_| /____/| __(____ /__| /____ >\___ >__| + \/ |__| \/ \/ \/ + Copyright (C) 2004 - 2021 Ingo Berg + + Redistribution and use in source and binary forms, with or without modification, are permitted + provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this list of + conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of + conditions and the following disclaimer in the documentation and/or other materials provided + with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include +#include +#include +#include +#include + +#include "muParserTokenReader.h" +#include "muParserBase.h" + +#if defined(_MSC_VER) + #pragma warning(push) + #pragma warning(disable : 26812) +#endif + +/** \file + \brief This file contains the parser token reader implementation. +*/ + + +namespace mu +{ + + // Forward declaration + class ParserBase; + + /** \brief Copy constructor. + + \sa Assign + \throw nothrow + */ + ParserTokenReader::ParserTokenReader(const ParserTokenReader& a_Reader) + { + Assign(a_Reader); + } + + + /** \brief Assignment operator. + + Self assignment will be suppressed otherwise #Assign is called. + + \param a_Reader Object to copy to this token reader. + \throw nothrow + */ + ParserTokenReader& ParserTokenReader::operator=(const ParserTokenReader& 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 ParserTokenReader::Assign(const ParserTokenReader& 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_bracketStack = a_Reader.m_bracketStack; + m_cArgSep = a_Reader.m_cArgSep; + m_fZero = a_Reader.m_fZero; + m_lastTok = a_Reader.m_lastTok; + } + + + /** \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. + */ + ParserTokenReader::ParserTokenReader(ParserBase* a_pParent) + :m_pParser(a_pParent) + , m_strFormula() + , m_iPos(0) + , m_iSynFlags(0) + , m_bIgnoreUndefVar(false) + , m_pFunDef(nullptr) + , m_pPostOprtDef(nullptr) + , m_pInfixOprtDef(nullptr) + , m_pOprtDef(nullptr) + , m_pConstDef(nullptr) + , m_pStrVarDef(nullptr) + , m_pVarDef(nullptr) + , m_pFactory(nullptr) + , m_pFactoryData(nullptr) + , m_vIdentFun() + , m_UsedVar() + , m_fZero(0) + , m_bracketStack() + , m_lastTok() + , m_cArgSep(',') + { + MUP_ASSERT(m_pParser != nullptr); + SetParent(m_pParser); + } + + + /** \brief Create instance of a ParserTokenReader 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 ParserTokenReader object. + \throw nothrow + */ + ParserTokenReader* ParserTokenReader::Clone(ParserBase* a_pParent) const + { + std::unique_ptr ptr(new ParserTokenReader(*this)); + ptr->SetParent(a_pParent); + return ptr.release(); + } + + + ParserTokenReader::token_type& ParserTokenReader::SaveBeforeReturn(const token_type& tok) + { + m_lastTok = tok; + return m_lastTok; + } + + + void ParserTokenReader::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 ParserTokenReader::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 ParserTokenReader::GetPos() const + { + return m_iPos; + } + + + /** \brief Return a reference to the formula. + + \return #m_strFormula + \throw nothrow + */ + const string_type& ParserTokenReader::GetExpr() const + { + return m_strFormula; + } + + + /** \brief Return a map containing the used variables only. */ + varmap_type& ParserTokenReader::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 ParserTokenReader::SetFormula(const string_type& a_strFormula) + { + m_strFormula = a_strFormula; + ReInit(); + } + + + /** \brief Set Flag that controls behaviour in case of undefined variables being 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 suppresses 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 ParserTokenReader::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 ParserTokenReader::ReInit() + { + m_iPos = 0; + m_iSynFlags = sfSTART_OF_LINE; + m_bracketStack = std::stack(); + m_UsedVar.clear(); + m_lastTok = token_type(); + } + + + /** \brief Read the next token from the string. */ + ParserTokenReader::token_type ParserTokenReader::ReadNextToken() + { + MUP_ASSERT(m_pParser != nullptr); + + const char_type* szExpr = m_strFormula.c_str(); + token_type tok; + + // Ignore all non printable characters when reading the expression + while (szExpr[m_iPos] > 0 && szExpr[m_iPos] <= 0x20) + { + // 14-31 are control characters. I donÄt want to have to deal with such strings at all! + // (see https://en.cppreference.com/w/cpp/string/byte/isprint) + if (szExpr[m_iPos] >= 14 && szExpr[m_iPos] <= 31) + Error(ecINVALID_CHARACTERS_FOUND, m_iPos); + + ++m_iPos; + } + + // Check for end of formula + if (IsEOF(tok)) + return SaveBeforeReturn(tok); + + // Check for user defined binary operator + if (IsOprt(tok)) + return SaveBeforeReturn(tok); + + // Check for function token + if (IsFunTok(tok)) + return SaveBeforeReturn(tok); + + // Check built in operators / tokens + if (IsBuiltIn(tok)) + return SaveBeforeReturn(tok); + + // Check for function argument separators + if (IsArgSep(tok)) + return SaveBeforeReturn(tok); + + // Check for values / constant tokens + if (IsValTok(tok)) + return SaveBeforeReturn(tok); + + // Check for variable tokens + if (IsVarTok(tok)) + return SaveBeforeReturn(tok); + + // Check for string variables + if (IsStrVarTok(tok)) + return SaveBeforeReturn(tok); + + // Check for String tokens + if (IsString(tok)) + return SaveBeforeReturn(tok); + + // Check for unary operators + if (IsInfixOpTok(tok)) + return SaveBeforeReturn(tok); + + // Check for unary operators + if (IsPostOpTok(tok)) + return SaveBeforeReturn(tok); + + // 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... + // + string_type strTok; + auto iEnd = ExtractToken(m_pParser->ValidNameChars(), strTok, (std::size_t)m_iPos); + if (iEnd != m_iPos) + Error(ecUNASSIGNABLE_TOKEN, m_iPos, strTok); + + Error(ecUNASSIGNABLE_TOKEN, m_iPos, m_strFormula.substr(m_iPos)); + return token_type(); // never reached + } + + + void ParserTokenReader::SetParent(ParserBase* 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 ParserTokenReader::ExtractToken(const char_type* a_szCharSet, string_type& a_sTok, std::size_t a_iPos) const + { + auto iEnd = m_strFormula.find_first_not_of(a_szCharSet, a_iPos); + + if (iEnd == string_type::npos) + iEnd = m_strFormula.length(); + + // Assign token string if there was something found + if (a_iPos != iEnd) + a_sTok = string_type(m_strFormula.begin() + a_iPos, m_strFormula.begin() + iEnd); + + return static_cast(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 ParserTokenReader::ExtractOperatorToken(string_type& a_sTok, std::size_t a_iPos) const + { + // Changed as per Issue 6: https://code.google.com/p/muparser/issues/detail?id=6 + auto iEnd = m_strFormula.find_first_not_of(m_pParser->ValidOprtChars(), a_iPos); + if (iEnd == string_type::npos) + iEnd = m_strFormula.length(); + + // Assign token string if there was something found + if (a_iPos != iEnd) + { + a_sTok = string_type(m_strFormula.begin() + a_iPos, m_strFormula.begin() + iEnd); + return static_cast(iEnd); + } + else + { + // There is still the chance of having to deal with an operator consisting exclusively + // of alphabetic characters. + return ExtractToken(_T("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"), a_sTok, (std::size_t)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 ParserTokenReader::IsBuiltIn(token_type& a_Tok) + { + const char_type** const pOprtDef = m_pParser->GetOprtDef(), + * const szFormula = m_strFormula.c_str(); + + // Compare token with function and operator strings + // check string for operator/function + for (int i = 0; pOprtDef[i]; i++) + { + std::size_t len(std::char_traits::length(pOprtDef[i])); + if (string_type(pOprtDef[i]) == string_type(szFormula + m_iPos, szFormula + m_iPos + len)) + { + switch (i) + { + 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: + // The assignment operator need special treatment + if (i == cmASSIGN && m_iSynFlags & noASSIGN) + Error(ecUNEXPECTED_OPERATOR, m_iPos, pOprtDef[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[i]); + } + + m_iSynFlags = noBC | noOPT | noARG_SEP | noPOSTOP | noASSIGN | noIF | noELSE | noEND; + break; + + case cmBO: + if (m_iSynFlags & noBO) + Error(ecUNEXPECTED_PARENS, m_iPos, pOprtDef[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_bracketStack.push(cmBO); + break; + + case cmBC: + if (m_iSynFlags & noBC) + Error(ecUNEXPECTED_PARENS, m_iPos, pOprtDef[i]); + + m_iSynFlags = noBO | noVAR | noVAL | noFUN | noINFIXOP | noSTR | noASSIGN; + + if (!m_bracketStack.empty()) + m_bracketStack.pop(); + else + Error(ecUNEXPECTED_PARENS, m_iPos, pOprtDef[i]); + break; + + case cmELSE: + if (m_iSynFlags & noELSE) + Error(ecUNEXPECTED_CONDITIONAL, m_iPos, pOprtDef[i]); + + m_iSynFlags = noBC | noPOSTOP | noEND | noOPT | noIF | noELSE | noSTR; + break; + + case cmIF: + if (m_iSynFlags & noIF) + Error(ecUNEXPECTED_CONDITIONAL, m_iPos, pOprtDef[i]); + + m_iSynFlags = noBC | noPOSTOP | noEND | noOPT | noIF | noELSE | noSTR; + 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 += (int)len; + a_Tok.Set((ECmdCode)i, pOprtDef[i]); + return true; + } // if operator string found + } // end of for all operator strings + + return false; + } + + + bool ParserTokenReader::IsArgSep(token_type& a_Tok) + { + const char_type* szFormula = m_strFormula.c_str(); + + if (szFormula[m_iPos] == m_cArgSep) + { + // copy the separator into null terminated string + char_type szSep[2]; + 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 ParserTokenReader::IsEOF(token_type& a_Tok) + { + const char_type* szFormula = m_strFormula.c_str(); + + // check for EOF + if (!szFormula[m_iPos] /*|| szFormula[m_iPos] == '\n'*/) + { + if (m_iSynFlags & noEND) + Error(ecUNEXPECTED_EOF, m_iPos); + + if (!m_bracketStack.empty()) + Error(ecMISSING_PARENS, m_iPos, _T(")")); + + 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 ParserTokenReader::IsInfixOpTok(token_type& a_Tok) + { + string_type sTok; + auto iEnd = ExtractToken(m_pParser->ValidInfixOprtChars(), sTok, (std::size_t)m_iPos); + if (iEnd == m_iPos) + return false; + + // iterate over all postfix operator strings + funmap_type::const_reverse_iterator it = m_pInfixOprtDef->rbegin(); + for (; it != m_pInfixOprtDef->rend(); ++it) + { + if (sTok.find(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 | noARG_SEP; + 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 ParserTokenReader::IsFunTok(token_type& a_Tok) + { + string_type strTok; + auto iEnd = ExtractToken(m_pParser->ValidNameChars(), strTok, (std::size_t)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 + const char_type* szFormula = m_strFormula.c_str(); + if (szFormula[iEnd] != '(') + return false; + + a_Tok.Set(item->second, strTok); + + m_iPos = (int)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 ParserTokenReader::IsOprt(token_type& a_Tok) + { + const char_type* const szExpr = m_strFormula.c_str(); + string_type strTok; + + auto iEnd = ExtractOperatorToken(strTok, (std::size_t)m_iPos); + if (iEnd == m_iPos) + return false; + + // Check if the operator is a built in operator, if so ignore it here + const char_type** const pOprtDef = m_pParser->GetOprtDef(); + for (int i = 0; m_pParser->HasBuiltInOprt() && pOprtDef[i]; ++i) + { + if (string_type(pOprtDef[i]) == 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 string_type& sID = it->first; + if (sID == string_type(szExpr + m_iPos, szExpr + 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 | noASSIGN; + return true; + } + } + + return false; + } + + + /** \brief Check if a string position contains a unary post value operator. */ + bool ParserTokenReader::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 + string_type sTok; + auto iEnd = ExtractToken(m_pParser->ValidOprtChars(), sTok, (std::size_t)m_iPos); + if (iEnd == m_iPos) + return false; + + // iterate over all postfix operator strings + funmap_type::const_reverse_iterator it = m_pPostOprtDef->rbegin(); + for (; it != m_pPostOprtDef->rend(); ++it) + { + if (sTok.find(it->first) != 0) + continue; + + a_Tok.Set(it->second, sTok); + m_iPos += (int)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 ParserTokenReader::IsValTok(token_type& a_Tok) + { + MUP_ASSERT(m_pConstDef != nullptr); + MUP_ASSERT(m_pParser != nullptr); + + string_type strTok; + value_type fVal(0); + + // 2.) Check for user defined constant + // Read everything that could be a constant name + auto iEnd = ExtractToken(m_pParser->ValidNameChars(), strTok, (std::size_t)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 - (int)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.c_str() + m_iPos, &m_iPos, &fVal) == 1) + { + // 2013-11-27 Issue 2: https://code.google.com/p/muparser/issues/detail?id=2 + strTok.assign(m_strFormula.c_str(), iStart, (std::size_t)m_iPos - iStart); + + if (m_iSynFlags & noVAL) + Error(ecUNEXPECTED_VAL, m_iPos - (int)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 ParserTokenReader::IsVarTok(token_type& a_Tok) + { + if (m_pVarDef->empty()) + return false; + + string_type strTok; + auto iEnd = ExtractToken(m_pParser->ValidNameChars(), strTok, (std::size_t)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 ParserTokenReader::IsStrVarTok(token_type& a_Tok) + { + if (!m_pStrVarDef || m_pStrVarDef->empty()) + return false; + + string_type strTok; + auto iEnd = ExtractToken(m_pParser->ValidNameChars(), strTok, (std::size_t)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 ParserTokenReader::IsUndefVarTok(token_type& a_Tok) + { + string_type strTok; + auto iEnd(ExtractToken(m_pParser->ValidNameChars(), strTok, (std::size_t)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) + { + value_type* fVar = m_pFactory(strTok.c_str(), 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 previously 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((value_type*)&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 ParserTokenReader::IsString(token_type& a_Tok) + { + if (m_strFormula[m_iPos] != '"') + return false; + + string_type strBuf(&m_strFormula[(std::size_t)m_iPos + 1]); + std::size_t iEnd(0), iSkip(0); + + // parser over escaped '\"' end replace them with '"' + for (iEnd = (int)strBuf.find(_T('\"')); iEnd != 0 && iEnd != string_type::npos; iEnd = (int)strBuf.find(_T('\"'), iEnd)) + { + if (strBuf[iEnd - 1] != '\\') break; + strBuf.replace(iEnd - 1, 2, _T("\"")); + iSkip++; + } + + if (iEnd == string_type::npos) + Error(ecUNTERMINATED_STRING, m_iPos, _T("\"")); + + string_type strTok(strBuf.begin(), strBuf.begin() + 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 + (int)iSkip; // +2 for quotes; +iSkip for escape characters + 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 ParserTokenReader::Error(EErrorCodes a_iErrc, int a_iPos, const string_type& a_sTok) const + { + m_pParser->Error(a_iErrc, a_iPos, a_sTok); + } + + + void ParserTokenReader::SetArgSep(char_type cArgSep) + { + m_cArgSep = cArgSep; + } + + + char_type ParserTokenReader::GetArgSep() const + { + return m_cArgSep; + } +} // namespace mu + +#if defined(_MSC_VER) + #pragma warning(pop) +#endif diff --git a/bundled/setup_bundled.cmake b/bundled/setup_bundled.cmake index 11cdeaf44c..e84e888aed 100644 --- a/bundled/setup_bundled.cmake +++ b/bundled/setup_bundled.cmake @@ -83,4 +83,4 @@ OPTION(DEAL_II_FORCE_BUNDLED_MUPARSER "Always use the bundled functionparser library instead of an external one." OFF) -SET(MUPARSER_FOLDER "${CMAKE_SOURCE_DIR}/bundled/muparser_v2_2_4/") +SET(MUPARSER_FOLDER "${CMAKE_SOURCE_DIR}/bundled/muparser_v2_3_3/") diff --git a/doc/news/changes/minor/20220328DavidWells b/doc/news/changes/minor/20220328DavidWells new file mode 100644 index 0000000000..6d433f497e --- /dev/null +++ b/doc/news/changes/minor/20220328DavidWells @@ -0,0 +1,3 @@ +Changed: The bundled version of muParser has been updated to version 2.3.3. +
+(David Wells, 2022/03/28)