+++ /dev/null
-#######################################################################\r
-# #\r
-# #\r
-# __________ #\r
-# _____ __ __\______ \_____ _______ ______ ____ _______ #\r
-# / \ | | \| ___/\__ \ \_ __ \/ ___/_/ __ \\_ __ \ #\r
-# | Y Y \| | /| | / __ \_| | \/\___ \ \ ___/ | | \/ #\r
-# |__|_| /|____/ |____| (____ /|__| /____ > \___ >|__| #\r
-# \/ \/ \/ \/ #\r
-# Fast math parser Library #\r
-# #\r
-# Copyright (C) 2011 Ingo Berg #\r
-# #\r
-# Web: muparser.beltoforion.de #\r
-# e-mail: muparser@beltoforion.de #\r
-# #\r
-# #\r
-#######################################################################\r
-\r
-\r
- Permission is hereby granted, free of charge, to any person obtaining a copy of this \r
- software and associated documentation files (the "Software"), to deal in the Software\r
- without restriction, including without limitation the rights to use, copy, modify, \r
- merge, publish, distribute, sublicense, and/or sell copies of the Software, and to \r
- permit persons to whom the Software is furnished to do so, subject to the following conditions:\r
-\r
- The above copyright notice and this permission notice shall be included in all copies or \r
- substantial portions of the Software.\r
-\r
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT\r
- NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND \r
- NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, \r
- DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, \r
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \r
- OR OTHER DEALINGS IN THE SOFTWARE.\r
+++ /dev/null
-/*\r
- __________ \r
- _____ __ __\______ \_____ _______ ______ ____ _______ \r
- / \ | | \| ___/\__ \ \_ __ \/ ___/_/ __ \\_ __ \\r
- | Y Y \| | /| | / __ \_| | \/\___ \ \ ___/ | | \/\r
- |__|_| /|____/ |____| (____ /|__| /____ > \___ >|__| \r
- \/ \/ \/ \/ \r
- Copyright (C) 2013 Ingo Berg\r
-\r
- Permission is hereby granted, free of charge, to any person obtaining a copy of this \r
- software and associated documentation files (the "Software"), to deal in the Software\r
- without restriction, including without limitation the rights to use, copy, modify, \r
- merge, publish, distribute, sublicense, and/or sell copies of the Software, and to \r
- permit persons to whom the Software is furnished to do so, subject to the following conditions:\r
-\r
- The above copyright notice and this permission notice shall be included in all copies or \r
- substantial portions of the Software.\r
-\r
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT\r
- NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND \r
- NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, \r
- DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, \r
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \r
-*/\r
-#ifndef MU_PARSER_H\r
-#define MU_PARSER_H\r
-\r
-//--- Standard includes ------------------------------------------------------------------------\r
-#include <vector>\r
-\r
-//--- Parser includes --------------------------------------------------------------------------\r
-#include "muParserBase.h"\r
-#include "muParserTemplateMagic.h"\r
-\r
-/** \file\r
- \brief Definition of the standard floating point parser.\r
-*/\r
-\r
-namespace mu\r
-{\r
- /** \brief Mathematical expressions parser.\r
- \r
- Standard implementation of the mathematical expressions parser. \r
- Can be used as a reference implementation for subclassing the parser.\r
-\r
- <small>\r
- (C) 2011 Ingo Berg<br>\r
- muparser(at)beltoforion.de\r
- </small>\r
- */\r
- /* final */ class Parser : public ParserBase\r
- {\r
- public:\r
-\r
- Parser();\r
-\r
- virtual void InitCharSets() override;\r
- virtual void InitFun() override;\r
- virtual void InitConst() override;\r
- virtual void InitOprt() override;\r
- virtual void OnDetectVar(string_type *pExpr, int &nStart, int &nEnd) override;\r
-\r
- value_type Diff(value_type *a_Var, \r
- value_type a_fPos, \r
- value_type a_fEpsilon = 0) const;\r
-\r
- protected:\r
-\r
- // Trigonometric functions\r
- static value_type Sin(value_type);\r
- static value_type Cos(value_type);\r
- static value_type Tan(value_type);\r
- static value_type Tan2(value_type, value_type);\r
- // arcus functions\r
- static value_type ASin(value_type);\r
- static value_type ACos(value_type);\r
- static value_type ATan(value_type);\r
- static value_type ATan2(value_type, value_type);\r
-\r
- // hyperbolic functions\r
- static value_type Sinh(value_type);\r
- static value_type Cosh(value_type);\r
- static value_type Tanh(value_type);\r
- // arcus hyperbolic functions\r
- static value_type ASinh(value_type);\r
- static value_type ACosh(value_type);\r
- static value_type ATanh(value_type);\r
- // Logarithm functions\r
- static value_type Log2(value_type); // Logarithm Base 2\r
- static value_type Log10(value_type); // Logarithm Base 10\r
- static value_type Ln(value_type); // Logarithm Base e (natural logarithm)\r
- // misc\r
- static value_type Exp(value_type);\r
- static value_type Abs(value_type);\r
- static value_type Sqrt(value_type);\r
- static value_type Rint(value_type);\r
- static value_type Sign(value_type);\r
-\r
- // Prefix operators\r
- // !!! Unary Minus is a MUST if you want to use negative signs !!!\r
- static value_type UnaryMinus(value_type);\r
- static value_type UnaryPlus(value_type);\r
-\r
- // Functions with variable number of arguments\r
- static value_type Sum(const value_type*, int); // sum\r
- static value_type Avg(const value_type*, int); // mean value\r
- static value_type Min(const value_type*, int); // minimum\r
- static value_type Max(const value_type*, int); // maximum\r
-\r
- static int IsVal(const char_type* a_szExpr, int *a_iPos, value_type *a_fVal);\r
- };\r
-} // namespace mu\r
-\r
-#endif\r
-\r
+++ /dev/null
-/*\r
- __________ \r
- _____ __ __\______ \_____ _______ ______ ____ _______ \r
- / \ | | \| ___/\__ \ \_ __ \/ ___/_/ __ \\_ __ \\r
- | Y Y \| | /| | / __ \_| | \/\___ \ \ ___/ | | \/\r
- |__|_| /|____/ |____| (____ /|__| /____ > \___ >|__| \r
- \/ \/ \/ \/ \r
- Copyright (C) 2013 Ingo Berg\r
-\r
- Permission is hereby granted, free of charge, to any person obtaining a copy of this \r
- software and associated documentation files (the "Software"), to deal in the Software\r
- without restriction, including without limitation the rights to use, copy, modify, \r
- merge, publish, distribute, sublicense, and/or sell copies of the Software, and to \r
- permit persons to whom the Software is furnished to do so, subject to the following conditions:\r
-\r
- The above copyright notice and this permission notice shall be included in all copies or \r
- substantial portions of the Software.\r
-\r
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT\r
- NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND \r
- NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, \r
- DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, \r
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \r
-*/\r
-#ifndef MU_PARSER_BASE_H\r
-#define MU_PARSER_BASE_H\r
-\r
-//--- Standard includes ------------------------------------------------------------------------\r
-#include <cmath>\r
-#include <string>\r
-#include <iostream>\r
-#include <map>\r
-#include <memory>\r
-#include <locale>\r
-#include <limits.h>\r
-\r
-//--- Parser includes --------------------------------------------------------------------------\r
-#include "muParserDef.h"\r
-#include "muParserStack.h"\r
-#include "muParserTokenReader.h"\r
-#include "muParserBytecode.h"\r
-#include "muParserError.h"\r
-\r
-\r
-namespace mu\r
-{\r
-/** \file\r
- \brief This file contains the class definition of the muparser engine.\r
-*/\r
-\r
-//--------------------------------------------------------------------------------------------------\r
-/** \brief Mathematical expressions parser (base parser engine).\r
- \author (C) 2013 Ingo Berg\r
-\r
- This is the implementation of a bytecode based mathematical expressions parser. \r
- The formula will be parsed from string and converted into a bytecode. \r
- Future calculations will be done with the bytecode instead the formula string\r
- resulting in a significant performance increase. \r
- Complementary to a set of internally implemented functions the parser is able to handle \r
- user defined functions and variables. \r
-*/\r
-class ParserBase \r
-{\r
-friend class ParserTokenReader;\r
-\r
-private:\r
-\r
- /** \brief Typedef for the parse functions. \r
- \r
- The parse function do the actual work. The parser exchanges\r
- the function pointer to the parser function depending on \r
- which state it is in. (i.e. bytecode parser vs. string parser)\r
- */\r
- typedef value_type (ParserBase::*ParseFunction)() const; \r
-\r
- /** \brief Type used for storing an array of values. */\r
- typedef std::vector<value_type> valbuf_type;\r
-\r
- /** \brief Type for a vector of strings. */\r
- typedef std::vector<string_type> stringbuf_type;\r
-\r
- /** \brief Typedef for the token reader. */\r
- typedef ParserTokenReader token_reader_type;\r
- \r
- /** \brief Type used for parser tokens. */\r
- typedef ParserToken<value_type, string_type> token_type;\r
-\r
- /** \brief Maximum number of threads spawned by OpenMP when using the bulk mode. */\r
- static const int s_MaxNumOpenMPThreads = 16;\r
-\r
- public:\r
-\r
- /** \brief Type of the error class. \r
- \r
- Included for backwards compatibility.\r
- */\r
- typedef ParserError exception_type;\r
-\r
- static void EnableDebugDump(bool bDumpCmd, bool bDumpStack);\r
-\r
- ParserBase(); \r
- ParserBase(const ParserBase &a_Parser);\r
- ParserBase& operator=(const ParserBase &a_Parser);\r
-\r
- virtual ~ParserBase();\r
- \r
- value_type Eval() const;\r
- value_type* Eval(int &nStackSize) const;\r
- void Eval(value_type *results, int nBulkSize);\r
-\r
- int GetNumResults() const;\r
-\r
- void SetExpr(const string_type &a_sExpr);\r
- void SetVarFactory(facfun_type a_pFactory, void *pUserData = NULL);\r
-\r
- void SetDecSep(char_type cDecSep);\r
- void SetThousandsSep(char_type cThousandsSep = 0);\r
- void ResetLocale();\r
-\r
- void EnableOptimizer(bool a_bIsOn=true);\r
- void EnableBuiltInOprt(bool a_bIsOn=true);\r
-\r
- bool HasBuiltInOprt() const;\r
- void AddValIdent(identfun_type a_pCallback);\r
-\r
- /** \fn void mu::ParserBase::DefineFun(const string_type &a_strName, fun_type0 a_pFun, bool a_bAllowOpt = true) \r
- \brief Define a parser function without arguments.\r
- \param a_strName Name of the function\r
- \param a_pFun Pointer to the callback function\r
- \param a_bAllowOpt A flag indicating this function may be optimized\r
- */\r
- template<typename T>\r
- void DefineFun(const string_type &a_strName, T a_pFun, bool a_bAllowOpt = true)\r
- {\r
- AddCallback( a_strName, ParserCallback(a_pFun, a_bAllowOpt), m_FunDef, ValidNameChars() );\r
- }\r
-\r
- void DefineOprt(const string_type &a_strName, \r
- fun_type2 a_pFun, \r
- unsigned a_iPri=0, \r
- EOprtAssociativity a_eAssociativity = oaLEFT,\r
- bool a_bAllowOpt = false);\r
- void DefineConst(const string_type &a_sName, value_type a_fVal);\r
- void DefineStrConst(const string_type &a_sName, const string_type &a_strVal);\r
- void DefineVar(const string_type &a_sName, value_type *a_fVar);\r
- void DefinePostfixOprt(const string_type &a_strFun, fun_type1 a_pOprt, bool a_bAllowOpt=true);\r
- void DefineInfixOprt(const string_type &a_strName, fun_type1 a_pOprt, int a_iPrec=prINFIX, bool a_bAllowOpt=true);\r
-\r
- // Clear user defined variables, constants or functions\r
- void ClearVar();\r
- void ClearFun();\r
- void ClearConst();\r
- void ClearInfixOprt();\r
- void ClearPostfixOprt();\r
- void ClearOprt();\r
- \r
- void RemoveVar(const string_type &a_strVarName);\r
- const varmap_type& GetUsedVar() const;\r
- const varmap_type& GetVar() const;\r
- const valmap_type& GetConst() const;\r
- const string_type& GetExpr() const;\r
- const funmap_type& GetFunDef() const;\r
- string_type GetVersion(EParserVersionInfo eInfo = pviFULL) const;\r
-\r
- const char_type ** GetOprtDef() const;\r
- void DefineNameChars(const char_type *a_szCharset);\r
- void DefineOprtChars(const char_type *a_szCharset);\r
- void DefineInfixOprtChars(const char_type *a_szCharset);\r
-\r
- const char_type* ValidNameChars() const;\r
- const char_type* ValidOprtChars() const;\r
- const char_type* ValidInfixOprtChars() const;\r
-\r
- void SetArgSep(char_type cArgSep);\r
- char_type GetArgSep() const;\r
- \r
- void Error(EErrorCodes a_iErrc, \r
- int a_iPos = (int)mu::string_type::npos, \r
- const string_type &a_strTok = string_type() ) const;\r
-\r
- protected:\r
- \r
- void Init();\r
-\r
- virtual void InitCharSets() = 0;\r
- virtual void InitFun() = 0;\r
- virtual void InitConst() = 0;\r
- virtual void InitOprt() = 0; \r
-\r
- virtual void OnDetectVar(string_type *pExpr, int &nStart, int &nEnd);\r
-\r
- static const char_type *c_DefaultOprt[]; \r
- static std::locale s_locale; ///< The locale used by the parser\r
- static bool g_DbgDumpCmdCode;\r
- static bool g_DbgDumpStack;\r
-\r
- /** \brief A facet class used to change decimal and thousands separator. */\r
- template<class TChar>\r
- class change_dec_sep : public std::numpunct<TChar>\r
- {\r
- public:\r
- \r
- explicit change_dec_sep(char_type cDecSep, char_type cThousandsSep = 0, int nGroup = 3)\r
- :std::numpunct<TChar>()\r
- ,m_nGroup(nGroup)\r
- ,m_cDecPoint(cDecSep)\r
- ,m_cThousandsSep(cThousandsSep)\r
- {}\r
- \r
- protected:\r
- \r
- virtual char_type do_decimal_point() const override\r
- {\r
- return m_cDecPoint;\r
- }\r
-\r
- virtual char_type do_thousands_sep() const override\r
- {\r
- return m_cThousandsSep;\r
- }\r
-\r
- virtual std::string do_grouping() const override\r
- { \r
- // fix for issue 4: https://code.google.com/p/muparser/issues/detail?id=4\r
- // courtesy of Jens Bartsch\r
- // original code:\r
- // return std::string(1, (char)m_nGroup); \r
- // new code:\r
- return std::string(1, (char)(m_cThousandsSep > 0 ? m_nGroup : CHAR_MAX));\r
- }\r
-\r
- private:\r
-\r
- int m_nGroup;\r
- char_type m_cDecPoint; \r
- char_type m_cThousandsSep;\r
- };\r
-\r
- private:\r
-\r
- void Assign(const ParserBase &a_Parser);\r
- void InitTokenReader();\r
- void ReInit() const;\r
-\r
- void AddCallback( const string_type &a_strName, \r
- const ParserCallback &a_Callback, \r
- funmap_type &a_Storage,\r
- const char_type *a_szCharSet );\r
-\r
- void ApplyRemainingOprt(ParserStack<token_type> &a_stOpt,\r
- ParserStack<token_type> &a_stVal) const;\r
- void ApplyBinOprt(ParserStack<token_type> &a_stOpt,\r
- ParserStack<token_type> &a_stVal) const;\r
-\r
- void ApplyIfElse(ParserStack<token_type> &a_stOpt,\r
- ParserStack<token_type> &a_stVal) const;\r
-\r
- void ApplyFunc(ParserStack<token_type> &a_stOpt,\r
- ParserStack<token_type> &a_stVal, \r
- int iArgCount) const; \r
-\r
- token_type ApplyStrFunc(const token_type &a_FunTok,\r
- const std::vector<token_type> &a_vArg) const;\r
-\r
- int GetOprtPrecedence(const token_type &a_Tok) const;\r
- EOprtAssociativity GetOprtAssociativity(const token_type &a_Tok) const;\r
-\r
- void CreateRPN() const;\r
-\r
- value_type ParseString() const; \r
- value_type ParseCmdCode() const;\r
- value_type ParseCmdCodeBulk(int nOffset, int nThreadID) const;\r
-\r
- void CheckName(const string_type &a_strName, const string_type &a_CharSet) const;\r
- void CheckOprt(const string_type &a_sName,\r
- const ParserCallback &a_Callback,\r
- const string_type &a_szCharSet) const;\r
-\r
- void StackDump(const ParserStack<token_type > &a_stVal, \r
- const ParserStack<token_type > &a_stOprt) const;\r
-\r
- /** \brief Pointer to the parser function. \r
- \r
- Eval() calls the function whose address is stored there.\r
- */\r
- mutable ParseFunction m_pParseFormula;\r
- mutable ParserByteCode m_vRPN; ///< The Bytecode class.\r
- mutable stringbuf_type m_vStringBuf; ///< String buffer, used for storing string function arguments\r
- stringbuf_type m_vStringVarBuf;\r
-\r
- std::unique_ptr<token_reader_type> m_pTokenReader; ///< Managed pointer to the token reader object.\r
-\r
- funmap_type m_FunDef; ///< Map of function names and pointers.\r
- funmap_type m_PostOprtDef; ///< Postfix operator callbacks\r
- funmap_type m_InfixOprtDef; ///< unary infix operator.\r
- funmap_type m_OprtDef; ///< Binary operator callbacks\r
- valmap_type m_ConstDef; ///< user constants.\r
- strmap_type m_StrVarDef; ///< user defined string constants\r
- varmap_type m_VarDef; ///< user defind variables.\r
-\r
- bool m_bBuiltInOp; ///< Flag that can be used for switching built in operators on and off\r
-\r
- string_type m_sNameChars; ///< Charset for names\r
- string_type m_sOprtChars; ///< Charset for postfix/ binary operator tokens\r
- string_type m_sInfixOprtChars; ///< Charset for infix operator tokens\r
- \r
- mutable int m_nIfElseCounter; ///< Internal counter for keeping track of nested if-then-else clauses\r
-\r
- // items merely used for caching state information\r
- mutable valbuf_type m_vStackBuffer; ///< This is merely a buffer used for the stack in the cmd parsing routine\r
- mutable int m_nFinalResultIdx;\r
-};\r
-\r
-} // namespace mu\r
-\r
-#endif\r
-\r
+++ /dev/null
-/*\r
- __________ \r
- _____ __ __\______ \_____ _______ ______ ____ _______ \r
- / \ | | \| ___/\__ \ \_ __ \/ ___/_/ __ \\_ __ \\r
- | Y Y \| | /| | / __ \_| | \/\___ \ \ ___/ | | \/\r
- |__|_| /|____/ |____| (____ /|__| /____ > \___ >|__| \r
- \/ \/ \/ \/ \r
- Copyright (C) 2004-2013 Ingo Berg\r
-\r
- Permission is hereby granted, free of charge, to any person obtaining a copy of this \r
- software and associated documentation files (the "Software"), to deal in the Software\r
- without restriction, including without limitation the rights to use, copy, modify, \r
- merge, publish, distribute, sublicense, and/or sell copies of the Software, and to \r
- permit persons to whom the Software is furnished to do so, subject to the following conditions:\r
-\r
- The above copyright notice and this permission notice shall be included in all copies or \r
- substantial portions of the Software.\r
-\r
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT\r
- NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND \r
- NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, \r
- DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, \r
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \r
-*/\r
-#ifndef MU_PARSER_BYTECODE_H\r
-#define MU_PARSER_BYTECODE_H\r
-\r
-#include <cassert>\r
-#include <string>\r
-#include <stack>\r
-#include <vector>\r
-\r
-#include "muParserDef.h"\r
-#include "muParserError.h"\r
-#include "muParserToken.h"\r
-\r
-/** \file\r
- \brief Definition of the parser bytecode class.\r
-*/\r
-\r
-\r
-namespace mu\r
-{\r
- struct SToken\r
- {\r
- ECmdCode Cmd;\r
- int StackPos;\r
-\r
- union\r
- {\r
- struct //SValData\r
- {\r
- value_type *ptr;\r
- value_type data;\r
- value_type data2;\r
- } Val;\r
-\r
- struct //SFunData\r
- {\r
- // Note: generic_fun_type is merely a placeholder. The real type could be \r
- // anything between gun_type1 and fun_type9. I can't use a void\r
- // pointer due to constraints in the ANSI standard which allows\r
- // data pointers and function pointers to differ in size.\r
- generic_fun_type ptr;\r
- int argc;\r
- int idx;\r
- } Fun;\r
-\r
- struct //SOprtData\r
- {\r
- value_type *ptr;\r
- int offset;\r
- } Oprt;\r
- } u;\r
- };\r
- \r
- \r
- /** \brief Bytecode implementation of the Math Parser.\r
-\r
- The bytecode contains the formula converted to revers polish notation stored in a continious\r
- memory area. Associated with this data are operator codes, variable pointers, constant \r
- values and function pointers. Those are necessary in order to calculate the result.\r
- All those data items will be casted to the underlying datatype of the bytecode.\r
-\r
- \author (C) 2004-2013 Ingo Berg \r
-*/\r
-class ParserByteCode\r
-{\r
-private:\r
-\r
- /** \brief Token type for internal use only. */\r
- typedef ParserToken<value_type, string_type> token_type;\r
-\r
- /** \brief Token vector for storing the RPN. */\r
- typedef std::vector<SToken> rpn_type;\r
-\r
- /** \brief Position in the Calculation array. */\r
- unsigned m_iStackPos;\r
-\r
- /** \brief Maximum size needed for the stack. */\r
- std::size_t m_iMaxStackSize;\r
- \r
- /** \brief The actual rpn storage. */\r
- rpn_type m_vRPN;\r
-\r
- bool m_bEnableOptimizer;\r
-\r
- void ConstantFolding(ECmdCode a_Oprt);\r
-\r
-public:\r
-\r
- ParserByteCode();\r
- ParserByteCode(const ParserByteCode &a_ByteCode);\r
- ParserByteCode& operator=(const ParserByteCode &a_ByteCode);\r
- void Assign(const ParserByteCode &a_ByteCode);\r
-\r
- void AddVar(value_type *a_pVar);\r
- void AddVal(value_type a_fVal);\r
- void AddOp(ECmdCode a_Oprt);\r
- void AddIfElse(ECmdCode a_Oprt);\r
- void AddAssignOp(value_type *a_pVar);\r
- void AddFun(generic_fun_type a_pFun, int a_iArgc);\r
- void AddBulkFun(generic_fun_type a_pFun, int a_iArgc);\r
- void AddStrFun(generic_fun_type a_pFun, int a_iArgc, int a_iIdx);\r
-\r
- void EnableOptimizer(bool bStat);\r
-\r
- void Finalize();\r
- void clear();\r
- std::size_t GetMaxStackSize() const;\r
- std::size_t GetSize() const;\r
-\r
- const SToken* GetBase() const;\r
- void AsciiDump();\r
-};\r
-\r
-} // namespace mu\r
-\r
-#endif\r
-\r
-\r
+++ /dev/null
-/*\r
- __________ \r
- _____ __ __\______ \_____ _______ ______ ____ _______ \r
- / \ | | \| ___/\__ \ \_ __ \/ ___/_/ __ \\_ __ \\r
- | Y Y \| | /| | / __ \_| | \/\___ \ \ ___/ | | \/\r
- |__|_| /|____/ |____| (____ /|__| /____ > \___ >|__| \r
- \/ \/ \/ \/ \r
- Copyright (C) 2004-2011 Ingo Berg\r
-\r
- Permission is hereby granted, free of charge, to any person obtaining a copy of this \r
- software and associated documentation files (the "Software"), to deal in the Software\r
- without restriction, including without limitation the rights to use, copy, modify, \r
- merge, publish, distribute, sublicense, and/or sell copies of the Software, and to \r
- permit persons to whom the Software is furnished to do so, subject to the following conditions:\r
-\r
- The above copyright notice and this permission notice shall be included in all copies or \r
- substantial portions of the Software.\r
-\r
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT\r
- NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND \r
- NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, \r
- DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, \r
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \r
-*/\r
-\r
-#ifndef MU_PARSER_CALLBACK_H\r
-#define MU_PARSER_CALLBACK_H\r
-\r
-#include "muParserDef.h"\r
-\r
-/** \file\r
- \brief Definition of the parser callback class.\r
-*/\r
-\r
-namespace mu\r
-{\r
-\r
-/** \brief Encapsulation of prototypes for a numerical parser function.\r
-\r
- Encapsulates the prototyp for numerical parser functions. The class\r
- stores the number of arguments for parser functions as well\r
- as additional flags indication the function is non optimizeable.\r
- The pointer to the callback function pointer is stored as void* \r
- and needs to be casted according to the argument count.\r
- Negative argument counts indicate a parser function with a variable number\r
- of arguments. \r
-\r
- \author (C) 2004-2011 Ingo Berg\r
-*/\r
-class ParserCallback\r
-{\r
-public:\r
- ParserCallback(fun_type0 a_pFun, bool a_bAllowOpti);\r
- ParserCallback(fun_type1 a_pFun, bool a_bAllowOpti, int a_iPrec = -1, ECmdCode a_iCode=cmFUNC);\r
- ParserCallback(fun_type2 a_pFun, bool a_bAllowOpti, int a_iPrec, EOprtAssociativity a_eAssociativity);\r
- ParserCallback(fun_type2 a_pFun, bool a_bAllowOpti);\r
- ParserCallback(fun_type3 a_pFun, bool a_bAllowOpti);\r
- ParserCallback(fun_type4 a_pFun, bool a_bAllowOpti);\r
- ParserCallback(fun_type5 a_pFun, bool a_bAllowOpti);\r
- ParserCallback(fun_type6 a_pFun, bool a_bAllowOpti);\r
- ParserCallback(fun_type7 a_pFun, bool a_bAllowOpti);\r
- ParserCallback(fun_type8 a_pFun, bool a_bAllowOpti);\r
- ParserCallback(fun_type9 a_pFun, bool a_bAllowOpti);\r
- ParserCallback(fun_type10 a_pFun, bool a_bAllowOpti);\r
-\r
- ParserCallback(bulkfun_type0 a_pFun, bool a_bAllowOpti);\r
- ParserCallback(bulkfun_type1 a_pFun, bool a_bAllowOpti);\r
- ParserCallback(bulkfun_type2 a_pFun, bool a_bAllowOpti);\r
- ParserCallback(bulkfun_type3 a_pFun, bool a_bAllowOpti);\r
- ParserCallback(bulkfun_type4 a_pFun, bool a_bAllowOpti);\r
- ParserCallback(bulkfun_type5 a_pFun, bool a_bAllowOpti);\r
- ParserCallback(bulkfun_type6 a_pFun, bool a_bAllowOpti);\r
- ParserCallback(bulkfun_type7 a_pFun, bool a_bAllowOpti);\r
- ParserCallback(bulkfun_type8 a_pFun, bool a_bAllowOpti);\r
- ParserCallback(bulkfun_type9 a_pFun, bool a_bAllowOpti);\r
- ParserCallback(bulkfun_type10 a_pFun, bool a_bAllowOpti);\r
-\r
- ParserCallback(multfun_type a_pFun, bool a_bAllowOpti);\r
- ParserCallback(strfun_type1 a_pFun, bool a_bAllowOpti);\r
- ParserCallback(strfun_type2 a_pFun, bool a_bAllowOpti);\r
- ParserCallback(strfun_type3 a_pFun, bool a_bAllowOpti);\r
- ParserCallback();\r
- ParserCallback(const ParserCallback &a_Fun);\r
-\r
- // deall.II: added the following line to remove Wdeprecated-copy warnings:\r
- ParserCallback& operator=(const ParserCallback&)=default;\r
- \r
- ParserCallback* Clone() const;\r
-\r
- bool IsOptimizable() const;\r
- void* GetAddr() const;\r
- ECmdCode GetCode() const;\r
- ETypeCode GetType() const;\r
- int GetPri() const;\r
- EOprtAssociativity GetAssociativity() const;\r
- int GetArgc() const;\r
-\r
-private:\r
- void *m_pFun; ///< Pointer to the callback function, casted to void\r
- \r
- /** \brief Number of numeric function arguments\r
- \r
- This number is negative for functions with variable number of arguments. in this cases\r
- they represent the actual number of arguments found.\r
- */\r
- int m_iArgc; \r
- int m_iPri; ///< Valid only for binary and infix operators; Operator precedence.\r
- EOprtAssociativity m_eOprtAsct; ///< Operator associativity; Valid only for binary operators \r
- ECmdCode m_iCode;\r
- ETypeCode m_iType;\r
- bool m_bAllowOpti; ///< Flag indication optimizeability \r
-};\r
-\r
-//------------------------------------------------------------------------------\r
-/** \brief Container for Callback objects. */\r
-typedef std::map<string_type, ParserCallback> funmap_type; \r
-\r
-} // namespace mu\r
-\r
-#endif\r
-\r
+++ /dev/null
-/*\r
- __________ \r
- _____ __ __\______ \_____ _______ ______ ____ _______ \r
- / \ | | \| ___/\__ \ \_ __ \/ ___/_/ __ \\_ __ \\r
- | Y Y \| | /| | / __ \_| | \/\___ \ \ ___/ | | \/\r
- |__|_| /|____/ |____| (____ /|__| /____ > \___ >|__| \r
- \/ \/ \/ \/ \r
- Copyright (C) 2014 Ingo Berg\r
-\r
- Permission is hereby granted, free of charge, to any person obtaining a copy of this \r
- software and associated documentation files (the "Software"), to deal in the Software\r
- without restriction, including without limitation the rights to use, copy, modify, \r
- merge, publish, distribute, sublicense, and/or sell copies of the Software, and to \r
- permit persons to whom the Software is furnished to do so, subject to the following conditions:\r
-\r
- The above copyright notice and this permission notice shall be included in all copies or \r
- substantial portions of the Software.\r
-\r
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT\r
- NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND \r
- NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, \r
- DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, \r
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \r
-*/\r
-#ifndef MUP_DEF_H\r
-#define MUP_DEF_H\r
-\r
-#include <iostream>\r
-#include <string>\r
-#include <sstream>\r
-#include <map>\r
-\r
-#include "muParserFixes.h"\r
-\r
-/** \file\r
- \brief This file contains standard definitions used by the parser.\r
-*/\r
-\r
-#define MUP_VERSION _T("2.2.4")\r
-#define MUP_VERSION_DATE _T("20141210; GC")\r
-\r
-#define MUP_CHARS _T("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")\r
-\r
-/** \brief If this macro is defined mathematical exceptions (div by zero) will be thrown as exceptions. */\r
-//#define MUP_MATH_EXCEPTIONS\r
-\r
-/** \brief Define the base datatype for values.\r
-\r
- This datatype must be a built in value type. You can not use custom classes.\r
- It should be working with all types except "int"!\r
-*/\r
-#define MUP_BASETYPE double\r
-\r
-/** \brief Activate this option in order to compile with OpenMP support. \r
-\r
- OpenMP is used only in the bulk mode it may increase the performance a bit. \r
-*/\r
-//#define MUP_USE_OPENMP\r
-\r
-#if defined(_UNICODE)\r
- /** \brief Definition of the basic parser string type. */\r
- #define MUP_STRING_TYPE std::wstring\r
-\r
- #if !defined(_T)\r
- #define _T(x) L##x\r
- #endif // not defined _T\r
-#else\r
- #ifndef _T\r
- #define _T(x) x\r
- #endif\r
- \r
- /** \brief Definition of the basic parser string type. */\r
- #define MUP_STRING_TYPE std::string\r
-#endif\r
-\r
-#if defined(_DEBUG)\r
- /** \brief Debug macro to force an abortion of the programm with a certain message.\r
- */\r
- #define MUP_FAIL(MSG) \\r
- { \\r
- bool MSG=false; \\r
- assert(MSG); \\r
- }\r
-\r
- /** \brief An assertion that does not kill the program.\r
-\r
- This macro is neutralised in UNICODE builds. It's\r
- too difficult to translate.\r
- */\r
- #define MUP_ASSERT(COND) \\r
- if (!(COND)) \\r
- { \\r
- stringstream_type ss; \\r
- ss << _T("Assertion \"") _T(#COND) _T("\" failed: ") \\r
- << __FILE__ << _T(" line ") \\r
- << __LINE__ << _T("."); \\r
- throw ParserError( ss.str() ); \\r
- }\r
-#else\r
- #define MUP_FAIL(MSG)\r
- #define MUP_ASSERT(COND)\r
-#endif\r
-\r
-\r
-namespace mu\r
-{\r
-#if defined(_UNICODE)\r
-\r
- //------------------------------------------------------------------------------\r
- /** \brief Encapsulate wcout. */\r
- inline std::wostream& console()\r
- {\r
- return std::wcout;\r
- }\r
-\r
- /** \brief Encapsulate cin. */\r
- inline std::wistream& console_in()\r
- {\r
- return std::wcin;\r
- }\r
-\r
-#else\r
-\r
- /** \brief Encapsulate cout. \r
- \r
- Used for supporting UNICODE more easily.\r
- */\r
- inline std::ostream& console()\r
- {\r
- return std::cout;\r
- }\r
-\r
- /** \brief Encapsulate cin. \r
-\r
- Used for supporting UNICODE more easily.\r
- */\r
- inline std::istream& console_in()\r
- {\r
- return std::cin;\r
- }\r
-\r
-#endif\r
-\r
- //------------------------------------------------------------------------------\r
- /** \brief Bytecode values.\r
-\r
- \attention The order of the operator entries must match the order in ParserBase::c_DefaultOprt!\r
- */\r
- enum ECmdCode\r
- {\r
- // The following are codes for built in binary operators\r
- // apart from built in operators the user has the opportunity to\r
- // add user defined operators.\r
- cmLE = 0, ///< Operator item: less or equal\r
- cmGE = 1, ///< Operator item: greater or equal\r
- cmNEQ = 2, ///< Operator item: not equal\r
- cmEQ = 3, ///< Operator item: equals\r
- cmLT = 4, ///< Operator item: less than\r
- cmGT = 5, ///< Operator item: greater than\r
- cmADD = 6, ///< Operator item: add\r
- cmSUB = 7, ///< Operator item: subtract\r
- cmMUL = 8, ///< Operator item: multiply\r
- cmDIV = 9, ///< Operator item: division\r
- cmPOW = 10, ///< Operator item: y to the power of ...\r
- cmLAND = 11,\r
- cmLOR = 12,\r
- cmASSIGN = 13, ///< Operator item: Assignment operator\r
- cmBO = 14, ///< Operator item: opening bracket\r
- cmBC = 15, ///< Operator item: closing bracket\r
- cmIF = 16, ///< For use in the ternary if-then-else operator\r
- cmELSE = 17, ///< For use in the ternary if-then-else operator\r
- cmENDIF = 18, ///< For use in the ternary if-then-else operator\r
- cmARG_SEP = 19, ///< function argument separator\r
- cmVAR = 20, ///< variable item\r
- cmVAL = 21, ///< value item\r
-\r
- // For optimization purposes\r
- cmVARPOW2,\r
- cmVARPOW3,\r
- cmVARPOW4,\r
- cmVARMUL,\r
- cmPOW2,\r
-\r
- // operators and functions\r
- cmFUNC, ///< Code for a generic function item\r
- cmFUNC_STR, ///< Code for a function with a string parameter\r
- cmFUNC_BULK, ///< Special callbacks for Bulk mode with an additional parameter for the bulk index \r
- cmSTRING, ///< Code for a string token\r
- cmOPRT_BIN, ///< user defined binary operator\r
- cmOPRT_POSTFIX, ///< code for postfix operators\r
- cmOPRT_INFIX, ///< code for infix operators\r
- cmEND, ///< end of formula\r
- cmUNKNOWN ///< uninitialized item\r
- };\r
-\r
- //------------------------------------------------------------------------------\r
- /** \brief Types internally used by the parser.\r
- */\r
- enum ETypeCode\r
- {\r
- tpSTR = 0, ///< String type (Function arguments and constants only, no string variables)\r
- tpDBL = 1, ///< Floating point variables\r
- tpVOID = 2 ///< Undefined type.\r
- };\r
-\r
- //------------------------------------------------------------------------------\r
- enum EParserVersionInfo\r
- {\r
- pviBRIEF,\r
- pviFULL\r
- };\r
-\r
- //------------------------------------------------------------------------------\r
- /** \brief Parser operator precedence values. */\r
- enum EOprtAssociativity\r
- {\r
- oaLEFT = 0,\r
- oaRIGHT = 1,\r
- oaNONE = 2\r
- };\r
-\r
- //------------------------------------------------------------------------------\r
- /** \brief Parser operator precedence values. */\r
- enum EOprtPrecedence\r
- {\r
- // binary operators\r
- prLOR = 1,\r
- prLAND = 2,\r
- prLOGIC = 3, ///< logic operators\r
- prCMP = 4, ///< comparsion operators\r
- prADD_SUB = 5, ///< addition\r
- prMUL_DIV = 6, ///< multiplication/division\r
- prPOW = 7, ///< power operator priority (highest)\r
-\r
- // infix operators\r
- prINFIX = 6, ///< Signs have a higher priority than ADD_SUB, but lower than power operator\r
- prPOSTFIX = 6 ///< Postfix operator priority (currently unused)\r
- };\r
-\r
- //------------------------------------------------------------------------------\r
- // basic types\r
-\r
- /** \brief The numeric datatype used by the parser. \r
- \r
- Normally this is a floating point type either single or double precision.\r
- */\r
- typedef MUP_BASETYPE value_type;\r
-\r
- /** \brief The stringtype used by the parser. \r
-\r
- Depends on wether UNICODE is used or not.\r
- */\r
- typedef MUP_STRING_TYPE string_type;\r
-\r
- /** \brief The character type used by the parser. \r
- \r
- Depends on wether UNICODE is used or not.\r
- */\r
- typedef string_type::value_type char_type;\r
-\r
- /** \brief Typedef for easily using stringstream that respect the parser stringtype. */\r
- typedef std::basic_stringstream<char_type,\r
- std::char_traits<char_type>,\r
- std::allocator<char_type> > stringstream_type;\r
-\r
- // Data container types\r
-\r
- /** \brief Type used for storing variables. */\r
- typedef std::map<string_type, value_type*> varmap_type;\r
- \r
- /** \brief Type used for storing constants. */\r
- typedef std::map<string_type, value_type> valmap_type;\r
- \r
- /** \brief Type for assigning a string name to an index in the internal string table. */\r
- typedef std::map<string_type, std::size_t> strmap_type;\r
-\r
- // Parser callbacks\r
- \r
- /** \brief Callback type used for functions without arguments. */\r
- typedef value_type (*generic_fun_type)();\r
-\r
- /** \brief Callback type used for functions without arguments. */\r
- typedef value_type (*fun_type0)();\r
-\r
- /** \brief Callback type used for functions with a single arguments. */\r
- typedef value_type (*fun_type1)(value_type);\r
-\r
- /** \brief Callback type used for functions with two arguments. */\r
- typedef value_type (*fun_type2)(value_type, value_type);\r
-\r
- /** \brief Callback type used for functions with three arguments. */\r
- typedef value_type (*fun_type3)(value_type, value_type, value_type);\r
-\r
- /** \brief Callback type used for functions with four arguments. */\r
- typedef value_type (*fun_type4)(value_type, value_type, value_type, value_type);\r
-\r
- /** \brief Callback type used for functions with five arguments. */\r
- typedef value_type (*fun_type5)(value_type, value_type, value_type, value_type, value_type);\r
-\r
- /** \brief Callback type used for functions with five arguments. */\r
- typedef value_type (*fun_type6)(value_type, value_type, value_type, value_type, value_type, value_type);\r
-\r
- /** \brief Callback type used for functions with five arguments. */\r
- typedef value_type (*fun_type7)(value_type, value_type, value_type, value_type, value_type, value_type, value_type);\r
-\r
- /** \brief Callback type used for functions with five arguments. */\r
- typedef value_type (*fun_type8)(value_type, value_type, value_type, value_type, value_type, value_type, value_type, value_type);\r
-\r
- /** \brief Callback type used for functions with five arguments. */\r
- typedef value_type (*fun_type9)(value_type, value_type, value_type, value_type, value_type, value_type, value_type, value_type, value_type);\r
-\r
- /** \brief Callback type used for functions with five arguments. */\r
- 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);\r
-\r
- /** \brief Callback type used for functions without arguments. */\r
- typedef value_type (*bulkfun_type0)(int, int);\r
-\r
- /** \brief Callback type used for functions with a single arguments. */\r
- typedef value_type (*bulkfun_type1)(int, int, value_type);\r
-\r
- /** \brief Callback type used for functions with two arguments. */\r
- typedef value_type (*bulkfun_type2)(int, int, value_type, value_type);\r
-\r
- /** \brief Callback type used for functions with three arguments. */\r
- typedef value_type (*bulkfun_type3)(int, int, value_type, value_type, value_type);\r
-\r
- /** \brief Callback type used for functions with four arguments. */\r
- typedef value_type (*bulkfun_type4)(int, int, value_type, value_type, value_type, value_type);\r
-\r
- /** \brief Callback type used for functions with five arguments. */\r
- typedef value_type (*bulkfun_type5)(int, int, value_type, value_type, value_type, value_type, value_type);\r
-\r
- /** \brief Callback type used for functions with five arguments. */\r
- typedef value_type (*bulkfun_type6)(int, int, value_type, value_type, value_type, value_type, value_type, value_type);\r
-\r
- /** \brief Callback type used for functions with five arguments. */\r
- typedef value_type (*bulkfun_type7)(int, int, value_type, value_type, value_type, value_type, value_type, value_type, value_type);\r
-\r
- /** \brief Callback type used for functions with five arguments. */\r
- typedef value_type (*bulkfun_type8)(int, int, value_type, value_type, value_type, value_type, value_type, value_type, value_type, value_type);\r
-\r
- /** \brief Callback type used for functions with five arguments. */\r
- 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);\r
-\r
- /** \brief Callback type used for functions with five arguments. */\r
- 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);\r
-\r
- /** \brief Callback type used for functions with a variable argument list. */\r
- typedef value_type (*multfun_type)(const value_type*, int);\r
-\r
- /** \brief Callback type used for functions taking a string as an argument. */\r
- typedef value_type (*strfun_type1)(const char_type*);\r
-\r
- /** \brief Callback type used for functions taking a string and a value as arguments. */\r
- typedef value_type (*strfun_type2)(const char_type*, value_type);\r
-\r
- /** \brief Callback type used for functions taking a string and two values as arguments. */\r
- typedef value_type (*strfun_type3)(const char_type*, value_type, value_type);\r
-\r
- /** \brief Callback used for functions that identify values in a string. */\r
- typedef int (*identfun_type)(const char_type *sExpr, int *nPos, value_type *fVal);\r
-\r
- /** \brief Callback used for variable creation factory functions. */\r
- typedef value_type* (*facfun_type)(const char_type*, void*);\r
-} // end of namespace\r
-\r
-#endif\r
-\r
+++ /dev/null
-/*\r
- __________ \r
- _____ __ __\______ \_____ _______ ______ ____ _______ \r
- / \ | | \| ___/\__ \ \_ __ \/ ___/_/ __ \\_ __ \\r
- | Y Y \| | /| | / __ \_| | \/\___ \ \ ___/ | | \/\r
- |__|_| /|____/ |____| (____ /|__| /____ > \___ >|__| \r
- \/ \/ \/ \/ \r
- Copyright (C) 2004-2011 Ingo Berg\r
-\r
- Permission is hereby granted, free of charge, to any person obtaining a copy of this \r
- software and associated documentation files (the "Software"), to deal in the Software\r
- without restriction, including without limitation the rights to use, copy, modify, \r
- merge, publish, distribute, sublicense, and/or sell copies of the Software, and to \r
- permit persons to whom the Software is furnished to do so, subject to the following conditions:\r
-\r
- The above copyright notice and this permission notice shall be included in all copies or \r
- substantial portions of the Software.\r
-\r
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT\r
- NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND \r
- NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, \r
- DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, \r
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \r
-*/\r
-\r
-#ifndef MU_PARSER_ERROR_H\r
-#define MU_PARSER_ERROR_H\r
-\r
-#include <cassert>\r
-#include <stdexcept>\r
-#include <string>\r
-#include <sstream>\r
-#include <vector>\r
-#include <memory>\r
-\r
-#include "muParserDef.h"\r
-\r
-/** \file \r
- \brief This file defines the error class used by the parser.\r
-*/\r
-\r
-namespace mu\r
-{\r
-\r
-/** \brief Error codes. */\r
-enum EErrorCodes\r
-{\r
- // Formula syntax errors\r
- ecUNEXPECTED_OPERATOR = 0, ///< Unexpected binary operator found\r
- ecUNASSIGNABLE_TOKEN = 1, ///< Token cant be identified.\r
- ecUNEXPECTED_EOF = 2, ///< Unexpected end of formula. (Example: "2+sin(")\r
- ecUNEXPECTED_ARG_SEP = 3, ///< An unexpected comma has been found. (Example: "1,23")\r
- ecUNEXPECTED_ARG = 4, ///< An unexpected argument has been found\r
- ecUNEXPECTED_VAL = 5, ///< An unexpected value token has been found\r
- ecUNEXPECTED_VAR = 6, ///< An unexpected variable token has been found\r
- ecUNEXPECTED_PARENS = 7, ///< Unexpected Parenthesis, opening or closing\r
- ecUNEXPECTED_STR = 8, ///< A string has been found at an inapropriate position\r
- ecSTRING_EXPECTED = 9, ///< A string function has been called with a different type of argument\r
- ecVAL_EXPECTED = 10, ///< A numerical function has been called with a non value type of argument\r
- ecMISSING_PARENS = 11, ///< Missing parens. (Example: "3*sin(3")\r
- ecUNEXPECTED_FUN = 12, ///< Unexpected function found. (Example: "sin(8)cos(9)")\r
- ecUNTERMINATED_STRING = 13, ///< unterminated string constant. (Example: "3*valueof("hello)")\r
- ecTOO_MANY_PARAMS = 14, ///< Too many function parameters\r
- ecTOO_FEW_PARAMS = 15, ///< Too few function parameters. (Example: "ite(1<2,2)")\r
- ecOPRT_TYPE_CONFLICT = 16, ///< binary operators may only be applied to value items of the same type\r
- ecSTR_RESULT = 17, ///< result is a string\r
-\r
- // Invalid Parser input Parameters\r
- ecINVALID_NAME = 18, ///< Invalid function, variable or constant name.\r
- ecINVALID_BINOP_IDENT = 19, ///< Invalid binary operator identifier\r
- ecINVALID_INFIX_IDENT = 20, ///< Invalid function, variable or constant name.\r
- ecINVALID_POSTFIX_IDENT = 21, ///< Invalid function, variable or constant name.\r
-\r
- ecBUILTIN_OVERLOAD = 22, ///< Trying to overload builtin operator\r
- ecINVALID_FUN_PTR = 23, ///< Invalid callback function pointer \r
- ecINVALID_VAR_PTR = 24, ///< Invalid variable pointer \r
- ecEMPTY_EXPRESSION = 25, ///< The Expression is empty\r
- ecNAME_CONFLICT = 26, ///< Name conflict\r
- ecOPT_PRI = 27, ///< Invalid operator priority\r
- // \r
- ecDOMAIN_ERROR = 28, ///< catch division by zero, sqrt(-1), log(0) (currently unused)\r
- ecDIV_BY_ZERO = 29, ///< Division by zero (currently unused)\r
- ecGENERIC = 30, ///< Generic error\r
- ecLOCALE = 31, ///< Conflict with current locale\r
-\r
- ecUNEXPECTED_CONDITIONAL = 32,\r
- ecMISSING_ELSE_CLAUSE = 33, \r
- ecMISPLACED_COLON = 34,\r
-\r
- ecUNREASONABLE_NUMBER_OF_COMPUTATIONS = 35,\r
-\r
- // internal errors\r
- ecINTERNAL_ERROR = 36, ///< Internal error of any kind.\r
- \r
- // The last two are special entries \r
- ecCOUNT, ///< This is no error code, It just stores just the total number of error codes\r
- ecUNDEFINED = -1 ///< Undefined message, placeholder to detect unassigned error messages\r
-};\r
-\r
-//---------------------------------------------------------------------------\r
-/** \brief A class that handles the error messages.\r
-*/\r
-class ParserErrorMsg\r
-{\r
-public:\r
- typedef ParserErrorMsg self_type;\r
-\r
- ParserErrorMsg& operator=(const ParserErrorMsg &);\r
- ParserErrorMsg(const ParserErrorMsg&);\r
- ParserErrorMsg();\r
-\r
- ~ParserErrorMsg();\r
-\r
- static const ParserErrorMsg& Instance();\r
- string_type operator[](unsigned a_iIdx) const;\r
-\r
-private:\r
- std::vector<string_type> m_vErrMsg; ///< A vector with the predefined error messages\r
- static const self_type m_Instance; ///< The instance pointer\r
-};\r
-\r
-//---------------------------------------------------------------------------\r
-/** \brief Error class of the parser. \r
- \author Ingo Berg\r
-\r
- Part of the math parser package.\r
-*/\r
-class ParserError\r
-{\r
-private:\r
-\r
- /** \brief Replace all ocuurences of a substring with another string. */\r
- void ReplaceSubString( string_type &strSource, \r
- const string_type &strFind,\r
- const string_type &strReplaceWith);\r
- void Reset();\r
-\r
-public:\r
-\r
- ParserError();\r
- explicit ParserError(EErrorCodes a_iErrc);\r
- explicit ParserError(const string_type &sMsg);\r
- ParserError( EErrorCodes a_iErrc,\r
- const string_type &sTok,\r
- const string_type &sFormula = string_type(),\r
- int a_iPos = -1);\r
- ParserError( EErrorCodes a_iErrc, \r
- int a_iPos, \r
- const string_type &sTok);\r
- ParserError( const char_type *a_szMsg, \r
- int a_iPos = -1, \r
- const string_type &sTok = string_type());\r
- ParserError(const ParserError &a_Obj);\r
- ParserError& operator=(const ParserError &a_Obj);\r
- ~ParserError();\r
-\r
- void SetFormula(const string_type &a_strFormula);\r
- const string_type& GetExpr() const;\r
- const string_type& GetMsg() const;\r
- int GetPos() const;\r
- const string_type& GetToken() const;\r
- EErrorCodes GetCode() const;\r
-\r
-private:\r
- string_type m_strMsg; ///< The message string\r
- string_type m_strFormula; ///< Formula string\r
- string_type m_strTok; ///< Token related with the error\r
- int m_iPos; ///< Formula position related to the error \r
- EErrorCodes m_iErrc; ///< Error code\r
- const ParserErrorMsg &m_ErrMsg;\r
-}; \r
-\r
-} // namespace mu\r
-\r
-#endif\r
-\r
+++ /dev/null
-/*\r
- __________ \r
- _____ __ __\______ \_____ _______ ______ ____ _______ \r
- / \ | | \| ___/\__ \ \_ __ \/ ___/_/ __ \\_ __ \\r
- | Y Y \| | /| | / __ \_| | \/\___ \ \ ___/ | | \/\r
- |__|_| /|____/ |____| (____ /|__| /____ > \___ >|__| \r
- \/ \/ \/ \/ \r
- Copyright (C) 2013 Ingo Berg\r
-\r
- Permission is hereby granted, free of charge, to any person obtaining a copy of this \r
- software and associated documentation files (the "Software"), to deal in the Software\r
- without restriction, including without limitation the rights to use, copy, modify, \r
- merge, publish, distribute, sublicense, and/or sell copies of the Software, and to \r
- permit persons to whom the Software is furnished to do so, subject to the following conditions:\r
-\r
- The above copyright notice and this permission notice shall be included in all copies or \r
- substantial portions of the Software.\r
-\r
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT\r
- NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND \r
- NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, \r
- DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, \r
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \r
-*/\r
-\r
-#ifndef MU_PARSER_FIXES_H\r
-#define MU_PARSER_FIXES_H\r
-\r
-/** \file\r
- \brief This file contains compatibility fixes for some platforms.\r
-*/\r
-\r
-//\r
-// Compatibility fixes\r
-//\r
-\r
-//---------------------------------------------------------------------------\r
-//\r
-// Intel Compiler\r
-//\r
-//---------------------------------------------------------------------------\r
-\r
-#ifdef __INTEL_COMPILER\r
-\r
-// remark #981: operands are evaluated in unspecified order\r
-// disabled -> completely pointless if the functions do not have side effects\r
-//\r
-#pragma warning(disable:981)\r
-\r
-// remark #383: value copied to temporary, reference to temporary used\r
-#pragma warning(disable:383)\r
-\r
-// remark #1572: floating-point equality and inequality comparisons are unreliable\r
-// disabled -> everyone knows it, the parser passes this problem\r
-// deliberately to the user\r
-#pragma warning(disable:1572)\r
-\r
-#endif\r
-\r
-#endif // include guard\r
-\r
-\r
+++ /dev/null
-/*\r
- __________ \r
- _____ __ __\______ \_____ _______ ______ ____ _______ \r
- / \ | | \| ___/\__ \ \_ __ \/ ___/_/ __ \\_ __ \\r
- | Y Y \| | /| | / __ \_| | \/\___ \ \ ___/ | | \/\r
- |__|_| /|____/ |____| (____ /|__| /____ > \___ >|__| \r
- \/ \/ \/ \/ \r
- Copyright (C) 2004-2013 Ingo Berg\r
-\r
- Permission is hereby granted, free of charge, to any person obtaining a copy of this \r
- software and associated documentation files (the "Software"), to deal in the Software\r
- without restriction, including without limitation the rights to use, copy, modify, \r
- merge, publish, distribute, sublicense, and/or sell copies of the Software, and to \r
- permit persons to whom the Software is furnished to do so, subject to the following conditions:\r
-\r
- The above copyright notice and this permission notice shall be included in all copies or \r
- substantial portions of the Software.\r
-\r
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT\r
- NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND \r
- NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, \r
- DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, \r
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \r
-*/\r
-\r
-#ifndef MU_PARSER_INT_H\r
-#define MU_PARSER_INT_H\r
-\r
-#include "muParserBase.h"\r
-#include <vector>\r
-\r
-\r
-/** \file\r
- \brief Definition of a parser using integer value.\r
-*/\r
-\r
-\r
-namespace mu\r
-{\r
-\r
-/** \brief Mathematical expressions parser.\r
- \r
- This version of the parser handles only integer numbers. It disables the built in operators thus it is \r
- slower than muParser. Integer values are stored in the double value_type and converted if needed.\r
-*/\r
-class ParserInt : public ParserBase\r
-{\r
-private:\r
- static int Round(value_type v) { return (int)(v + ((v>=0) ? 0.5 : -0.5) ); };\r
- \r
- static value_type Abs(value_type);\r
- static value_type Sign(value_type);\r
- static value_type Ite(value_type, value_type, value_type);\r
- // !! The unary Minus is a MUST, otherwise you cant use negative signs !!\r
- static value_type UnaryMinus(value_type);\r
- // Functions with variable number of arguments\r
- static value_type Sum(const value_type* a_afArg, int a_iArgc); // sum\r
- static value_type Min(const value_type* a_afArg, int a_iArgc); // minimum\r
- static value_type Max(const value_type* a_afArg, int a_iArgc); // maximum\r
- // binary operator callbacks\r
- static value_type Add(value_type v1, value_type v2);\r
- static value_type Sub(value_type v1, value_type v2);\r
- static value_type Mul(value_type v1, value_type v2);\r
- static value_type Div(value_type v1, value_type v2);\r
- static value_type Mod(value_type v1, value_type v2);\r
- static value_type Pow(value_type v1, value_type v2);\r
- static value_type Shr(value_type v1, value_type v2);\r
- static value_type Shl(value_type v1, value_type v2);\r
- static value_type LogAnd(value_type v1, value_type v2);\r
- static value_type LogOr(value_type v1, value_type v2);\r
- static value_type And(value_type v1, value_type v2);\r
- static value_type Or(value_type v1, value_type v2);\r
- static value_type Xor(value_type v1, value_type v2);\r
- static value_type Less(value_type v1, value_type v2);\r
- static value_type Greater(value_type v1, value_type v2);\r
- static value_type LessEq(value_type v1, value_type v2);\r
- static value_type GreaterEq(value_type v1, value_type v2);\r
- static value_type Equal(value_type v1, value_type v2);\r
- static value_type NotEqual(value_type v1, value_type v2);\r
- static value_type Not(value_type v1);\r
-\r
- static int IsHexVal(const char_type* a_szExpr, int *a_iPos, value_type *a_iVal);\r
- static int IsBinVal(const char_type* a_szExpr, int *a_iPos, value_type *a_iVal);\r
- static int IsVal (const char_type* a_szExpr, int *a_iPos, value_type *a_iVal);\r
-\r
- /** \brief A facet class used to change decimal and thousands separator. */\r
- template<class TChar>\r
- class change_dec_sep : public std::numpunct<TChar>\r
- {\r
- public:\r
- \r
- explicit change_dec_sep(char_type cDecSep, char_type cThousandsSep = 0, int nGroup = 3)\r
- :std::numpunct<TChar>()\r
- ,m_cDecPoint(cDecSep)\r
- ,m_cThousandsSep(cThousandsSep)\r
- ,m_nGroup(nGroup)\r
- {}\r
- \r
- protected:\r
- \r
- virtual char_type do_decimal_point() const\r
- {\r
- return m_cDecPoint;\r
- }\r
-\r
- virtual char_type do_thousands_sep() const\r
- {\r
- return m_cThousandsSep;\r
- }\r
-\r
- virtual std::string do_grouping() const \r
- { \r
- // fix for issue 4: https://code.google.com/p/muparser/issues/detail?id=4\r
- // courtesy of Jens Bartsch\r
- // original code:\r
- // return std::string(1, (char)m_nGroup); \r
- // new code:\r
- return std::string(1, (char)(m_cThousandsSep > 0 ? m_nGroup : CHAR_MAX));\r
- }\r
-\r
- private:\r
-\r
- int m_nGroup;\r
- char_type m_cDecPoint; \r
- char_type m_cThousandsSep;\r
- };\r
-\r
-public:\r
- ParserInt();\r
-\r
- virtual void InitFun() override;\r
- virtual void InitOprt() override;\r
- virtual void InitConst() override;\r
- virtual void InitCharSets() override;\r
-};\r
-\r
-} // namespace mu\r
-\r
-#endif\r
-\r
+++ /dev/null
-#ifndef MU_PARSER_TEMPLATE_MAGIC_H\r
-#define MU_PARSER_TEMPLATE_MAGIC_H\r
-\r
-#include <cmath>\r
-#include "muParserError.h"\r
-\r
-\r
-namespace mu\r
-{\r
- //-----------------------------------------------------------------------------------------------\r
- //\r
- // Compile time type detection\r
- //\r
- //-----------------------------------------------------------------------------------------------\r
-\r
- /** \brief A class singling out integer types at compile time using \r
- template meta programming.\r
- */\r
- template<typename T>\r
- struct TypeInfo\r
- {\r
- static bool IsInteger() { return false; }\r
- };\r
-\r
- template<>\r
- struct TypeInfo<char>\r
- {\r
- static bool IsInteger() { return true; }\r
- };\r
-\r
- template<>\r
- struct TypeInfo<short>\r
- {\r
- static bool IsInteger() { return true; }\r
- };\r
-\r
- template<>\r
- struct TypeInfo<int>\r
- {\r
- static bool IsInteger() { return true; }\r
- };\r
-\r
- template<>\r
- struct TypeInfo<long>\r
- {\r
- static bool IsInteger() { return true; }\r
- };\r
-\r
- template<>\r
- struct TypeInfo<unsigned char>\r
- {\r
- static bool IsInteger() { return true; }\r
- };\r
-\r
- template<>\r
- struct TypeInfo<unsigned short>\r
- {\r
- static bool IsInteger() { return true; }\r
- };\r
-\r
- template<>\r
- struct TypeInfo<unsigned int>\r
- {\r
- static bool IsInteger() { return true; }\r
- };\r
-\r
- template<>\r
- struct TypeInfo<unsigned long>\r
- {\r
- static bool IsInteger() { return true; }\r
- };\r
-\r
-\r
- //-----------------------------------------------------------------------------------------------\r
- //\r
- // Standard math functions with dummy overload for integer types\r
- //\r
- //-----------------------------------------------------------------------------------------------\r
-\r
- /** \brief A template class for providing wrappers for essential math functions.\r
-\r
- This template is spezialized for several types in order to provide a unified interface\r
- for parser internal math function calls regardless of the data type.\r
- */\r
- template<typename T>\r
- struct MathImpl\r
- {\r
- static T Sin(T v) { return sin(v); }\r
- static T Cos(T v) { return cos(v); }\r
- static T Tan(T v) { return tan(v); }\r
- static T ASin(T v) { return asin(v); }\r
- static T ACos(T v) { return acos(v); }\r
- static T ATan(T v) { return atan(v); }\r
- static T ATan2(T v1, T v2) { return atan2(v1, v2); }\r
- static T Sinh(T v) { return sinh(v); }\r
- static T Cosh(T v) { return cosh(v); }\r
- static T Tanh(T v) { return tanh(v); }\r
- static T ASinh(T v) { return log(v + sqrt(v * v + 1)); }\r
- static T ACosh(T v) { return log(v + sqrt(v * v - 1)); }\r
- static T ATanh(T v) { return ((T)0.5 * log((1 + v) / (1 - v))); }\r
- static T Log(T v) { return log(v); } \r
- static T Log2(T v) { return log(v)/log((T)2); } // Logarithm base 2\r
- static T Log10(T v) { return log10(v); } // Logarithm base 10\r
- static T Exp(T v) { return exp(v); }\r
- static T Abs(T v) { return (v>=0) ? v : -v; }\r
- static T Sqrt(T v) { return sqrt(v); }\r
- static T Rint(T v) { return floor(v + (T)0.5); }\r
- static T Sign(T v) { return (T)((v<0) ? -1 : (v>0) ? 1 : 0); }\r
- static T Pow(T v1, T v2) { return std::pow(v1, v2); }\r
- };\r
-}\r
-\r
-#endif\r
+++ /dev/null
-/*\r
- __________ \r
- _____ __ __\______ \_____ _______ ______ ____ _______ \r
- / \ | | \| ___/\__ \ \_ __ \/ ___/_/ __ \\_ __ \\r
- | Y Y \| | /| | / __ \_| | \/\___ \ \ ___/ | | \/\r
- |__|_| /|____/ |____| (____ /|__| /____ > \___ >|__| \r
- \/ \/ \/ \/ \r
- Copyright (C) 2013 Ingo Berg\r
-\r
- Permission is hereby granted, free of charge, to any person obtaining a copy of this \r
- software and associated documentation files (the "Software"), to deal in the Software\r
- without restriction, including without limitation the rights to use, copy, modify, \r
- merge, publish, distribute, sublicense, and/or sell copies of the Software, and to \r
- permit persons to whom the Software is furnished to do so, subject to the following conditions:\r
-\r
- The above copyright notice and this permission notice shall be included in all copies or \r
- substantial portions of the Software.\r
-\r
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT\r
- NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND \r
- NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, \r
- DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, \r
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \r
-*/\r
-\r
-#ifndef MU_PARSER_TEST_H\r
-#define MU_PARSER_TEST_H\r
-\r
-#include <string>\r
-#include <cstdlib>\r
-#include <numeric> // for accumulate\r
-#include "muParser.h"\r
-#include "muParserInt.h"\r
-\r
-/** \file\r
- \brief This file contains the parser test class.\r
-*/\r
-\r
-namespace mu\r
-{\r
- /** \brief Namespace for test cases. */\r
- namespace Test\r
- {\r
- //------------------------------------------------------------------------------\r
- /** \brief Test cases for unit testing.\r
-\r
- (C) 2004-2011 Ingo Berg\r
- */\r
- class ParserTester // final\r
- {\r
- private:\r
- static int c_iCount;\r
-\r
- // Multiarg callbacks\r
- static value_type f1of1(value_type v) { return v;};\r
- \r
- static value_type f1of2(value_type v, value_type ) {return v;};\r
- static value_type f2of2(value_type , value_type v) {return v;};\r
-\r
- static value_type f1of3(value_type v, value_type , value_type ) {return v;};\r
- static value_type f2of3(value_type , value_type v, value_type ) {return v;};\r
- static value_type f3of3(value_type , value_type , value_type v) {return v;};\r
- \r
- static value_type f1of4(value_type v, value_type, value_type , value_type ) {return v;}\r
- static value_type f2of4(value_type , value_type v, value_type , value_type ) {return v;}\r
- static value_type f3of4(value_type , value_type, value_type v, value_type ) {return v;}\r
- static value_type f4of4(value_type , value_type, value_type , value_type v) {return v;}\r
-\r
- static value_type f1of5(value_type v, value_type, value_type , value_type , value_type ) { return v; }\r
- static value_type f2of5(value_type , value_type v, value_type , value_type , value_type ) { return v; }\r
- static value_type f3of5(value_type , value_type, value_type v, value_type , value_type ) { return v; }\r
- static value_type f4of5(value_type , value_type, value_type , value_type v, value_type ) { return v; }\r
- static value_type f5of5(value_type , value_type, value_type , value_type , value_type v) { return v; }\r
-\r
- static value_type Min(value_type a_fVal1, value_type a_fVal2) { return (a_fVal1<a_fVal2) ? a_fVal1 : a_fVal2; }\r
- static value_type Max(value_type a_fVal1, value_type a_fVal2) { return (a_fVal1>a_fVal2) ? a_fVal1 : a_fVal2; }\r
-\r
- static value_type plus2(value_type v1) { return v1+2; }\r
- static value_type times3(value_type v1) { return v1*3; }\r
- static value_type sqr(value_type v1) { return v1*v1; }\r
- static value_type sign(value_type v) { return -v; }\r
- static value_type add(value_type v1, value_type v2) { return v1+v2; }\r
- static value_type land(value_type v1, value_type v2) { return (int)v1 & (int)v2; }\r
- \r
-\r
- static value_type FirstArg(const value_type* a_afArg, int a_iArgc)\r
- {\r
- if (!a_iArgc) \r
- throw mu::Parser::exception_type( _T("too few arguments for function FirstArg.") );\r
-\r
- return a_afArg[0];\r
- }\r
-\r
- static value_type LastArg(const value_type* a_afArg, int a_iArgc)\r
- {\r
- if (!a_iArgc) \r
- throw mu::Parser::exception_type( _T("too few arguments for function LastArg.") );\r
-\r
- return a_afArg[a_iArgc-1];\r
- }\r
-\r
- static value_type Sum(const value_type* a_afArg, int a_iArgc)\r
- { \r
- if (!a_iArgc) \r
- throw mu::Parser::exception_type( _T("too few arguments for function sum.") );\r
-\r
- value_type fRes=0;\r
- for (int i=0; i<a_iArgc; ++i) fRes += a_afArg[i];\r
- return fRes;\r
- }\r
-\r
- static value_type Rnd(value_type v)\r
- {\r
- return (value_type)(1+(v*std::rand()/(RAND_MAX+1.0)));\r
- }\r
-\r
- static value_type RndWithString(const char_type*)\r
- {\r
- return (value_type)( 1 + (1000.0f * std::rand() / (RAND_MAX + 1.0) ) );\r
- }\r
-\r
- static value_type Ping()\r
- { \r
- return 10; \r
- }\r
-\r
- static value_type ValueOf(const char_type*) \r
- { \r
- return 123; \r
- }\r
-\r
- static value_type StrFun1(const char_type* v1) \r
- { \r
- int val(0);\r
- stringstream_type(v1) >> val;\r
- return (value_type)val;\r
- }\r
-\r
- static value_type StrFun2(const char_type* v1, value_type v2) \r
- { \r
- int val(0);\r
- stringstream_type(v1) >> val;\r
- return (value_type)(val + v2);\r
- }\r
- \r
- static value_type StrFun3(const char_type* v1, value_type v2, value_type v3) \r
- { \r
- int val(0);\r
- stringstream_type(v1) >> val;\r
- return val + v2 + v3;\r
- }\r
-\r
- static value_type StrToFloat(const char_type* a_szMsg)\r
- {\r
- value_type val(0);\r
- stringstream_type(a_szMsg) >> val;\r
- return val;\r
- }\r
-\r
- // postfix operator callback\r
- static value_type Mega(value_type a_fVal) { return a_fVal * (value_type)1e6; }\r
- static value_type Micro(value_type a_fVal) { return a_fVal * (value_type)1e-6; }\r
- static value_type Milli(value_type a_fVal) { return a_fVal / (value_type)1e3; }\r
-\r
- // Custom value recognition\r
- static int IsHexVal(const char_type *a_szExpr, int *a_iPos, value_type *a_fVal);\r
-\r
- int TestNames();\r
- int TestSyntax();\r
- int TestMultiArg();\r
- int TestPostFix();\r
- int TestExpression();\r
- int TestInfixOprt();\r
- int TestBinOprt();\r
- int TestVarConst();\r
- int TestInterface();\r
- int TestException();\r
- int TestStrArg();\r
- int TestIfThenElse();\r
- int TestBulkMode();\r
-\r
- void Abort() const;\r
-\r
- public:\r
- typedef int (ParserTester::*testfun_type)();\r
-\r
- ParserTester();\r
- void Run();\r
-\r
- private:\r
- std::vector<testfun_type> m_vTestFun;\r
- void AddTest(testfun_type a_pFun);\r
-\r
- // Test Double Parser\r
- int EqnTest(const string_type& a_str, double a_fRes, bool a_fPass);\r
- int EqnTestWithVarChange(const string_type& a_str, \r
- double a_fRes1, \r
- double a_fVar1, \r
- double a_fRes2, \r
- double a_fVar2);\r
- int ThrowTest(const string_type& a_str, int a_iErrc, bool a_bFail = true);\r
-\r
- // Test Int Parser\r
- int EqnTestInt(const string_type& a_str, double a_fRes, bool a_fPass);\r
-\r
- // Test Bulkmode\r
- int EqnTestBulk(const string_type& a_str, double a_fRes[4], bool a_fPass);\r
- };\r
- } // namespace Test\r
-} // namespace mu\r
-\r
-#endif\r
-\r
-\r
+++ /dev/null
-/*\r
- __________ \r
- _____ __ __\______ \_____ _______ ______ ____ _______ \r
- / \ | | \| ___/\__ \ \_ __ \/ ___/_/ __ \\_ __ \\r
- | Y Y \| | /| | / __ \_| | \/\___ \ \ ___/ | | \/\r
- |__|_| /|____/ |____| (____ /|__| /____ > \___ >|__| \r
- \/ \/ \/ \/ \r
- Copyright (C) 2004-2013 Ingo Berg\r
-\r
- Permission is hereby granted, free of charge, to any person obtaining a copy of this \r
- software and associated documentation files (the "Software"), to deal in the Software\r
- without restriction, including without limitation the rights to use, copy, modify, \r
- merge, publish, distribute, sublicense, and/or sell copies of the Software, and to \r
- permit persons to whom the Software is furnished to do so, subject to the following conditions:\r
-\r
- The above copyright notice and this permission notice shall be included in all copies or \r
- substantial portions of the Software.\r
-\r
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT\r
- NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND \r
- NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, \r
- DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, \r
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \r
-*/\r
-\r
-#ifndef MU_PARSER_TOKEN_H\r
-#define MU_PARSER_TOKEN_H\r
-\r
-#include <cassert>\r
-#include <string>\r
-#include <stack>\r
-#include <vector>\r
-#include <memory>\r
-\r
-#include "muParserError.h"\r
-#include "muParserCallback.h"\r
-\r
-/** \file\r
- \brief This file contains the parser token definition.\r
-*/\r
-\r
-namespace mu\r
-{\r
- /** \brief Encapsulation of the data for a single formula token. \r
-\r
- Formula token implementation. Part of the Math Parser Package.\r
- Formula tokens can be either one of the following:\r
- <ul>\r
- <li>value</li>\r
- <li>variable</li>\r
- <li>function with numerical arguments</li>\r
- <li>functions with a string as argument</li>\r
- <li>prefix operators</li>\r
- <li>infix operators</li>\r
- <li>binary operator</li>\r
- </ul>\r
-\r
- \author (C) 2004-2013 Ingo Berg \r
- */\r
- template<typename TBase, typename TString>\r
- class ParserToken\r
- {\r
- private:\r
-\r
- ECmdCode m_iCode; ///< Type of the token; The token type is a constant of type #ECmdCode.\r
- ETypeCode m_iType;\r
- void *m_pTok; ///< Stores Token pointer; not applicable for all tokens\r
- int m_iIdx; ///< An otional index to an external buffer storing the token data\r
- TString m_strTok; ///< Token string\r
- TString m_strVal; ///< Value for string variables\r
- value_type m_fVal; ///< the value \r
- std::unique_ptr<ParserCallback> m_pCallback;\r
-\r
- public:\r
-\r
- //---------------------------------------------------------------------------\r
- /** \brief Constructor (default).\r
- \r
- Sets token to an neutral state of type cmUNKNOWN.\r
- \throw nothrow\r
- \sa ECmdCode\r
- */\r
- ParserToken()\r
- :m_iCode(cmUNKNOWN)\r
- ,m_iType(tpVOID)\r
- ,m_pTok(0)\r
- ,m_iIdx(-1)\r
- ,m_strTok()\r
- ,m_strVal()\r
- ,m_fVal(0)\r
- ,m_pCallback()\r
- {}\r
-\r
- //------------------------------------------------------------------------------\r
- /** \brief Create token from another one.\r
- \r
- Implemented by calling Assign(...)\r
- \throw nothrow\r
- \post m_iType==cmUNKNOWN\r
- \sa #Assign\r
- */\r
- ParserToken(const ParserToken &a_Tok)\r
- {\r
- Assign(a_Tok);\r
- }\r
- \r
- //------------------------------------------------------------------------------\r
- /** \brief Assignement operator. \r
- \r
- Copy token state from another token and return this.\r
- Implemented by calling Assign(...).\r
- \throw nothrow\r
- */\r
- ParserToken& operator=(const ParserToken &a_Tok)\r
- {\r
- Assign(a_Tok);\r
- return *this;\r
- }\r
-\r
- //------------------------------------------------------------------------------\r
- /** \brief Copy token information from argument.\r
- \r
- \throw nothrow\r
- */\r
- void Assign(const ParserToken &a_Tok)\r
- {\r
- m_iCode = a_Tok.m_iCode;\r
- m_pTok = a_Tok.m_pTok;\r
- m_strTok = a_Tok.m_strTok;\r
- m_iIdx = a_Tok.m_iIdx;\r
- m_strVal = a_Tok.m_strVal;\r
- m_iType = a_Tok.m_iType;\r
- m_fVal = a_Tok.m_fVal;\r
- // create new callback object if a_Tok has one \r
- m_pCallback.reset(a_Tok.m_pCallback.get() ? a_Tok.m_pCallback->Clone() : 0);\r
- }\r
-\r
- //------------------------------------------------------------------------------\r
- /** \brief Assign a token type. \r
-\r
- Token may not be of type value, variable or function. Those have seperate set functions. \r
-\r
- \pre [assert] a_iType!=cmVAR\r
- \pre [assert] a_iType!=cmVAL\r
- \pre [assert] a_iType!=cmFUNC\r
- \post m_fVal = 0\r
- \post m_pTok = 0\r
- */\r
- ParserToken& Set(ECmdCode a_iType, const TString &a_strTok=TString())\r
- {\r
- // The following types cant be set this way, they have special Set functions\r
- assert(a_iType!=cmVAR);\r
- assert(a_iType!=cmVAL);\r
- assert(a_iType!=cmFUNC);\r
-\r
- m_iCode = a_iType;\r
- m_iType = tpVOID;\r
- m_pTok = 0;\r
- m_strTok = a_strTok;\r
- m_iIdx = -1;\r
-\r
- return *this;\r
- }\r
-\r
- //------------------------------------------------------------------------------\r
- /** \brief Set Callback type. */\r
- ParserToken& Set(const ParserCallback &a_pCallback, const TString &a_sTok)\r
- {\r
- assert(a_pCallback.GetAddr());\r
-\r
- m_iCode = a_pCallback.GetCode();\r
- m_iType = tpVOID;\r
- m_strTok = a_sTok;\r
- m_pCallback.reset(new ParserCallback(a_pCallback));\r
-\r
- m_pTok = 0;\r
- m_iIdx = -1;\r
- \r
- return *this;\r
- }\r
-\r
- //------------------------------------------------------------------------------\r
- /** \brief Make this token a value token. \r
- \r
- Member variables not necessary for value tokens will be invalidated.\r
- \throw nothrow\r
- */\r
- ParserToken& SetVal(TBase a_fVal, const TString &a_strTok=TString())\r
- {\r
- m_iCode = cmVAL;\r
- m_iType = tpDBL;\r
- m_fVal = a_fVal;\r
- m_strTok = a_strTok;\r
- m_iIdx = -1;\r
- \r
- m_pTok = 0;\r
- m_pCallback.reset(0);\r
-\r
- return *this;\r
- }\r
-\r
- //------------------------------------------------------------------------------\r
- /** \brief make this token a variable token. \r
- \r
- Member variables not necessary for variable tokens will be invalidated.\r
- \throw nothrow\r
- */\r
- ParserToken& SetVar(TBase *a_pVar, const TString &a_strTok)\r
- {\r
- m_iCode = cmVAR;\r
- m_iType = tpDBL;\r
- m_strTok = a_strTok;\r
- m_iIdx = -1;\r
- m_pTok = (void*)a_pVar;\r
- m_pCallback.reset(0);\r
- return *this;\r
- }\r
-\r
- //------------------------------------------------------------------------------\r
- /** \brief Make this token a variable token. \r
- \r
- Member variables not necessary for variable tokens will be invalidated.\r
- \throw nothrow\r
- */\r
- ParserToken& SetString(const TString &a_strTok, std::size_t a_iSize)\r
- {\r
- m_iCode = cmSTRING;\r
- m_iType = tpSTR;\r
- m_strTok = a_strTok;\r
- m_iIdx = static_cast<int>(a_iSize);\r
-\r
- m_pTok = 0;\r
- m_pCallback.reset(0);\r
- return *this;\r
- }\r
-\r
- //------------------------------------------------------------------------------\r
- /** \brief Set an index associated with the token related data. \r
- \r
- In cmSTRFUNC - This is the index to a string table in the main parser.\r
- \param a_iIdx The index the string function result will take in the bytecode parser.\r
- \throw exception_type if #a_iIdx<0 or #m_iType!=cmSTRING\r
- */\r
- void SetIdx(int a_iIdx)\r
- {\r
- if (m_iCode!=cmSTRING || a_iIdx<0)\r
- throw ParserError(ecINTERNAL_ERROR);\r
- \r
- m_iIdx = a_iIdx;\r
- }\r
-\r
- //------------------------------------------------------------------------------\r
- /** \brief Return Index associated with the token related data. \r
- \r
- In cmSTRFUNC - This is the index to a string table in the main parser.\r
-\r
- \throw exception_type if #m_iIdx<0 or #m_iType!=cmSTRING\r
- \return The index the result will take in the Bytecode calculatin array (#m_iIdx).\r
- */\r
- int GetIdx() const\r
- {\r
- if (m_iIdx<0 || m_iCode!=cmSTRING )\r
- throw ParserError(ecINTERNAL_ERROR);\r
-\r
- return m_iIdx;\r
- }\r
-\r
- //------------------------------------------------------------------------------\r
- /** \brief Return the token type.\r
- \r
- \return #m_iType\r
- \throw nothrow\r
- */\r
- ECmdCode GetCode() const\r
- {\r
- if (m_pCallback.get())\r
- {\r
- return m_pCallback->GetCode();\r
- }\r
- else\r
- {\r
- return m_iCode;\r
- }\r
- }\r
-\r
- //------------------------------------------------------------------------------\r
- ETypeCode GetType() const\r
- {\r
- if (m_pCallback.get())\r
- {\r
- return m_pCallback->GetType();\r
- }\r
- else\r
- {\r
- return m_iType;\r
- }\r
- }\r
- \r
- //------------------------------------------------------------------------------\r
- int GetPri() const\r
- {\r
- if ( !m_pCallback.get())\r
- throw ParserError(ecINTERNAL_ERROR);\r
- \r
- if ( m_pCallback->GetCode()!=cmOPRT_BIN && m_pCallback->GetCode()!=cmOPRT_INFIX)\r
- throw ParserError(ecINTERNAL_ERROR);\r
-\r
- return m_pCallback->GetPri();\r
- }\r
-\r
- //------------------------------------------------------------------------------\r
- EOprtAssociativity GetAssociativity() const\r
- {\r
- if (m_pCallback.get()==NULL || m_pCallback->GetCode()!=cmOPRT_BIN)\r
- throw ParserError(ecINTERNAL_ERROR);\r
-\r
- return m_pCallback->GetAssociativity();\r
- }\r
-\r
- //------------------------------------------------------------------------------\r
- /** \brief Return the address of the callback function assoziated with\r
- function and operator tokens.\r
-\r
- \return The pointer stored in #m_pTok.\r
- \throw exception_type if token type is non of:\r
- <ul>\r
- <li>cmFUNC</li>\r
- <li>cmSTRFUNC</li>\r
- <li>cmPOSTOP</li>\r
- <li>cmINFIXOP</li>\r
- <li>cmOPRT_BIN</li>\r
- </ul>\r
- \sa ECmdCode\r
- */\r
- generic_fun_type GetFuncAddr() const\r
- {\r
- return (m_pCallback.get()) ? (generic_fun_type)m_pCallback->GetAddr() : 0;\r
- }\r
-\r
- //------------------------------------------------------------------------------\r
- /** \biref Get value of the token.\r
- \r
- Only applicable to variable and value tokens.\r
- \throw exception_type if token is no value/variable token.\r
- */\r
- TBase GetVal() const\r
- {\r
- switch (m_iCode)\r
- {\r
- case cmVAL: return m_fVal;\r
- case cmVAR: return *((TBase*)m_pTok);\r
- default: throw ParserError(ecVAL_EXPECTED);\r
- }\r
- }\r
-\r
- //------------------------------------------------------------------------------\r
- /** \brief Get address of a variable token.\r
-\r
- Valid only if m_iType==CmdVar.\r
- \throw exception_type if token is no variable token.\r
- */\r
- TBase* GetVar() const\r
- {\r
- if (m_iCode!=cmVAR)\r
- throw ParserError(ecINTERNAL_ERROR);\r
-\r
- return (TBase*)m_pTok;\r
- }\r
-\r
- //------------------------------------------------------------------------------\r
- /** \brief Return the number of function arguments. \r
-\r
- Valid only if m_iType==CmdFUNC.\r
- */\r
- int GetArgCount() const\r
- {\r
- assert(m_pCallback.get());\r
-\r
- if (!m_pCallback->GetAddr())\r
- throw ParserError(ecINTERNAL_ERROR);\r
-\r
- return m_pCallback->GetArgc();\r
- }\r
-\r
- //------------------------------------------------------------------------------\r
- /** \brief Return the token identifier. \r
- \r
- If #m_iType is cmSTRING the token identifier is the value of the string argument\r
- for a string function.\r
- \return #m_strTok\r
- \throw nothrow\r
- \sa m_strTok\r
- */\r
- const TString& GetAsString() const\r
- {\r
- return m_strTok;\r
- }\r
- };\r
-} // namespace mu\r
-\r
-#endif\r
+++ /dev/null
-/*\r
- __________ \r
- _____ __ __\______ \_____ _______ ______ ____ _______ \r
- / \ | | \| ___/\__ \ \_ __ \/ ___/_/ __ \\_ __ \\r
- | Y Y \| | /| | / __ \_| | \/\___ \ \ ___/ | | \/\r
- |__|_| /|____/ |____| (____ /|__| /____ > \___ >|__| \r
- \/ \/ \/ \/ \r
- Copyright (C) 2004-2013 Ingo Berg\r
-\r
- Permission is hereby granted, free of charge, to any person obtaining a copy of this \r
- software and associated documentation files (the "Software"), to deal in the Software\r
- without restriction, including without limitation the rights to use, copy, modify, \r
- merge, publish, distribute, sublicense, and/or sell copies of the Software, and to \r
- permit persons to whom the Software is furnished to do so, subject to the following conditions:\r
-\r
- The above copyright notice and this permission notice shall be included in all copies or \r
- substantial portions of the Software.\r
-\r
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT\r
- NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND \r
- NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, \r
- DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, \r
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \r
-*/\r
-\r
-#ifndef MU_PARSER_TOKEN_READER_H\r
-#define MU_PARSER_TOKEN_READER_H\r
-\r
-#include <cassert>\r
-#include <cstdio>\r
-#include <cstring>\r
-#include <list>\r
-#include <map>\r
-#include <memory>\r
-#include <stack>\r
-#include <string>\r
-\r
-#include "muParserDef.h"\r
-#include "muParserToken.h"\r
-\r
-/** \file\r
- \brief This file contains the parser token reader definition.\r
-*/\r
-\r
-\r
-namespace mu\r
-{\r
- // Forward declaration\r
- class ParserBase;\r
-\r
- /** \brief Token reader for the ParserBase class.\r
-\r
- */\r
- class ParserTokenReader \r
- {\r
- private:\r
-\r
- typedef ParserToken<value_type, string_type> token_type;\r
-\r
- public:\r
-\r
- ParserTokenReader(ParserBase *a_pParent);\r
- ParserTokenReader* Clone(ParserBase *a_pParent) const;\r
-\r
- void AddValIdent(identfun_type a_pCallback);\r
- void SetVarCreator(facfun_type a_pFactory, void *pUserData);\r
- void SetFormula(const string_type &a_strFormula);\r
- void SetArgSep(char_type cArgSep);\r
-\r
- int GetPos() const;\r
- const string_type& GetExpr() const;\r
- varmap_type& GetUsedVar();\r
- char_type GetArgSep() const;\r
-\r
- void IgnoreUndefVar(bool bIgnore);\r
- void ReInit();\r
- token_type ReadNextToken();\r
-\r
- private:\r
-\r
- /** \brief Syntax codes. \r
- \r
- The syntax codes control the syntax check done during the first time parsing of \r
- the expression string. They are flags that indicate which tokens are allowed next\r
- if certain tokens are identified.\r
- */\r
- enum ESynCodes\r
- {\r
- noBO = 1 << 0, ///< to avoid i.e. "cos(7)(" \r
- noBC = 1 << 1, ///< to avoid i.e. "sin)" or "()"\r
- noVAL = 1 << 2, ///< to avoid i.e. "tan 2" or "sin(8)3.14"\r
- noVAR = 1 << 3, ///< to avoid i.e. "sin a" or "sin(8)a"\r
- noARG_SEP = 1 << 4, ///< to avoid i.e. ",," or "+," ...\r
- noFUN = 1 << 5, ///< to avoid i.e. "sqrt cos" or "(1)sin" \r
- noOPT = 1 << 6, ///< to avoid i.e. "(+)"\r
- noPOSTOP = 1 << 7, ///< to avoid i.e. "(5!!)" "sin!"\r
- noINFIXOP = 1 << 8, ///< to avoid i.e. "++4" "!!4"\r
- noEND = 1 << 9, ///< to avoid unexpected end of formula\r
- noSTR = 1 << 10, ///< to block numeric arguments on string functions\r
- noASSIGN = 1 << 11, ///< to block assignement to constant i.e. "4=7"\r
- noIF = 1 << 12,\r
- noELSE = 1 << 13,\r
- sfSTART_OF_LINE = noOPT | noBC | noPOSTOP | noASSIGN | noIF | noELSE | noARG_SEP,\r
- noANY = ~0 ///< All of he above flags set\r
- }; \r
-\r
- ParserTokenReader(const ParserTokenReader &a_Reader);\r
- ParserTokenReader& operator=(const ParserTokenReader &a_Reader);\r
- void Assign(const ParserTokenReader &a_Reader);\r
-\r
- void SetParent(ParserBase *a_pParent);\r
- int ExtractToken(const char_type *a_szCharSet, \r
- string_type &a_strTok, \r
- int a_iPos) const;\r
- int ExtractOperatorToken(string_type &a_sTok, int a_iPos) const;\r
-\r
- bool IsBuiltIn(token_type &a_Tok);\r
- bool IsArgSep(token_type &a_Tok);\r
- bool IsEOF(token_type &a_Tok);\r
- bool IsInfixOpTok(token_type &a_Tok);\r
- bool IsFunTok(token_type &a_Tok);\r
- bool IsPostOpTok(token_type &a_Tok);\r
- bool IsOprt(token_type &a_Tok);\r
- bool IsValTok(token_type &a_Tok);\r
- bool IsVarTok(token_type &a_Tok);\r
- bool IsStrVarTok(token_type &a_Tok);\r
- bool IsUndefVarTok(token_type &a_Tok);\r
- bool IsString(token_type &a_Tok);\r
- void Error(EErrorCodes a_iErrc, \r
- int a_iPos = -1, \r
- const string_type &a_sTok = string_type() ) const;\r
-\r
- token_type& SaveBeforeReturn(const token_type &tok);\r
-\r
- ParserBase *m_pParser;\r
- string_type m_strFormula;\r
- int m_iPos;\r
- int m_iSynFlags;\r
- bool m_bIgnoreUndefVar;\r
-\r
- const funmap_type *m_pFunDef;\r
- const funmap_type *m_pPostOprtDef;\r
- const funmap_type *m_pInfixOprtDef;\r
- const funmap_type *m_pOprtDef;\r
- const valmap_type *m_pConstDef;\r
- const strmap_type *m_pStrVarDef;\r
- varmap_type *m_pVarDef; ///< The only non const pointer to parser internals\r
- facfun_type m_pFactory;\r
- void *m_pFactoryData;\r
- std::list<identfun_type> m_vIdentFun; ///< Value token identification function\r
- varmap_type m_UsedVar;\r
- value_type m_fZero; ///< Dummy value of zero, referenced by undefined variables\r
- int m_iBrackets;\r
- token_type m_lastTok;\r
- char_type m_cArgSep; ///< The character used for separating function arguments\r
- };\r
-} // namespace mu\r
-\r
-#endif\r
-\r
-\r
+++ /dev/null
-/* \r
- __________ \r
- _____ __ __\______ \_____ _______ ______ ____ _______ \r
- / \ | | \| ___/\__ \ \_ __ \/ ___/_/ __ \\_ __ \\r
- | Y Y \| | /| | / __ \_| | \/\___ \ \ ___/ | | \/\r
- |__|_| /|____/ |____| (____ /|__| /____ > \___ >|__| \r
- \/ \/ \/ \/ \r
-\r
- Copyright (C) 2013 Ingo Berg\r
-\r
- Permission is hereby granted, free of charge, to any person obtaining a copy of this \r
- software and associated documentation files (the "Software"), to deal in the Software\r
- without restriction, including without limitation the rights to use, copy, modify, \r
- merge, publish, distribute, sublicense, and/or sell copies of the Software, and to \r
- permit persons to whom the Software is furnished to do so, subject to the following conditions:\r
-\r
- The above copyright notice and this permission notice shall be included in all copies or \r
- substantial portions of the Software.\r
-\r
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT\r
- NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND \r
- NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, \r
- DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, \r
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \r
-*/\r
-#include "muParser.h"\r
-#include "muParserTemplateMagic.h"\r
-\r
-//--- Standard includes ------------------------------------------------------------------------\r
-#include <cmath>\r
-#include <algorithm>\r
-#include <numeric>\r
-\r
-/** \brief Pi (what else?). */\r
-#define PARSER_CONST_PI 3.141592653589793238462643\r
-\r
-/** \brief The eulerian number. */\r
-#define PARSER_CONST_E 2.718281828459045235360287\r
-\r
-using namespace std;\r
-\r
-/** \file\r
- \brief Implementation of the standard floating point parser.\r
-*/\r
-\r
-\r
-\r
-/** \brief Namespace for mathematical applications. */\r
-namespace mu\r
-{\r
-\r
-\r
- //---------------------------------------------------------------------------\r
- // Trigonometric function\r
- value_type Parser::Sin(value_type v) { return MathImpl<value_type>::Sin(v); }\r
- value_type Parser::Cos(value_type v) { return MathImpl<value_type>::Cos(v); }\r
- value_type Parser::Tan(value_type v) { return MathImpl<value_type>::Tan(v); }\r
- value_type Parser::ASin(value_type v) { return MathImpl<value_type>::ASin(v); }\r
- value_type Parser::ACos(value_type v) { return MathImpl<value_type>::ACos(v); }\r
- value_type Parser::ATan(value_type v) { return MathImpl<value_type>::ATan(v); }\r
- value_type Parser::ATan2(value_type v1, value_type v2) { return MathImpl<value_type>::ATan2(v1, v2); }\r
- value_type Parser::Sinh(value_type v) { return MathImpl<value_type>::Sinh(v); }\r
- value_type Parser::Cosh(value_type v) { return MathImpl<value_type>::Cosh(v); }\r
- value_type Parser::Tanh(value_type v) { return MathImpl<value_type>::Tanh(v); }\r
- value_type Parser::ASinh(value_type v) { return MathImpl<value_type>::ASinh(v); }\r
- value_type Parser::ACosh(value_type v) { return MathImpl<value_type>::ACosh(v); }\r
- value_type Parser::ATanh(value_type v) { return MathImpl<value_type>::ATanh(v); }\r
-\r
- //---------------------------------------------------------------------------\r
- // Logarithm functions\r
-\r
- // Logarithm base 2\r
- value_type Parser::Log2(value_type v) \r
- { \r
- #ifdef MUP_MATH_EXCEPTIONS\r
- if (v<=0)\r
- throw ParserError(ecDOMAIN_ERROR, _T("Log2"));\r
- #endif\r
-\r
- return MathImpl<value_type>::Log2(v); \r
- } \r
-\r
- // Logarithm base 10\r
- value_type Parser::Log10(value_type v) \r
- { \r
- #ifdef MUP_MATH_EXCEPTIONS\r
- if (v<=0)\r
- throw ParserError(ecDOMAIN_ERROR, _T("Log10"));\r
- #endif\r
-\r
- return MathImpl<value_type>::Log10(v); \r
- } \r
-\r
-// Logarithm base e (natural logarithm)\r
- value_type Parser::Ln(value_type v) \r
- { \r
- #ifdef MUP_MATH_EXCEPTIONS\r
- if (v<=0)\r
- throw ParserError(ecDOMAIN_ERROR, _T("Ln"));\r
- #endif\r
-\r
- return MathImpl<value_type>::Log(v); \r
- } \r
-\r
- //---------------------------------------------------------------------------\r
- // misc\r
- value_type Parser::Exp(value_type v) { return MathImpl<value_type>::Exp(v); }\r
- value_type Parser::Abs(value_type v) { return MathImpl<value_type>::Abs(v); }\r
- value_type Parser::Sqrt(value_type v) \r
- { \r
- #ifdef MUP_MATH_EXCEPTIONS\r
- if (v<0)\r
- throw ParserError(ecDOMAIN_ERROR, _T("sqrt"));\r
- #endif\r
-\r
- return MathImpl<value_type>::Sqrt(v); \r
- }\r
- value_type Parser::Rint(value_type v) { return MathImpl<value_type>::Rint(v); }\r
- value_type Parser::Sign(value_type v) { return MathImpl<value_type>::Sign(v); }\r
-\r
- //---------------------------------------------------------------------------\r
- /** \brief Callback for the unary minus operator.\r
- \param v The value to negate\r
- \return -v\r
- */\r
- value_type Parser::UnaryMinus(value_type v) \r
- { \r
- return -v; \r
- }\r
-\r
- //---------------------------------------------------------------------------\r
- /** \brief Callback for the unary minus operator.\r
- \param v The value to negate\r
- \return -v\r
- */\r
- value_type Parser::UnaryPlus(value_type v) \r
- { \r
- return v; \r
- }\r
-\r
- //---------------------------------------------------------------------------\r
- /** \brief Callback for adding multiple values. \r
- \param [in] a_afArg Vector with the function arguments\r
- \param [in] a_iArgc The size of a_afArg\r
- */\r
- value_type Parser::Sum(const value_type *a_afArg, int a_iArgc)\r
- { \r
- if (!a_iArgc) \r
- throw exception_type(_T("too few arguments for function sum."));\r
-\r
- value_type fRes=0;\r
- for (int i=0; i<a_iArgc; ++i) fRes += a_afArg[i];\r
- return fRes;\r
- }\r
-\r
- //---------------------------------------------------------------------------\r
- /** \brief Callback for averaging multiple values. \r
- \param [in] a_afArg Vector with the function arguments\r
- \param [in] a_iArgc The size of a_afArg\r
- */\r
- value_type Parser::Avg(const value_type *a_afArg, int a_iArgc)\r
- { \r
- if (!a_iArgc) \r
- throw exception_type(_T("too few arguments for function sum."));\r
-\r
- value_type fRes=0;\r
- for (int i=0; i<a_iArgc; ++i) fRes += a_afArg[i];\r
- return fRes/(value_type)a_iArgc;\r
- }\r
-\r
-\r
- //---------------------------------------------------------------------------\r
- /** \brief Callback for determining the minimum value out of a vector. \r
- \param [in] a_afArg Vector with the function arguments\r
- \param [in] a_iArgc The size of a_afArg\r
- */\r
- value_type Parser::Min(const value_type *a_afArg, int a_iArgc)\r
- { \r
- if (!a_iArgc) \r
- throw exception_type(_T("too few arguments for function min."));\r
-\r
- value_type fRes=a_afArg[0];\r
- for (int i=0; i<a_iArgc; ++i) \r
- fRes = std::min(fRes, a_afArg[i]);\r
-\r
- return fRes;\r
- }\r
-\r
-\r
- //---------------------------------------------------------------------------\r
- /** \brief Callback for determining the maximum value out of a vector. \r
- \param [in] a_afArg Vector with the function arguments\r
- \param [in] a_iArgc The size of a_afArg\r
- */\r
- value_type Parser::Max(const value_type *a_afArg, int a_iArgc)\r
- { \r
- if (!a_iArgc) \r
- throw exception_type(_T("too few arguments for function min."));\r
-\r
- value_type fRes=a_afArg[0];\r
- for (int i=0; i<a_iArgc; ++i) fRes = std::max(fRes, a_afArg[i]);\r
-\r
- return fRes;\r
- }\r
-\r
-\r
- //---------------------------------------------------------------------------\r
- /** \brief Default value recognition callback. \r
- \param [in] a_szExpr Pointer to the expression\r
- \param [in, out] a_iPos Pointer to an index storing the current position within the expression\r
- \param [out] a_fVal Pointer where the value should be stored in case one is found.\r
- \return 1 if a value was found 0 otherwise.\r
- */\r
- int Parser::IsVal(const char_type* a_szExpr, int *a_iPos, value_type *a_fVal)\r
- {\r
- value_type fVal(0);\r
-\r
- stringstream_type stream(a_szExpr);\r
- stream.seekg(0); // todo: check if this really is necessary\r
- stream.imbue(Parser::s_locale);\r
- stream >> fVal;\r
- stringstream_type::pos_type iEnd = stream.tellg(); // Position after reading\r
-\r
- if (iEnd==(stringstream_type::pos_type)-1)\r
- return 0;\r
-\r
- *a_iPos += (int)iEnd;\r
- *a_fVal = fVal;\r
- return 1;\r
- }\r
-\r
-\r
- //---------------------------------------------------------------------------\r
- /** \brief Constructor. \r
-\r
- Call ParserBase class constructor and trigger Function, Operator and Constant initialization.\r
- */\r
- Parser::Parser()\r
- :ParserBase()\r
- {\r
- AddValIdent(IsVal);\r
-\r
- InitCharSets();\r
- InitFun();\r
- InitConst();\r
- InitOprt();\r
- }\r
-\r
- //---------------------------------------------------------------------------\r
- /** \brief Define the character sets. \r
- \sa DefineNameChars, DefineOprtChars, DefineInfixOprtChars\r
- \r
- This function is used for initializing the default character sets that define\r
- the characters to be useable in function and variable names and operators.\r
- */\r
- void Parser::InitCharSets()\r
- {\r
- DefineNameChars( _T("0123456789_abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ") );\r
- DefineOprtChars( _T("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ+-*^/?<>=#!$%&|~'_{}") );\r
- DefineInfixOprtChars( _T("/+-*^?<>=#!$%&|~'_") );\r
- }\r
-\r
- //---------------------------------------------------------------------------\r
- /** \brief Initialize the default functions. */\r
- void Parser::InitFun()\r
- {\r
- if (mu::TypeInfo<mu::value_type>::IsInteger())\r
- {\r
- // When setting MUP_BASETYPE to an integer type\r
- // Place functions for dealing with integer values here\r
- // ...\r
- // ...\r
- // ...\r
- }\r
- else\r
- {\r
- // trigonometric functions\r
- DefineFun(_T("sin"), Sin);\r
- DefineFun(_T("cos"), Cos);\r
- DefineFun(_T("tan"), Tan);\r
- // arcus functions\r
- DefineFun(_T("asin"), ASin);\r
- DefineFun(_T("acos"), ACos);\r
- DefineFun(_T("atan"), ATan);\r
- DefineFun(_T("atan2"), ATan2);\r
- // hyperbolic functions\r
- DefineFun(_T("sinh"), Sinh);\r
- DefineFun(_T("cosh"), Cosh);\r
- DefineFun(_T("tanh"), Tanh);\r
- // arcus hyperbolic functions\r
- DefineFun(_T("asinh"), ASinh);\r
- DefineFun(_T("acosh"), ACosh);\r
- DefineFun(_T("atanh"), ATanh);\r
- // Logarithm functions\r
- DefineFun(_T("log2"), Log2);\r
- DefineFun(_T("log10"), Log10);\r
- DefineFun(_T("log"), Ln);\r
- DefineFun(_T("ln"), Ln);\r
- // misc\r
- DefineFun(_T("exp"), Exp);\r
- DefineFun(_T("sqrt"), Sqrt);\r
- DefineFun(_T("sign"), Sign);\r
- DefineFun(_T("rint"), Rint);\r
- DefineFun(_T("abs"), Abs);\r
- // Functions with variable number of arguments\r
- DefineFun(_T("sum"), Sum);\r
- DefineFun(_T("avg"), Avg);\r
- DefineFun(_T("min"), Min);\r
- DefineFun(_T("max"), Max);\r
- }\r
- }\r
-\r
- //---------------------------------------------------------------------------\r
- /** \brief Initialize constants.\r
- \r
- By default the parser recognizes two constants. Pi ("pi") and the eulerian\r
- number ("_e").\r
- */\r
- void Parser::InitConst()\r
- {\r
- DefineConst(_T("_pi"), (value_type)PARSER_CONST_PI);\r
- DefineConst(_T("_e"), (value_type)PARSER_CONST_E);\r
- }\r
-\r
- //---------------------------------------------------------------------------\r
- /** \brief Initialize operators. \r
- \r
- By default only the unary minus operator is added.\r
- */\r
- void Parser::InitOprt()\r
- {\r
- DefineInfixOprt(_T("-"), UnaryMinus);\r
- DefineInfixOprt(_T("+"), UnaryPlus);\r
- }\r
-\r
- //---------------------------------------------------------------------------\r
- void Parser::OnDetectVar(string_type * /*pExpr*/, int & /*nStart*/, int & /*nEnd*/)\r
- {\r
- // this is just sample code to illustrate modifying variable names on the fly.\r
- // I'm not sure anyone really needs such a feature...\r
- /*\r
-\r
-\r
- string sVar(pExpr->begin()+nStart, pExpr->begin()+nEnd);\r
- string sRepl = std::string("_") + sVar + "_";\r
- \r
- int nOrigVarEnd = nEnd;\r
- cout << "variable detected!\n";\r
- cout << " Expr: " << *pExpr << "\n";\r
- cout << " Start: " << nStart << "\n";\r
- cout << " End: " << nEnd << "\n";\r
- cout << " Var: \"" << sVar << "\"\n";\r
- cout << " Repl: \"" << sRepl << "\"\n";\r
- nEnd = nStart + sRepl.length();\r
- cout << " End: " << nEnd << "\n";\r
- pExpr->replace(pExpr->begin()+nStart, pExpr->begin()+nOrigVarEnd, sRepl);\r
- cout << " New expr: " << *pExpr << "\n";\r
- */\r
- }\r
-\r
- //---------------------------------------------------------------------------\r
- /** \brief Numerically differentiate with regard to a variable. \r
- \param [in] a_Var Pointer to the differentiation variable.\r
- \param [in] a_fPos Position at which the differentiation should take place.\r
- \param [in] a_fEpsilon Epsilon used for the numerical differentiation.\r
-\r
- Numerical differentiation uses a 5 point operator yielding a 4th order \r
- formula. The default value for epsilon is 0.00074 which is\r
- numeric_limits<double>::epsilon() ^ (1/5) as suggested in the muparser\r
- forum:\r
-\r
- http://sourceforge.net/forum/forum.php?thread_id=1994611&forum_id=462843\r
- */\r
- value_type Parser::Diff(value_type *a_Var, \r
- value_type a_fPos, \r
- value_type a_fEpsilon) const\r
- {\r
- value_type fRes(0), \r
- fBuf(*a_Var),\r
- f[4] = {0,0,0,0},\r
- fEpsilon(a_fEpsilon);\r
-\r
- // Backwards compatible calculation of epsilon inc case the user doesnt provide\r
- // his own epsilon\r
- if (fEpsilon==0)\r
- fEpsilon = (a_fPos==0) ? (value_type)1e-10 : (value_type)1e-7 * a_fPos;\r
-\r
- *a_Var = a_fPos+2 * fEpsilon; f[0] = Eval();\r
- *a_Var = a_fPos+1 * fEpsilon; f[1] = Eval();\r
- *a_Var = a_fPos-1 * fEpsilon; f[2] = Eval();\r
- *a_Var = a_fPos-2 * fEpsilon; f[3] = Eval();\r
- *a_Var = fBuf; // restore variable\r
-\r
- fRes = (-f[0] + 8*f[1] - 8*f[2] + f[3]) / (12*fEpsilon);\r
- return fRes;\r
- }\r
-} // namespace mu\r
+++ /dev/null
-/*\r
- __________ \r
- _____ __ __\______ \_____ _______ ______ ____ _______ \r
- / \ | | \| ___/\__ \ \_ __ \/ ___/_/ __ \\_ __ \\r
- | Y Y \| | /| | / __ \_| | \/\___ \ \ ___/ | | \/\r
- |__|_| /|____/ |____| (____ /|__| /____ > \___ >|__| \r
- \/ \/ \/ \/ \r
- Copyright (C) 2011 Ingo Berg\r
-\r
- Permission is hereby granted, free of charge, to any person obtaining a copy of this \r
- software and associated documentation files (the "Software"), to deal in the Software\r
- without restriction, including without limitation the rights to use, copy, modify, \r
- merge, publish, distribute, sublicense, and/or sell copies of the Software, and to \r
- permit persons to whom the Software is furnished to do so, subject to the following conditions:\r
-\r
- The above copyright notice and this permission notice shall be included in all copies or \r
- substantial portions of the Software.\r
-\r
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT\r
- NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND \r
- NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, \r
- DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, \r
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \r
-*/\r
-\r
-#include "muParserBase.h"\r
-#include "muParserTemplateMagic.h"\r
-\r
-//--- Standard includes ------------------------------------------------------------------------\r
-#include <cassert>\r
-#include <algorithm>\r
-#include <cmath>\r
-#include <memory>\r
-#include <vector>\r
-#include <deque>\r
-#include <sstream>\r
-#include <locale>\r
-\r
-#ifdef MUP_USE_OPENMP\r
- #include <omp.h>\r
-#endif\r
-\r
-using namespace std;\r
-\r
-/** \file\r
- \brief This file contains the basic implementation of the muparser engine.\r
-*/\r
-\r
-namespace mu\r
-{\r
- std::locale ParserBase::s_locale = std::locale(std::locale::classic(), new change_dec_sep<char_type>('.'));\r
-\r
- bool ParserBase::g_DbgDumpCmdCode = false;\r
- bool ParserBase::g_DbgDumpStack = false;\r
-\r
- //------------------------------------------------------------------------------\r
- /** \brief Identifiers for built in binary operators. \r
-\r
- When defining custom binary operators with #AddOprt(...) make sure not to choose \r
- names conflicting with these definitions. \r
- */\r
- const char_type* ParserBase::c_DefaultOprt[] = \r
- { \r
- _T("<="), _T(">="), _T("!="), \r
- _T("=="), _T("<"), _T(">"), \r
- _T("+"), _T("-"), _T("*"), \r
- _T("/"), _T("^"), _T("&&"), \r
- _T("||"), _T("="), _T("("), \r
- _T(")"), _T("?"), _T(":"), 0 \r
- };\r
-\r
- //------------------------------------------------------------------------------\r
- /** \brief Constructor.\r
- \param a_szFormula the formula to interpret.\r
- \throw ParserException if a_szFormula is null.\r
- */\r
- ParserBase::ParserBase()\r
- :m_pParseFormula(&ParserBase::ParseString)\r
- ,m_vRPN()\r
- ,m_vStringBuf()\r
- ,m_pTokenReader()\r
- ,m_FunDef()\r
- ,m_PostOprtDef()\r
- ,m_InfixOprtDef()\r
- ,m_OprtDef()\r
- ,m_ConstDef()\r
- ,m_StrVarDef()\r
- ,m_VarDef()\r
- ,m_bBuiltInOp(true)\r
- ,m_sNameChars()\r
- ,m_sOprtChars()\r
- ,m_sInfixOprtChars()\r
- ,m_nIfElseCounter(0)\r
- ,m_vStackBuffer()\r
- ,m_nFinalResultIdx(0)\r
- {\r
- InitTokenReader();\r
- }\r
-\r
- //---------------------------------------------------------------------------\r
- /** \brief Copy constructor. \r
-\r
- Tha parser can be safely copy constructed but the bytecode is reset during\r
- copy construction.\r
- */\r
- ParserBase::ParserBase(const ParserBase &a_Parser)\r
- :m_pParseFormula(&ParserBase::ParseString)\r
- ,m_vRPN()\r
- ,m_vStringBuf()\r
- ,m_pTokenReader()\r
- ,m_FunDef()\r
- ,m_PostOprtDef()\r
- ,m_InfixOprtDef()\r
- ,m_OprtDef()\r
- ,m_ConstDef()\r
- ,m_StrVarDef()\r
- ,m_VarDef()\r
- ,m_bBuiltInOp(true)\r
- ,m_sNameChars()\r
- ,m_sOprtChars()\r
- ,m_sInfixOprtChars()\r
- ,m_nIfElseCounter(0)\r
- {\r
- m_pTokenReader.reset(new token_reader_type(this));\r
- Assign(a_Parser);\r
- }\r
-\r
- //---------------------------------------------------------------------------\r
- ParserBase::~ParserBase()\r
- {}\r
-\r
- //---------------------------------------------------------------------------\r
- /** \brief Assignement operator. \r
-\r
- Implemented by calling Assign(a_Parser). Self assignement is suppressed.\r
- \param a_Parser Object to copy to this.\r
- \return *this\r
- \throw nothrow\r
- */\r
- ParserBase& ParserBase::operator=(const ParserBase &a_Parser)\r
- {\r
- Assign(a_Parser);\r
- return *this;\r
- }\r
-\r
- //---------------------------------------------------------------------------\r
- /** \brief Copy state of a parser object to this. \r
-\r
- Clears Variables and Functions of this parser.\r
- Copies the states of all internal variables.\r
- Resets parse function to string parse mode.\r
-\r
- \param a_Parser the source object.\r
- */\r
- void ParserBase::Assign(const ParserBase &a_Parser)\r
- {\r
- if (&a_Parser==this)\r
- return;\r
-\r
- // Don't copy bytecode instead cause the parser to create new bytecode\r
- // by resetting the parse function.\r
- ReInit();\r
-\r
- m_ConstDef = a_Parser.m_ConstDef; // Copy user define constants\r
- m_VarDef = a_Parser.m_VarDef; // Copy user defined variables\r
- m_bBuiltInOp = a_Parser.m_bBuiltInOp;\r
- m_vStringBuf = a_Parser.m_vStringBuf;\r
- m_vStackBuffer = a_Parser.m_vStackBuffer;\r
- m_nFinalResultIdx = a_Parser.m_nFinalResultIdx;\r
- m_StrVarDef = a_Parser.m_StrVarDef;\r
- m_vStringVarBuf = a_Parser.m_vStringVarBuf;\r
- m_nIfElseCounter = a_Parser.m_nIfElseCounter;\r
- m_pTokenReader.reset(a_Parser.m_pTokenReader->Clone(this));\r
-\r
- // Copy function and operator callbacks\r
- m_FunDef = a_Parser.m_FunDef; // Copy function definitions\r
- m_PostOprtDef = a_Parser.m_PostOprtDef; // post value unary operators\r
- m_InfixOprtDef = a_Parser.m_InfixOprtDef; // unary operators for infix notation\r
- m_OprtDef = a_Parser.m_OprtDef; // binary operators\r
-\r
- m_sNameChars = a_Parser.m_sNameChars;\r
- m_sOprtChars = a_Parser.m_sOprtChars;\r
- m_sInfixOprtChars = a_Parser.m_sInfixOprtChars;\r
- }\r
-\r
- //---------------------------------------------------------------------------\r
- /** \brief Set the decimal separator.\r
- \param cDecSep Decimal separator as a character value.\r
- \sa SetThousandsSep\r
-\r
- By default muparser uses the "C" locale. The decimal separator of this\r
- locale is overwritten by the one provided here.\r
- */\r
- void ParserBase::SetDecSep(char_type cDecSep)\r
- {\r
- char_type cThousandsSep = std::use_facet< change_dec_sep<char_type> >(s_locale).thousands_sep();\r
- s_locale = std::locale(std::locale("C"), new change_dec_sep<char_type>(cDecSep, cThousandsSep));\r
- }\r
- \r
- //---------------------------------------------------------------------------\r
- /** \brief Sets the thousands operator. \r
- \param cThousandsSep The thousands separator as a character\r
- \sa SetDecSep\r
-\r
- By default muparser uses the "C" locale. The thousands separator of this\r
- locale is overwritten by the one provided here.\r
- */\r
- void ParserBase::SetThousandsSep(char_type cThousandsSep)\r
- {\r
- char_type cDecSep = std::use_facet< change_dec_sep<char_type> >(s_locale).decimal_point();\r
- s_locale = std::locale(std::locale("C"), new change_dec_sep<char_type>(cDecSep, cThousandsSep));\r
- }\r
-\r
- //---------------------------------------------------------------------------\r
- /** \brief Resets the locale. \r
-\r
- The default locale used "." as decimal separator, no thousands separator and\r
- "," as function argument separator.\r
- */\r
- void ParserBase::ResetLocale()\r
- {\r
- s_locale = std::locale(std::locale("C"), new change_dec_sep<char_type>('.'));\r
- SetArgSep(',');\r
- }\r
-\r
- //---------------------------------------------------------------------------\r
- /** \brief Initialize the token reader. \r
-\r
- Create new token reader object and submit pointers to function, operator,\r
- constant and variable definitions.\r
-\r
- \post m_pTokenReader.get()!=0\r
- \throw nothrow\r
- */\r
- void ParserBase::InitTokenReader()\r
- {\r
- m_pTokenReader.reset(new token_reader_type(this));\r
- }\r
-\r
- //---------------------------------------------------------------------------\r
- /** \brief Reset parser to string parsing mode and clear internal buffers.\r
-\r
- Clear bytecode, reset the token reader.\r
- \throw nothrow\r
- */\r
- void ParserBase::ReInit() const\r
- {\r
- m_pParseFormula = &ParserBase::ParseString;\r
- m_vStringBuf.clear();\r
- m_vRPN.clear();\r
- m_pTokenReader->ReInit();\r
- m_nIfElseCounter = 0;\r
- }\r
-\r
- //---------------------------------------------------------------------------\r
- void ParserBase::OnDetectVar(string_type * /*pExpr*/, int & /*nStart*/, int & /*nEnd*/)\r
- {}\r
-\r
- //---------------------------------------------------------------------------\r
- /** \brief Returns the version of muparser. \r
- \param eInfo A flag indicating whether the full version info should be \r
- returned or not.\r
-\r
- Format is as follows: "MAJOR.MINOR (COMPILER_FLAGS)" The COMPILER_FLAGS\r
- are returned only if eInfo==pviFULL.\r
- */\r
- string_type ParserBase::GetVersion(EParserVersionInfo eInfo) const\r
- {\r
- stringstream_type ss;\r
-\r
- ss << MUP_VERSION;\r
-\r
- if (eInfo==pviFULL)\r
- {\r
- ss << _T(" (") << MUP_VERSION_DATE;\r
- ss << std::dec << _T("; ") << sizeof(void*)*8 << _T("BIT");\r
-\r
-#ifdef _DEBUG\r
- ss << _T("; DEBUG");\r
-#else \r
- ss << _T("; RELEASE");\r
-#endif\r
-\r
-#ifdef _UNICODE\r
- ss << _T("; UNICODE");\r
-#else\r
- #ifdef _MBCS\r
- ss << _T("; MBCS");\r
- #else\r
- ss << _T("; ASCII");\r
- #endif\r
-#endif\r
-\r
-#ifdef MUP_USE_OPENMP\r
- ss << _T("; OPENMP");\r
-//#else\r
-// ss << _T("; NO_OPENMP");\r
-#endif\r
-\r
-#if defined(MUP_MATH_EXCEPTIONS)\r
- ss << _T("; MATHEXC");\r
-//#else\r
-// ss << _T("; NO_MATHEXC");\r
-#endif\r
-\r
- ss << _T(")");\r
- }\r
-\r
- return ss.str();\r
- }\r
-\r
- //---------------------------------------------------------------------------\r
- /** \brief Add a value parsing function. \r
- \r
- When parsing an expression muParser tries to detect values in the expression\r
- string using different valident callbacks. Thuis it's possible to parse\r
- for hex values, binary values and floating point values. \r
- */\r
- void ParserBase::AddValIdent(identfun_type a_pCallback)\r
- {\r
- m_pTokenReader->AddValIdent(a_pCallback);\r
- }\r
-\r
- //---------------------------------------------------------------------------\r
- /** \brief Set a function that can create variable pointer for unknown expression variables. \r
- \param a_pFactory A pointer to the variable factory.\r
- \param pUserData A user defined context pointer.\r
- */\r
- void ParserBase::SetVarFactory(facfun_type a_pFactory, void *pUserData)\r
- {\r
- m_pTokenReader->SetVarCreator(a_pFactory, pUserData); \r
- }\r
-\r
- //---------------------------------------------------------------------------\r
- /** \brief Add a function or operator callback to the parser. */\r
- void ParserBase::AddCallback( const string_type &a_strName,\r
- const ParserCallback &a_Callback, \r
- funmap_type &a_Storage,\r
- const char_type *a_szCharSet )\r
- {\r
- if (a_Callback.GetAddr()==0)\r
- Error(ecINVALID_FUN_PTR);\r
-\r
- const funmap_type *pFunMap = &a_Storage;\r
-\r
- // Check for conflicting operator or function names\r
- if ( pFunMap!=&m_FunDef && m_FunDef.find(a_strName)!=m_FunDef.end() )\r
- Error(ecNAME_CONFLICT, -1, a_strName);\r
-\r
- if ( pFunMap!=&m_PostOprtDef && m_PostOprtDef.find(a_strName)!=m_PostOprtDef.end() )\r
- Error(ecNAME_CONFLICT, -1, a_strName);\r
-\r
- if ( pFunMap!=&m_InfixOprtDef && pFunMap!=&m_OprtDef && m_InfixOprtDef.find(a_strName)!=m_InfixOprtDef.end() )\r
- Error(ecNAME_CONFLICT, -1, a_strName);\r
-\r
- if ( pFunMap!=&m_InfixOprtDef && pFunMap!=&m_OprtDef && m_OprtDef.find(a_strName)!=m_OprtDef.end() )\r
- Error(ecNAME_CONFLICT, -1, a_strName);\r
-\r
- CheckOprt(a_strName, a_Callback, a_szCharSet);\r
- a_Storage[a_strName] = a_Callback;\r
- ReInit();\r
- }\r
-\r
- //---------------------------------------------------------------------------\r
- /** \brief Check if a name contains invalid characters. \r
-\r
- \throw ParserException if the name contains invalid charakters.\r
- */\r
- void ParserBase::CheckOprt(const string_type &a_sName,\r
- const ParserCallback &a_Callback,\r
- const string_type &a_szCharSet) const\r
- {\r
- if ( !a_sName.length() ||\r
- (a_sName.find_first_not_of(a_szCharSet)!=string_type::npos) ||\r
- (a_sName[0]>='0' && a_sName[0]<='9'))\r
- {\r
- switch(a_Callback.GetCode())\r
- {\r
- case cmOPRT_POSTFIX: Error(ecINVALID_POSTFIX_IDENT, -1, a_sName);\r
- case cmOPRT_INFIX: Error(ecINVALID_INFIX_IDENT, -1, a_sName);\r
- default: Error(ecINVALID_NAME, -1, a_sName);\r
- }\r
- }\r
- }\r
-\r
- //---------------------------------------------------------------------------\r
- /** \brief Check if a name contains invalid characters. \r
-\r
- \throw ParserException if the name contains invalid charakters.\r
- */\r
- void ParserBase::CheckName(const string_type &a_sName,\r
- const string_type &a_szCharSet) const\r
- {\r
- if ( !a_sName.length() ||\r
- (a_sName.find_first_not_of(a_szCharSet)!=string_type::npos) ||\r
- (a_sName[0]>='0' && a_sName[0]<='9'))\r
- {\r
- Error(ecINVALID_NAME);\r
- }\r
- }\r
-\r
- //---------------------------------------------------------------------------\r
- /** \brief Set the formula. \r
- \param a_strFormula Formula as string_type\r
- \throw ParserException in case of syntax errors.\r
-\r
- Triggers first time calculation thus the creation of the bytecode and\r
- scanning of used variables.\r
- */\r
- void ParserBase::SetExpr(const string_type &a_sExpr)\r
- {\r
- // Check locale compatibility\r
- std::locale loc;\r
- if (m_pTokenReader->GetArgSep()==std::use_facet<numpunct<char_type> >(loc).decimal_point())\r
- Error(ecLOCALE);\r
-\r
- // <ibg> 20060222: Bugfix for Borland-Kylix:\r
- // adding a space to the expression will keep Borlands KYLIX from going wild\r
- // when calling tellg on a stringstream created from the expression after \r
- // reading a value at the end of an expression. (mu::Parser::IsVal function)\r
- // (tellg returns -1 otherwise causing the parser to ignore the value)\r
- string_type sBuf(a_sExpr + _T(" ") );\r
- m_pTokenReader->SetFormula(sBuf);\r
- ReInit();\r
- }\r
-\r
- //---------------------------------------------------------------------------\r
- /** \brief Get the default symbols used for the built in operators. \r
- \sa c_DefaultOprt\r
- */\r
- const char_type** ParserBase::GetOprtDef() const\r
- {\r
- return (const char_type **)(&c_DefaultOprt[0]);\r
- }\r
-\r
- //---------------------------------------------------------------------------\r
- /** \brief Define the set of valid characters to be used in names of\r
- functions, variables, constants.\r
- */\r
- void ParserBase::DefineNameChars(const char_type *a_szCharset)\r
- {\r
- m_sNameChars = a_szCharset;\r
- }\r
-\r
- //---------------------------------------------------------------------------\r
- /** \brief Define the set of valid characters to be used in names of\r
- binary operators and postfix operators.\r
- */\r
- void ParserBase::DefineOprtChars(const char_type *a_szCharset)\r
- {\r
- m_sOprtChars = a_szCharset;\r
- }\r
-\r
- //---------------------------------------------------------------------------\r
- /** \brief Define the set of valid characters to be used in names of\r
- infix operators.\r
- */\r
- void ParserBase::DefineInfixOprtChars(const char_type *a_szCharset)\r
- {\r
- m_sInfixOprtChars = a_szCharset;\r
- }\r
-\r
- //---------------------------------------------------------------------------\r
- /** \brief Virtual function that defines the characters allowed in name identifiers. \r
- \sa #ValidOprtChars, #ValidPrefixOprtChars\r
- */ \r
- const char_type* ParserBase::ValidNameChars() const\r
- {\r
- assert(m_sNameChars.size());\r
- return m_sNameChars.c_str();\r
- }\r
-\r
- //---------------------------------------------------------------------------\r
- /** \brief Virtual function that defines the characters allowed in operator definitions. \r
- \sa #ValidNameChars, #ValidPrefixOprtChars\r
- */\r
- const char_type* ParserBase::ValidOprtChars() const\r
- {\r
- assert(m_sOprtChars.size());\r
- return m_sOprtChars.c_str();\r
- }\r
-\r
- //---------------------------------------------------------------------------\r
- /** \brief Virtual function that defines the characters allowed in infix operator definitions.\r
- \sa #ValidNameChars, #ValidOprtChars\r
- */\r
- const char_type* ParserBase::ValidInfixOprtChars() const\r
- {\r
- assert(m_sInfixOprtChars.size());\r
- return m_sInfixOprtChars.c_str();\r
- }\r
-\r
- //---------------------------------------------------------------------------\r
- /** \brief Add a user defined operator. \r
- \post Will reset the Parser to string parsing mode.\r
- */\r
- void ParserBase::DefinePostfixOprt(const string_type &a_sName, \r
- fun_type1 a_pFun,\r
- bool a_bAllowOpt)\r
- {\r
- AddCallback(a_sName, \r
- ParserCallback(a_pFun, a_bAllowOpt, prPOSTFIX, cmOPRT_POSTFIX),\r
- m_PostOprtDef, \r
- ValidOprtChars() );\r
- }\r
-\r
- //---------------------------------------------------------------------------\r
- /** \brief Initialize user defined functions. \r
- \r
- Calls the virtual functions InitFun(), InitConst() and InitOprt().\r
- */\r
- void ParserBase::Init()\r
- {\r
- InitCharSets();\r
- InitFun();\r
- InitConst();\r
- InitOprt();\r
- }\r
-\r
- //---------------------------------------------------------------------------\r
- /** \brief Add a user defined operator. \r
- \post Will reset the Parser to string parsing mode.\r
- \param [in] a_sName operator Identifier \r
- \param [in] a_pFun Operator callback function\r
- \param [in] a_iPrec Operator Precedence (default=prSIGN)\r
- \param [in] a_bAllowOpt True if operator is volatile (default=false)\r
- \sa EPrec\r
- */\r
- void ParserBase::DefineInfixOprt(const string_type &a_sName, \r
- fun_type1 a_pFun, \r
- int a_iPrec, \r
- bool a_bAllowOpt)\r
- {\r
- AddCallback(a_sName, \r
- ParserCallback(a_pFun, a_bAllowOpt, a_iPrec, cmOPRT_INFIX), \r
- m_InfixOprtDef, \r
- ValidInfixOprtChars() );\r
- }\r
-\r
-\r
- //---------------------------------------------------------------------------\r
- /** \brief Define a binary operator. \r
- \param [in] a_sName The identifier of the operator.\r
- \param [in] a_pFun Pointer to the callback function.\r
- \param [in] a_iPrec Precedence of the operator.\r
- \param [in] a_eAssociativity The associativity of the operator.\r
- \param [in] a_bAllowOpt If this is true the operator may be optimized away.\r
- \r
- Adds a new Binary operator the the parser instance. \r
- */\r
- void ParserBase::DefineOprt( const string_type &a_sName, \r
- fun_type2 a_pFun, \r
- unsigned a_iPrec, \r
- EOprtAssociativity a_eAssociativity,\r
- bool a_bAllowOpt )\r
- {\r
- // Check for conflicts with built in operator names\r
- for (int i=0; m_bBuiltInOp && i<cmENDIF; ++i)\r
- if (a_sName == string_type(c_DefaultOprt[i]))\r
- Error(ecBUILTIN_OVERLOAD, -1, a_sName);\r
-\r
- AddCallback(a_sName, \r
- ParserCallback(a_pFun, a_bAllowOpt, a_iPrec, a_eAssociativity), \r
- m_OprtDef, \r
- ValidOprtChars() );\r
- }\r
-\r
- //---------------------------------------------------------------------------\r
- /** \brief Define a new string constant.\r
- \param [in] a_strName The name of the constant.\r
- \param [in] a_strVal the value of the constant. \r
- */\r
- void ParserBase::DefineStrConst(const string_type &a_strName, const string_type &a_strVal)\r
- {\r
- // Test if a constant with that names already exists\r
- if (m_StrVarDef.find(a_strName)!=m_StrVarDef.end())\r
- Error(ecNAME_CONFLICT);\r
-\r
- CheckName(a_strName, ValidNameChars());\r
- \r
- m_vStringVarBuf.push_back(a_strVal); // Store variable string in internal buffer\r
- m_StrVarDef[a_strName] = m_vStringVarBuf.size()-1; // bind buffer index to variable name\r
-\r
- ReInit();\r
- }\r
-\r
- //---------------------------------------------------------------------------\r
- /** \brief Add a user defined variable. \r
- \param [in] a_sName the variable name\r
- \param [in] a_pVar A pointer to the variable vaule.\r
- \post Will reset the Parser to string parsing mode.\r
- \throw ParserException in case the name contains invalid signs or a_pVar is NULL.\r
- */\r
- void ParserBase::DefineVar(const string_type &a_sName, value_type *a_pVar)\r
- {\r
- if (a_pVar==0)\r
- Error(ecINVALID_VAR_PTR);\r
-\r
- // Test if a constant with that names already exists\r
- if (m_ConstDef.find(a_sName)!=m_ConstDef.end())\r
- Error(ecNAME_CONFLICT);\r
-\r
- CheckName(a_sName, ValidNameChars());\r
- m_VarDef[a_sName] = a_pVar;\r
- ReInit();\r
- }\r
-\r
- //---------------------------------------------------------------------------\r
- /** \brief Add a user defined constant. \r
- \param [in] a_sName The name of the constant.\r
- \param [in] a_fVal the value of the constant.\r
- \post Will reset the Parser to string parsing mode.\r
- \throw ParserException in case the name contains invalid signs.\r
- */\r
- void ParserBase::DefineConst(const string_type &a_sName, value_type a_fVal)\r
- {\r
- CheckName(a_sName, ValidNameChars());\r
- m_ConstDef[a_sName] = a_fVal;\r
- ReInit();\r
- }\r
-\r
- //---------------------------------------------------------------------------\r
- /** \brief Get operator priority.\r
- \throw ParserException if a_Oprt is no operator code\r
- */\r
- int ParserBase::GetOprtPrecedence(const token_type &a_Tok) const\r
- {\r
- switch (a_Tok.GetCode())\r
- {\r
- // built in operators\r
- case cmEND: return -5;\r
- case cmARG_SEP: return -4;\r
- case cmASSIGN: return -1; \r
- case cmELSE:\r
- case cmIF: return 0;\r
- case cmLAND: return prLAND;\r
- case cmLOR: return prLOR;\r
- case cmLT:\r
- case cmGT:\r
- case cmLE:\r
- case cmGE:\r
- case cmNEQ:\r
- case cmEQ: return prCMP; \r
- case cmADD:\r
- case cmSUB: return prADD_SUB;\r
- case cmMUL:\r
- case cmDIV: return prMUL_DIV;\r
- case cmPOW: return prPOW;\r
-\r
- // user defined binary operators\r
- case cmOPRT_INFIX: \r
- case cmOPRT_BIN: return a_Tok.GetPri();\r
- default: Error(ecINTERNAL_ERROR, 5);\r
- return 999;\r
- } \r
- }\r
-\r
- //---------------------------------------------------------------------------\r
- /** \brief Get operator priority.\r
- \throw ParserException if a_Oprt is no operator code\r
- */\r
- EOprtAssociativity ParserBase::GetOprtAssociativity(const token_type &a_Tok) const\r
- {\r
- switch (a_Tok.GetCode())\r
- {\r
- case cmASSIGN:\r
- case cmLAND:\r
- case cmLOR:\r
- case cmLT:\r
- case cmGT:\r
- case cmLE:\r
- case cmGE:\r
- case cmNEQ:\r
- case cmEQ: \r
- case cmADD:\r
- case cmSUB:\r
- case cmMUL:\r
- case cmDIV: return oaLEFT;\r
- case cmPOW: return oaRIGHT;\r
- case cmOPRT_BIN: return a_Tok.GetAssociativity();\r
- default: return oaNONE;\r
- } \r
- }\r
-\r
- //---------------------------------------------------------------------------\r
- /** \brief Return a map containing the used variables only. */\r
- const varmap_type& ParserBase::GetUsedVar() const\r
- {\r
- try\r
- {\r
- m_pTokenReader->IgnoreUndefVar(true);\r
- CreateRPN(); // try to create bytecode, but don't use it for any further calculations since it\r
- // may contain references to nonexisting variables.\r
- m_pParseFormula = &ParserBase::ParseString;\r
- m_pTokenReader->IgnoreUndefVar(false);\r
- }\r
- catch(exception_type & /*e*/)\r
- {\r
- // Make sure to stay in string parse mode, dont call ReInit()\r
- // because it deletes the array with the used variables\r
- m_pParseFormula = &ParserBase::ParseString;\r
- m_pTokenReader->IgnoreUndefVar(false);\r
- throw;\r
- }\r
- \r
- return m_pTokenReader->GetUsedVar();\r
- }\r
-\r
- //---------------------------------------------------------------------------\r
- /** \brief Return a map containing the used variables only. */\r
- const varmap_type& ParserBase::GetVar() const\r
- {\r
- return m_VarDef;\r
- }\r
-\r
- //---------------------------------------------------------------------------\r
- /** \brief Return a map containing all parser constants. */\r
- const valmap_type& ParserBase::GetConst() const\r
- {\r
- return m_ConstDef;\r
- }\r
-\r
- //---------------------------------------------------------------------------\r
- /** \brief Return prototypes of all parser functions.\r
- \return #m_FunDef\r
- \sa FunProt\r
- \throw nothrow\r
- \r
- The return type is a map of the public type #funmap_type containing the prototype\r
- definitions for all numerical parser functions. String functions are not part of \r
- this map. The Prototype definition is encapsulated in objects of the class FunProt\r
- one per parser function each associated with function names via a map construct.\r
- */\r
- const funmap_type& ParserBase::GetFunDef() const\r
- {\r
- return m_FunDef;\r
- }\r
-\r
- //---------------------------------------------------------------------------\r
- /** \brief Retrieve the formula. */\r
- const string_type& ParserBase::GetExpr() const\r
- {\r
- return m_pTokenReader->GetExpr();\r
- }\r
-\r
- //---------------------------------------------------------------------------\r
- /** \brief Execute a function that takes a single string argument.\r
- \param a_FunTok Function token.\r
- \throw exception_type If the function token is not a string function\r
- */\r
- ParserBase::token_type ParserBase::ApplyStrFunc(const token_type &a_FunTok,\r
- const std::vector<token_type> &a_vArg) const\r
- {\r
- if (a_vArg.back().GetCode()!=cmSTRING)\r
- Error(ecSTRING_EXPECTED, m_pTokenReader->GetPos(), a_FunTok.GetAsString());\r
-\r
- token_type valTok;\r
- generic_fun_type pFunc = a_FunTok.GetFuncAddr();\r
- assert(pFunc);\r
-\r
- try\r
- {\r
- // Check function arguments; write dummy value into valtok to represent the result\r
- switch(a_FunTok.GetArgCount())\r
- {\r
- case 0: valTok.SetVal(1); a_vArg[0].GetAsString(); break;\r
- case 1: valTok.SetVal(1); a_vArg[1].GetAsString(); a_vArg[0].GetVal(); break;\r
- case 2: valTok.SetVal(1); a_vArg[2].GetAsString(); a_vArg[1].GetVal(); a_vArg[0].GetVal(); break;\r
- default: Error(ecINTERNAL_ERROR);\r
- }\r
- }\r
- catch(ParserError& )\r
- {\r
- Error(ecVAL_EXPECTED, m_pTokenReader->GetPos(), a_FunTok.GetAsString());\r
- }\r
-\r
- // string functions won't be optimized\r
- m_vRPN.AddStrFun(pFunc, a_FunTok.GetArgCount(), a_vArg.back().GetIdx());\r
- \r
- // Push dummy value representing the function result to the stack\r
- return valTok;\r
- }\r
-\r
- //---------------------------------------------------------------------------\r
- /** \brief Apply a function token. \r
- \param iArgCount Number of Arguments actually gathered used only for multiarg functions.\r
- \post The result is pushed to the value stack\r
- \post The function token is removed from the stack\r
- \throw exception_type if Argument count does not mach function requirements.\r
- */\r
- void ParserBase::ApplyFunc( ParserStack<token_type> &a_stOpt,\r
- ParserStack<token_type> &a_stVal, \r
- int a_iArgCount) const\r
- { \r
- assert(m_pTokenReader.get());\r
-\r
- // Operator stack empty or does not contain tokens with callback functions\r
- if (a_stOpt.empty() || a_stOpt.top().GetFuncAddr()==0 )\r
- return;\r
-\r
- token_type funTok = a_stOpt.pop();\r
- assert(funTok.GetFuncAddr());\r
-\r
- // Binary operators must rely on their internal operator number\r
- // since counting of operators relies on commas for function arguments\r
- // binary operators do not have commas in their expression\r
- int iArgCount = (funTok.GetCode()==cmOPRT_BIN) ? funTok.GetArgCount() : a_iArgCount;\r
-\r
- // determine how many parameters the function needs. To remember iArgCount includes the \r
- // string parameter whilst GetArgCount() counts only numeric parameters.\r
- int iArgRequired = funTok.GetArgCount() + ((funTok.GetType()==tpSTR) ? 1 : 0);\r
-\r
- // Thats the number of numerical parameters\r
- int iArgNumerical = iArgCount - ((funTok.GetType()==tpSTR) ? 1 : 0);\r
-\r
- if (funTok.GetCode()==cmFUNC_STR && iArgCount-iArgNumerical>1)\r
- Error(ecINTERNAL_ERROR);\r
-\r
- if (funTok.GetArgCount()>=0 && iArgCount>iArgRequired) \r
- Error(ecTOO_MANY_PARAMS, m_pTokenReader->GetPos()-1, funTok.GetAsString());\r
-\r
- if (funTok.GetCode()!=cmOPRT_BIN && iArgCount<iArgRequired )\r
- Error(ecTOO_FEW_PARAMS, m_pTokenReader->GetPos()-1, funTok.GetAsString());\r
-\r
- if (funTok.GetCode()==cmFUNC_STR && iArgCount>iArgRequired )\r
- Error(ecTOO_MANY_PARAMS, m_pTokenReader->GetPos()-1, funTok.GetAsString());\r
-\r
- // Collect the numeric function arguments from the value stack and store them\r
- // in a vector\r
- std::vector<token_type> stArg; \r
- for (int i=0; i<iArgNumerical; ++i)\r
- {\r
- stArg.push_back( a_stVal.pop() );\r
- if ( stArg.back().GetType()==tpSTR && funTok.GetType()!=tpSTR )\r
- Error(ecVAL_EXPECTED, m_pTokenReader->GetPos(), funTok.GetAsString());\r
- }\r
-\r
- switch(funTok.GetCode())\r
- {\r
- case cmFUNC_STR: \r
- stArg.push_back(a_stVal.pop());\r
- \r
- if ( stArg.back().GetType()==tpSTR && funTok.GetType()!=tpSTR )\r
- Error(ecVAL_EXPECTED, m_pTokenReader->GetPos(), funTok.GetAsString());\r
-\r
- ApplyStrFunc(funTok, stArg); \r
- break;\r
-\r
- case cmFUNC_BULK: \r
- m_vRPN.AddBulkFun(funTok.GetFuncAddr(), (int)stArg.size()); \r
- break;\r
-\r
- case cmOPRT_BIN:\r
- case cmOPRT_POSTFIX:\r
- case cmOPRT_INFIX:\r
- case cmFUNC:\r
- if (funTok.GetArgCount()==-1 && iArgCount==0)\r
- Error(ecTOO_FEW_PARAMS, m_pTokenReader->GetPos(), funTok.GetAsString());\r
-\r
- m_vRPN.AddFun(funTok.GetFuncAddr(), (funTok.GetArgCount()==-1) ? -iArgNumerical : iArgNumerical);\r
- break;\r
- default:\r
- break;\r
- }\r
-\r
- // Push dummy value representing the function result to the stack\r
- token_type token;\r
- token.SetVal(1); \r
- a_stVal.push(token);\r
- }\r
-\r
- //---------------------------------------------------------------------------\r
- void ParserBase::ApplyIfElse(ParserStack<token_type> &a_stOpt,\r
- ParserStack<token_type> &a_stVal) const\r
- {\r
- // Check if there is an if Else clause to be calculated\r
- while (a_stOpt.size() && a_stOpt.top().GetCode()==cmELSE)\r
- {\r
- token_type opElse = a_stOpt.pop();\r
- MUP_ASSERT(a_stOpt.size()>0);\r
-\r
- // Take the value associated with the else branch from the value stack\r
- token_type vVal2 = a_stVal.pop();\r
-\r
- MUP_ASSERT(a_stOpt.size()>0);\r
- MUP_ASSERT(a_stVal.size()>=2);\r
-\r
- // it then else is a ternary operator Pop all three values from the value s\r
- // tack and just return the right value\r
- token_type vVal1 = a_stVal.pop();\r
- token_type vExpr = a_stVal.pop();\r
-\r
- a_stVal.push( (vExpr.GetVal()!=0) ? vVal1 : vVal2);\r
-\r
- token_type opIf = a_stOpt.pop();\r
- MUP_ASSERT(opElse.GetCode()==cmELSE);\r
- MUP_ASSERT(opIf.GetCode()==cmIF);\r
-\r
- m_vRPN.AddIfElse(cmENDIF);\r
- } // while pending if-else-clause found\r
- }\r
-\r
- //---------------------------------------------------------------------------\r
- /** \brief Performs the necessary steps to write code for\r
- the execution of binary operators into the bytecode. \r
- */\r
- void ParserBase::ApplyBinOprt(ParserStack<token_type> &a_stOpt,\r
- ParserStack<token_type> &a_stVal) const\r
- {\r
- // is it a user defined binary operator?\r
- if (a_stOpt.top().GetCode()==cmOPRT_BIN)\r
- {\r
- ApplyFunc(a_stOpt, a_stVal, 2);\r
- }\r
- else\r
- {\r
- MUP_ASSERT(a_stVal.size()>=2);\r
- token_type valTok1 = a_stVal.pop(),\r
- valTok2 = a_stVal.pop(),\r
- optTok = a_stOpt.pop(),\r
- resTok; \r
-\r
- if ( valTok1.GetType()!=valTok2.GetType() || \r
- (valTok1.GetType()==tpSTR && valTok2.GetType()==tpSTR) )\r
- Error(ecOPRT_TYPE_CONFLICT, m_pTokenReader->GetPos(), optTok.GetAsString());\r
-\r
- if (optTok.GetCode()==cmASSIGN)\r
- {\r
- if (valTok2.GetCode()!=cmVAR)\r
- Error(ecUNEXPECTED_OPERATOR, -1, _T("="));\r
- \r
- m_vRPN.AddAssignOp(valTok2.GetVar());\r
- }\r
- else\r
- m_vRPN.AddOp(optTok.GetCode());\r
-\r
- resTok.SetVal(1);\r
- a_stVal.push(resTok);\r
- }\r
- }\r
-\r
- //---------------------------------------------------------------------------\r
- /** \brief Apply a binary operator. \r
- \param a_stOpt The operator stack\r
- \param a_stVal The value stack\r
- */\r
- void ParserBase::ApplyRemainingOprt(ParserStack<token_type> &stOpt,\r
- ParserStack<token_type> &stVal) const\r
- {\r
- while (stOpt.size() && \r
- stOpt.top().GetCode() != cmBO &&\r
- stOpt.top().GetCode() != cmIF)\r
- {\r
- token_type tok = stOpt.top();\r
- switch (tok.GetCode())\r
- {\r
- case cmOPRT_INFIX:\r
- case cmOPRT_BIN:\r
- case cmLE:\r
- case cmGE:\r
- case cmNEQ:\r
- case cmEQ:\r
- case cmLT:\r
- case cmGT:\r
- case cmADD:\r
- case cmSUB:\r
- case cmMUL:\r
- case cmDIV:\r
- case cmPOW:\r
- case cmLAND:\r
- case cmLOR:\r
- case cmASSIGN:\r
- if (stOpt.top().GetCode()==cmOPRT_INFIX)\r
- ApplyFunc(stOpt, stVal, 1);\r
- else\r
- ApplyBinOprt(stOpt, stVal);\r
- break;\r
-\r
- case cmELSE:\r
- ApplyIfElse(stOpt, stVal);\r
- break;\r
-\r
- default:\r
- Error(ecINTERNAL_ERROR);\r
- }\r
- }\r
- }\r
-\r
- //---------------------------------------------------------------------------\r
- /** \brief Parse the command code.\r
- \sa ParseString(...)\r
-\r
- Command code contains precalculated stack positions of the values and the\r
- associated operators. The Stack is filled beginning from index one the \r
- value at index zero is not used at all.\r
- */\r
- value_type ParserBase::ParseCmdCode() const\r
- {\r
- return ParseCmdCodeBulk(0, 0);\r
- }\r
-\r
- //---------------------------------------------------------------------------\r
- /** \brief Evaluate the RPN. \r
- \param nOffset The offset added to variable addresses (for bulk mode)\r
- \param nThreadID OpenMP Thread id of the calling thread\r
- */\r
- value_type ParserBase::ParseCmdCodeBulk(int nOffset, int nThreadID) const\r
- {\r
- assert(nThreadID<=s_MaxNumOpenMPThreads);\r
-\r
- // Note: The check for nOffset==0 and nThreadID here is not necessary but \r
- // brings a minor performance gain when not in bulk mode.\r
- value_type *Stack = ((nOffset==0) && (nThreadID==0)) ? &m_vStackBuffer[0] : &m_vStackBuffer[nThreadID * (m_vStackBuffer.size() / s_MaxNumOpenMPThreads)];\r
- value_type buf;\r
- int sidx(0);\r
- for (const SToken *pTok = m_vRPN.GetBase(); pTok->Cmd!=cmEND ; ++pTok)\r
- {\r
- switch (pTok->Cmd)\r
- {\r
- // built in binary operators\r
- case cmLE: --sidx; Stack[sidx] = Stack[sidx] <= Stack[sidx+1]; continue;\r
- case cmGE: --sidx; Stack[sidx] = Stack[sidx] >= Stack[sidx+1]; continue;\r
- case cmNEQ: --sidx; Stack[sidx] = Stack[sidx] != Stack[sidx+1]; continue;\r
- case cmEQ: --sidx; Stack[sidx] = Stack[sidx] == Stack[sidx+1]; continue;\r
- case cmLT: --sidx; Stack[sidx] = Stack[sidx] < Stack[sidx+1]; continue;\r
- case cmGT: --sidx; Stack[sidx] = Stack[sidx] > Stack[sidx+1]; continue;\r
- case cmADD: --sidx; Stack[sidx] += Stack[1+sidx]; continue;\r
- case cmSUB: --sidx; Stack[sidx] -= Stack[1+sidx]; continue;\r
- case cmMUL: --sidx; Stack[sidx] *= Stack[1+sidx]; continue;\r
- case cmDIV: --sidx;\r
-\r
- #if defined(MUP_MATH_EXCEPTIONS)\r
- if (Stack[1+sidx]==0)\r
- Error(ecDIV_BY_ZERO);\r
- #endif\r
- Stack[sidx] /= Stack[1+sidx]; \r
- continue;\r
-\r
- case cmPOW: \r
- --sidx; Stack[sidx] = MathImpl<value_type>::Pow(Stack[sidx], Stack[1+sidx]);\r
- continue;\r
-\r
- case cmLAND: --sidx; Stack[sidx] = (Stack[sidx] != 0.) && (Stack[sidx+1] != 0.); continue;\r
- case cmLOR: --sidx; Stack[sidx] = (Stack[sidx] != 0.) || (Stack[sidx+1] != 0.); continue;\r
-\r
- case cmASSIGN: \r
- // Bugfix for Bulkmode:\r
- // for details see:\r
- // 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\r
- --sidx; Stack[sidx] = *(pTok->u.Oprt.ptr + nOffset) = Stack[sidx + 1]; continue;\r
- // original code:\r
- //--sidx; Stack[sidx] = *pTok->u.Oprt.ptr = Stack[sidx+1]; continue;\r
-\r
- //case cmBO: // unused, listed for compiler optimization purposes\r
- //case cmBC:\r
- // MUP_FAIL(INVALID_CODE_IN_BYTECODE);\r
- // continue;\r
-\r
- case cmIF:\r
- if (Stack[sidx--]==0)\r
- pTok += pTok->u.Oprt.offset;\r
- continue;\r
-\r
- case cmELSE:\r
- pTok += pTok->u.Oprt.offset;\r
- continue;\r
-\r
- case cmENDIF:\r
- continue;\r
-\r
- //case cmARG_SEP:\r
- // MUP_FAIL(INVALID_CODE_IN_BYTECODE);\r
- // continue;\r
-\r
- // value and variable tokens\r
- case cmVAR: Stack[++sidx] = *(pTok->u.Val.ptr + nOffset); continue;\r
- case cmVAL: Stack[++sidx] = pTok->u.Val.data2; continue;\r
- \r
- case cmVARPOW2: buf = *(pTok->u.Val.ptr + nOffset);\r
- Stack[++sidx] = buf*buf;\r
- continue;\r
-\r
- case cmVARPOW3: buf = *(pTok->u.Val.ptr + nOffset);\r
- Stack[++sidx] = buf*buf*buf;\r
- continue;\r
-\r
- case cmVARPOW4: buf = *(pTok->u.Val.ptr + nOffset);\r
- Stack[++sidx] = buf*buf*buf*buf;\r
- continue;\r
- \r
- case cmVARMUL: Stack[++sidx] = *(pTok->u.Val.ptr + nOffset) * pTok->u.Val.data + pTok->u.Val.data2;\r
- continue;\r
-\r
- // Next is treatment of numeric functions\r
- case cmFUNC:\r
- {\r
- int iArgCount = pTok->u.Fun.argc;\r
-\r
- // switch according to argument count\r
- switch(iArgCount) \r
- {\r
- case 0: sidx += 1; Stack[sidx] = (*(fun_type0)pTok->u.Fun.ptr)(); continue;\r
- case 1: Stack[sidx] = (*(fun_type1)pTok->u.Fun.ptr)(Stack[sidx]); continue;\r
- case 2: sidx -= 1; Stack[sidx] = (*(fun_type2)pTok->u.Fun.ptr)(Stack[sidx], Stack[sidx+1]); continue;\r
- case 3: sidx -= 2; Stack[sidx] = (*(fun_type3)pTok->u.Fun.ptr)(Stack[sidx], Stack[sidx+1], Stack[sidx+2]); continue;\r
- case 4: sidx -= 3; Stack[sidx] = (*(fun_type4)pTok->u.Fun.ptr)(Stack[sidx], Stack[sidx+1], Stack[sidx+2], Stack[sidx+3]); continue;\r
- 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;\r
- 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;\r
- 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;\r
- 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;\r
- 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;\r
- 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;\r
- default:\r
- if (iArgCount>0) // function with variable arguments store the number as a negative value\r
- Error(ecINTERNAL_ERROR, 1);\r
-\r
- sidx -= -iArgCount - 1;\r
- Stack[sidx] =(*(multfun_type)pTok->u.Fun.ptr)(&Stack[sidx], -iArgCount);\r
- continue;\r
- }\r
- }\r
-\r
- // Next is treatment of string functions\r
- case cmFUNC_STR:\r
- {\r
- sidx -= pTok->u.Fun.argc -1;\r
-\r
- // The index of the string argument in the string table\r
- int iIdxStack = pTok->u.Fun.idx; \r
- MUP_ASSERT( iIdxStack>=0 && iIdxStack<(int)m_vStringBuf.size() );\r
-\r
- switch(pTok->u.Fun.argc) // switch according to argument count\r
- {\r
- case 0: Stack[sidx] = (*(strfun_type1)pTok->u.Fun.ptr)(m_vStringBuf[iIdxStack].c_str()); continue;\r
- case 1: Stack[sidx] = (*(strfun_type2)pTok->u.Fun.ptr)(m_vStringBuf[iIdxStack].c_str(), Stack[sidx]); continue;\r
- case 2: Stack[sidx] = (*(strfun_type3)pTok->u.Fun.ptr)(m_vStringBuf[iIdxStack].c_str(), Stack[sidx], Stack[sidx+1]); continue;\r
- }\r
-\r
- continue;\r
- }\r
-\r
- case cmFUNC_BULK:\r
- {\r
- int iArgCount = pTok->u.Fun.argc;\r
-\r
- // switch according to argument count\r
- switch(iArgCount) \r
- {\r
- case 0: sidx += 1; Stack[sidx] = (*(bulkfun_type0 )pTok->u.Fun.ptr)(nOffset, nThreadID); continue;\r
- case 1: Stack[sidx] = (*(bulkfun_type1 )pTok->u.Fun.ptr)(nOffset, nThreadID, Stack[sidx]); continue;\r
- case 2: sidx -= 1; Stack[sidx] = (*(bulkfun_type2 )pTok->u.Fun.ptr)(nOffset, nThreadID, Stack[sidx], Stack[sidx+1]); continue;\r
- case 3: sidx -= 2; Stack[sidx] = (*(bulkfun_type3 )pTok->u.Fun.ptr)(nOffset, nThreadID, Stack[sidx], Stack[sidx+1], Stack[sidx+2]); continue;\r
- 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;\r
- 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;\r
- 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;\r
- 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;\r
- 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;\r
- 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;\r
- 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;\r
- default:\r
- Error(ecINTERNAL_ERROR, 2);\r
- continue;\r
- }\r
- }\r
-\r
- default:\r
- Error(ecINTERNAL_ERROR, 3);\r
- return 0;\r
- } // switch CmdCode\r
- } // for all bytecode tokens\r
-\r
- return Stack[m_nFinalResultIdx]; \r
- }\r
-\r
- //---------------------------------------------------------------------------\r
- void ParserBase::CreateRPN() const\r
- {\r
- if (!m_pTokenReader->GetExpr().length())\r
- Error(ecUNEXPECTED_EOF, 0);\r
-\r
- ParserStack<token_type> stOpt, stVal;\r
- ParserStack<int> stArgCount;\r
- token_type opta, opt; // for storing operators\r
- token_type val, tval; // for storing value\r
-\r
- ReInit();\r
- \r
- // The outermost counter counts the number of seperated items\r
- // such as in "a=10,b=20,c=c+a"\r
- stArgCount.push(1);\r
- \r
- for(;;)\r
- {\r
- opt = m_pTokenReader->ReadNextToken();\r
-\r
- switch (opt.GetCode())\r
- {\r
- //\r
- // Next three are different kind of value entries\r
- //\r
- case cmSTRING:\r
- opt.SetIdx((int)m_vStringBuf.size()); // Assign buffer index to token \r
- stVal.push(opt);\r
- m_vStringBuf.push_back(opt.GetAsString()); // Store string in internal buffer\r
- break;\r
- \r
- case cmVAR:\r
- stVal.push(opt);\r
- m_vRPN.AddVar( static_cast<value_type*>(opt.GetVar()) );\r
- break;\r
-\r
- case cmVAL:\r
- stVal.push(opt);\r
- m_vRPN.AddVal( opt.GetVal() );\r
- break;\r
-\r
- case cmELSE:\r
- m_nIfElseCounter--;\r
- if (m_nIfElseCounter<0)\r
- Error(ecMISPLACED_COLON, m_pTokenReader->GetPos());\r
-\r
- ApplyRemainingOprt(stOpt, stVal);\r
- m_vRPN.AddIfElse(cmELSE);\r
- stOpt.push(opt);\r
- break;\r
-\r
-\r
- case cmARG_SEP:\r
- if (stArgCount.empty())\r
- Error(ecUNEXPECTED_ARG_SEP, m_pTokenReader->GetPos());\r
-\r
- ++stArgCount.top();\r
- // fallthrough intentional (no break!)\r
-\r
- case cmEND:\r
- ApplyRemainingOprt(stOpt, stVal);\r
- break;\r
-\r
- case cmBC:\r
- {\r
- // The argument count for parameterless functions is zero\r
- // by default an opening bracket sets parameter count to 1\r
- // in preparation of arguments to come. If the last token\r
- // was an opening bracket we know better...\r
- if (opta.GetCode()==cmBO)\r
- --stArgCount.top();\r
- \r
- ApplyRemainingOprt(stOpt, stVal);\r
-\r
- // Check if the bracket content has been evaluated completely\r
- if (stOpt.size() && stOpt.top().GetCode()==cmBO)\r
- {\r
- // if opt is ")" and opta is "(" the bracket has been evaluated, now its time to check\r
- // if there is either a function or a sign pending\r
- // neither the opening nor the closing bracket will be pushed back to\r
- // the operator stack\r
- // Check if a function is standing in front of the opening bracket, \r
- // if yes evaluate it afterwards check for infix operators\r
- assert(stArgCount.size());\r
- int iArgCount = stArgCount.pop();\r
- \r
- stOpt.pop(); // Take opening bracket from stack\r
-\r
- if (iArgCount>1 && ( stOpt.size()==0 || \r
- (stOpt.top().GetCode()!=cmFUNC && \r
- stOpt.top().GetCode()!=cmFUNC_BULK && \r
- stOpt.top().GetCode()!=cmFUNC_STR) ) )\r
- Error(ecUNEXPECTED_ARG, m_pTokenReader->GetPos());\r
- \r
- // The opening bracket was popped from the stack now check if there\r
- // was a function before this bracket\r
- if (stOpt.size() && \r
- stOpt.top().GetCode()!=cmOPRT_INFIX && \r
- stOpt.top().GetCode()!=cmOPRT_BIN && \r
- stOpt.top().GetFuncAddr()!=0)\r
- {\r
- ApplyFunc(stOpt, stVal, iArgCount);\r
- }\r
- }\r
- } // if bracket content is evaluated\r
- break;\r
-\r
- //\r
- // Next are the binary operator entries\r
- //\r
- //case cmAND: // built in binary operators\r
- //case cmOR:\r
- //case cmXOR:\r
- case cmIF:\r
- m_nIfElseCounter++;\r
- // fallthrough intentional (no break!)\r
-\r
- case cmLAND:\r
- case cmLOR:\r
- case cmLT:\r
- case cmGT:\r
- case cmLE:\r
- case cmGE:\r
- case cmNEQ:\r
- case cmEQ:\r
- case cmADD:\r
- case cmSUB:\r
- case cmMUL:\r
- case cmDIV:\r
- case cmPOW:\r
- case cmASSIGN:\r
- case cmOPRT_BIN:\r
-\r
- // A binary operator (user defined or built in) has been found. \r
- while ( stOpt.size() && \r
- stOpt.top().GetCode() != cmBO &&\r
- stOpt.top().GetCode() != cmELSE &&\r
- stOpt.top().GetCode() != cmIF)\r
- {\r
- int nPrec1 = GetOprtPrecedence(stOpt.top()),\r
- nPrec2 = GetOprtPrecedence(opt);\r
-\r
- if (stOpt.top().GetCode()==opt.GetCode())\r
- {\r
-\r
- // Deal with operator associativity\r
- EOprtAssociativity eOprtAsct = GetOprtAssociativity(opt);\r
- if ( (eOprtAsct==oaRIGHT && (nPrec1 <= nPrec2)) || \r
- (eOprtAsct==oaLEFT && (nPrec1 < nPrec2)) )\r
- {\r
- break;\r
- }\r
- }\r
- else if (nPrec1 < nPrec2)\r
- {\r
- // In case the operators are not equal the precedence decides alone...\r
- break;\r
- }\r
- \r
- if (stOpt.top().GetCode()==cmOPRT_INFIX)\r
- ApplyFunc(stOpt, stVal, 1);\r
- else\r
- ApplyBinOprt(stOpt, stVal);\r
- } // while ( ... )\r
-\r
- if (opt.GetCode()==cmIF)\r
- m_vRPN.AddIfElse(opt.GetCode());\r
-\r
- // The operator can't be evaluated right now, push back to the operator stack\r
- stOpt.push(opt);\r
- break;\r
-\r
- //\r
- // Last section contains functions and operators implicitely mapped to functions\r
- //\r
- case cmBO:\r
- stArgCount.push(1);\r
- stOpt.push(opt);\r
- break;\r
-\r
- case cmOPRT_INFIX:\r
- case cmFUNC:\r
- case cmFUNC_BULK:\r
- case cmFUNC_STR: \r
- stOpt.push(opt);\r
- break;\r
-\r
- case cmOPRT_POSTFIX:\r
- stOpt.push(opt);\r
- ApplyFunc(stOpt, stVal, 1); // this is the postfix operator\r
- break;\r
-\r
- default: Error(ecINTERNAL_ERROR, 3);\r
- } // end of switch operator-token\r
-\r
- opta = opt;\r
-\r
- if ( opt.GetCode() == cmEND )\r
- {\r
- m_vRPN.Finalize();\r
- break;\r
- }\r
-\r
- if (ParserBase::g_DbgDumpStack)\r
- {\r
- StackDump(stVal, stOpt);\r
- m_vRPN.AsciiDump();\r
- }\r
- } // while (true)\r
-\r
- if (ParserBase::g_DbgDumpCmdCode)\r
- m_vRPN.AsciiDump();\r
-\r
- if (m_nIfElseCounter>0)\r
- Error(ecMISSING_ELSE_CLAUSE);\r
-\r
- // get the last value (= final result) from the stack\r
- MUP_ASSERT(stArgCount.size()==1);\r
- m_nFinalResultIdx = stArgCount.top();\r
- if (m_nFinalResultIdx==0)\r
- Error(ecINTERNAL_ERROR, 9);\r
-\r
- if (stVal.size()==0)\r
- Error(ecEMPTY_EXPRESSION);\r
-\r
- if (stVal.top().GetType()!=tpDBL)\r
- Error(ecSTR_RESULT);\r
-\r
- m_vStackBuffer.resize(m_vRPN.GetMaxStackSize() * s_MaxNumOpenMPThreads);\r
- }\r
-\r
- //---------------------------------------------------------------------------\r
- /** \brief One of the two main parse functions.\r
- \sa ParseCmdCode(...)\r
-\r
- Parse expression from input string. Perform syntax checking and create \r
- bytecode. After parsing the string and creating the bytecode the function \r
- pointer #m_pParseFormula will be changed to the second parse routine the \r
- uses bytecode instead of string parsing.\r
- */\r
- value_type ParserBase::ParseString() const\r
- {\r
- try\r
- {\r
- CreateRPN();\r
- m_pParseFormula = &ParserBase::ParseCmdCode;\r
- return (this->*m_pParseFormula)(); \r
- }\r
- catch(ParserError &exc)\r
- {\r
- exc.SetFormula(m_pTokenReader->GetExpr());\r
- throw;\r
- }\r
- }\r
-\r
- //---------------------------------------------------------------------------\r
- /** \brief Create an error containing the parse error position.\r
-\r
- This function will create an Parser Exception object containing the error text and\r
- its position.\r
-\r
- \param a_iErrc [in] The error code of type #EErrorCodes.\r
- \param a_iPos [in] The position where the error was detected.\r
- \param a_strTok [in] The token string representation associated with the error.\r
- \throw ParserException always throws thats the only purpose of this function.\r
- */\r
- void ParserBase::Error(EErrorCodes a_iErrc, int a_iPos, const string_type &a_sTok) const\r
- {\r
- throw exception_type(a_iErrc, a_sTok, m_pTokenReader->GetExpr(), a_iPos);\r
- }\r
-\r
- //------------------------------------------------------------------------------\r
- /** \brief Clear all user defined variables.\r
- \throw nothrow\r
-\r
- Resets the parser to string parsing mode by calling #ReInit.\r
- */\r
- void ParserBase::ClearVar()\r
- {\r
- m_VarDef.clear();\r
- ReInit();\r
- }\r
-\r
- //------------------------------------------------------------------------------\r
- /** \brief Remove a variable from internal storage.\r
- \throw nothrow\r
-\r
- Removes a variable if it exists. If the Variable does not exist nothing will be done.\r
- */\r
- void ParserBase::RemoveVar(const string_type &a_strVarName)\r
- {\r
- varmap_type::iterator item = m_VarDef.find(a_strVarName);\r
- if (item!=m_VarDef.end())\r
- {\r
- m_VarDef.erase(item);\r
- ReInit();\r
- }\r
- }\r
-\r
- //------------------------------------------------------------------------------\r
- /** \brief Clear all functions.\r
- \post Resets the parser to string parsing mode.\r
- \throw nothrow\r
- */\r
- void ParserBase::ClearFun()\r
- {\r
- m_FunDef.clear();\r
- ReInit();\r
- }\r
-\r
- //------------------------------------------------------------------------------\r
- /** \brief Clear all user defined constants.\r
-\r
- Both numeric and string constants will be removed from the internal storage.\r
- \post Resets the parser to string parsing mode.\r
- \throw nothrow\r
- */\r
- void ParserBase::ClearConst()\r
- {\r
- m_ConstDef.clear();\r
- m_StrVarDef.clear();\r
- ReInit();\r
- }\r
-\r
- //------------------------------------------------------------------------------\r
- /** \brief Clear all user defined postfix operators.\r
- \post Resets the parser to string parsing mode.\r
- \throw nothrow\r
- */\r
- void ParserBase::ClearPostfixOprt()\r
- {\r
- m_PostOprtDef.clear();\r
- ReInit();\r
- }\r
-\r
- //------------------------------------------------------------------------------\r
- /** \brief Clear all user defined binary operators.\r
- \post Resets the parser to string parsing mode.\r
- \throw nothrow\r
- */\r
- void ParserBase::ClearOprt()\r
- {\r
- m_OprtDef.clear();\r
- ReInit();\r
- }\r
-\r
- //------------------------------------------------------------------------------\r
- /** \brief Clear the user defined Prefix operators. \r
- \post Resets the parser to string parser mode.\r
- \throw nothrow\r
- */\r
- void ParserBase::ClearInfixOprt()\r
- {\r
- m_InfixOprtDef.clear();\r
- ReInit();\r
- }\r
-\r
- //------------------------------------------------------------------------------\r
- /** \brief Enable or disable the formula optimization feature. \r
- \post Resets the parser to string parser mode.\r
- \throw nothrow\r
- */\r
- void ParserBase::EnableOptimizer(bool a_bIsOn)\r
- {\r
- m_vRPN.EnableOptimizer(a_bIsOn);\r
- ReInit();\r
- }\r
-\r
- //---------------------------------------------------------------------------\r
- /** \brief Enable the dumping of bytecode amd stack content on the console. \r
- \param bDumpCmd Flag to enable dumping of the current bytecode to the console.\r
- \param bDumpStack Flag to enable dumping of the stack content is written to the console.\r
-\r
- This function is for debug purposes only!\r
- */\r
- void ParserBase::EnableDebugDump(bool bDumpCmd, bool bDumpStack)\r
- {\r
- ParserBase::g_DbgDumpCmdCode = bDumpCmd;\r
- ParserBase::g_DbgDumpStack = bDumpStack;\r
- }\r
-\r
- //------------------------------------------------------------------------------\r
- /** \brief Enable or disable the built in binary operators.\r
- \throw nothrow\r
- \sa m_bBuiltInOp, ReInit()\r
-\r
- If you disable the built in binary operators there will be no binary operators\r
- defined. Thus you must add them manually one by one. It is not possible to\r
- disable built in operators selectively. This function will Reinitialize the\r
- parser by calling ReInit().\r
- */\r
- void ParserBase::EnableBuiltInOprt(bool a_bIsOn)\r
- {\r
- m_bBuiltInOp = a_bIsOn;\r
- ReInit();\r
- }\r
-\r
- //------------------------------------------------------------------------------\r
- /** \brief Query status of built in variables.\r
- \return #m_bBuiltInOp; true if built in operators are enabled.\r
- \throw nothrow\r
- */\r
- bool ParserBase::HasBuiltInOprt() const\r
- {\r
- return m_bBuiltInOp;\r
- }\r
-\r
- //------------------------------------------------------------------------------\r
- /** \brief Get the argument separator character. \r
- */\r
- char_type ParserBase::GetArgSep() const\r
- {\r
- return m_pTokenReader->GetArgSep();\r
- }\r
-\r
- //------------------------------------------------------------------------------\r
- /** \brief Set argument separator. \r
- \param cArgSep the argument separator character.\r
- */\r
- void ParserBase::SetArgSep(char_type cArgSep)\r
- {\r
- m_pTokenReader->SetArgSep(cArgSep);\r
- }\r
-\r
- //------------------------------------------------------------------------------\r
- /** \brief Dump stack content. \r
-\r
- This function is used for debugging only.\r
- */\r
- void ParserBase::StackDump(const ParserStack<token_type> &a_stVal, \r
- const ParserStack<token_type> &a_stOprt) const\r
- {\r
- ParserStack<token_type> stOprt(a_stOprt), \r
- stVal(a_stVal);\r
-\r
- mu::console() << _T("\nValue stack:\n");\r
- while ( !stVal.empty() ) \r
- {\r
- token_type val = stVal.pop();\r
- if (val.GetType()==tpSTR)\r
- mu::console() << _T(" \"") << val.GetAsString() << _T("\" ");\r
- else\r
- mu::console() << _T(" ") << val.GetVal() << _T(" ");\r
- }\r
- mu::console() << "\nOperator stack:\n";\r
-\r
- while ( !stOprt.empty() )\r
- {\r
- if (stOprt.top().GetCode()<=cmASSIGN) \r
- {\r
- mu::console() << _T("OPRT_INTRNL \"")\r
- << ParserBase::c_DefaultOprt[stOprt.top().GetCode()] \r
- << _T("\" \n");\r
- }\r
- else\r
- {\r
- switch(stOprt.top().GetCode())\r
- {\r
- case cmVAR: mu::console() << _T("VAR\n"); break;\r
- case cmVAL: mu::console() << _T("VAL\n"); break;\r
- case cmFUNC: mu::console() << _T("FUNC \"") \r
- << stOprt.top().GetAsString() \r
- << _T("\"\n"); break;\r
- case cmFUNC_BULK: mu::console() << _T("FUNC_BULK \"") \r
- << stOprt.top().GetAsString() \r
- << _T("\"\n"); break;\r
- case cmOPRT_INFIX: mu::console() << _T("OPRT_INFIX \"")\r
- << stOprt.top().GetAsString() \r
- << _T("\"\n"); break;\r
- case cmOPRT_BIN: mu::console() << _T("OPRT_BIN \"") \r
- << stOprt.top().GetAsString() \r
- << _T("\"\n"); break;\r
- case cmFUNC_STR: mu::console() << _T("FUNC_STR\n"); break;\r
- case cmEND: mu::console() << _T("END\n"); break;\r
- case cmUNKNOWN: mu::console() << _T("UNKNOWN\n"); break;\r
- case cmBO: mu::console() << _T("BRACKET \"(\"\n"); break;\r
- case cmBC: mu::console() << _T("BRACKET \")\"\n"); break;\r
- case cmIF: mu::console() << _T("IF\n"); break;\r
- case cmELSE: mu::console() << _T("ELSE\n"); break;\r
- case cmENDIF: mu::console() << _T("ENDIF\n"); break;\r
- default: mu::console() << stOprt.top().GetCode() << _T(" "); break;\r
- }\r
- } \r
- stOprt.pop();\r
- }\r
-\r
- mu::console() << dec << endl;\r
- }\r
-\r
- //------------------------------------------------------------------------------\r
- /** \brief Evaluate an expression containing comma seperated subexpressions \r
- \param [out] nStackSize The total number of results available\r
- \return Pointer to the array containing all expression results\r
-\r
- This member function can be used to retriev all results of an expression\r
- made up of multiple comma seperated subexpressions (i.e. "x+y,sin(x),cos(y)")\r
- */\r
- value_type* ParserBase::Eval(int &nStackSize) const\r
- {\r
- (this->*m_pParseFormula)(); \r
- nStackSize = m_nFinalResultIdx;\r
-\r
- // (for historic reasons the stack starts at position 1)\r
- return &m_vStackBuffer[1];\r
- }\r
-\r
- //---------------------------------------------------------------------------\r
- /** \brief Return the number of results on the calculation stack. \r
- \r
- If the expression contains comma seperated subexpressions (i.e. "sin(y), x+y"). \r
- There mey be more than one return value. This function returns the number of \r
- available results.\r
- */\r
- int ParserBase::GetNumResults() const\r
- {\r
- return m_nFinalResultIdx;\r
- }\r
-\r
- //---------------------------------------------------------------------------\r
- /** \brief Calculate the result.\r
-\r
- A note on const correctness: \r
- I consider it important that Calc is a const function.\r
- Due to caching operations Calc changes only the state of internal variables with one exception\r
- m_UsedVar this is reset during string parsing and accessible from the outside. Instead of making\r
- Calc non const GetUsedVar is non const because it explicitely calls Eval() forcing this update. \r
-\r
- \pre A formula must be set.\r
- \pre Variables must have been set (if needed)\r
-\r
- \sa #m_pParseFormula\r
- \return The evaluation result\r
- \throw ParseException if no Formula is set or in case of any other error related to the formula.\r
- */\r
- value_type ParserBase::Eval() const\r
- {\r
- return (this->*m_pParseFormula)(); \r
- }\r
-\r
- //---------------------------------------------------------------------------\r
- void ParserBase::Eval(value_type *results, int nBulkSize)\r
- {\r
-/* <ibg 2014-09-24/> Commented because it is making a unit test impossible\r
-\r
- // Parallelization does not make sense for fewer than 10000 computations \r
- // due to thread creation overhead. If the bulk size is below 2000\r
- // computation is refused. \r
- if (nBulkSize<2000)\r
- {\r
- throw ParserError(ecUNREASONABLE_NUMBER_OF_COMPUTATIONS);\r
- }\r
-*/\r
- CreateRPN();\r
-\r
- int i = 0;\r
-\r
-#ifdef MUP_USE_OPENMP\r
-//#define DEBUG_OMP_STUFF\r
- #ifdef DEBUG_OMP_STUFF\r
- int *pThread = new int[nBulkSize];\r
- int *pIdx = new int[nBulkSize];\r
- #endif\r
-\r
- int nMaxThreads = std::min(omp_get_max_threads(), s_MaxNumOpenMPThreads);\r
- int nThreadID = 0, ct = 0;\r
- omp_set_num_threads(nMaxThreads);\r
-\r
- #pragma omp parallel for schedule(static, nBulkSize/nMaxThreads) private(nThreadID)\r
- for (i=0; i<nBulkSize; ++i)\r
- {\r
- nThreadID = omp_get_thread_num();\r
- results[i] = ParseCmdCodeBulk(i, nThreadID);\r
-\r
- #ifdef DEBUG_OMP_STUFF\r
- #pragma omp critical\r
- {\r
- pThread[ct] = nThreadID; \r
- pIdx[ct] = i; \r
- ct++;\r
- }\r
- #endif\r
- }\r
-\r
-#ifdef DEBUG_OMP_STUFF\r
- FILE *pFile = fopen("bulk_dbg.txt", "w");\r
- for (i=0; i<nBulkSize; ++i)\r
- {\r
- fprintf(pFile, "idx: %d thread: %d \n", pIdx[i], pThread[i]);\r
- }\r
- \r
- delete [] pIdx;\r
- delete [] pThread;\r
-\r
- fclose(pFile);\r
-#endif\r
-\r
-#else\r
- for (i=0; i<nBulkSize; ++i)\r
- {\r
- results[i] = ParseCmdCodeBulk(i, 0);\r
- }\r
-#endif\r
-\r
- }\r
-} // namespace mu\r
+++ /dev/null
-/*\r
- __________ \r
- _____ __ __\______ \_____ _______ ______ ____ _______ \r
- / \ | | \| ___/\__ \ \_ __ \/ ___/_/ __ \\_ __ \\r
- | Y Y \| | /| | / __ \_| | \/\___ \ \ ___/ | | \/\r
- |__|_| /|____/ |____| (____ /|__| /____ > \___ >|__| \r
- \/ \/ \/ \/ \r
- Copyright (C) 2011 Ingo Berg\r
-\r
- Permission is hereby granted, free of charge, to any person obtaining a copy of this \r
- software and associated documentation files (the "Software"), to deal in the Software\r
- without restriction, including without limitation the rights to use, copy, modify, \r
- merge, publish, distribute, sublicense, and/or sell copies of the Software, and to \r
- permit persons to whom the Software is furnished to do so, subject to the following conditions:\r
-\r
- The above copyright notice and this permission notice shall be included in all copies or \r
- substantial portions of the Software.\r
-\r
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT\r
- NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND \r
- NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, \r
- DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, \r
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \r
-*/\r
-\r
-#include "muParserBytecode.h"\r
-\r
-#include <algorithm>\r
-#include <cassert>\r
-#include <string>\r
-#include <stack>\r
-#include <vector>\r
-#include <iostream>\r
-\r
-#include "muParserDef.h"\r
-#include "muParserError.h"\r
-#include "muParserToken.h"\r
-#include "muParserStack.h"\r
-#include "muParserTemplateMagic.h"\r
-\r
-\r
-namespace mu\r
-{\r
- //---------------------------------------------------------------------------\r
- /** \brief Bytecode default constructor. */\r
- ParserByteCode::ParserByteCode()\r
- :m_iStackPos(0)\r
- ,m_iMaxStackSize(0)\r
- ,m_vRPN()\r
- ,m_bEnableOptimizer(true)\r
- {\r
- m_vRPN.reserve(50);\r
- }\r
-\r
- //---------------------------------------------------------------------------\r
- /** \brief Copy constructor. \r
- \r
- Implemented in Terms of Assign(const ParserByteCode &a_ByteCode)\r
- */\r
- ParserByteCode::ParserByteCode(const ParserByteCode &a_ByteCode)\r
- {\r
- Assign(a_ByteCode);\r
- }\r
-\r
- //---------------------------------------------------------------------------\r
- /** \brief Assignment operator.\r
- \r
- Implemented in Terms of Assign(const ParserByteCode &a_ByteCode)\r
- */\r
- ParserByteCode& ParserByteCode::operator=(const ParserByteCode &a_ByteCode)\r
- {\r
- Assign(a_ByteCode);\r
- return *this;\r
- }\r
-\r
- //---------------------------------------------------------------------------\r
- void ParserByteCode::EnableOptimizer(bool bStat)\r
- {\r
- m_bEnableOptimizer = bStat;\r
- }\r
-\r
- //---------------------------------------------------------------------------\r
- /** \brief Copy state of another object to this. \r
- \r
- \throw nowthrow\r
- */\r
- void ParserByteCode::Assign(const ParserByteCode &a_ByteCode)\r
- {\r
- if (this==&a_ByteCode) \r
- return;\r
-\r
- m_iStackPos = a_ByteCode.m_iStackPos;\r
- m_vRPN = a_ByteCode.m_vRPN;\r
- m_iMaxStackSize = a_ByteCode.m_iMaxStackSize;\r
- m_bEnableOptimizer = a_ByteCode.m_bEnableOptimizer;\r
- }\r
-\r
- //---------------------------------------------------------------------------\r
- /** \brief Add a Variable pointer to bytecode. \r
- \param a_pVar Pointer to be added.\r
- \throw nothrow\r
- */\r
- void ParserByteCode::AddVar(value_type *a_pVar)\r
- {\r
- ++m_iStackPos;\r
- m_iMaxStackSize = std::max(m_iMaxStackSize, (size_t)m_iStackPos);\r
-\r
- // optimization does not apply\r
- SToken tok;\r
- tok.Cmd = cmVAR;\r
- tok.u.Val.ptr = a_pVar;\r
- tok.u.Val.data = 1;\r
- tok.u.Val.data2 = 0;\r
- m_vRPN.push_back(tok);\r
- }\r
-\r
- //---------------------------------------------------------------------------\r
- /** \brief Add a Variable pointer to bytecode. \r
-\r
- Value entries in byte code consist of:\r
- <ul>\r
- <li>value array position of the value</li>\r
- <li>the operator code according to ParserToken::cmVAL</li>\r
- <li>the value stored in #mc_iSizeVal number of bytecode entries.</li>\r
- </ul>\r
-\r
- \param a_pVal Value to be added.\r
- \throw nothrow\r
- */\r
- void ParserByteCode::AddVal(value_type a_fVal)\r
- {\r
- ++m_iStackPos;\r
- m_iMaxStackSize = std::max(m_iMaxStackSize, (size_t)m_iStackPos);\r
-\r
- // If optimization does not apply\r
- SToken tok;\r
- tok.Cmd = cmVAL;\r
- tok.u.Val.ptr = NULL;\r
- tok.u.Val.data = 0;\r
- tok.u.Val.data2 = a_fVal;\r
- m_vRPN.push_back(tok);\r
- }\r
-\r
- //---------------------------------------------------------------------------\r
- void ParserByteCode::ConstantFolding(ECmdCode a_Oprt)\r
- {\r
- std::size_t sz = m_vRPN.size();\r
- value_type &x = m_vRPN[sz-2].u.Val.data2,\r
- &y = m_vRPN[sz-1].u.Val.data2;\r
- switch (a_Oprt)\r
- {\r
- case cmLAND: x = (int)x && (int)y; m_vRPN.pop_back(); break;\r
- case cmLOR: x = (int)x || (int)y; m_vRPN.pop_back(); break;\r
- case cmLT: x = x < y; m_vRPN.pop_back(); break;\r
- case cmGT: x = x > y; m_vRPN.pop_back(); break;\r
- case cmLE: x = x <= y; m_vRPN.pop_back(); break;\r
- case cmGE: x = x >= y; m_vRPN.pop_back(); break;\r
- case cmNEQ: x = x != y; m_vRPN.pop_back(); break;\r
- case cmEQ: x = x == y; m_vRPN.pop_back(); break;\r
- case cmADD: x = x + y; m_vRPN.pop_back(); break;\r
- case cmSUB: x = x - y; m_vRPN.pop_back(); break;\r
- case cmMUL: x = x * y; m_vRPN.pop_back(); break;\r
- case cmDIV: \r
-\r
-#if defined(MUP_MATH_EXCEPTIONS)\r
- if (y==0)\r
- throw ParserError(ecDIV_BY_ZERO, _T("0"));\r
-#endif\r
-\r
- x = x / y; \r
- m_vRPN.pop_back();\r
- break;\r
-\r
- case cmPOW: x = MathImpl<value_type>::Pow(x, y); \r
- m_vRPN.pop_back();\r
- break;\r
-\r
- default:\r
- break;\r
- } // switch opcode\r
- }\r
-\r
- //---------------------------------------------------------------------------\r
- /** \brief Add an operator identifier to bytecode. \r
- \r
- Operator entries in byte code consist of:\r
- <ul>\r
- <li>value array position of the result</li>\r
- <li>the operator code according to ParserToken::ECmdCode</li>\r
- </ul>\r
-\r
- \sa ParserToken::ECmdCode\r
- */\r
- void ParserByteCode::AddOp(ECmdCode a_Oprt)\r
- {\r
- bool bOptimized = false;\r
-\r
- if (m_bEnableOptimizer)\r
- {\r
- std::size_t sz = m_vRPN.size();\r
-\r
- // Check for foldable constants like:\r
- // cmVAL cmVAL cmADD \r
- // where cmADD can stand fopr any binary operator applied to\r
- // two constant values.\r
- if (sz>=2 && m_vRPN[sz-2].Cmd == cmVAL && m_vRPN[sz-1].Cmd == cmVAL)\r
- {\r
- ConstantFolding(a_Oprt);\r
- bOptimized = true;\r
- }\r
- else\r
- {\r
- switch(a_Oprt)\r
- {\r
- case cmPOW:\r
- // Optimization for ploynomials of low order\r
- if (m_vRPN[sz-2].Cmd == cmVAR && m_vRPN[sz-1].Cmd == cmVAL)\r
- {\r
- if (m_vRPN[sz-1].u.Val.data2==2)\r
- m_vRPN[sz-2].Cmd = cmVARPOW2;\r
- else if (m_vRPN[sz-1].u.Val.data2==3)\r
- m_vRPN[sz-2].Cmd = cmVARPOW3;\r
- else if (m_vRPN[sz-1].u.Val.data2==4)\r
- m_vRPN[sz-2].Cmd = cmVARPOW4;\r
- else\r
- break;\r
-\r
- m_vRPN.pop_back();\r
- bOptimized = true;\r
- }\r
- break;\r
-\r
- case cmSUB:\r
- case cmADD:\r
- // Simple optimization based on pattern recognition for a shitload of different\r
- // bytecode combinations of addition/subtraction\r
- if ( (m_vRPN[sz-1].Cmd == cmVAR && m_vRPN[sz-2].Cmd == cmVAL) ||\r
- (m_vRPN[sz-1].Cmd == cmVAL && m_vRPN[sz-2].Cmd == cmVAR) || \r
- (m_vRPN[sz-1].Cmd == cmVAL && m_vRPN[sz-2].Cmd == cmVARMUL) ||\r
- (m_vRPN[sz-1].Cmd == cmVARMUL && m_vRPN[sz-2].Cmd == cmVAL) ||\r
- (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) ||\r
- (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) ||\r
- (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) ||\r
- (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) )\r
- {\r
- assert( (m_vRPN[sz-2].u.Val.ptr==NULL && m_vRPN[sz-1].u.Val.ptr!=NULL) ||\r
- (m_vRPN[sz-2].u.Val.ptr!=NULL && m_vRPN[sz-1].u.Val.ptr==NULL) || \r
- (m_vRPN[sz-2].u.Val.ptr == m_vRPN[sz-1].u.Val.ptr) );\r
-\r
- m_vRPN[sz-2].Cmd = cmVARMUL;\r
- 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\r
- m_vRPN[sz-2].u.Val.data2 += ((a_Oprt==cmSUB) ? -1 : 1) * m_vRPN[sz-1].u.Val.data2; // offset\r
- m_vRPN[sz-2].u.Val.data += ((a_Oprt==cmSUB) ? -1 : 1) * m_vRPN[sz-1].u.Val.data; // multiplikatior\r
- m_vRPN.pop_back();\r
- bOptimized = true;\r
- } \r
- break;\r
-\r
- case cmMUL:\r
- if ( (m_vRPN[sz-1].Cmd == cmVAR && m_vRPN[sz-2].Cmd == cmVAL) ||\r
- (m_vRPN[sz-1].Cmd == cmVAL && m_vRPN[sz-2].Cmd == cmVAR) ) \r
- {\r
- m_vRPN[sz-2].Cmd = cmVARMUL;\r
- 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));\r
- m_vRPN[sz-2].u.Val.data = m_vRPN[sz-2].u.Val.data2 + m_vRPN[sz-1].u.Val.data2;\r
- m_vRPN[sz-2].u.Val.data2 = 0;\r
- m_vRPN.pop_back();\r
- bOptimized = true;\r
- } \r
- else if ( (m_vRPN[sz-1].Cmd == cmVAL && m_vRPN[sz-2].Cmd == cmVARMUL) ||\r
- (m_vRPN[sz-1].Cmd == cmVARMUL && m_vRPN[sz-2].Cmd == cmVAL) )\r
- {\r
- // Optimization: 2*(3*b+1) or (3*b+1)*2 -> 6*b+2\r
- m_vRPN[sz-2].Cmd = cmVARMUL;\r
- 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));\r
- if (m_vRPN[sz-1].Cmd == cmVAL)\r
- {\r
- m_vRPN[sz-2].u.Val.data *= m_vRPN[sz-1].u.Val.data2;\r
- m_vRPN[sz-2].u.Val.data2 *= m_vRPN[sz-1].u.Val.data2;\r
- }\r
- else\r
- {\r
- m_vRPN[sz-2].u.Val.data = m_vRPN[sz-1].u.Val.data * m_vRPN[sz-2].u.Val.data2;\r
- m_vRPN[sz-2].u.Val.data2 = m_vRPN[sz-1].u.Val.data2 * m_vRPN[sz-2].u.Val.data2;\r
- }\r
- m_vRPN.pop_back();\r
- bOptimized = true;\r
- }\r
- else if (m_vRPN[sz-1].Cmd == cmVAR && m_vRPN[sz-2].Cmd == cmVAR &&\r
- m_vRPN[sz-1].u.Val.ptr == m_vRPN[sz-2].u.Val.ptr)\r
- {\r
- // Optimization: a*a -> a^2\r
- m_vRPN[sz-2].Cmd = cmVARPOW2;\r
- m_vRPN.pop_back();\r
- bOptimized = true;\r
- }\r
- break;\r
-\r
- case cmDIV:\r
- if (m_vRPN[sz-1].Cmd == cmVAL && m_vRPN[sz-2].Cmd == cmVARMUL && m_vRPN[sz-1].u.Val.data2!=0)\r
- {\r
- // Optimization: 4*a/2 -> 2*a\r
- m_vRPN[sz-2].u.Val.data /= m_vRPN[sz-1].u.Val.data2;\r
- m_vRPN[sz-2].u.Val.data2 /= m_vRPN[sz-1].u.Val.data2;\r
- m_vRPN.pop_back();\r
- bOptimized = true;\r
- }\r
- break;\r
- default:\r
- break;\r
- } // switch a_Oprt\r
- }\r
- }\r
-\r
- // If optimization can't be applied just write the value\r
- if (!bOptimized)\r
- {\r
- --m_iStackPos;\r
- SToken tok;\r
- tok.Cmd = a_Oprt;\r
- m_vRPN.push_back(tok);\r
- }\r
- }\r
-\r
- //---------------------------------------------------------------------------\r
- void ParserByteCode::AddIfElse(ECmdCode a_Oprt)\r
- {\r
- SToken tok;\r
- tok.Cmd = a_Oprt;\r
- m_vRPN.push_back(tok);\r
- }\r
-\r
- //---------------------------------------------------------------------------\r
- /** \brief Add an assignement operator\r
- \r
- Operator entries in byte code consist of:\r
- <ul>\r
- <li>cmASSIGN code</li>\r
- <li>the pointer of the destination variable</li>\r
- </ul>\r
-\r
- \sa ParserToken::ECmdCode\r
- */\r
- void ParserByteCode::AddAssignOp(value_type *a_pVar)\r
- {\r
- --m_iStackPos;\r
-\r
- SToken tok;\r
- tok.Cmd = cmASSIGN;\r
- tok.u.Oprt.ptr = a_pVar;\r
- m_vRPN.push_back(tok);\r
- }\r
-\r
- //---------------------------------------------------------------------------\r
- /** \brief Add function to bytecode. \r
-\r
- \param a_iArgc Number of arguments, negative numbers indicate multiarg functions.\r
- \param a_pFun Pointer to function callback.\r
- */\r
- void ParserByteCode::AddFun(generic_fun_type a_pFun, int a_iArgc)\r
- {\r
- if (a_iArgc>=0)\r
- {\r
- m_iStackPos = m_iStackPos - a_iArgc + 1; \r
- }\r
- else\r
- {\r
- // function with unlimited number of arguments\r
- m_iStackPos = m_iStackPos + a_iArgc + 1; \r
- }\r
- m_iMaxStackSize = std::max(m_iMaxStackSize, (size_t)m_iStackPos);\r
-\r
- SToken tok;\r
- tok.Cmd = cmFUNC;\r
- tok.u.Fun.argc = a_iArgc;\r
- tok.u.Fun.ptr = a_pFun;\r
- m_vRPN.push_back(tok);\r
- }\r
-\r
- //---------------------------------------------------------------------------\r
- /** \brief Add a bulk function to bytecode. \r
-\r
- \param a_iArgc Number of arguments, negative numbers indicate multiarg functions.\r
- \param a_pFun Pointer to function callback.\r
- */\r
- void ParserByteCode::AddBulkFun(generic_fun_type a_pFun, int a_iArgc)\r
- {\r
- m_iStackPos = m_iStackPos - a_iArgc + 1; \r
- m_iMaxStackSize = std::max(m_iMaxStackSize, (size_t)m_iStackPos);\r
-\r
- SToken tok;\r
- tok.Cmd = cmFUNC_BULK;\r
- tok.u.Fun.argc = a_iArgc;\r
- tok.u.Fun.ptr = a_pFun;\r
- m_vRPN.push_back(tok);\r
- }\r
-\r
- //---------------------------------------------------------------------------\r
- /** \brief Add Strung function entry to the parser bytecode. \r
- \throw nothrow\r
-\r
- A string function entry consists of the stack position of the return value,\r
- followed by a cmSTRFUNC code, the function pointer and an index into the \r
- string buffer maintained by the parser.\r
- */\r
- void ParserByteCode::AddStrFun(generic_fun_type a_pFun, int a_iArgc, int a_iIdx)\r
- {\r
- m_iStackPos = m_iStackPos - a_iArgc + 1;\r
-\r
- SToken tok;\r
- tok.Cmd = cmFUNC_STR;\r
- tok.u.Fun.argc = a_iArgc;\r
- tok.u.Fun.idx = a_iIdx;\r
- tok.u.Fun.ptr = a_pFun;\r
- m_vRPN.push_back(tok);\r
-\r
- m_iMaxStackSize = std::max(m_iMaxStackSize, (size_t)m_iStackPos);\r
- }\r
-\r
- //---------------------------------------------------------------------------\r
- /** \brief Add end marker to bytecode.\r
- \r
- \throw nothrow \r
- */\r
- void ParserByteCode::Finalize()\r
- {\r
- SToken tok;\r
- tok.Cmd = cmEND;\r
- m_vRPN.push_back(tok);\r
- rpn_type(m_vRPN).swap(m_vRPN); // shrink bytecode vector to fit\r
-\r
- // Determine the if-then-else jump offsets\r
- ParserStack<int> stIf, stElse;\r
- int idx;\r
- for (int i=0; i<(int)m_vRPN.size(); ++i)\r
- {\r
- switch(m_vRPN[i].Cmd)\r
- {\r
- case cmIF:\r
- stIf.push(i);\r
- break;\r
-\r
- case cmELSE:\r
- stElse.push(i);\r
- idx = stIf.pop();\r
- m_vRPN[idx].u.Oprt.offset = i - idx;\r
- break;\r
- \r
- case cmENDIF:\r
- idx = stElse.pop();\r
- m_vRPN[idx].u.Oprt.offset = i - idx;\r
- break;\r
-\r
- default:\r
- break;\r
- }\r
- }\r
- }\r
-\r
- //---------------------------------------------------------------------------\r
- const SToken* ParserByteCode::GetBase() const\r
- {\r
- if (m_vRPN.size()==0)\r
- throw ParserError(ecINTERNAL_ERROR);\r
- else\r
- return &m_vRPN[0];\r
- }\r
-\r
- //---------------------------------------------------------------------------\r
- std::size_t ParserByteCode::GetMaxStackSize() const\r
- {\r
- return m_iMaxStackSize+1;\r
- }\r
-\r
- //---------------------------------------------------------------------------\r
- /** \brief Returns the number of entries in the bytecode. */\r
- std::size_t ParserByteCode::GetSize() const\r
- {\r
- return m_vRPN.size();\r
- }\r
-\r
- //---------------------------------------------------------------------------\r
- /** \brief Delete the bytecode. \r
- \r
- \throw nothrow\r
-\r
- The name of this function is a violation of my own coding guidelines\r
- but this way it's more in line with the STL functions thus more \r
- intuitive.\r
- */\r
- void ParserByteCode::clear()\r
- {\r
- m_vRPN.clear();\r
- m_iStackPos = 0;\r
- m_iMaxStackSize = 0;\r
- }\r
-\r
- //---------------------------------------------------------------------------\r
- /** \brief Dump bytecode (for debugging only!). */\r
- void ParserByteCode::AsciiDump()\r
- {\r
- if (!m_vRPN.size()) \r
- {\r
- mu::console() << _T("No bytecode available\n");\r
- return;\r
- }\r
-\r
- mu::console() << _T("Number of RPN tokens:") << (int)m_vRPN.size() << _T("\n");\r
- for (std::size_t i=0; i<m_vRPN.size() && m_vRPN[i].Cmd!=cmEND; ++i)\r
- {\r
- mu::console() << std::dec << i << _T(" : \t");\r
- switch (m_vRPN[i].Cmd)\r
- {\r
- case cmVAL: mu::console() << _T("VAL \t");\r
- mu::console() << _T("[") << m_vRPN[i].u.Val.data2 << _T("]\n");\r
- break;\r
-\r
- case cmVAR: mu::console() << _T("VAR \t");\r
- mu::console() << _T("[ADDR: 0x") << std::hex << m_vRPN[i].u.Val.ptr << _T("]\n"); \r
- break;\r
-\r
- case cmVARPOW2: mu::console() << _T("VARPOW2 \t");\r
- mu::console() << _T("[ADDR: 0x") << std::hex << m_vRPN[i].u.Val.ptr << _T("]\n"); \r
- break;\r
-\r
- case cmVARPOW3: mu::console() << _T("VARPOW3 \t");\r
- mu::console() << _T("[ADDR: 0x") << std::hex << m_vRPN[i].u.Val.ptr << _T("]\n"); \r
- break;\r
-\r
- case cmVARPOW4: mu::console() << _T("VARPOW4 \t");\r
- mu::console() << _T("[ADDR: 0x") << std::hex << m_vRPN[i].u.Val.ptr << _T("]\n"); \r
- break;\r
-\r
- case cmVARMUL: mu::console() << _T("VARMUL \t");\r
- mu::console() << _T("[ADDR: 0x") << std::hex << m_vRPN[i].u.Val.ptr << _T("]"); \r
- mu::console() << _T(" * [") << m_vRPN[i].u.Val.data << _T("]");\r
- mu::console() << _T(" + [") << m_vRPN[i].u.Val.data2 << _T("]\n");\r
- break;\r
-\r
- case cmFUNC: mu::console() << _T("CALL\t");\r
- mu::console() << _T("[ARG:") << std::dec << m_vRPN[i].u.Fun.argc << _T("]"); \r
- mu::console() << _T("[ADDR: 0x") << std::hex << m_vRPN[i].u.Fun.ptr << _T("]"); \r
- mu::console() << _T("\n");\r
- break;\r
-\r
- case cmFUNC_STR:\r
- mu::console() << _T("CALL STRFUNC\t");\r
- mu::console() << _T("[ARG:") << std::dec << m_vRPN[i].u.Fun.argc << _T("]");\r
- mu::console() << _T("[IDX:") << std::dec << m_vRPN[i].u.Fun.idx << _T("]");\r
- mu::console() << _T("[ADDR: 0x") << m_vRPN[i].u.Fun.ptr << _T("]\n"); \r
- break;\r
-\r
- case cmLT: mu::console() << _T("LT\n"); break;\r
- case cmGT: mu::console() << _T("GT\n"); break;\r
- case cmLE: mu::console() << _T("LE\n"); break;\r
- case cmGE: mu::console() << _T("GE\n"); break;\r
- case cmEQ: mu::console() << _T("EQ\n"); break;\r
- case cmNEQ: mu::console() << _T("NEQ\n"); break;\r
- case cmADD: mu::console() << _T("ADD\n"); break;\r
- case cmLAND: mu::console() << _T("&&\n"); break;\r
- case cmLOR: mu::console() << _T("||\n"); break;\r
- case cmSUB: mu::console() << _T("SUB\n"); break;\r
- case cmMUL: mu::console() << _T("MUL\n"); break;\r
- case cmDIV: mu::console() << _T("DIV\n"); break;\r
- case cmPOW: mu::console() << _T("POW\n"); break;\r
-\r
- case cmIF: mu::console() << _T("IF\t");\r
- mu::console() << _T("[OFFSET:") << std::dec << m_vRPN[i].u.Oprt.offset << _T("]\n");\r
- break;\r
-\r
- case cmELSE: mu::console() << _T("ELSE\t");\r
- mu::console() << _T("[OFFSET:") << std::dec << m_vRPN[i].u.Oprt.offset << _T("]\n");\r
- break;\r
-\r
- case cmENDIF: mu::console() << _T("ENDIF\n"); break;\r
-\r
- case cmASSIGN: \r
- mu::console() << _T("ASSIGN\t");\r
- mu::console() << _T("[ADDR: 0x") << m_vRPN[i].u.Oprt.ptr << _T("]\n"); \r
- break; \r
-\r
- default: mu::console() << _T("(unknown code: ") << m_vRPN[i].Cmd << _T(")\n"); \r
- break;\r
- } // switch cmdCode\r
- } // while bytecode\r
-\r
- mu::console() << _T("END") << std::endl;\r
- }\r
-} // namespace mu\r
+++ /dev/null
-/*\r
- __________ \r
- _____ __ __\______ \_____ _______ ______ ____ _______ \r
- / \ | | \| ___/\__ \ \_ __ \/ ___/_/ __ \\_ __ \\r
- | Y Y \| | /| | / __ \_| | \/\___ \ \ ___/ | | \/\r
- |__|_| /|____/ |____| (____ /|__| /____ > \___ >|__| \r
- \/ \/ \/ \/ \r
- Copyright (C) 2004-2011 Ingo Berg\r
-\r
- Permission is hereby granted, free of charge, to any person obtaining a copy of this \r
- software and associated documentation files (the "Software"), to deal in the Software\r
- without restriction, including without limitation the rights to use, copy, modify, \r
- merge, publish, distribute, sublicense, and/or sell copies of the Software, and to \r
- permit persons to whom the Software is furnished to do so, subject to the following conditions:\r
-\r
- The above copyright notice and this permission notice shall be included in all copies or \r
- substantial portions of the Software.\r
-\r
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT\r
- NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND \r
- NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, \r
- DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, \r
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \r
-*/\r
-\r
-#include "muParserCallback.h"\r
-\r
-/** \file\r
- \brief Implementation of the parser callback class.\r
-*/\r
-\r
-\r
-namespace mu\r
-{\r
- //---------------------------------------------------------------------------\r
- ParserCallback::ParserCallback(fun_type0 a_pFun, bool a_bAllowOpti)\r
- :m_pFun((void*)a_pFun)\r
- ,m_iArgc(0)\r
- ,m_iPri(-1)\r
- ,m_eOprtAsct(oaNONE)\r
- ,m_iCode(cmFUNC)\r
- ,m_iType(tpDBL)\r
- ,m_bAllowOpti(a_bAllowOpti)\r
- {}\r
-\r
- //---------------------------------------------------------------------------\r
- ParserCallback::ParserCallback(fun_type1 a_pFun, bool a_bAllowOpti, int a_iPrec, ECmdCode a_iCode)\r
- :m_pFun((void*)a_pFun)\r
- ,m_iArgc(1)\r
- ,m_iPri(a_iPrec)\r
- ,m_eOprtAsct(oaNONE)\r
- ,m_iCode(a_iCode)\r
- ,m_iType(tpDBL)\r
- ,m_bAllowOpti(a_bAllowOpti)\r
- {}\r
-\r
-\r
- //---------------------------------------------------------------------------\r
- /** \brief Constructor for constructing funcstion callbacks taking two arguments. \r
- \throw nothrow\r
- */\r
- ParserCallback::ParserCallback(fun_type2 a_pFun, bool a_bAllowOpti)\r
- :m_pFun((void*)a_pFun)\r
- ,m_iArgc(2)\r
- ,m_iPri(-1)\r
- ,m_eOprtAsct(oaNONE)\r
- ,m_iCode(cmFUNC)\r
- ,m_iType(tpDBL)\r
- ,m_bAllowOpti(a_bAllowOpti)\r
- {}\r
-\r
- //---------------------------------------------------------------------------\r
- /** \brief Constructor for constructing binary operator callbacks. \r
- \param a_pFun Pointer to a static function taking two arguments\r
- \param a_bAllowOpti A flag indicating this funcation can be optimized\r
- \param a_iPrec The operator precedence\r
- \param a_eOprtAsct The operators associativity\r
- \throw nothrow\r
- */\r
- ParserCallback::ParserCallback(fun_type2 a_pFun, \r
- bool a_bAllowOpti, \r
- int a_iPrec, \r
- EOprtAssociativity a_eOprtAsct)\r
- :m_pFun((void*)a_pFun)\r
- ,m_iArgc(2)\r
- ,m_iPri(a_iPrec)\r
- ,m_eOprtAsct(a_eOprtAsct)\r
- ,m_iCode(cmOPRT_BIN)\r
- ,m_iType(tpDBL)\r
- ,m_bAllowOpti(a_bAllowOpti)\r
- {}\r
-\r
- //---------------------------------------------------------------------------\r
- ParserCallback::ParserCallback(fun_type3 a_pFun, bool a_bAllowOpti)\r
- :m_pFun((void*)a_pFun)\r
- ,m_iArgc(3)\r
- ,m_iPri(-1)\r
- ,m_eOprtAsct(oaNONE)\r
- ,m_iCode(cmFUNC)\r
- ,m_iType(tpDBL)\r
- ,m_bAllowOpti(a_bAllowOpti)\r
- {}\r
-\r
-\r
- //---------------------------------------------------------------------------\r
- ParserCallback::ParserCallback(fun_type4 a_pFun, bool a_bAllowOpti)\r
- :m_pFun((void*)a_pFun)\r
- ,m_iArgc(4)\r
- ,m_iPri(-1)\r
- ,m_eOprtAsct(oaNONE)\r
- ,m_iCode(cmFUNC)\r
- ,m_iType(tpDBL)\r
- ,m_bAllowOpti(a_bAllowOpti)\r
- {}\r
-\r
-\r
- //---------------------------------------------------------------------------\r
- ParserCallback::ParserCallback(fun_type5 a_pFun, bool a_bAllowOpti)\r
- :m_pFun((void*)a_pFun)\r
- ,m_iArgc(5)\r
- ,m_iPri(-1)\r
- ,m_eOprtAsct(oaNONE)\r
- ,m_iCode(cmFUNC)\r
- ,m_iType(tpDBL)\r
- ,m_bAllowOpti(a_bAllowOpti)\r
- {}\r
-\r
- //---------------------------------------------------------------------------\r
- ParserCallback::ParserCallback(fun_type6 a_pFun, bool a_bAllowOpti)\r
- :m_pFun((void*)a_pFun)\r
- ,m_iArgc(6)\r
- ,m_iPri(-1)\r
- ,m_eOprtAsct(oaNONE)\r
- ,m_iCode(cmFUNC)\r
- ,m_iType(tpDBL)\r
- ,m_bAllowOpti(a_bAllowOpti)\r
- {}\r
-\r
- //---------------------------------------------------------------------------\r
- ParserCallback::ParserCallback(fun_type7 a_pFun, bool a_bAllowOpti)\r
- :m_pFun((void*)a_pFun)\r
- ,m_iArgc(7)\r
- ,m_iPri(-1)\r
- ,m_eOprtAsct(oaNONE)\r
- ,m_iCode(cmFUNC)\r
- ,m_iType(tpDBL)\r
- ,m_bAllowOpti(a_bAllowOpti)\r
- {}\r
-\r
- //---------------------------------------------------------------------------\r
- ParserCallback::ParserCallback(fun_type8 a_pFun, bool a_bAllowOpti)\r
- :m_pFun((void*)a_pFun)\r
- ,m_iArgc(8)\r
- ,m_iPri(-1)\r
- ,m_eOprtAsct(oaNONE)\r
- ,m_iCode(cmFUNC)\r
- ,m_iType(tpDBL)\r
- ,m_bAllowOpti(a_bAllowOpti)\r
- {}\r
-\r
- //---------------------------------------------------------------------------\r
- ParserCallback::ParserCallback(fun_type9 a_pFun, bool a_bAllowOpti)\r
- :m_pFun((void*)a_pFun)\r
- ,m_iArgc(9)\r
- ,m_iPri(-1)\r
- ,m_eOprtAsct(oaNONE)\r
- ,m_iCode(cmFUNC)\r
- ,m_iType(tpDBL)\r
- ,m_bAllowOpti(a_bAllowOpti)\r
- {}\r
-\r
- //---------------------------------------------------------------------------\r
- ParserCallback::ParserCallback(fun_type10 a_pFun, bool a_bAllowOpti)\r
- :m_pFun((void*)a_pFun)\r
- ,m_iArgc(10)\r
- ,m_iPri(-1)\r
- ,m_eOprtAsct(oaNONE)\r
- ,m_iCode(cmFUNC)\r
- ,m_iType(tpDBL)\r
- ,m_bAllowOpti(a_bAllowOpti)\r
- {}\r
-\r
- //---------------------------------------------------------------------------\r
- ParserCallback::ParserCallback(bulkfun_type0 a_pFun, bool a_bAllowOpti)\r
- :m_pFun((void*)a_pFun)\r
- ,m_iArgc(0)\r
- ,m_iPri(-1)\r
- ,m_eOprtAsct(oaNONE)\r
- ,m_iCode(cmFUNC_BULK)\r
- ,m_iType(tpDBL)\r
- ,m_bAllowOpti(a_bAllowOpti)\r
- {}\r
-\r
- //---------------------------------------------------------------------------\r
- ParserCallback::ParserCallback(bulkfun_type1 a_pFun, bool a_bAllowOpti)\r
- :m_pFun((void*)a_pFun)\r
- ,m_iArgc(1)\r
- ,m_iPri(-1)\r
- ,m_eOprtAsct(oaNONE)\r
- ,m_iCode(cmFUNC_BULK)\r
- ,m_iType(tpDBL)\r
- ,m_bAllowOpti(a_bAllowOpti)\r
- {}\r
-\r
-\r
- //---------------------------------------------------------------------------\r
- /** \brief Constructor for constructing funcstion callbacks taking two arguments. \r
- \throw nothrow\r
- */\r
- ParserCallback::ParserCallback(bulkfun_type2 a_pFun, bool a_bAllowOpti)\r
- :m_pFun((void*)a_pFun)\r
- ,m_iArgc(2)\r
- ,m_iPri(-1)\r
- ,m_eOprtAsct(oaNONE)\r
- ,m_iCode(cmFUNC_BULK)\r
- ,m_iType(tpDBL)\r
- ,m_bAllowOpti(a_bAllowOpti)\r
- {}\r
-\r
- //---------------------------------------------------------------------------\r
- ParserCallback::ParserCallback(bulkfun_type3 a_pFun, bool a_bAllowOpti)\r
- :m_pFun((void*)a_pFun)\r
- ,m_iArgc(3)\r
- ,m_iPri(-1)\r
- ,m_eOprtAsct(oaNONE)\r
- ,m_iCode(cmFUNC_BULK)\r
- ,m_iType(tpDBL)\r
- ,m_bAllowOpti(a_bAllowOpti)\r
- {}\r
-\r
-\r
- //---------------------------------------------------------------------------\r
- ParserCallback::ParserCallback(bulkfun_type4 a_pFun, bool a_bAllowOpti)\r
- :m_pFun((void*)a_pFun)\r
- ,m_iArgc(4)\r
- ,m_iPri(-1)\r
- ,m_eOprtAsct(oaNONE)\r
- ,m_iCode(cmFUNC_BULK)\r
- ,m_iType(tpDBL)\r
- ,m_bAllowOpti(a_bAllowOpti)\r
- {}\r
-\r
-\r
- //---------------------------------------------------------------------------\r
- ParserCallback::ParserCallback(bulkfun_type5 a_pFun, bool a_bAllowOpti)\r
- :m_pFun((void*)a_pFun)\r
- ,m_iArgc(5)\r
- ,m_iPri(-1)\r
- ,m_eOprtAsct(oaNONE)\r
- ,m_iCode(cmFUNC_BULK)\r
- ,m_iType(tpDBL)\r
- ,m_bAllowOpti(a_bAllowOpti)\r
- {}\r
-\r
- //---------------------------------------------------------------------------\r
- ParserCallback::ParserCallback(bulkfun_type6 a_pFun, bool a_bAllowOpti)\r
- :m_pFun((void*)a_pFun)\r
- ,m_iArgc(6)\r
- ,m_iPri(-1)\r
- ,m_eOprtAsct(oaNONE)\r
- ,m_iCode(cmFUNC_BULK)\r
- ,m_iType(tpDBL)\r
- ,m_bAllowOpti(a_bAllowOpti)\r
- {}\r
-\r
- //---------------------------------------------------------------------------\r
- ParserCallback::ParserCallback(bulkfun_type7 a_pFun, bool a_bAllowOpti)\r
- :m_pFun((void*)a_pFun)\r
- ,m_iArgc(7)\r
- ,m_iPri(-1)\r
- ,m_eOprtAsct(oaNONE)\r
- ,m_iCode(cmFUNC_BULK)\r
- ,m_iType(tpDBL)\r
- ,m_bAllowOpti(a_bAllowOpti)\r
- {}\r
-\r
- //---------------------------------------------------------------------------\r
- ParserCallback::ParserCallback(bulkfun_type8 a_pFun, bool a_bAllowOpti)\r
- :m_pFun((void*)a_pFun)\r
- ,m_iArgc(8)\r
- ,m_iPri(-1)\r
- ,m_eOprtAsct(oaNONE)\r
- ,m_iCode(cmFUNC_BULK)\r
- ,m_iType(tpDBL)\r
- ,m_bAllowOpti(a_bAllowOpti)\r
- {}\r
-\r
- //---------------------------------------------------------------------------\r
- ParserCallback::ParserCallback(bulkfun_type9 a_pFun, bool a_bAllowOpti)\r
- :m_pFun((void*)a_pFun)\r
- ,m_iArgc(9)\r
- ,m_iPri(-1)\r
- ,m_eOprtAsct(oaNONE)\r
- ,m_iCode(cmFUNC_BULK)\r
- ,m_iType(tpDBL)\r
- ,m_bAllowOpti(a_bAllowOpti)\r
- {}\r
-\r
- //---------------------------------------------------------------------------\r
- ParserCallback::ParserCallback(bulkfun_type10 a_pFun, bool a_bAllowOpti)\r
- :m_pFun((void*)a_pFun)\r
- ,m_iArgc(10)\r
- ,m_iPri(-1)\r
- ,m_eOprtAsct(oaNONE)\r
- ,m_iCode(cmFUNC_BULK)\r
- ,m_iType(tpDBL)\r
- ,m_bAllowOpti(a_bAllowOpti)\r
- {}\r
-\r
-\r
- //---------------------------------------------------------------------------\r
- ParserCallback::ParserCallback(multfun_type a_pFun, bool a_bAllowOpti)\r
- :m_pFun((void*)a_pFun)\r
- ,m_iArgc(-1)\r
- ,m_iPri(-1)\r
- ,m_eOprtAsct(oaNONE)\r
- ,m_iCode(cmFUNC)\r
- ,m_iType(tpDBL)\r
- ,m_bAllowOpti(a_bAllowOpti)\r
- {}\r
-\r
-\r
- //---------------------------------------------------------------------------\r
- ParserCallback::ParserCallback(strfun_type1 a_pFun, bool a_bAllowOpti)\r
- :m_pFun((void*)a_pFun)\r
- ,m_iArgc(0)\r
- ,m_iPri(-1)\r
- ,m_eOprtAsct(oaNONE)\r
- ,m_iCode(cmFUNC_STR)\r
- ,m_iType(tpSTR)\r
- ,m_bAllowOpti(a_bAllowOpti)\r
- {}\r
-\r
-\r
- //---------------------------------------------------------------------------\r
- ParserCallback::ParserCallback(strfun_type2 a_pFun, bool a_bAllowOpti)\r
- :m_pFun((void*)a_pFun)\r
- ,m_iArgc(1)\r
- ,m_iPri(-1)\r
- ,m_eOprtAsct(oaNONE)\r
- ,m_iCode(cmFUNC_STR)\r
- ,m_iType(tpSTR)\r
- ,m_bAllowOpti(a_bAllowOpti)\r
- {}\r
-\r
-\r
- //---------------------------------------------------------------------------\r
- ParserCallback::ParserCallback(strfun_type3 a_pFun, bool a_bAllowOpti)\r
- :m_pFun((void*)a_pFun)\r
- ,m_iArgc(2)\r
- ,m_iPri(-1)\r
- ,m_eOprtAsct(oaNONE)\r
- ,m_iCode(cmFUNC_STR)\r
- ,m_iType(tpSTR)\r
- ,m_bAllowOpti(a_bAllowOpti)\r
- {}\r
-\r
-\r
- //---------------------------------------------------------------------------\r
- /** \brief Default constructor. \r
- \throw nothrow\r
- */\r
- ParserCallback::ParserCallback()\r
- :m_pFun(0)\r
- ,m_iArgc(0)\r
- ,m_iPri(-1)\r
- ,m_eOprtAsct(oaNONE)\r
- ,m_iCode(cmUNKNOWN)\r
- ,m_iType(tpVOID)\r
- ,m_bAllowOpti(0)\r
- {}\r
-\r
-\r
- //---------------------------------------------------------------------------\r
- /** \brief Copy constructor. \r
- \throw nothrow\r
- */\r
- ParserCallback::ParserCallback(const ParserCallback &ref)\r
- {\r
- m_pFun = ref.m_pFun;\r
- m_iArgc = ref.m_iArgc;\r
- m_bAllowOpti = ref.m_bAllowOpti;\r
- m_iCode = ref.m_iCode;\r
- m_iType = ref.m_iType;\r
- m_iPri = ref.m_iPri;\r
- m_eOprtAsct = ref.m_eOprtAsct;\r
- }\r
-\r
- //---------------------------------------------------------------------------\r
- /** \brief Clone this instance and return a pointer to the new instance. */\r
- ParserCallback* ParserCallback::Clone() const\r
- {\r
- return new ParserCallback(*this);\r
- }\r
-\r
- //---------------------------------------------------------------------------\r
- /** \brief Return tru if the function is conservative.\r
-\r
- Conservative functions return always the same result for the same argument.\r
- \throw nothrow\r
- */\r
- bool ParserCallback::IsOptimizable() const \r
- { \r
- return m_bAllowOpti; \r
- }\r
-\r
- //---------------------------------------------------------------------------\r
- /** \brief Get the callback address for the parser function. \r
- \r
- The type of the address is void. It needs to be recasted according to the\r
- argument number to the right type.\r
-\r
- \throw nothrow\r
- \return #pFun\r
- */\r
- void* ParserCallback::GetAddr() const \r
- { \r
- return m_pFun; \r
- }\r
-\r
- //---------------------------------------------------------------------------\r
- /** \brief Return the callback code. */\r
- ECmdCode ParserCallback::GetCode() const \r
- { \r
- return m_iCode; \r
- }\r
- \r
- //---------------------------------------------------------------------------\r
- ETypeCode ParserCallback::GetType() const \r
- { \r
- return m_iType; \r
- }\r
-\r
-\r
- //---------------------------------------------------------------------------\r
- /** \brief Return the operator precedence. \r
- \throw nothrown\r
-\r
- Only valid if the callback token is an operator token (binary or infix).\r
- */\r
- int ParserCallback::GetPri() const \r
- { \r
- return m_iPri; \r
- }\r
-\r
- //---------------------------------------------------------------------------\r
- /** \brief Return the operators associativity. \r
- \throw nothrown\r
-\r
- Only valid if the callback token is a binary operator token.\r
- */\r
- EOprtAssociativity ParserCallback::GetAssociativity() const\r
- {\r
- return m_eOprtAsct;\r
- }\r
-\r
- //---------------------------------------------------------------------------\r
- /** \brief Returns the number of function Arguments. */\r
- int ParserCallback::GetArgc() const \r
- { \r
- return m_iArgc; \r
- }\r
-} // namespace mu\r
+++ /dev/null
-/*\r
- __________ \r
- _____ __ __\______ \_____ _______ ______ ____ _______ \r
- / \ | | \| ___/\__ \ \_ __ \/ ___/_/ __ \\_ __ \\r
- | Y Y \| | /| | / __ \_| | \/\___ \ \ ___/ | | \/\r
- |__|_| /|____/ |____| (____ /|__| /____ > \___ >|__| \r
- \/ \/ \/ \/ \r
- Copyright (C) 2011 Ingo Berg\r
-\r
- Permission is hereby granted, free of charge, to any person obtaining a copy of this \r
- software and associated documentation files (the "Software"), to deal in the Software\r
- without restriction, including without limitation the rights to use, copy, modify, \r
- merge, publish, distribute, sublicense, and/or sell copies of the Software, and to \r
- permit persons to whom the Software is furnished to do so, subject to the following conditions:\r
-\r
- The above copyright notice and this permission notice shall be included in all copies or \r
- substantial portions of the Software.\r
-\r
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT\r
- NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND \r
- NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, \r
- DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, \r
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \r
-*/\r
-#include "muParserError.h"\r
-\r
-\r
-namespace mu\r
-{\r
- const ParserErrorMsg ParserErrorMsg::m_Instance;\r
-\r
- //------------------------------------------------------------------------------\r
- const ParserErrorMsg& ParserErrorMsg::Instance()\r
- {\r
- return m_Instance;\r
- }\r
-\r
- //------------------------------------------------------------------------------\r
- string_type ParserErrorMsg::operator[](unsigned a_iIdx) const\r
- {\r
- return (a_iIdx<m_vErrMsg.size()) ? m_vErrMsg[a_iIdx] : string_type();\r
- }\r
-\r
- //---------------------------------------------------------------------------\r
- ParserErrorMsg::~ParserErrorMsg()\r
- {}\r
-\r
- //---------------------------------------------------------------------------\r
- /** \brief Assignement operator is deactivated.\r
- */\r
- ParserErrorMsg& ParserErrorMsg::operator=(const ParserErrorMsg& )\r
- {\r
- assert(false);\r
- return *this;\r
- }\r
-\r
- //---------------------------------------------------------------------------\r
- ParserErrorMsg::ParserErrorMsg(const ParserErrorMsg&)\r
- {}\r
-\r
- //---------------------------------------------------------------------------\r
- ParserErrorMsg::ParserErrorMsg()\r
- :m_vErrMsg(0)\r
- {\r
- m_vErrMsg.resize(ecCOUNT);\r
-\r
- m_vErrMsg[ecUNASSIGNABLE_TOKEN] = _T("Unexpected token \"$TOK$\" found at position $POS$.");\r
- m_vErrMsg[ecINTERNAL_ERROR] = _T("Internal error");\r
- m_vErrMsg[ecINVALID_NAME] = _T("Invalid function-, variable- or constant name: \"$TOK$\".");\r
- m_vErrMsg[ecINVALID_BINOP_IDENT] = _T("Invalid binary operator identifier: \"$TOK$\".");\r
- m_vErrMsg[ecINVALID_INFIX_IDENT] = _T("Invalid infix operator identifier: \"$TOK$\".");\r
- m_vErrMsg[ecINVALID_POSTFIX_IDENT] = _T("Invalid postfix operator identifier: \"$TOK$\".");\r
- m_vErrMsg[ecINVALID_FUN_PTR] = _T("Invalid pointer to callback function.");\r
- m_vErrMsg[ecEMPTY_EXPRESSION] = _T("Expression is empty.");\r
- m_vErrMsg[ecINVALID_VAR_PTR] = _T("Invalid pointer to variable.");\r
- m_vErrMsg[ecUNEXPECTED_OPERATOR] = _T("Unexpected operator \"$TOK$\" found at position $POS$");\r
- m_vErrMsg[ecUNEXPECTED_EOF] = _T("Unexpected end of expression at position $POS$");\r
- m_vErrMsg[ecUNEXPECTED_ARG_SEP] = _T("Unexpected argument separator at position $POS$");\r
- m_vErrMsg[ecUNEXPECTED_PARENS] = _T("Unexpected parenthesis \"$TOK$\" at position $POS$");\r
- m_vErrMsg[ecUNEXPECTED_FUN] = _T("Unexpected function \"$TOK$\" at position $POS$");\r
- m_vErrMsg[ecUNEXPECTED_VAL] = _T("Unexpected value \"$TOK$\" found at position $POS$");\r
- m_vErrMsg[ecUNEXPECTED_VAR] = _T("Unexpected variable \"$TOK$\" found at position $POS$");\r
- m_vErrMsg[ecUNEXPECTED_ARG] = _T("Function arguments used without a function (position: $POS$)");\r
- m_vErrMsg[ecMISSING_PARENS] = _T("Missing parenthesis");\r
- m_vErrMsg[ecTOO_MANY_PARAMS] = _T("Too many parameters for function \"$TOK$\" at expression position $POS$");\r
- m_vErrMsg[ecTOO_FEW_PARAMS] = _T("Too few parameters for function \"$TOK$\" at expression position $POS$");\r
- m_vErrMsg[ecDIV_BY_ZERO] = _T("Divide by zero");\r
- m_vErrMsg[ecDOMAIN_ERROR] = _T("Domain error");\r
- m_vErrMsg[ecNAME_CONFLICT] = _T("Name conflict");\r
- m_vErrMsg[ecOPT_PRI] = _T("Invalid value for operator priority (must be greater or equal to zero).");\r
- m_vErrMsg[ecBUILTIN_OVERLOAD] = _T("user defined binary operator \"$TOK$\" conflicts with a built in operator.");\r
- m_vErrMsg[ecUNEXPECTED_STR] = _T("Unexpected string token found at position $POS$.");\r
- m_vErrMsg[ecUNTERMINATED_STRING] = _T("Unterminated string starting at position $POS$.");\r
- m_vErrMsg[ecSTRING_EXPECTED] = _T("String function called with a non string type of argument.");\r
- m_vErrMsg[ecVAL_EXPECTED] = _T("String value used where a numerical argument is expected.");\r
- m_vErrMsg[ecOPRT_TYPE_CONFLICT] = _T("No suitable overload for operator \"$TOK$\" at position $POS$.");\r
- m_vErrMsg[ecSTR_RESULT] = _T("Function result is a string.");\r
- m_vErrMsg[ecGENERIC] = _T("Parser error.");\r
- m_vErrMsg[ecLOCALE] = _T("Decimal separator is identic to function argument separator.");\r
- m_vErrMsg[ecUNEXPECTED_CONDITIONAL] = _T("The \"$TOK$\" operator must be preceeded by a closing bracket.");\r
- m_vErrMsg[ecMISSING_ELSE_CLAUSE] = _T("If-then-else operator is missing an else clause");\r
- m_vErrMsg[ecMISPLACED_COLON] = _T("Misplaced colon at position $POS$");\r
- m_vErrMsg[ecUNREASONABLE_NUMBER_OF_COMPUTATIONS] = _T("Number of computations to small for bulk mode. (Vectorisation overhead too costly)");\r
- \r
- #if defined(_DEBUG)\r
- for (int i=0; i<ecCOUNT; ++i)\r
- if (!m_vErrMsg[i].length())\r
- assert(false);\r
- #endif\r
- }\r
-\r
- //---------------------------------------------------------------------------\r
- //\r
- // ParserError class\r
- //\r
- //---------------------------------------------------------------------------\r
-\r
- /** \brief Default constructor. */\r
- ParserError::ParserError()\r
- :m_strMsg()\r
- ,m_strFormula()\r
- ,m_strTok()\r
- ,m_iPos(-1)\r
- ,m_iErrc(ecUNDEFINED)\r
- ,m_ErrMsg(ParserErrorMsg::Instance())\r
- {\r
- }\r
-\r
- //------------------------------------------------------------------------------\r
- /** \brief This Constructor is used for internal exceptions only. \r
- \r
- It does not contain any information but the error code.\r
- */\r
- ParserError::ParserError(EErrorCodes a_iErrc) \r
- :m_strMsg()\r
- ,m_strFormula()\r
- ,m_strTok()\r
- ,m_iPos(-1)\r
- ,m_iErrc(a_iErrc)\r
- ,m_ErrMsg(ParserErrorMsg::Instance())\r
- {\r
- m_strMsg = m_ErrMsg[m_iErrc];\r
- stringstream_type stream;\r
- stream << (int)m_iPos;\r
- ReplaceSubString(m_strMsg, _T("$POS$"), stream.str());\r
- ReplaceSubString(m_strMsg, _T("$TOK$"), m_strTok);\r
- }\r
-\r
- //------------------------------------------------------------------------------\r
- /** \brief Construct an error from a message text. */\r
- ParserError::ParserError(const string_type &sMsg) \r
- :m_ErrMsg(ParserErrorMsg::Instance())\r
- {\r
- Reset();\r
- m_strMsg = sMsg;\r
- }\r
-\r
- //------------------------------------------------------------------------------\r
- /** \brief Construct an error object. \r
- \param [in] a_iErrc the error code.\r
- \param [in] sTok The token string related to this error.\r
- \param [in] sExpr The expression related to the error.\r
- \param [in] a_iPos the position in the expression where the error occured. \r
- */\r
- ParserError::ParserError( EErrorCodes iErrc,\r
- const string_type &sTok,\r
- const string_type &sExpr,\r
- int iPos )\r
- :m_strMsg()\r
- ,m_strFormula(sExpr)\r
- ,m_strTok(sTok)\r
- ,m_iPos(iPos)\r
- ,m_iErrc(iErrc)\r
- ,m_ErrMsg(ParserErrorMsg::Instance())\r
- {\r
- m_strMsg = m_ErrMsg[m_iErrc];\r
- stringstream_type stream;\r
- stream << (int)m_iPos;\r
- ReplaceSubString(m_strMsg, _T("$POS$"), stream.str());\r
- ReplaceSubString(m_strMsg, _T("$TOK$"), m_strTok);\r
- }\r
-\r
- //------------------------------------------------------------------------------\r
- /** \brief Construct an error object. \r
- \param [in] iErrc the error code.\r
- \param [in] iPos the position in the expression where the error occured. \r
- \param [in] sTok The token string related to this error.\r
- */\r
- ParserError::ParserError(EErrorCodes iErrc, int iPos, const string_type &sTok) \r
- :m_strMsg()\r
- ,m_strFormula()\r
- ,m_strTok(sTok)\r
- ,m_iPos(iPos)\r
- ,m_iErrc(iErrc)\r
- ,m_ErrMsg(ParserErrorMsg::Instance())\r
- {\r
- m_strMsg = m_ErrMsg[m_iErrc];\r
- stringstream_type stream;\r
- stream << (int)m_iPos;\r
- ReplaceSubString(m_strMsg, _T("$POS$"), stream.str());\r
- ReplaceSubString(m_strMsg, _T("$TOK$"), m_strTok);\r
- }\r
-\r
- //------------------------------------------------------------------------------\r
- /** \brief Construct an error object. \r
- \param [in] szMsg The error message text.\r
- \param [in] iPos the position related to the error.\r
- \param [in] sTok The token string related to this error.\r
- */\r
- ParserError::ParserError(const char_type *szMsg, int iPos, const string_type &sTok) \r
- :m_strMsg(szMsg)\r
- ,m_strFormula()\r
- ,m_strTok(sTok)\r
- ,m_iPos(iPos)\r
- ,m_iErrc(ecGENERIC)\r
- ,m_ErrMsg(ParserErrorMsg::Instance())\r
- {\r
- stringstream_type stream;\r
- stream << (int)m_iPos;\r
- ReplaceSubString(m_strMsg, _T("$POS$"), stream.str());\r
- ReplaceSubString(m_strMsg, _T("$TOK$"), m_strTok);\r
- }\r
-\r
- //------------------------------------------------------------------------------\r
- /** \brief Copy constructor. */\r
- ParserError::ParserError(const ParserError &a_Obj)\r
- :m_strMsg(a_Obj.m_strMsg)\r
- ,m_strFormula(a_Obj.m_strFormula)\r
- ,m_strTok(a_Obj.m_strTok)\r
- ,m_iPos(a_Obj.m_iPos)\r
- ,m_iErrc(a_Obj.m_iErrc)\r
- ,m_ErrMsg(ParserErrorMsg::Instance())\r
- {\r
- }\r
-\r
- //------------------------------------------------------------------------------\r
- /** \brief Assignment operator. */\r
- ParserError& ParserError::operator=(const ParserError &a_Obj)\r
- {\r
- if (this==&a_Obj)\r
- return *this;\r
-\r
- m_strMsg = a_Obj.m_strMsg;\r
- m_strFormula = a_Obj.m_strFormula;\r
- m_strTok = a_Obj.m_strTok;\r
- m_iPos = a_Obj.m_iPos;\r
- m_iErrc = a_Obj.m_iErrc;\r
- return *this;\r
- }\r
-\r
- //------------------------------------------------------------------------------\r
- ParserError::~ParserError()\r
- {}\r
-\r
- //------------------------------------------------------------------------------\r
- /** \brief Replace all ocuurences of a substring with another string. \r
- \param strFind The string that shall be replaced.\r
- \param strReplaceWith The string that should be inserted instead of strFind\r
- */\r
- void ParserError::ReplaceSubString( string_type &strSource,\r
- const string_type &strFind,\r
- const string_type &strReplaceWith)\r
- {\r
- string_type strResult;\r
- string_type::size_type iPos(0), iNext(0);\r
-\r
- for(;;)\r
- {\r
- iNext = strSource.find(strFind, iPos);\r
- strResult.append(strSource, iPos, iNext-iPos);\r
-\r
- if( iNext==string_type::npos )\r
- break;\r
-\r
- strResult.append(strReplaceWith);\r
- iPos = iNext + strFind.length();\r
- } \r
-\r
- strSource.swap(strResult);\r
- }\r
-\r
- //------------------------------------------------------------------------------\r
- /** \brief Reset the erro object. */\r
- void ParserError::Reset()\r
- {\r
- m_strMsg = _T("");\r
- m_strFormula = _T("");\r
- m_strTok = _T("");\r
- m_iPos = -1;\r
- m_iErrc = ecUNDEFINED;\r
- }\r
- \r
- //------------------------------------------------------------------------------\r
- /** \brief Set the expression related to this error. */\r
- void ParserError::SetFormula(const string_type &a_strFormula)\r
- {\r
- m_strFormula = a_strFormula;\r
- }\r
-\r
- //------------------------------------------------------------------------------\r
- /** \brief gets the expression related tp this error.*/\r
- const string_type& ParserError::GetExpr() const \r
- {\r
- return m_strFormula;\r
- }\r
-\r
- //------------------------------------------------------------------------------\r
- /** \brief Returns the message string for this error. */\r
- const string_type& ParserError::GetMsg() const\r
- {\r
- return m_strMsg;\r
- }\r
-\r
- //------------------------------------------------------------------------------\r
- /** \brief Return the formula position related to the error. \r
-\r
- If the error is not related to a distinct position this will return -1\r
- */\r
- int ParserError::GetPos() const\r
- {\r
- return m_iPos;\r
- }\r
-\r
- //------------------------------------------------------------------------------\r
- /** \brief Return string related with this token (if available). */\r
- const string_type& ParserError::GetToken() const\r
- {\r
- return m_strTok;\r
- }\r
-\r
- //------------------------------------------------------------------------------\r
- /** \brief Return the error code. */\r
- EErrorCodes ParserError::GetCode() const\r
- {\r
- return m_iErrc;\r
- }\r
-} // namespace mu\r
+++ /dev/null
-/*\r
- __________ \r
- _____ __ __\______ \_____ _______ ______ ____ _______ \r
- / \ | | \| ___/\__ \ \_ __ \/ ___/_/ __ \\_ __ \\r
- | Y Y \| | /| | / __ \_| | \/\___ \ \ ___/ | | \/\r
- |__|_| /|____/ |____| (____ /|__| /____ > \___ >|__| \r
- \/ \/ \/ \/ \r
- Copyright (C) 2011 Ingo Berg\r
-\r
- Permission is hereby granted, free of charge, to any person obtaining a copy of this \r
- software and associated documentation files (the "Software"), to deal in the Software\r
- without restriction, including without limitation the rights to use, copy, modify, \r
- merge, publish, distribute, sublicense, and/or sell copies of the Software, and to \r
- permit persons to whom the Software is furnished to do so, subject to the following conditions:\r
-\r
- The above copyright notice and this permission notice shall be included in all copies or \r
- substantial portions of the Software.\r
-\r
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT\r
- NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND \r
- NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, \r
- DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, \r
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \r
-*/\r
-\r
-#include "muParserInt.h"\r
-\r
-#include <cmath>\r
-#include <algorithm>\r
-#include <numeric>\r
-\r
-using namespace std;\r
-\r
-/** \file\r
- \brief Implementation of a parser using integer value.\r
-*/\r
-\r
-/** \brief Namespace for mathematical applications. */\r
-namespace mu\r
-{\r
-value_type ParserInt::Abs(value_type v) { return (value_type)Round(fabs((double)v)); }\r
-value_type ParserInt::Sign(value_type v) { return (Round(v)<0) ? -1 : (Round(v)>0) ? 1 : 0; }\r
-value_type ParserInt::Ite(value_type v1, \r
- value_type v2, \r
- value_type v3) { return (Round(v1)==1) ? Round(v2) : Round(v3); }\r
-value_type ParserInt::Add(value_type v1, value_type v2) { return Round(v1) + Round(v2); }\r
-value_type ParserInt::Sub(value_type v1, value_type v2) { return Round(v1) - Round(v2); }\r
-value_type ParserInt::Mul(value_type v1, value_type v2) { return Round(v1) * Round(v2); }\r
-value_type ParserInt::Div(value_type v1, value_type v2) { return Round(v1) / Round(v2); }\r
-value_type ParserInt::Mod(value_type v1, value_type v2) { return Round(v1) % Round(v2); }\r
-value_type ParserInt::Shr(value_type v1, value_type v2) { return Round(v1) >> Round(v2); }\r
-value_type ParserInt::Shl(value_type v1, value_type v2) { return Round(v1) << Round(v2); }\r
-value_type ParserInt::LogAnd(value_type v1, value_type v2) { return Round(v1) & Round(v2); }\r
-value_type ParserInt::LogOr(value_type v1, value_type v2) { return Round(v1) | Round(v2); }\r
-value_type ParserInt::And(value_type v1, value_type v2) { return Round(v1) && Round(v2); }\r
-value_type ParserInt::Or(value_type v1, value_type v2) { return Round(v1) || Round(v2); }\r
-value_type ParserInt::Less(value_type v1, value_type v2) { return Round(v1) < Round(v2); }\r
-value_type ParserInt::Greater(value_type v1, value_type v2) { return Round(v1) > Round(v2); }\r
-value_type ParserInt::LessEq(value_type v1, value_type v2) { return Round(v1) <= Round(v2); }\r
-value_type ParserInt::GreaterEq(value_type v1, value_type v2) { return Round(v1) >= Round(v2); }\r
-value_type ParserInt::Equal(value_type v1, value_type v2) { return Round(v1) == Round(v2); }\r
-value_type ParserInt::NotEqual(value_type v1, value_type v2) { return Round(v1) != Round(v2); }\r
-value_type ParserInt::Not(value_type v) { return !Round(v); }\r
-\r
-value_type ParserInt::Pow(value_type v1, value_type v2) \r
-{ \r
- return std::pow((double)Round(v1), (double)Round(v2)); \r
-}\r
-\r
-//---------------------------------------------------------------------------\r
-// Unary operator Callbacks: Infix operators\r
-value_type ParserInt::UnaryMinus(value_type v) \r
-{ \r
- return -Round(v); \r
-}\r
-\r
-//---------------------------------------------------------------------------\r
-value_type ParserInt::Sum(const value_type* a_afArg, int a_iArgc)\r
-{ \r
- if (!a_iArgc) \r
- throw ParserError(_T("too few arguments for function sum."));\r
-\r
- value_type fRes=0;\r
- for (int i=0; i<a_iArgc; ++i) \r
- fRes += a_afArg[i];\r
-\r
- return fRes;\r
-}\r
-\r
-//---------------------------------------------------------------------------\r
-value_type ParserInt::Min(const value_type* a_afArg, int a_iArgc)\r
-{ \r
- if (!a_iArgc) \r
- throw ParserError( _T("too few arguments for function min.") );\r
-\r
- value_type fRes=a_afArg[0];\r
- for (int i=0; i<a_iArgc; ++i) \r
- fRes = std::min(fRes, a_afArg[i]);\r
-\r
- return fRes;\r
-}\r
-\r
-//---------------------------------------------------------------------------\r
-value_type ParserInt::Max(const value_type* a_afArg, int a_iArgc)\r
-{ \r
- if (!a_iArgc) \r
- throw ParserError(_T("too few arguments for function min."));\r
-\r
- value_type fRes=a_afArg[0];\r
- for (int i=0; i<a_iArgc; ++i) \r
- fRes = std::max(fRes, a_afArg[i]);\r
-\r
- return fRes;\r
-}\r
-\r
-//---------------------------------------------------------------------------\r
-// Default value recognition callback\r
-int ParserInt::IsVal(const char_type *a_szExpr, int *a_iPos, value_type *a_fVal)\r
-{\r
- string_type buf(a_szExpr);\r
- std::size_t pos = buf.find_first_not_of(_T("0123456789"));\r
-\r
- if (pos==std::string::npos)\r
- return 0;\r
-\r
- stringstream_type stream( buf.substr(0, pos ) );\r
- int iVal(0);\r
-\r
- stream >> iVal;\r
- if (stream.fail())\r
- return 0;\r
- \r
- stringstream_type::pos_type iEnd = stream.tellg(); // Position after reading\r
- if (stream.fail())\r
- iEnd = stream.str().length(); \r
-\r
- if (iEnd==(stringstream_type::pos_type)-1)\r
- return 0;\r
-\r
- *a_iPos += (int)iEnd;\r
- *a_fVal = (value_type)iVal;\r
- return 1;\r
-}\r
-\r
-//---------------------------------------------------------------------------\r
-/** \brief Check a given position in the expression for the presence of \r
- a hex value. \r
- \param a_szExpr Pointer to the expression string\r
- \param [in/out] a_iPos Pointer to an interger value holding the current parsing \r
- position in the expression.\r
- \param [out] a_fVal Pointer to the position where the detected value shall be stored.\r
-\r
- Hey values must be prefixed with "0x" in order to be detected properly.\r
-*/\r
-int ParserInt::IsHexVal(const char_type *a_szExpr, int *a_iPos, value_type *a_fVal)\r
-{\r
- if (a_szExpr[1]==0 || (a_szExpr[0]!='0' || a_szExpr[1]!='x') ) \r
- return 0;\r
-\r
- unsigned iVal(0);\r
-\r
- // New code based on streams for UNICODE compliance:\r
- stringstream_type::pos_type nPos(0);\r
- stringstream_type ss(a_szExpr + 2);\r
- ss >> std::hex >> iVal;\r
- nPos = ss.tellg();\r
-\r
- if (nPos==(stringstream_type::pos_type)0)\r
- return 1;\r
-\r
- *a_iPos += (int)(2 + nPos);\r
- *a_fVal = (value_type)iVal;\r
- return 1;\r
-}\r
-\r
-//---------------------------------------------------------------------------\r
-int ParserInt::IsBinVal(const char_type *a_szExpr, int *a_iPos, value_type *a_fVal)\r
-{\r
- if (a_szExpr[0]!='#') \r
- return 0;\r
-\r
- unsigned iVal(0), \r
- iBits(sizeof(iVal)*8),\r
- i(0);\r
-\r
- for (i=0; (a_szExpr[i+1]=='0' || a_szExpr[i+1]=='1') && i<iBits; ++i)\r
- iVal |= (int)(a_szExpr[i+1]=='1') << ((iBits-1)-i);\r
-\r
- if (i==0) \r
- return 0;\r
-\r
- if (i==iBits)\r
- throw exception_type(_T("Binary to integer conversion error (overflow)."));\r
-\r
- *a_fVal = (unsigned)(iVal >> (iBits-i) );\r
- *a_iPos += i+1;\r
-\r
- return 1;\r
-}\r
-\r
-//---------------------------------------------------------------------------\r
-/** \brief Constructor. \r
-\r
- Call ParserBase class constructor and trigger Function, Operator and Constant initialization.\r
-*/\r
-ParserInt::ParserInt()\r
- :ParserBase()\r
-{\r
- AddValIdent(IsVal); // lowest priority\r
- AddValIdent(IsBinVal);\r
- AddValIdent(IsHexVal); // highest priority\r
-\r
- InitCharSets();\r
- InitFun();\r
- InitOprt();\r
-}\r
-\r
-//---------------------------------------------------------------------------\r
-void ParserInt::InitConst()\r
-{\r
-}\r
-\r
-//---------------------------------------------------------------------------\r
-void ParserInt::InitCharSets()\r
-{\r
- DefineNameChars( _T("0123456789_abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ") );\r
- DefineOprtChars( _T("+-*^/?<>=!%&|~'_") );\r
- DefineInfixOprtChars( _T("/+-*^?<>=!%&|~'_") );\r
-}\r
-\r
-//---------------------------------------------------------------------------\r
-/** \brief Initialize the default functions. */\r
-void ParserInt::InitFun()\r
-{\r
- DefineFun( _T("sign"), Sign);\r
- DefineFun( _T("abs"), Abs);\r
- DefineFun( _T("if"), Ite);\r
- DefineFun( _T("sum"), Sum);\r
- DefineFun( _T("min"), Min);\r
- DefineFun( _T("max"), Max);\r
-}\r
-\r
-//---------------------------------------------------------------------------\r
-/** \brief Initialize operators. */\r
-void ParserInt::InitOprt()\r
-{\r
- // disable all built in operators, not all of them usefull for integer numbers\r
- // (they don't do rounding of values)\r
- EnableBuiltInOprt(false);\r
-\r
- // Disable all built in operators, they wont work with integer numbers\r
- // since they are designed for floating point numbers\r
- DefineInfixOprt( _T("-"), UnaryMinus);\r
- DefineInfixOprt( _T("!"), Not);\r
-\r
- DefineOprt( _T("&"), LogAnd, prLOGIC);\r
- DefineOprt( _T("|"), LogOr, prLOGIC);\r
- DefineOprt( _T("&&"), And, prLOGIC);\r
- DefineOprt( _T("||"), Or, prLOGIC);\r
-\r
- DefineOprt( _T("<"), Less, prCMP);\r
- DefineOprt( _T(">"), Greater, prCMP);\r
- DefineOprt( _T("<="), LessEq, prCMP);\r
- DefineOprt( _T(">="), GreaterEq, prCMP);\r
- DefineOprt( _T("=="), Equal, prCMP);\r
- DefineOprt( _T("!="), NotEqual, prCMP);\r
-\r
- DefineOprt( _T("+"), Add, prADD_SUB);\r
- DefineOprt( _T("-"), Sub, prADD_SUB);\r
-\r
- DefineOprt( _T("*"), Mul, prMUL_DIV);\r
- DefineOprt( _T("/"), Div, prMUL_DIV);\r
- DefineOprt( _T("%"), Mod, prMUL_DIV);\r
-\r
- DefineOprt( _T("^"), Pow, prPOW, oaRIGHT);\r
- DefineOprt( _T(">>"), Shr, prMUL_DIV+1);\r
- DefineOprt( _T("<<"), Shl, prMUL_DIV+1);\r
-}\r
-\r
-} // namespace mu\r
+++ /dev/null
-/*\r
- __________ \r
- _____ __ __\______ \_____ _______ ______ ____ _______ \r
- / \ | | \| ___/\__ \ \_ __ \/ ___/_/ __ \\_ __ \\r
- | Y Y \| | /| | / __ \_| | \/\___ \ \ ___/ | | \/\r
- |__|_| /|____/ |____| (____ /|__| /____ > \___ >|__| \r
- \/ \/ \/ \/ \r
- Copyright (C) 2013 Ingo Berg\r
-\r
- Permission is hereby granted, free of charge, to any person obtaining a copy of this \r
- software and associated documentation files (the "Software"), to deal in the Software\r
- without restriction, including without limitation the rights to use, copy, modify, \r
- merge, publish, distribute, sublicense, and/or sell copies of the Software, and to \r
- permit persons to whom the Software is furnished to do so, subject to the following conditions:\r
-\r
- The above copyright notice and this permission notice shall be included in all copies or \r
- substantial portions of the Software.\r
-\r
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT\r
- NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND \r
- NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, \r
- DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, \r
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \r
-*/\r
-\r
-#include "muParserTest.h"\r
-\r
-#include <cstdio>\r
-#include <cmath>\r
-#include <iostream>\r
-#include <limits>\r
-\r
-#define PARSER_CONST_PI 3.141592653589793238462643\r
-#define PARSER_CONST_E 2.718281828459045235360287\r
-\r
-using namespace std;\r
-\r
-/** \file\r
- \brief This file contains the implementation of parser test cases.\r
-*/\r
-\r
-namespace mu\r
-{\r
- namespace Test\r
- {\r
- int ParserTester::c_iCount = 0;\r
-\r
- //---------------------------------------------------------------------------------------------\r
- ParserTester::ParserTester()\r
- :m_vTestFun()\r
- {\r
- AddTest(&ParserTester::TestNames);\r
- AddTest(&ParserTester::TestSyntax);\r
- AddTest(&ParserTester::TestPostFix);\r
- AddTest(&ParserTester::TestInfixOprt);\r
- AddTest(&ParserTester::TestVarConst);\r
- AddTest(&ParserTester::TestMultiArg);\r
- AddTest(&ParserTester::TestExpression);\r
- AddTest(&ParserTester::TestIfThenElse);\r
- AddTest(&ParserTester::TestInterface);\r
- AddTest(&ParserTester::TestBinOprt);\r
- AddTest(&ParserTester::TestException);\r
- AddTest(&ParserTester::TestStrArg);\r
- AddTest(&ParserTester::TestBulkMode);\r
-\r
- ParserTester::c_iCount = 0;\r
- }\r
-\r
- //---------------------------------------------------------------------------------------------\r
- int ParserTester::IsHexVal(const char_type *a_szExpr, int *a_iPos, value_type *a_fVal)\r
- {\r
- if (a_szExpr[1]==0 || (a_szExpr[0]!='0' || a_szExpr[1]!='x') ) \r
- return 0;\r
-\r
- unsigned iVal(0);\r
-\r
- // New code based on streams for UNICODE compliance:\r
- stringstream_type::pos_type nPos(0);\r
- stringstream_type ss(a_szExpr + 2);\r
- ss >> std::hex >> iVal;\r
- nPos = ss.tellg();\r
-\r
- if (nPos==(stringstream_type::pos_type)0)\r
- return 1;\r
-\r
- *a_iPos += (int)(2 + nPos);\r
- *a_fVal = (value_type)iVal;\r
- return 1;\r
- }\r
-\r
- //---------------------------------------------------------------------------------------------\r
- int ParserTester::TestInterface()\r
- {\r
- int iStat = 0;\r
- mu::console() << _T("testing member functions...");\r
- \r
- // Test RemoveVar\r
- value_type afVal[3] = {1,2,3};\r
- Parser p;\r
- \r
- try\r
- {\r
- p.DefineVar( _T("a"), &afVal[0]);\r
- p.DefineVar( _T("b"), &afVal[1]);\r
- p.DefineVar( _T("c"), &afVal[2]);\r
- p.SetExpr( _T("a+b+c") );\r
- p.Eval();\r
- }\r
- catch(...)\r
- {\r
- iStat += 1; // this is not supposed to happen \r
- }\r
-\r
- try\r
- {\r
- p.RemoveVar( _T("c") );\r
- p.Eval();\r
- iStat += 1; // not supposed to reach this, nonexisting variable "c" deleted...\r
- }\r
- catch(...)\r
- {\r
- // failure is expected...\r
- }\r
-\r
- if (iStat==0) \r
- mu::console() << _T("passed") << endl;\r
- else \r
- mu::console() << _T("\n failed with ") << iStat << _T(" errors") << endl;\r
-\r
- return iStat;\r
- }\r
-\r
- //---------------------------------------------------------------------------------------------\r
- int ParserTester::TestStrArg()\r
- {\r
- int iStat = 0;\r
- mu::console() << _T("testing string arguments...");\r
- \r
- iStat += EqnTest(_T("valueof(\"\")"), 123, true); // empty string arguments caused a crash\r
- iStat += EqnTest(_T("valueof(\"aaa\")+valueof(\"bbb\") "), 246, true);\r
- iStat += EqnTest(_T("2*(valueof(\"aaa\")-23)+valueof(\"bbb\")"), 323, true);\r
- // use in expressions with variables\r
- iStat += EqnTest(_T("a*(atof(\"10\")-b)"), 8, true);\r
- iStat += EqnTest(_T("a-(atof(\"10\")*b)"), -19, true);\r
- // string + numeric arguments\r
- iStat += EqnTest(_T("strfun1(\"100\")"), 100, true);\r
- iStat += EqnTest(_T("strfun2(\"100\",1)"), 101, true);\r
- iStat += EqnTest(_T("strfun3(\"99\",1,2)"), 102, true);\r
- // string constants\r
- iStat += EqnTest(_T("atof(str1)+atof(str2)"), 3.33, true);\r
-\r
- if (iStat==0)\r
- mu::console() << _T("passed") << endl;\r
- else \r
- mu::console() << _T("\n failed with ") << iStat << _T(" errors") << endl;\r
-\r
- return iStat;\r
- }\r
-\r
- //---------------------------------------------------------------------------------------------\r
- int ParserTester::TestBulkMode()\r
- {\r
- int iStat = 0;\r
- mu::console() << _T("testing bulkmode...");\r
-\r
-#define EQN_TEST_BULK(EXPR, R1, R2, R3, R4, PASS) \\r
- { \\r
- double res[] = { R1, R2, R3, R4 }; \\r
- iStat += EqnTestBulk(_T(EXPR), res, (PASS)); \\r
- }\r
-\r
- // Bulk Variables for the test:\r
- // a: 1,2,3,4\r
- // b: 2,2,2,2\r
- // c: 3,3,3,3\r
- // d: 5,4,3,2\r
- EQN_TEST_BULK("a", 1, 1, 1, 1, false)\r
- EQN_TEST_BULK("a", 1, 2, 3, 4, true)\r
- EQN_TEST_BULK("b=a", 1, 2, 3, 4, true)\r
- EQN_TEST_BULK("b=a, b*10", 10, 20, 30, 40, true)\r
- EQN_TEST_BULK("b=a, b*10, a", 1, 2, 3, 4, true)\r
- EQN_TEST_BULK("a+b", 3, 4, 5, 6, true)\r
- EQN_TEST_BULK("c*(a+b)", 9, 12, 15, 18, true)\r
-#undef EQN_TEST_BULK\r
-\r
- if (iStat == 0)\r
- mu::console() << _T("passed") << endl;\r
- else\r
- mu::console() << _T("\n failed with ") << iStat << _T(" errors") << endl;\r
-\r
- return iStat;\r
- }\r
-\r
- //---------------------------------------------------------------------------------------------\r
- int ParserTester::TestBinOprt()\r
- {\r
- int iStat = 0;\r
- mu::console() << _T("testing binary operators...");\r
- \r
- // built in operators\r
- // xor operator\r
-\r
- iStat += EqnTest(_T("a++b"), 3, true);\r
- iStat += EqnTest(_T("a ++ b"), 3, true);\r
- iStat += EqnTest(_T("1++2"), 3, true);\r
- iStat += EqnTest(_T("1 ++ 2"), 3, true);\r
- iStat += EqnTest(_T("a add b"), 3, true);\r
- iStat += EqnTest(_T("1 add 2"), 3, true);\r
- iStat += EqnTest(_T("a<b"), 1, true);\r
- iStat += EqnTest(_T("b>a"), 1, true);\r
- iStat += EqnTest(_T("a>a"), 0, true);\r
- iStat += EqnTest(_T("a<a"), 0, true);\r
- iStat += EqnTest(_T("a>a"), 0, true);\r
- iStat += EqnTest(_T("a<=a"), 1, true);\r
- iStat += EqnTest(_T("a<=b"), 1, true);\r
- iStat += EqnTest(_T("b<=a"), 0, true);\r
- iStat += EqnTest(_T("a>=a"), 1, true);\r
- iStat += EqnTest(_T("b>=a"), 1, true);\r
- iStat += EqnTest(_T("a>=b"), 0, true);\r
-\r
- // Test logical operators, expecially if user defined "&" and the internal "&&" collide\r
- iStat += EqnTest(_T("1 && 1"), 1, true); \r
- iStat += EqnTest(_T("1 && 0"), 0, true); \r
- iStat += EqnTest(_T("(a<b) && (b>a)"), 1, true); \r
- iStat += EqnTest(_T("(a<b) && (a>b)"), 0, true); \r
- //iStat += EqnTest(_T("12 and 255"), 12, true); \r
- //iStat += EqnTest(_T("12 and 0"), 0, true); \r
- iStat += EqnTest(_T("12 & 255"), 12, true); \r
- iStat += EqnTest(_T("12 & 0"), 0, true); \r
- iStat += EqnTest(_T("12&255"), 12, true); \r
- iStat += EqnTest(_T("12&0"), 0, true); \r
-\r
- // Assignement operator\r
- iStat += EqnTest(_T("a = b"), 2, true); \r
- iStat += EqnTest(_T("a = sin(b)"), 0.909297, true); \r
- iStat += EqnTest(_T("a = 1+sin(b)"), 1.909297, true);\r
- iStat += EqnTest(_T("(a=b)*2"), 4, true);\r
- iStat += EqnTest(_T("2*(a=b)"), 4, true);\r
- iStat += EqnTest(_T("2*(a=b+1)"), 6, true);\r
- iStat += EqnTest(_T("(a=b+1)*2"), 6, true);\r
- iStat += EqnTest(_T("a=c, a*10"), 30, true);\r
-\r
- iStat += EqnTest(_T("2^2^3"), 256, true); \r
- iStat += EqnTest(_T("1/2/3"), 1.0/6.0, true); \r
-\r
- // reference: http://www.wolframalpha.com/input/?i=3%2B4*2%2F%281-5%29^2^3\r
- iStat += EqnTest(_T("3+4*2/(1-5)^2^3"), 3.0001220703125, true); \r
-\r
- // Test user defined binary operators\r
- iStat += EqnTestInt(_T("1 | 2"), 3, true); \r
- iStat += EqnTestInt(_T("1 || 2"), 1, true); \r
- iStat += EqnTestInt(_T("123 & 456"), 72, true); \r
- iStat += EqnTestInt(_T("(123 & 456) % 10"), 2, true);\r
- iStat += EqnTestInt(_T("1 && 0"), 0, true); \r
- iStat += EqnTestInt(_T("123 && 456"), 1, true); \r
- iStat += EqnTestInt(_T("1 << 3"), 8, true); \r
- iStat += EqnTestInt(_T("8 >> 3"), 1, true); \r
- iStat += EqnTestInt(_T("9 / 4"), 2, true); \r
- iStat += EqnTestInt(_T("9 % 4"), 1, true); \r
- iStat += EqnTestInt(_T("if(5%2,1,0)"), 1, true);\r
- iStat += EqnTestInt(_T("if(4%2,1,0)"), 0, true);\r
- iStat += EqnTestInt(_T("-10+1"), -9, true);\r
- iStat += EqnTestInt(_T("1+2*3"), 7, true);\r
- iStat += EqnTestInt(_T("const1 != const2"), 1, true);\r
- iStat += EqnTestInt(_T("const1 != const2"), 0, false);\r
- iStat += EqnTestInt(_T("const1 == const2"), 0, true);\r
- iStat += EqnTestInt(_T("const1 == 1"), 1, true);\r
- iStat += EqnTestInt(_T("10*(const1 == 1)"), 10, true);\r
- iStat += EqnTestInt(_T("2*(const1 | const2)"), 6, true);\r
- iStat += EqnTestInt(_T("2*(const1 | const2)"), 7, false);\r
- iStat += EqnTestInt(_T("const1 < const2"), 1, true);\r
- iStat += EqnTestInt(_T("const2 > const1"), 1, true);\r
- iStat += EqnTestInt(_T("const1 <= 1"), 1, true);\r
- iStat += EqnTestInt(_T("const2 >= 2"), 1, true);\r
- iStat += EqnTestInt(_T("2*(const1 + const2)"), 6, true);\r
- iStat += EqnTestInt(_T("2*(const1 - const2)"), -2, true);\r
- iStat += EqnTestInt(_T("a != b"), 1, true);\r
- iStat += EqnTestInt(_T("a != b"), 0, false);\r
- iStat += EqnTestInt(_T("a == b"), 0, true);\r
- iStat += EqnTestInt(_T("a == 1"), 1, true);\r
- iStat += EqnTestInt(_T("10*(a == 1)"), 10, true);\r
- iStat += EqnTestInt(_T("2*(a | b)"), 6, true);\r
- iStat += EqnTestInt(_T("2*(a | b)"), 7, false);\r
- iStat += EqnTestInt(_T("a < b"), 1, true);\r
- iStat += EqnTestInt(_T("b > a"), 1, true);\r
- iStat += EqnTestInt(_T("a <= 1"), 1, true);\r
- iStat += EqnTestInt(_T("b >= 2"), 1, true);\r
- iStat += EqnTestInt(_T("2*(a + b)"), 6, true);\r
- iStat += EqnTestInt(_T("2*(a - b)"), -2, true);\r
- iStat += EqnTestInt(_T("a + (a << b)"), 5, true);\r
- iStat += EqnTestInt(_T("-2^2"), -4, true);\r
- iStat += EqnTestInt(_T("3--a"), 4, true);\r
- iStat += EqnTestInt(_T("3+-3^2"), -6, true);\r
-\r
- // Test reading of hex values:\r
- iStat += EqnTestInt(_T("0xff"), 255, true);\r
- iStat += EqnTestInt(_T("10+0xff"), 265, true);\r
- iStat += EqnTestInt(_T("0xff+10"), 265, true);\r
- iStat += EqnTestInt(_T("10*0xff"), 2550, true);\r
- iStat += EqnTestInt(_T("0xff*10"), 2550, true);\r
- iStat += EqnTestInt(_T("10+0xff+1"), 266, true);\r
- iStat += EqnTestInt(_T("1+0xff+10"), 266, true);\r
-\r
-// incorrect: '^' is yor here, not power\r
-// iStat += EqnTestInt("-(1+2)^2", -9, true);\r
-// iStat += EqnTestInt("-1^3", -1, true); \r
-\r
- // Test precedence\r
- // a=1, b=2, c=3\r
- iStat += EqnTestInt(_T("a + b * c"), 7, true);\r
- iStat += EqnTestInt(_T("a * b + c"), 5, true);\r
- iStat += EqnTestInt(_T("a<b && b>10"), 0, true);\r
- iStat += EqnTestInt(_T("a<b && b<10"), 1, true);\r
-\r
- iStat += EqnTestInt(_T("a + b << c"), 17, true);\r
- iStat += EqnTestInt(_T("a << b + c"), 7, true);\r
- iStat += EqnTestInt(_T("c * b < a"), 0, true);\r
- iStat += EqnTestInt(_T("c * b == 6 * a"), 1, true);\r
- iStat += EqnTestInt(_T("2^2^3"), 256, true); \r
-\r
-\r
- if (iStat==0)\r
- mu::console() << _T("passed") << endl;\r
- else \r
- mu::console() << _T("\n failed with ") << iStat << _T(" errors") << endl;\r
-\r
- return iStat;\r
- }\r
-\r
- //---------------------------------------------------------------------------------------------\r
- /** \brief Check muParser name restriction enforcement. */\r
- int ParserTester::TestNames()\r
- {\r
- int iStat= 0,\r
- iErr = 0;\r
-\r
- mu::console() << "testing name restriction enforcement...";\r
- \r
- Parser p;\r
-\r
- #define PARSER_THROWCHECK(DOMAIN, FAIL, EXPR, ARG) \\r
- iErr = 0; \\r
- ParserTester::c_iCount++; \\r
- try \\r
- { \\r
- p.Define##DOMAIN(EXPR, ARG); \\r
- } \\r
- catch(Parser::exception_type&) \\r
- { \\r
- iErr = (FAIL==false) ? 0 : 1; \\r
- } \\r
- iStat += iErr; \r
- \r
- // constant names\r
- PARSER_THROWCHECK(Const, false, _T("0a"), 1)\r
- PARSER_THROWCHECK(Const, false, _T("9a"), 1)\r
- PARSER_THROWCHECK(Const, false, _T("+a"), 1)\r
- PARSER_THROWCHECK(Const, false, _T("-a"), 1)\r
- PARSER_THROWCHECK(Const, false, _T("a-"), 1)\r
- PARSER_THROWCHECK(Const, false, _T("a*"), 1)\r
- PARSER_THROWCHECK(Const, false, _T("a?"), 1)\r
- PARSER_THROWCHECK(Const, true, _T("a"), 1)\r
- PARSER_THROWCHECK(Const, true, _T("a_min"), 1)\r
- PARSER_THROWCHECK(Const, true, _T("a_min0"), 1)\r
- PARSER_THROWCHECK(Const, true, _T("a_min9"), 1)\r
- // variable names\r
- value_type a;\r
- p.ClearConst();\r
- PARSER_THROWCHECK(Var, false, _T("123abc"), &a)\r
- PARSER_THROWCHECK(Var, false, _T("9a"), &a)\r
- PARSER_THROWCHECK(Var, false, _T("0a"), &a)\r
- PARSER_THROWCHECK(Var, false, _T("+a"), &a)\r
- PARSER_THROWCHECK(Var, false, _T("-a"), &a)\r
- PARSER_THROWCHECK(Var, false, _T("?a"), &a)\r
- PARSER_THROWCHECK(Var, false, _T("!a"), &a)\r
- PARSER_THROWCHECK(Var, false, _T("a+"), &a)\r
- PARSER_THROWCHECK(Var, false, _T("a-"), &a)\r
- PARSER_THROWCHECK(Var, false, _T("a*"), &a)\r
- PARSER_THROWCHECK(Var, false, _T("a?"), &a)\r
- PARSER_THROWCHECK(Var, true, _T("a"), &a)\r
- PARSER_THROWCHECK(Var, true, _T("a_min"), &a)\r
- PARSER_THROWCHECK(Var, true, _T("a_min0"), &a)\r
- PARSER_THROWCHECK(Var, true, _T("a_min9"), &a)\r
- PARSER_THROWCHECK(Var, false, _T("a_min9"), 0)\r
- // Postfix operators\r
- // fail\r
- PARSER_THROWCHECK(PostfixOprt, false, _T("(k"), f1of1)\r
- PARSER_THROWCHECK(PostfixOprt, false, _T("9+"), f1of1)\r
- PARSER_THROWCHECK(PostfixOprt, false, _T("+"), 0)\r
- // pass\r
- PARSER_THROWCHECK(PostfixOprt, true, _T("-a"), f1of1)\r
- PARSER_THROWCHECK(PostfixOprt, true, _T("?a"), f1of1)\r
- PARSER_THROWCHECK(PostfixOprt, true, _T("_"), f1of1)\r
- PARSER_THROWCHECK(PostfixOprt, true, _T("#"), f1of1)\r
- PARSER_THROWCHECK(PostfixOprt, true, _T("&&"), f1of1)\r
- PARSER_THROWCHECK(PostfixOprt, true, _T("||"), f1of1)\r
- PARSER_THROWCHECK(PostfixOprt, true, _T("&"), f1of1)\r
- PARSER_THROWCHECK(PostfixOprt, true, _T("|"), f1of1)\r
- PARSER_THROWCHECK(PostfixOprt, true, _T("++"), f1of1)\r
- PARSER_THROWCHECK(PostfixOprt, true, _T("--"), f1of1)\r
- PARSER_THROWCHECK(PostfixOprt, true, _T("?>"), f1of1)\r
- PARSER_THROWCHECK(PostfixOprt, true, _T("?<"), f1of1)\r
- PARSER_THROWCHECK(PostfixOprt, true, _T("**"), f1of1)\r
- PARSER_THROWCHECK(PostfixOprt, true, _T("xor"), f1of1)\r
- PARSER_THROWCHECK(PostfixOprt, true, _T("and"), f1of1)\r
- PARSER_THROWCHECK(PostfixOprt, true, _T("or"), f1of1)\r
- PARSER_THROWCHECK(PostfixOprt, true, _T("not"), f1of1)\r
- PARSER_THROWCHECK(PostfixOprt, true, _T("!"), f1of1)\r
- // Binary operator\r
- // The following must fail with builtin operators activated\r
- // p.EnableBuiltInOp(true); -> this is the default\r
- p.ClearPostfixOprt();\r
- PARSER_THROWCHECK(Oprt, false, _T("+"), f1of2)\r
- PARSER_THROWCHECK(Oprt, false, _T("-"), f1of2)\r
- PARSER_THROWCHECK(Oprt, false, _T("*"), f1of2)\r
- PARSER_THROWCHECK(Oprt, false, _T("/"), f1of2)\r
- PARSER_THROWCHECK(Oprt, false, _T("^"), f1of2)\r
- PARSER_THROWCHECK(Oprt, false, _T("&&"), f1of2)\r
- PARSER_THROWCHECK(Oprt, false, _T("||"), f1of2)\r
- // without activated built in operators it should work\r
- p.EnableBuiltInOprt(false);\r
- PARSER_THROWCHECK(Oprt, true, _T("+"), f1of2)\r
- PARSER_THROWCHECK(Oprt, true, _T("-"), f1of2)\r
- PARSER_THROWCHECK(Oprt, true, _T("*"), f1of2)\r
- PARSER_THROWCHECK(Oprt, true, _T("/"), f1of2)\r
- PARSER_THROWCHECK(Oprt, true, _T("^"), f1of2)\r
- PARSER_THROWCHECK(Oprt, true, _T("&&"), f1of2)\r
- PARSER_THROWCHECK(Oprt, true, _T("||"), f1of2)\r
- #undef PARSER_THROWCHECK\r
-\r
- if (iStat==0) \r
- mu::console() << _T("passed") << endl;\r
- else \r
- mu::console() << _T("\n failed with ") << iStat << _T(" errors") << endl;\r
-\r
- return iStat;\r
- }\r
-\r
- //---------------------------------------------------------------------------\r
- int ParserTester::TestSyntax()\r
- {\r
- int iStat = 0;\r
- mu::console() << _T("testing syntax engine...");\r
-\r
- iStat += ThrowTest(_T("1,"), ecUNEXPECTED_EOF); // incomplete hex definition\r
- iStat += ThrowTest(_T("a,"), ecUNEXPECTED_EOF); // incomplete hex definition\r
- iStat += ThrowTest(_T("sin(8),"), ecUNEXPECTED_EOF); // incomplete hex definition\r
- iStat += ThrowTest(_T("(sin(8)),"), ecUNEXPECTED_EOF); // incomplete hex definition\r
- iStat += ThrowTest(_T("a{m},"), ecUNEXPECTED_EOF); // incomplete hex definition\r
-\r
- iStat += EqnTest(_T("(1+ 2*a)"), 3, true); // Spaces within formula\r
- iStat += EqnTest(_T("sqrt((4))"), 2, true); // Multiple brackets\r
- iStat += EqnTest(_T("sqrt((2)+2)"), 2, true);// Multiple brackets\r
- iStat += EqnTest(_T("sqrt(2+(2))"), 2, true);// Multiple brackets\r
- iStat += EqnTest(_T("sqrt(a+(3))"), 2, true);// Multiple brackets\r
- iStat += EqnTest(_T("sqrt((3)+a)"), 2, true);// Multiple brackets\r
- iStat += EqnTest(_T("order(1,2)"), 1, true); // May not cause name collision with operator "or"\r
- iStat += EqnTest(_T("(2+"), 0, false); // missing closing bracket \r
- iStat += EqnTest(_T("2++4"), 0, false); // unexpected operator\r
- iStat += EqnTest(_T("2+-4"), 0, false); // unexpected operator\r
- iStat += EqnTest(_T("(2+)"), 0, false); // unexpected closing bracket\r
- iStat += EqnTest(_T("--2"), 0, false); // double sign\r
- iStat += EqnTest(_T("ksdfj"), 0, false); // unknown token\r
- iStat += EqnTest(_T("()"), 0, false); // empty bracket without a function\r
- iStat += EqnTest(_T("5+()"), 0, false); // empty bracket without a function\r
- iStat += EqnTest(_T("sin(cos)"), 0, false); // unexpected function\r
- iStat += EqnTest(_T("5t6"), 0, false); // unknown token\r
- iStat += EqnTest(_T("5 t 6"), 0, false); // unknown token\r
- iStat += EqnTest(_T("8*"), 0, false); // unexpected end of formula\r
- iStat += EqnTest(_T(",3"), 0, false); // unexpected comma\r
- iStat += EqnTest(_T("3,5"), 0, false); // unexpected comma\r
- iStat += EqnTest(_T("sin(8,8)"), 0, false); // too many function args\r
- iStat += EqnTest(_T("(7,8)"), 0, false); // too many function args\r
- iStat += EqnTest(_T("sin)"), 0, false); // unexpected closing bracket\r
- iStat += EqnTest(_T("a)"), 0, false); // unexpected closing bracket\r
- iStat += EqnTest(_T("pi)"), 0, false); // unexpected closing bracket\r
- iStat += EqnTest(_T("sin(())"), 0, false); // unexpected closing bracket\r
- iStat += EqnTest(_T("sin()"), 0, false); // unexpected closing bracket\r
-\r
- if (iStat==0)\r
- mu::console() << _T("passed") << endl;\r
- else \r
- mu::console() << _T("\n failed with ") << iStat << _T(" errors") << endl;\r
-\r
- return iStat;\r
- }\r
-\r
- //---------------------------------------------------------------------------\r
- int ParserTester::TestVarConst()\r
- {\r
- int iStat = 0;\r
- mu::console() << _T("testing variable/constant detection...");\r
-\r
- // Test if the result changes when a variable changes\r
- iStat += EqnTestWithVarChange( _T("a"), 1, 1, 2, 2 );\r
- iStat += EqnTestWithVarChange( _T("2*a"), 2, 4, 3, 6 );\r
-\r
- // distinguish constants with same basename\r
- iStat += EqnTest( _T("const"), 1, true);\r
- iStat += EqnTest( _T("const1"), 2, true);\r
- iStat += EqnTest( _T("const2"), 3, true);\r
- iStat += EqnTest( _T("2*const"), 2, true);\r
- iStat += EqnTest( _T("2*const1"), 4, true);\r
- iStat += EqnTest( _T("2*const2"), 6, true);\r
- iStat += EqnTest( _T("2*const+1"), 3, true);\r
- iStat += EqnTest( _T("2*const1+1"), 5, true);\r
- iStat += EqnTest( _T("2*const2+1"), 7, true);\r
- iStat += EqnTest( _T("const"), 0, false);\r
- iStat += EqnTest( _T("const1"), 0, false);\r
- iStat += EqnTest( _T("const2"), 0, false);\r
-\r
- // distinguish variables with same basename\r
- iStat += EqnTest( _T("a"), 1, true);\r
- iStat += EqnTest( _T("aa"), 2, true);\r
- iStat += EqnTest( _T("2*a"), 2, true);\r
- iStat += EqnTest( _T("2*aa"), 4, true);\r
- iStat += EqnTest( _T("2*a-1"), 1, true);\r
- iStat += EqnTest( _T("2*aa-1"), 3, true);\r
-\r
- // custom value recognition\r
- iStat += EqnTest( _T("0xff"), 255, true);\r
- iStat += EqnTest( _T("0x97 + 0xff"), 406, true);\r
-\r
- // Finally test querying of used variables\r
- try\r
- {\r
- int idx;\r
- mu::Parser p;\r
- mu::value_type vVarVal[] = { 1, 2, 3, 4, 5};\r
- p.DefineVar( _T("a"), &vVarVal[0]);\r
- p.DefineVar( _T("b"), &vVarVal[1]);\r
- p.DefineVar( _T("c"), &vVarVal[2]);\r
- p.DefineVar( _T("d"), &vVarVal[3]);\r
- p.DefineVar( _T("e"), &vVarVal[4]);\r
-\r
- // Test lookup of defined variables\r
- // 4 used variables\r
- p.SetExpr( _T("a+b+c+d") );\r
- mu::varmap_type UsedVar = p.GetUsedVar();\r
- int iCount = (int)UsedVar.size();\r
- if (iCount!=4) \r
- throw false;\r
- \r
- // the next check will fail if the parser \r
- // erroneousely creates new variables internally\r
- if (p.GetVar().size()!=5)\r
- throw false;\r
-\r
- mu::varmap_type::const_iterator item = UsedVar.begin();\r
- for (idx=0; item!=UsedVar.end(); ++item)\r
- {\r
- if (&vVarVal[idx++]!=item->second) \r
- throw false;\r
- }\r
-\r
- // Test lookup of undefined variables\r
- p.SetExpr( _T("undef1+undef2+undef3") );\r
- UsedVar = p.GetUsedVar();\r
- iCount = (int)UsedVar.size();\r
- if (iCount!=3) \r
- throw false;\r
-\r
- // the next check will fail if the parser \r
- // erroneousely creates new variables internally\r
- if (p.GetVar().size()!=5)\r
- throw false;\r
-\r
- for (item = UsedVar.begin(); item!=UsedVar.end(); ++item)\r
- {\r
- if (item->second!=0) \r
- throw false; // all pointers to undefined variables must be null\r
- }\r
-\r
- // 1 used variables\r
- p.SetExpr( _T("a+b") );\r
- UsedVar = p.GetUsedVar();\r
- iCount = (int)UsedVar.size();\r
- if (iCount!=2) throw false;\r
- item = UsedVar.begin();\r
- for (idx=0; item!=UsedVar.end(); ++item)\r
- if (&vVarVal[idx++]!=item->second) throw false;\r
-\r
- }\r
- catch(...)\r
- {\r
- iStat += 1;\r
- }\r
-\r
- if (iStat==0) \r
- mu::console() << _T("passed") << endl;\r
- else\r
- mu::console() << _T("\n failed with ") << iStat << _T(" errors") << endl;\r
-\r
- return iStat;\r
- }\r
-\r
- //---------------------------------------------------------------------------\r
- int ParserTester::TestMultiArg()\r
- {\r
- int iStat = 0;\r
- mu::console() << _T("testing multiarg functions...");\r
- \r
- // Compound expressions\r
- iStat += EqnTest( _T("1,2,3"), 3, true);\r
- iStat += EqnTest( _T("a,b,c"), 3, true);\r
- iStat += EqnTest( _T("a=10,b=20,c=a*b"), 200, true);\r
- iStat += EqnTest( _T("1,\n2,\n3"), 3, true);\r
- iStat += EqnTest( _T("a,\nb,\nc"), 3, true);\r
- iStat += EqnTest( _T("a=10,\nb=20,\nc=a*b"), 200, true);\r
- iStat += EqnTest( _T("1,\r\n2,\r\n3"), 3, true);\r
- iStat += EqnTest( _T("a,\r\nb,\r\nc"), 3, true);\r
- iStat += EqnTest( _T("a=10,\r\nb=20,\r\nc=a*b"), 200, true);\r
-\r
- // picking the right argument\r
- iStat += EqnTest( _T("f1of1(1)"), 1, true);\r
- iStat += EqnTest( _T("f1of2(1, 2)"), 1, true);\r
- iStat += EqnTest( _T("f2of2(1, 2)"), 2, true);\r
- iStat += EqnTest( _T("f1of3(1, 2, 3)"), 1, true);\r
- iStat += EqnTest( _T("f2of3(1, 2, 3)"), 2, true);\r
- iStat += EqnTest( _T("f3of3(1, 2, 3)"), 3, true);\r
- iStat += EqnTest( _T("f1of4(1, 2, 3, 4)"), 1, true);\r
- iStat += EqnTest( _T("f2of4(1, 2, 3, 4)"), 2, true);\r
- iStat += EqnTest( _T("f3of4(1, 2, 3, 4)"), 3, true);\r
- iStat += EqnTest( _T("f4of4(1, 2, 3, 4)"), 4, true);\r
- iStat += EqnTest( _T("f1of5(1, 2, 3, 4, 5)"), 1, true);\r
- iStat += EqnTest( _T("f2of5(1, 2, 3, 4, 5)"), 2, true);\r
- iStat += EqnTest( _T("f3of5(1, 2, 3, 4, 5)"), 3, true);\r
- iStat += EqnTest( _T("f4of5(1, 2, 3, 4, 5)"), 4, true);\r
- iStat += EqnTest( _T("f5of5(1, 2, 3, 4, 5)"), 5, true);\r
- // Too few arguments / Too many arguments\r
- iStat += EqnTest( _T("1+ping()"), 11, true);\r
- iStat += EqnTest( _T("ping()+1"), 11, true);\r
- iStat += EqnTest( _T("2*ping()"), 20, true);\r
- iStat += EqnTest( _T("ping()*2"), 20, true);\r
- iStat += EqnTest( _T("ping(1,2)"), 0, false);\r
- iStat += EqnTest( _T("1+ping(1,2)"), 0, false);\r
- iStat += EqnTest( _T("f1of1(1,2)"), 0, false);\r
- iStat += EqnTest( _T("f1of1()"), 0, false);\r
- iStat += EqnTest( _T("f1of2(1, 2, 3)"), 0, false);\r
- iStat += EqnTest( _T("f1of2(1)"), 0, false);\r
- iStat += EqnTest( _T("f1of3(1, 2, 3, 4)"), 0, false);\r
- iStat += EqnTest( _T("f1of3(1)"), 0, false);\r
- iStat += EqnTest( _T("f1of4(1, 2, 3, 4, 5)"), 0, false);\r
- iStat += EqnTest( _T("f1of4(1)"), 0, false);\r
- iStat += EqnTest( _T("(1,2,3)"), 0, false);\r
- iStat += EqnTest( _T("1,2,3"), 0, false);\r
- iStat += EqnTest( _T("(1*a,2,3)"), 0, false);\r
- iStat += EqnTest( _T("1,2*a,3"), 0, false);\r
- \r
- // correct calculation of arguments\r
- iStat += EqnTest( _T("min(a, 1)"), 1, true);\r
- iStat += EqnTest( _T("min(3*2, 1)"), 1, true);\r
- iStat += EqnTest( _T("min(3*2, 1)"), 6, false);\r
- iStat += EqnTest( _T("firstArg(2,3,4)"), 2, true);\r
- iStat += EqnTest( _T("lastArg(2,3,4)"), 4, true);\r
- iStat += EqnTest( _T("min(3*a+1, 1)"), 1, true);\r
- iStat += EqnTest( _T("max(3*a+1, 1)"), 4, true);\r
- iStat += EqnTest( _T("max(3*a+1, 1)*2"), 8, true);\r
- iStat += EqnTest( _T("2*max(3*a+1, 1)+2"), 10, true);\r
-\r
- // functions with Variable argument count\r
- iStat += EqnTest( _T("sum(a)"), 1, true);\r
- iStat += EqnTest( _T("sum(1,2,3)"), 6, true);\r
- iStat += EqnTest( _T("sum(a,b,c)"), 6, true);\r
- iStat += EqnTest( _T("sum(1,-max(1,2),3)*2"), 4, true);\r
- iStat += EqnTest( _T("2*sum(1,2,3)"), 12, true);\r
- iStat += EqnTest( _T("2*sum(1,2,3)+2"), 14, true);\r
- iStat += EqnTest( _T("2*sum(-1,2,3)+2"), 10, true);\r
- iStat += EqnTest( _T("2*sum(-1,2,-(-a))+2"), 6, true);\r
- iStat += EqnTest( _T("2*sum(-1,10,-a)+2"), 18, true);\r
- iStat += EqnTest( _T("2*sum(1,2,3)*2"), 24, true);\r
- iStat += EqnTest( _T("sum(1,-max(1,2),3)*2"), 4, true);\r
- iStat += EqnTest( _T("sum(1*3, 4, a+2)"), 10, true);\r
- iStat += EqnTest( _T("sum(1*3, 2*sum(1,2,2), a+2)"), 16, true);\r
- 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);\r
-\r
- // some failures\r
- iStat += EqnTest( _T("sum()"), 0, false);\r
- iStat += EqnTest( _T("sum(,)"), 0, false);\r
- iStat += EqnTest( _T("sum(1,2,)"), 0, false);\r
- iStat += EqnTest( _T("sum(,1,2)"), 0, false);\r
-\r
- if (iStat==0) \r
- mu::console() << _T("passed") << endl;\r
- else\r
- mu::console() << _T("\n failed with ") << iStat << _T(" errors") << endl;\r
- \r
- return iStat;\r
- }\r
-\r
-\r
- //---------------------------------------------------------------------------\r
- int ParserTester::TestInfixOprt()\r
- {\r
- int iStat(0);\r
- mu::console() << "testing infix operators...";\r
-\r
- iStat += EqnTest( _T("+1"), +1, true);\r
- iStat += EqnTest( _T("-(+1)"), -1, true);\r
- iStat += EqnTest( _T("-(+1)*2"), -2, true);\r
- iStat += EqnTest( _T("-(+2)*sqrt(4)"), -4, true);\r
- iStat += EqnTest( _T("3-+a"), 2, true);\r
- iStat += EqnTest( _T("+1*3"), 3, true);\r
-\r
- iStat += EqnTest( _T("-1"), -1, true);\r
- iStat += EqnTest( _T("-(-1)"), 1, true);\r
- iStat += EqnTest( _T("-(-1)*2"), 2, true);\r
- iStat += EqnTest( _T("-(-2)*sqrt(4)"), 4, true);\r
- iStat += EqnTest( _T("-_pi"), -PARSER_CONST_PI, true);\r
- iStat += EqnTest( _T("-a"), -1, true);\r
- iStat += EqnTest( _T("-(a)"), -1, true);\r
- iStat += EqnTest( _T("-(-a)"), 1, true);\r
- iStat += EqnTest( _T("-(-a)*2"), 2, true);\r
- iStat += EqnTest( _T("-(8)"), -8, true);\r
- iStat += EqnTest( _T("-8"), -8, true);\r
- iStat += EqnTest( _T("-(2+1)"), -3, true);\r
- iStat += EqnTest( _T("-(f1of1(1+2*3)+1*2)"), -9, true);\r
- iStat += EqnTest( _T("-(-f1of1(1+2*3)+1*2)"), 5, true);\r
- iStat += EqnTest( _T("-sin(8)"), -0.989358, true);\r
- iStat += EqnTest( _T("3-(-a)"), 4, true);\r
- iStat += EqnTest( _T("3--a"), 4, true);\r
- iStat += EqnTest( _T("-1*3"), -3, true);\r
-\r
- // Postfix / infix priorities\r
- iStat += EqnTest( _T("~2#"), 8, true);\r
- iStat += EqnTest( _T("~f1of1(2)#"), 8, true);\r
- iStat += EqnTest( _T("~(b)#"), 8, true);\r
- iStat += EqnTest( _T("(~b)#"), 12, true);\r
- iStat += EqnTest( _T("~(2#)"), 8, true);\r
- iStat += EqnTest( _T("~(f1of1(2)#)"), 8, true);\r
- //\r
- iStat += EqnTest( _T("-2^2"),-4, true);\r
- iStat += EqnTest( _T("-(a+b)^2"),-9, true);\r
- iStat += EqnTest( _T("(-3)^2"),9, true);\r
- iStat += EqnTest( _T("-(-2^2)"),4, true);\r
- iStat += EqnTest( _T("3+-3^2"),-6, true);\r
- // The following assumes use of sqr as postfix operator ("§") together\r
- // with a sign operator of low priority:\r
- iStat += EqnTest( _T("-2'"), -4, true);\r
- iStat += EqnTest( _T("-(1+1)'"),-4, true);\r
- iStat += EqnTest( _T("2+-(1+1)'"),-2, true);\r
- iStat += EqnTest( _T("2+-2'"), -2, true);\r
- // This is the classic behaviour of the infix sign operator (here: "$") which is\r
- // now deprecated:\r
- iStat += EqnTest( _T("$2^2"),4, true);\r
- iStat += EqnTest( _T("$(a+b)^2"),9, true);\r
- iStat += EqnTest( _T("($3)^2"),9, true);\r
- iStat += EqnTest( _T("$($2^2)"),-4, true);\r
- iStat += EqnTest( _T("3+$3^2"),12, true);\r
-\r
- // infix operators sharing the first few characters\r
- iStat += EqnTest( _T("~ 123"), 123+2, true);\r
- iStat += EqnTest( _T("~~ 123"), 123+2, true);\r
-\r
- if (iStat==0)\r
- mu::console() << _T("passed") << endl;\r
- else\r
- mu::console() << _T("\n failed with ") << iStat << _T(" errors") << endl;\r
-\r
- return iStat;\r
- }\r
-\r
-\r
- //---------------------------------------------------------------------------\r
- int ParserTester::TestPostFix()\r
- {\r
- int iStat = 0;\r
- mu::console() << _T("testing postfix operators...");\r
-\r
- // application\r
- iStat += EqnTest( _T("3{m}+5"), 5.003, true);\r
- iStat += EqnTest( _T("1000{m}"), 1, true);\r
- iStat += EqnTest( _T("1000 {m}"), 1, true);\r
- iStat += EqnTest( _T("(a){m}"), 1e-3, true);\r
- iStat += EqnTest( _T("a{m}"), 1e-3, true);\r
- iStat += EqnTest( _T("a {m}"), 1e-3, true);\r
- iStat += EqnTest( _T("-(a){m}"), -1e-3, true);\r
- iStat += EqnTest( _T("-2{m}"), -2e-3, true);\r
- iStat += EqnTest( _T("-2 {m}"), -2e-3, true);\r
- iStat += EqnTest( _T("f1of1(1000){m}"), 1, true);\r
- iStat += EqnTest( _T("-f1of1(1000){m}"), -1, true);\r
- iStat += EqnTest( _T("-f1of1(-1000){m}"), 1, true);\r
- iStat += EqnTest( _T("f4of4(0,0,0,1000){m}"), 1, true);\r
- iStat += EqnTest( _T("2+(a*1000){m}"), 3, true);\r
-\r
- // can postfix operators "m" und "meg" be told apart properly?\r
- iStat += EqnTest( _T("2*3000meg+2"), 2*3e9+2, true); \r
-\r
- // some incorrect results\r
- iStat += EqnTest( _T("1000{m}"), 0.1, false);\r
- iStat += EqnTest( _T("(a){m}"), 2, false);\r
- // failure due to syntax checking\r
- iStat += ThrowTest(_T("0x"), ecUNASSIGNABLE_TOKEN); // incomplete hex definition\r
- iStat += ThrowTest(_T("3+"), ecUNEXPECTED_EOF);\r
- iStat += ThrowTest( _T("4 + {m}"), ecUNASSIGNABLE_TOKEN);\r
- iStat += ThrowTest( _T("{m}4"), ecUNASSIGNABLE_TOKEN);\r
- iStat += ThrowTest( _T("sin({m})"), ecUNASSIGNABLE_TOKEN);\r
- iStat += ThrowTest( _T("{m} {m}"), ecUNASSIGNABLE_TOKEN);\r
- iStat += ThrowTest( _T("{m}(8)"), ecUNASSIGNABLE_TOKEN);\r
- iStat += ThrowTest( _T("4,{m}"), ecUNASSIGNABLE_TOKEN);\r
- iStat += ThrowTest( _T("-{m}"), ecUNASSIGNABLE_TOKEN);\r
- iStat += ThrowTest( _T("2(-{m})"), ecUNEXPECTED_PARENS);\r
- iStat += ThrowTest( _T("2({m})"), ecUNEXPECTED_PARENS);\r
- \r
- iStat += ThrowTest( _T("multi*1.0"), ecUNASSIGNABLE_TOKEN);\r
-\r
- if (iStat==0)\r
- mu::console() << _T("passed") << endl;\r
- else\r
- mu::console() << _T("\n failed with ") << iStat << _T(" errors") << endl;\r
-\r
- return iStat;\r
- }\r
-\r
- //---------------------------------------------------------------------------\r
- int ParserTester::TestExpression()\r
- {\r
- int iStat = 0;\r
- mu::console() << _T("testing expression samples...");\r
-\r
- value_type b = 2;\r
-\r
- // Optimization\r
- iStat += EqnTest( _T("2*b*5"), 20, true);\r
- iStat += EqnTest( _T("2*b*5 + 4*b"), 28, true);\r
- iStat += EqnTest( _T("2*a/3"), 2.0/3.0, true);\r
-\r
- // Addition auf cmVARMUL \r
- iStat += EqnTest( _T("3+b"), b+3, true);\r
- iStat += EqnTest( _T("b+3"), b+3, true);\r
- iStat += EqnTest( _T("b*3+2"), b*3+2, true);\r
- iStat += EqnTest( _T("3*b+2"), b*3+2, true);\r
- iStat += EqnTest( _T("2+b*3"), b*3+2, true);\r
- iStat += EqnTest( _T("2+3*b"), b*3+2, true);\r
- iStat += EqnTest( _T("b+3*b"), b+3*b, true);\r
- iStat += EqnTest( _T("3*b+b"), b+3*b, true);\r
-\r
- iStat += EqnTest( _T("2+b*3+b"), 2+b*3+b, true);\r
- iStat += EqnTest( _T("b+2+b*3"), b+2+b*3, true);\r
-\r
- iStat += EqnTest( _T("(2*b+1)*4"), (2*b+1)*4, true);\r
- iStat += EqnTest( _T("4*(2*b+1)"), (2*b+1)*4, true);\r
-\r
- // operator precedencs\r
- iStat += EqnTest( _T("1+2-3*4/5^6"), 2.99923, true);\r
- iStat += EqnTest( _T("1^2/3*4-5+6"), 2.33333333, true);\r
- iStat += EqnTest( _T("1+2*3"), 7, true);\r
- iStat += EqnTest( _T("1+2*3"), 7, true);\r
- iStat += EqnTest( _T("(1+2)*3"), 9, true);\r
- iStat += EqnTest( _T("(1+2)*(-3)"), -9, true);\r
- iStat += EqnTest( _T("2/4"), 0.5, true);\r
-\r
- iStat += EqnTest( _T("exp(ln(7))"), 7, true);\r
- iStat += EqnTest( _T("e^ln(7)"), 7, true);\r
- iStat += EqnTest( _T("e^(ln(7))"), 7, true);\r
- iStat += EqnTest( _T("(e^(ln(7)))"), 7, true);\r
- iStat += EqnTest( _T("1-(e^(ln(7)))"), -6, true);\r
- iStat += EqnTest( _T("2*(e^(ln(7)))"), 14, true);\r
- iStat += EqnTest( _T("10^log(5)"), pow(10.0, log(5.0)), true);\r
- iStat += EqnTest( _T("10^log10(5)"), 5, true);\r
- iStat += EqnTest( _T("2^log2(4)"), 4, true);\r
- iStat += EqnTest( _T("-(sin(0)+1)"), -1, true);\r
- iStat += EqnTest( _T("-(2^1.1)"), -2.14354692, true);\r
-\r
- iStat += EqnTest( _T("(cos(2.41)/b)"), -0.372056, true);\r
- iStat += EqnTest( _T("(1*(2*(3*(4*(5*(6*(a+b)))))))"), 2160, true);\r
- iStat += EqnTest( _T("(1*(2*(3*(4*(5*(6*(7*(a+b))))))))"), 15120, true);\r
- 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);\r
-\r
- // long formula (Reference: Matlab)\r
- iStat += EqnTest(\r
- _T("(((-9))-e/(((((((pi-(((-7)+(-3)/4/e))))/(((-5))-2)-((pi+(-0))*(sqrt((e+e))*(-8))*(((-pi)+(-pi)-(-9)*(6*5))")\r
- _T("/(-e)-e))/2)/((((sqrt(2/(-e)+6)-(4-2))+((5/(-2))/(1*(-pi)+3))/8)*pi*((pi/((-2)/(-6)*1*(-1))*(-6)+(-e)))))/")\r
- _T("((e+(-2)+(-e)*((((-3)*9+(-e)))+(-9)))))))-((((e-7+(((5/pi-(3/1+pi)))))/e)/(-5))/(sqrt((((((1+(-7))))+((((-")\r
- _T("e)*(-e)))-8))*(-5)/((-e)))*(-6)-((((((-2)-(-9)-(-e)-1)/3))))/(sqrt((8+(e-((-6))+(9*(-9))))*(((3+2-8))*(7+6")\r
- _T("+(-5))+((0/(-e)*(-pi))+7)))+(((((-e)/e/e)+((-6)*5)*e+(3+(-5)/pi))))+pi))/sqrt((((9))+((((pi))-8+2))+pi))/e")\r
- _T("*4)*((-5)/(((-pi))*(sqrt(e)))))-(((((((-e)*(e)-pi))/4+(pi)*(-9)))))))+(-pi)"), -12.23016549, true);\r
-\r
- // long formula (Reference: Matlab)\r
- iStat += EqnTest(\r
- _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")\r
- _T(")+a)))*2.77)"), -2.16995656, true);\r
-\r
- // long formula (Reference: Matlab)\r
- iStat += EqnTest( _T("1+2-3*4/5^6*(2*(1-5+(3*7^9)*(4+6*7-3)))+12"), -7995810.09926, true);\r
- \r
- if (iStat==0) \r
- mu::console() << _T("passed") << endl; \r
- else \r
- mu::console() << _T("\n failed with ") << iStat << _T(" errors") << endl;\r
-\r
- return iStat;\r
- }\r
-\r
-\r
-\r
- //---------------------------------------------------------------------------\r
- int ParserTester::TestIfThenElse()\r
- {\r
- int iStat = 0;\r
- mu::console() << _T("testing if-then-else operator...");\r
-\r
- // Test error detection\r
- iStat += ThrowTest(_T(":3"), ecUNEXPECTED_CONDITIONAL); \r
- iStat += ThrowTest(_T("? 1 : 2"), ecUNEXPECTED_CONDITIONAL); \r
- iStat += ThrowTest(_T("(a<b) ? (b<c) ? 1 : 2"), ecMISSING_ELSE_CLAUSE); \r
- iStat += ThrowTest(_T("(a<b) ? 1"), ecMISSING_ELSE_CLAUSE); \r
- iStat += ThrowTest(_T("(a<b) ? a"), ecMISSING_ELSE_CLAUSE); \r
- iStat += ThrowTest(_T("(a<b) ? a+b"), ecMISSING_ELSE_CLAUSE); \r
- iStat += ThrowTest(_T("a : b"), ecMISPLACED_COLON); \r
- iStat += ThrowTest(_T("1 : 2"), ecMISPLACED_COLON); \r
- iStat += ThrowTest(_T("(1) ? 1 : 2 : 3"), ecMISPLACED_COLON); \r
- iStat += ThrowTest(_T("(true) ? 1 : 2 : 3"), ecUNASSIGNABLE_TOKEN); \r
-\r
- iStat += EqnTest(_T("1 ? 128 : 255"), 128, true);\r
- iStat += EqnTest(_T("1<2 ? 128 : 255"), 128, true);\r
- iStat += EqnTest(_T("a<b ? 128 : 255"), 128, true);\r
- iStat += EqnTest(_T("(a<b) ? 128 : 255"), 128, true);\r
- iStat += EqnTest(_T("(1) ? 10 : 11"), 10, true);\r
- iStat += EqnTest(_T("(0) ? 10 : 11"), 11, true);\r
- iStat += EqnTest(_T("(1) ? a+b : c+d"), 3, true);\r
- iStat += EqnTest(_T("(0) ? a+b : c+d"), 1, true);\r
- iStat += EqnTest(_T("(1) ? 0 : 1"), 0, true);\r
- iStat += EqnTest(_T("(0) ? 0 : 1"), 1, true);\r
- iStat += EqnTest(_T("(a<b) ? 10 : 11"), 10, true);\r
- iStat += EqnTest(_T("(a>b) ? 10 : 11"), 11, true);\r
- iStat += EqnTest(_T("(a<b) ? c : d"), 3, true);\r
- iStat += EqnTest(_T("(a>b) ? c : d"), -2, true);\r
-\r
- iStat += EqnTest(_T("(a>b) ? 1 : 0"), 0, true);\r
- iStat += EqnTest(_T("((a>b) ? 1 : 0) ? 1 : 2"), 2, true);\r
- iStat += EqnTest(_T("((a>b) ? 1 : 0) ? 1 : sum((a>b) ? 1 : 2)"), 2, true);\r
- iStat += EqnTest(_T("((a>b) ? 0 : 1) ? 1 : sum((a>b) ? 1 : 2)"), 1, true);\r
-\r
- iStat += EqnTest(_T("sum((a>b) ? 1 : 2)"), 2, true);\r
- iStat += EqnTest(_T("sum((1) ? 1 : 2)"), 1, true);\r
- iStat += EqnTest(_T("sum((a>b) ? 1 : 2, 100)"), 102, true);\r
- iStat += EqnTest(_T("sum((1) ? 1 : 2, 100)"), 101, true);\r
- iStat += EqnTest(_T("sum(3, (a>b) ? 3 : 10)"), 13, true);\r
- iStat += EqnTest(_T("sum(3, (a<b) ? 3 : 10)"), 6, true);\r
- iStat += EqnTest(_T("10*sum(3, (a>b) ? 3 : 10)"), 130, true);\r
- iStat += EqnTest(_T("10*sum(3, (a<b) ? 3 : 10)"), 60, true);\r
- iStat += EqnTest(_T("sum(3, (a>b) ? 3 : 10)*10"), 130, true);\r
- iStat += EqnTest(_T("sum(3, (a<b) ? 3 : 10)*10"), 60, true);\r
- iStat += EqnTest(_T("(a<b) ? sum(3, (a<b) ? 3 : 10)*10 : 99"), 60, true);\r
- iStat += EqnTest(_T("(a>b) ? sum(3, (a<b) ? 3 : 10)*10 : 99"), 99, true);\r
- iStat += EqnTest(_T("(a<b) ? sum(3, (a<b) ? 3 : 10,10,20)*10 : 99"), 360, true);\r
- iStat += EqnTest(_T("(a>b) ? sum(3, (a<b) ? 3 : 10,10,20)*10 : 99"), 99, true);\r
- iStat += EqnTest(_T("(a>b) ? sum(3, (a<b) ? 3 : 10,10,20)*10 : sum(3, (a<b) ? 3 : 10)*10"), 60, true);\r
-\r
- // todo: auch für muParserX hinzufügen!\r
- iStat += EqnTest(_T("(a<b)&&(a<b) ? 128 : 255"), 128, true);\r
- iStat += EqnTest(_T("(a>b)&&(a<b) ? 128 : 255"), 255, true);\r
- iStat += EqnTest(_T("(1<2)&&(1<2) ? 128 : 255"), 128, true);\r
- iStat += EqnTest(_T("(1>2)&&(1<2) ? 128 : 255"), 255, true);\r
- iStat += EqnTest(_T("((1<2)&&(1<2)) ? 128 : 255"), 128, true);\r
- iStat += EqnTest(_T("((1>2)&&(1<2)) ? 128 : 255"), 255, true);\r
- iStat += EqnTest(_T("((a<b)&&(a<b)) ? 128 : 255"), 128, true);\r
- iStat += EqnTest(_T("((a>b)&&(a<b)) ? 128 : 255"), 255, true);\r
-\r
- iStat += EqnTest(_T("1>0 ? 1>2 ? 128 : 255 : 1>0 ? 32 : 64"), 255, true);\r
- iStat += EqnTest(_T("1>0 ? 1>2 ? 128 : 255 :(1>0 ? 32 : 64)"), 255, true);\r
- iStat += EqnTest(_T("1>0 ? 1>0 ? 128 : 255 : 1>2 ? 32 : 64"), 128, true);\r
- iStat += EqnTest(_T("1>0 ? 1>0 ? 128 : 255 :(1>2 ? 32 : 64)"), 128, true);\r
- iStat += EqnTest(_T("1>2 ? 1>2 ? 128 : 255 : 1>0 ? 32 : 64"), 32, true);\r
- iStat += EqnTest(_T("1>2 ? 1>0 ? 128 : 255 : 1>2 ? 32 : 64"), 64, true);\r
- iStat += EqnTest(_T("1>0 ? 50 : 1>0 ? 128 : 255"), 50, true);\r
- iStat += EqnTest(_T("1>0 ? 50 : (1>0 ? 128 : 255)"), 50, true);\r
- iStat += EqnTest(_T("1>0 ? 1>0 ? 128 : 255 : 50"), 128, true);\r
- iStat += EqnTest(_T("1>2 ? 1>2 ? 128 : 255 : 1>0 ? 32 : 1>2 ? 64 : 16"), 32, true);\r
- iStat += EqnTest(_T("1>2 ? 1>2 ? 128 : 255 : 1>0 ? 32 :(1>2 ? 64 : 16)"), 32, true);\r
- iStat += EqnTest(_T("1>0 ? 1>2 ? 128 : 255 : 1>0 ? 32 :1>2 ? 64 : 16"), 255, true);\r
- iStat += EqnTest(_T("1>0 ? 1>2 ? 128 : 255 : (1>0 ? 32 :1>2 ? 64 : 16)"), 255, true);\r
- iStat += EqnTest(_T("1 ? 0 ? 128 : 255 : 1 ? 32 : 64"), 255, true);\r
-\r
- // assignment operators\r
- iStat += EqnTest(_T("a= 0 ? 128 : 255, a"), 255, true);\r
- iStat += EqnTest(_T("a=((a>b)&&(a<b)) ? 128 : 255, a"), 255, true);\r
- iStat += EqnTest(_T("c=(a<b)&&(a<b) ? 128 : 255, c"), 128, true);\r
- iStat += EqnTest(_T("0 ? a=a+1 : 666, a"), 1, true);\r
- iStat += EqnTest(_T("1?a=10:a=20, a"), 10, true);\r
- iStat += EqnTest(_T("0?a=10:a=20, a"), 20, true);\r
- iStat += EqnTest(_T("0?a=sum(3,4):10, a"), 1, true); // a should not change its value due to lazy calculation\r
- \r
- iStat += EqnTest(_T("a=1?b=1?3:4:5, a"), 3, true);\r
- iStat += EqnTest(_T("a=1?b=1?3:4:5, b"), 3, true);\r
- iStat += EqnTest(_T("a=0?b=1?3:4:5, a"), 5, true);\r
- iStat += EqnTest(_T("a=0?b=1?3:4:5, b"), 2, true);\r
-\r
- iStat += EqnTest(_T("a=1?5:b=1?3:4, a"), 5, true);\r
- iStat += EqnTest(_T("a=1?5:b=1?3:4, b"), 2, true);\r
- iStat += EqnTest(_T("a=0?5:b=1?3:4, a"), 3, true);\r
- iStat += EqnTest(_T("a=0?5:b=1?3:4, b"), 3, true);\r
-\r
- if (iStat==0) \r
- mu::console() << _T("passed") << endl; \r
- else \r
- mu::console() << _T("\n failed with ") << iStat << _T(" errors") << endl;\r
-\r
- return iStat;\r
- }\r
-\r
- //---------------------------------------------------------------------------\r
- int ParserTester::TestException()\r
- {\r
- int iStat = 0;\r
- mu::console() << _T("testing error codes...");\r
-\r
- iStat += ThrowTest(_T("3+"), ecUNEXPECTED_EOF);\r
- iStat += ThrowTest(_T("3+)"), ecUNEXPECTED_PARENS);\r
- iStat += ThrowTest(_T("()"), ecUNEXPECTED_PARENS);\r
- iStat += ThrowTest(_T("3+()"), ecUNEXPECTED_PARENS);\r
- iStat += ThrowTest(_T("sin(3,4)"), ecTOO_MANY_PARAMS);\r
- iStat += ThrowTest(_T("sin()"), ecTOO_FEW_PARAMS);\r
- iStat += ThrowTest(_T("(1+2"), ecMISSING_PARENS);\r
- iStat += ThrowTest(_T("sin(3)3"), ecUNEXPECTED_VAL);\r
- iStat += ThrowTest(_T("sin(3)xyz"), ecUNASSIGNABLE_TOKEN);\r
- iStat += ThrowTest(_T("sin(3)cos(3)"), ecUNEXPECTED_FUN);\r
- iStat += ThrowTest(_T("a+b+c=10"), ecUNEXPECTED_OPERATOR);\r
- iStat += ThrowTest(_T("a=b=3"), ecUNEXPECTED_OPERATOR);\r
- \r
-#if defined(MUP_MATH_EXCEPTIONS)\r
- // divide by zero whilst constant folding\r
- iStat += ThrowTest(_T("1/0"), ecDIV_BY_ZERO);\r
- // square root of a negative number\r
- iStat += ThrowTest(_T("sqrt(-1)"), ecDOMAIN_ERROR);\r
- // logarithms of zero\r
- iStat += ThrowTest(_T("ln(0)"), ecDOMAIN_ERROR);\r
- iStat += ThrowTest(_T("log2(0)"), ecDOMAIN_ERROR);\r
- iStat += ThrowTest(_T("log10(0)"), ecDOMAIN_ERROR);\r
- iStat += ThrowTest(_T("log(0)"), ecDOMAIN_ERROR);\r
- // logarithms of negative values\r
- iStat += ThrowTest(_T("ln(-1)"), ecDOMAIN_ERROR);\r
- iStat += ThrowTest(_T("log2(-1)"), ecDOMAIN_ERROR);\r
- iStat += ThrowTest(_T("log10(-1)"), ecDOMAIN_ERROR);\r
- iStat += ThrowTest(_T("log(-1)"), ecDOMAIN_ERROR);\r
-#endif\r
-\r
- // functions without parameter\r
- iStat += ThrowTest( _T("3+ping(2)"), ecTOO_MANY_PARAMS);\r
- iStat += ThrowTest( _T("3+ping(a+2)"), ecTOO_MANY_PARAMS);\r
- iStat += ThrowTest( _T("3+ping(sin(a)+2)"), ecTOO_MANY_PARAMS);\r
- iStat += ThrowTest( _T("3+ping(1+sin(a))"), ecTOO_MANY_PARAMS);\r
-\r
- // String function related\r
- iStat += ThrowTest( _T("valueof(\"xxx\")"), 999, false);\r
- iStat += ThrowTest( _T("valueof()"), ecUNEXPECTED_PARENS);\r
- iStat += ThrowTest( _T("1+valueof(\"abc\""), ecMISSING_PARENS);\r
- iStat += ThrowTest( _T("valueof(\"abc\""), ecMISSING_PARENS);\r
- iStat += ThrowTest( _T("valueof(\"abc"), ecUNTERMINATED_STRING);\r
- iStat += ThrowTest( _T("valueof(\"abc\",3)"), ecTOO_MANY_PARAMS);\r
- iStat += ThrowTest( _T("valueof(3)"), ecSTRING_EXPECTED);\r
- iStat += ThrowTest( _T("sin(\"abc\")"), ecVAL_EXPECTED);\r
- iStat += ThrowTest( _T("valueof(\"\\\"abc\\\"\")"), 999, false);\r
- iStat += ThrowTest( _T("\"hello world\""), ecSTR_RESULT);\r
- iStat += ThrowTest( _T("(\"hello world\")"), ecSTR_RESULT);\r
- iStat += ThrowTest( _T("\"abcd\"+100"), ecOPRT_TYPE_CONFLICT);\r
- iStat += ThrowTest( _T("\"a\"+\"b\""), ecOPRT_TYPE_CONFLICT);\r
- iStat += ThrowTest( _T("strfun1(\"100\",3)"), ecTOO_MANY_PARAMS);\r
- iStat += ThrowTest( _T("strfun2(\"100\",3,5)"), ecTOO_MANY_PARAMS);\r
- iStat += ThrowTest( _T("strfun3(\"100\",3,5,6)"), ecTOO_MANY_PARAMS);\r
- iStat += ThrowTest( _T("strfun2(\"100\")"), ecTOO_FEW_PARAMS);\r
- iStat += ThrowTest( _T("strfun3(\"100\",6)"), ecTOO_FEW_PARAMS);\r
- iStat += ThrowTest( _T("strfun2(1,1)"), ecSTRING_EXPECTED);\r
- iStat += ThrowTest( _T("strfun2(a,1)"), ecSTRING_EXPECTED);\r
- iStat += ThrowTest( _T("strfun2(1,1,1)"), ecTOO_MANY_PARAMS);\r
- iStat += ThrowTest( _T("strfun2(a,1,1)"), ecTOO_MANY_PARAMS);\r
- iStat += ThrowTest( _T("strfun3(1,2,3)"), ecSTRING_EXPECTED);\r
- iStat += ThrowTest( _T("strfun3(1, \"100\",3)"), ecSTRING_EXPECTED);\r
- iStat += ThrowTest( _T("strfun3(\"1\", \"100\",3)"), ecVAL_EXPECTED);\r
- iStat += ThrowTest( _T("strfun3(\"1\", 3, \"100\")"), ecVAL_EXPECTED);\r
- iStat += ThrowTest( _T("strfun3(\"1\", \"100\", \"100\", \"100\")"), ecTOO_MANY_PARAMS);\r
-\r
- // assignement operator\r
- iStat += ThrowTest( _T("3=4"), ecUNEXPECTED_OPERATOR);\r
- iStat += ThrowTest( _T("sin(8)=4"), ecUNEXPECTED_OPERATOR);\r
- iStat += ThrowTest( _T("\"test\"=a"), ecUNEXPECTED_OPERATOR);\r
-\r
- // <ibg 20090529>\r
- // this is now legal, for reference see:\r
- // https://sourceforge.net/forum/message.php?msg_id=7411373\r
- // iStat += ThrowTest( _T("sin=9"), ecUNEXPECTED_OPERATOR); \r
- // </ibg>\r
-\r
- iStat += ThrowTest( _T("(8)=5"), ecUNEXPECTED_OPERATOR);\r
- iStat += ThrowTest( _T("(a)=5"), ecUNEXPECTED_OPERATOR);\r
- iStat += ThrowTest( _T("a=\"tttt\""), ecOPRT_TYPE_CONFLICT);\r
-\r
- if (iStat==0) \r
- mu::console() << _T("passed") << endl;\r
- else \r
- mu::console() << _T("\n failed with ") << iStat << _T(" errors") << endl;\r
-\r
- return iStat;\r
- }\r
-\r
-\r
- //---------------------------------------------------------------------------\r
- void ParserTester::AddTest(testfun_type a_pFun)\r
- {\r
- m_vTestFun.push_back(a_pFun);\r
- }\r
-\r
- //---------------------------------------------------------------------------\r
- void ParserTester::Run()\r
- {\r
- int iStat = 0;\r
- try\r
- {\r
- for (int i=0; i<(int)m_vTestFun.size(); ++i)\r
- iStat += (this->*m_vTestFun[i])();\r
- }\r
- catch(Parser::exception_type &e)\r
- {\r
- mu::console() << "\n" << e.GetMsg() << endl;\r
- mu::console() << e.GetToken() << endl;\r
- Abort();\r
- }\r
- catch(std::exception &e)\r
- {\r
- mu::console() << e.what() << endl;\r
- Abort();\r
- }\r
- catch(...)\r
- {\r
- mu::console() << "Internal error";\r
- Abort();\r
- }\r
-\r
- if (iStat==0) \r
- {\r
- mu::console() << "Test passed (" << ParserTester::c_iCount << " expressions)" << endl;\r
- }\r
- else \r
- {\r
- mu::console() << "Test failed with " << iStat \r
- << " errors (" << ParserTester::c_iCount \r
- << " expressions)" << endl;\r
- }\r
- ParserTester::c_iCount = 0;\r
- }\r
-\r
-\r
- //---------------------------------------------------------------------------\r
- int ParserTester::ThrowTest(const string_type &a_str, int a_iErrc, bool a_bFail)\r
- {\r
- ParserTester::c_iCount++;\r
-\r
- try\r
- {\r
- value_type fVal[] = {1,1,1};\r
- Parser p;\r
-\r
- p.DefineVar( _T("a"), &fVal[0]);\r
- p.DefineVar( _T("b"), &fVal[1]);\r
- p.DefineVar( _T("c"), &fVal[2]);\r
- p.DefinePostfixOprt( _T("{m}"), Milli);\r
- p.DefinePostfixOprt( _T("m"), Milli);\r
- p.DefineFun( _T("ping"), Ping);\r
- p.DefineFun( _T("valueof"), ValueOf);\r
- p.DefineFun( _T("strfun1"), StrFun1);\r
- p.DefineFun( _T("strfun2"), StrFun2);\r
- p.DefineFun( _T("strfun3"), StrFun3);\r
- p.SetExpr(a_str);\r
- p.Eval();\r
- }\r
- catch(ParserError &e)\r
- {\r
- // output the formula in case of an failed test\r
- if (a_bFail==false || (a_bFail==true && a_iErrc!=e.GetCode()) )\r
- {\r
- mu::console() << _T("\n ") \r
- << _T("Expression: ") << a_str \r
- << _T(" Code:") << e.GetCode() << _T("(") << e.GetMsg() << _T(")")\r
- << _T(" Expected:") << a_iErrc;\r
- }\r
-\r
- return (a_iErrc==e.GetCode()) ? 0 : 1;\r
- }\r
-\r
- // if a_bFail==false no exception is expected\r
- bool bRet((a_bFail==false) ? 0 : 1);\r
- if (bRet==1)\r
- {\r
- mu::console() << _T("\n ") \r
- << _T("Expression: ") << a_str \r
- << _T(" did evaluate; Expected error:") << a_iErrc;\r
- }\r
-\r
- return bRet; \r
- }\r
-\r
- //---------------------------------------------------------------------------\r
- /** \brief Evaluate a tet expression. \r
-\r
- \return 1 in case of a failure, 0 otherwise.\r
- */\r
- int ParserTester::EqnTestWithVarChange(const string_type &a_str, \r
- double a_fVar1, \r
- double a_fRes1, \r
- double a_fVar2, \r
- double a_fRes2)\r
- {\r
- ParserTester::c_iCount++;\r
-\r
- try\r
- {\r
- value_type fVal[2] = {-999, -999 }; // should be equalinitially\r
- \r
- Parser p;\r
- value_type var = 0;\r
-\r
- // variable\r
- p.DefineVar( _T("a"), &var);\r
- p.SetExpr(a_str);\r
-\r
- var = a_fVar1;\r
- fVal[0] = p.Eval();\r
-\r
- var = a_fVar2;\r
- fVal[1] = p.Eval();\r
- \r
- if ( fabs(a_fRes1-fVal[0]) > 0.0000000001)\r
- throw std::runtime_error("incorrect result (first pass)");\r
-\r
- if ( fabs(a_fRes2-fVal[1]) > 0.0000000001)\r
- throw std::runtime_error("incorrect result (second pass)");\r
- }\r
- catch(Parser::exception_type &e)\r
- {\r
- mu::console() << _T("\n fail: ") << a_str.c_str() << _T(" (") << e.GetMsg() << _T(")");\r
- return 1;\r
- }\r
- catch(std::exception &e)\r
- {\r
- mu::console() << _T("\n fail: ") << a_str.c_str() << _T(" (") << e.what() << _T(")");\r
- return 1; // always return a failure since this exception is not expected\r
- }\r
- catch(...)\r
- {\r
- mu::console() << _T("\n fail: ") << a_str.c_str() << _T(" (unexpected exception)");\r
- return 1; // exceptions other than ParserException are not allowed\r
- }\r
-\r
- return 0;\r
- }\r
-\r
- //---------------------------------------------------------------------------\r
- /** \brief Evaluate a tet expression. \r
-\r
- \return 1 in case of a failure, 0 otherwise.\r
- */\r
- int ParserTester::EqnTest(const string_type &a_str, double a_fRes, bool a_fPass)\r
- {\r
- ParserTester::c_iCount++;\r
- int iRet(0);\r
- value_type fVal[5] = {-999, -998, -997, -996, -995}; // initially should be different\r
-\r
- try\r
- {\r
- std::unique_ptr<Parser> p1;\r
- Parser p2, p3; // three parser objects\r
- // they will be used for testing copy and assihnment operators\r
- // p1 is a pointer since i'm going to delete it in order to test if\r
- // parsers after copy construction still refer to members of it.\r
- // !! If this is the case this function will crash !!\r
- \r
- p1.reset(new mu::Parser()); \r
- // Add constants\r
- p1->DefineConst( _T("pi"), (value_type)PARSER_CONST_PI);\r
- p1->DefineConst( _T("e"), (value_type)PARSER_CONST_E);\r
- p1->DefineConst( _T("const"), 1);\r
- p1->DefineConst( _T("const1"), 2);\r
- p1->DefineConst( _T("const2"), 3);\r
- // string constants\r
- p1->DefineStrConst( _T("str1"), _T("1.11"));\r
- p1->DefineStrConst( _T("str2"), _T("2.22"));\r
- // variables\r
- value_type vVarVal[] = { 1, 2, 3, -2};\r
- p1->DefineVar( _T("a"), &vVarVal[0]);\r
- p1->DefineVar( _T("aa"), &vVarVal[1]);\r
- p1->DefineVar( _T("b"), &vVarVal[1]);\r
- p1->DefineVar( _T("c"), &vVarVal[2]);\r
- p1->DefineVar( _T("d"), &vVarVal[3]);\r
- \r
- // custom value ident functions\r
- p1->AddValIdent(&ParserTester::IsHexVal); \r
-\r
- // functions\r
- p1->DefineFun( _T("ping"), Ping);\r
- p1->DefineFun( _T("f1of1"), f1of1); // one parameter\r
- p1->DefineFun( _T("f1of2"), f1of2); // two parameter\r
- p1->DefineFun( _T("f2of2"), f2of2);\r
- p1->DefineFun( _T("f1of3"), f1of3); // three parameter\r
- p1->DefineFun( _T("f2of3"), f2of3);\r
- p1->DefineFun( _T("f3of3"), f3of3);\r
- p1->DefineFun( _T("f1of4"), f1of4); // four parameter\r
- p1->DefineFun( _T("f2of4"), f2of4);\r
- p1->DefineFun( _T("f3of4"), f3of4);\r
- p1->DefineFun( _T("f4of4"), f4of4);\r
- p1->DefineFun( _T("f1of5"), f1of5); // five parameter\r
- p1->DefineFun( _T("f2of5"), f2of5);\r
- p1->DefineFun( _T("f3of5"), f3of5);\r
- p1->DefineFun( _T("f4of5"), f4of5);\r
- p1->DefineFun( _T("f5of5"), f5of5);\r
-\r
- // binary operators\r
- p1->DefineOprt( _T("add"), add, 0);\r
- p1->DefineOprt( _T("++"), add, 0);\r
- p1->DefineOprt( _T("&"), land, prLAND);\r
-\r
- // sample functions\r
- p1->DefineFun( _T("min"), Min);\r
- p1->DefineFun( _T("max"), Max);\r
- p1->DefineFun( _T("sum"), Sum);\r
- p1->DefineFun( _T("valueof"), ValueOf);\r
- p1->DefineFun( _T("atof"), StrToFloat);\r
- p1->DefineFun( _T("strfun1"), StrFun1);\r
- p1->DefineFun( _T("strfun2"), StrFun2);\r
- p1->DefineFun( _T("strfun3"), StrFun3);\r
- p1->DefineFun( _T("lastArg"), LastArg);\r
- p1->DefineFun( _T("firstArg"), FirstArg);\r
- p1->DefineFun( _T("order"), FirstArg);\r
-\r
- // infix / postfix operator\r
- // Note: Identifiers used here do not have any meaning \r
- // they are mere placeholders to test certain features.\r
- p1->DefineInfixOprt( _T("$"), sign, prPOW+1); // sign with high priority\r
- p1->DefineInfixOprt( _T("~"), plus2); // high priority\r
- p1->DefineInfixOprt( _T("~~"), plus2);\r
- p1->DefinePostfixOprt( _T("{m}"), Milli);\r
- p1->DefinePostfixOprt( _T("{M}"), Mega);\r
- p1->DefinePostfixOprt( _T("m"), Milli);\r
- p1->DefinePostfixOprt( _T("meg"), Mega);\r
- p1->DefinePostfixOprt( _T("#"), times3);\r
- p1->DefinePostfixOprt( _T("'"), sqr); \r
- p1->SetExpr(a_str);\r
-\r
- // Test bytecode integrity\r
- // String parsing and bytecode parsing must yield the same result\r
- fVal[0] = p1->Eval(); // result from stringparsing\r
- fVal[1] = p1->Eval(); // result from bytecode\r
- if (fVal[0]!=fVal[1])\r
- throw Parser::exception_type( _T("Bytecode / string parsing mismatch.") );\r
-\r
- // Test copy and assignement operators\r
- try\r
- {\r
- // Test copy constructor\r
- std::vector<mu::Parser> vParser;\r
- vParser.push_back(*(p1.get()));\r
- mu::Parser p2 = vParser[0]; // take parser from vector\r
- \r
- // destroy the originals from p2\r
- vParser.clear(); // delete the vector\r
- p1.reset(0);\r
-\r
- fVal[2] = p2.Eval();\r
-\r
- // Test assignement operator\r
- // additionally disable Optimizer this time\r
- mu::Parser p3;\r
- p3 = p2;\r
- p3.EnableOptimizer(false);\r
- fVal[3] = p3.Eval();\r
-\r
- // Test Eval function for multiple return values\r
- // use p2 since it has the optimizer enabled!\r
- int nNum;\r
- value_type *v = p2.Eval(nNum);\r
- fVal[4] = v[nNum-1];\r
- }\r
- catch(std::exception &e)\r
- {\r
- mu::console() << _T("\n ") << e.what() << _T("\n");\r
- }\r
-\r
- // limited floating point accuracy requires the following test\r
- bool bCloseEnough(true);\r
- for (unsigned i=0; i<sizeof(fVal)/sizeof(value_type); ++i)\r
- {\r
- bCloseEnough &= (fabs(a_fRes-fVal[i]) <= fabs(fVal[i]*0.00001));\r
-\r
- // The tests equations never result in infinity, if they do thats a bug.\r
- // reference:\r
- // http://sourceforge.net/projects/muparser/forums/forum/462843/topic/5037825\r
- #pragma warning(push)\r
- #pragma warning(disable:4127)\r
- if (std::numeric_limits<value_type>::has_infinity)\r
- #pragma warning(pop)\r
- {\r
- bCloseEnough &= (fabs(fVal[i]) != numeric_limits<value_type>::infinity());\r
- }\r
- }\r
-\r
- iRet = ((bCloseEnough && a_fPass) || (!bCloseEnough && !a_fPass)) ? 0 : 1;\r
- \r
- \r
- if (iRet==1)\r
- {\r
- mu::console() << _T("\n fail: ") << a_str.c_str() \r
- << _T(" (incorrect result; expected: ") << a_fRes\r
- << _T(" ;calculated: ") << fVal[0] << _T(",") \r
- << fVal[1] << _T(",")\r
- << fVal[2] << _T(",")\r
- << fVal[3] << _T(",")\r
- << fVal[4] << _T(").");\r
- }\r
- }\r
- catch(Parser::exception_type &e)\r
- {\r
- if (a_fPass)\r
- {\r
- if (fVal[0]!=fVal[2] && fVal[0]!=-999 && fVal[1]!=-998)\r
- mu::console() << _T("\n fail: ") << a_str.c_str() << _T(" (copy construction)");\r
- else\r
- mu::console() << _T("\n fail: ") << a_str.c_str() << _T(" (") << e.GetMsg() << _T(")");\r
- return 1;\r
- }\r
- }\r
- catch(std::exception &e)\r
- {\r
- mu::console() << _T("\n fail: ") << a_str.c_str() << _T(" (") << e.what() << _T(")");\r
- return 1; // always return a failure since this exception is not expected\r
- }\r
- catch(...)\r
- {\r
- mu::console() << _T("\n fail: ") << a_str.c_str() << _T(" (unexpected exception)");\r
- return 1; // exceptions other than ParserException are not allowed\r
- }\r
-\r
- return iRet;\r
- }\r
-\r
- //---------------------------------------------------------------------------\r
- int ParserTester::EqnTestInt(const string_type &a_str, double a_fRes, bool a_fPass)\r
- {\r
- ParserTester::c_iCount++;\r
-\r
- value_type vVarVal[] = {1, 2, 3}; // variable values\r
- int iRet(0);\r
-\r
- try\r
- {\r
- value_type fVal[2] = {-99, -999}; // results: initially should be different\r
- ParserInt p;\r
- p.DefineConst( _T("const1"), 1);\r
- p.DefineConst( _T("const2"), 2);\r
- p.DefineVar( _T("a"), &vVarVal[0]);\r
- p.DefineVar( _T("b"), &vVarVal[1]);\r
- p.DefineVar( _T("c"), &vVarVal[2]);\r
-\r
- p.SetExpr(a_str);\r
- fVal[0] = p.Eval(); // result from stringparsing\r
- fVal[1] = p.Eval(); // result from bytecode\r
-\r
- if (fVal[0]!=fVal[1])\r
- throw Parser::exception_type( _T("Bytecode corrupt.") );\r
-\r
- iRet = ( (a_fRes==fVal[0] && a_fPass) || \r
- (a_fRes!=fVal[0] && !a_fPass) ) ? 0 : 1;\r
- if (iRet==1)\r
- {\r
- mu::console() << _T("\n fail: ") << a_str.c_str() \r
- << _T(" (incorrect result; expected: ") << a_fRes \r
- << _T(" ;calculated: ") << fVal[0]<< _T(").");\r
- }\r
- }\r
- catch(Parser::exception_type &e)\r
- {\r
- if (a_fPass)\r
- {\r
- mu::console() << _T("\n fail: ") << e.GetExpr() << _T(" : ") << e.GetMsg();\r
- iRet = 1;\r
- }\r
- }\r
- catch(...)\r
- {\r
- mu::console() << _T("\n fail: ") << a_str.c_str() << _T(" (unexpected exception)");\r
- iRet = 1; // exceptions other than ParserException are not allowed\r
- }\r
-\r
- return iRet;\r
- }\r
-\r
- //---------------------------------------------------------------------------\r
- /** \brief Test an expression in Bulk Mode. */\r
- int ParserTester::EqnTestBulk(const string_type &a_str, double a_fRes[4], bool a_fPass)\r
- {\r
- ParserTester::c_iCount++;\r
-\r
- // Define Bulk Variables\r
- int nBulkSize = 4;\r
- value_type vVariableA[] = { 1, 2, 3, 4 }; // variable values\r
- value_type vVariableB[] = { 2, 2, 2, 2 }; // variable values\r
- value_type vVariableC[] = { 3, 3, 3, 3 }; // variable values\r
- value_type vResults[] = { 0, 0, 0, 0 }; // variable values\r
- int iRet(0);\r
-\r
- try\r
- {\r
- Parser p;\r
- p.DefineConst(_T("const1"), 1);\r
- p.DefineConst(_T("const2"), 2);\r
- p.DefineVar(_T("a"), vVariableA);\r
- p.DefineVar(_T("b"), vVariableB);\r
- p.DefineVar(_T("c"), vVariableC);\r
-\r
- p.SetExpr(a_str);\r
- p.Eval(vResults, nBulkSize);\r
-\r
- bool bCloseEnough(true);\r
- for (int i = 0; i < nBulkSize; ++i)\r
- {\r
- bCloseEnough &= (fabs(a_fRes[i] - vResults[i]) <= fabs(a_fRes[i] * 0.00001));\r
- }\r
-\r
- iRet = ((bCloseEnough && a_fPass) || (!bCloseEnough && !a_fPass)) ? 0 : 1;\r
- if (iRet == 1)\r
- {\r
- mu::console() << _T("\n fail: ") << a_str.c_str()\r
- << _T(" (incorrect result; expected: {") << a_fRes[0] << _T(",") << a_fRes[1] << _T(",") << a_fRes[2] << _T(",") << a_fRes[3] << _T("}")\r
- << _T(" ;calculated: ") << vResults[0] << _T(",") << vResults[1] << _T(",") << vResults[2] << _T(",") << vResults[3] << _T("}");\r
- }\r
- }\r
- catch (Parser::exception_type &e)\r
- {\r
- if (a_fPass)\r
- {\r
- mu::console() << _T("\n fail: ") << e.GetExpr() << _T(" : ") << e.GetMsg();\r
- iRet = 1;\r
- }\r
- }\r
- catch (...)\r
- {\r
- mu::console() << _T("\n fail: ") << a_str.c_str() << _T(" (unexpected exception)");\r
- iRet = 1; // exceptions other than ParserException are not allowed\r
- }\r
-\r
- return iRet;\r
- }\r
-\r
- //---------------------------------------------------------------------------\r
- /** \brief Internal error in test class Test is going to be aborted. */\r
- void ParserTester::Abort() const\r
- {\r
- mu::console() << _T("Test failed (internal error in test class)") << endl;\r
- while (!getchar());\r
- exit(-1);\r
- }\r
- } // namespace test\r
-} // namespace mu\r
+++ /dev/null
-/*\r
- __________ \r
- _____ __ __\______ \_____ _______ ______ ____ _______ \r
- / \ | | \| ___/\__ \ \_ __ \/ ___/_/ __ \\_ __ \\r
- | Y Y \| | /| | / __ \_| | \/\___ \ \ ___/ | | \/\r
- |__|_| /|____/ |____| (____ /|__| /____ > \___ >|__| \r
- \/ \/ \/ \/ \r
- Copyright (C) 2013 Ingo Berg\r
-\r
- Permission is hereby granted, free of charge, to any person obtaining a copy of this \r
- software and associated documentation files (the "Software"), to deal in the Software\r
- without restriction, including without limitation the rights to use, copy, modify, \r
- merge, publish, distribute, sublicense, and/or sell copies of the Software, and to \r
- permit persons to whom the Software is furnished to do so, subject to the following conditions:\r
-\r
- The above copyright notice and this permission notice shall be included in all copies or \r
- substantial portions of the Software.\r
-\r
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT\r
- NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND \r
- NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, \r
- DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, \r
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \r
-*/\r
-#include <cassert>\r
-#include <cstdio>\r
-#include <cstring>\r
-#include <map>\r
-#include <stack>\r
-#include <string>\r
-\r
-#include "muParserTokenReader.h"\r
-#include "muParserBase.h"\r
-\r
-/** \file\r
- \brief This file contains the parser token reader implementation.\r
-*/\r
-\r
-\r
-namespace mu\r
-{\r
-\r
- // Forward declaration\r
- class ParserBase;\r
-\r
- //---------------------------------------------------------------------------\r
- /** \brief Copy constructor.\r
-\r
- \sa Assign\r
- \throw nothrow\r
- */\r
- ParserTokenReader::ParserTokenReader(const ParserTokenReader &a_Reader) \r
- { \r
- Assign(a_Reader);\r
- }\r
- \r
- //---------------------------------------------------------------------------\r
- /** \brief Assignement operator.\r
-\r
- Self assignement will be suppressed otherwise #Assign is called.\r
-\r
- \param a_Reader Object to copy to this token reader.\r
- \throw nothrow\r
- */\r
- ParserTokenReader& ParserTokenReader::operator=(const ParserTokenReader &a_Reader) \r
- {\r
- if (&a_Reader!=this)\r
- Assign(a_Reader);\r
-\r
- return *this;\r
- }\r
-\r
- //---------------------------------------------------------------------------\r
- /** \brief Assign state of a token reader to this token reader. \r
- \r
- \param a_Reader Object from which the state should be copied.\r
- \throw nothrow\r
- */\r
- void ParserTokenReader::Assign(const ParserTokenReader &a_Reader)\r
- {\r
- m_pParser = a_Reader.m_pParser;\r
- m_strFormula = a_Reader.m_strFormula;\r
- m_iPos = a_Reader.m_iPos;\r
- m_iSynFlags = a_Reader.m_iSynFlags;\r
- \r
- m_UsedVar = a_Reader.m_UsedVar;\r
- m_pFunDef = a_Reader.m_pFunDef;\r
- m_pConstDef = a_Reader.m_pConstDef;\r
- m_pVarDef = a_Reader.m_pVarDef;\r
- m_pStrVarDef = a_Reader.m_pStrVarDef;\r
- m_pPostOprtDef = a_Reader.m_pPostOprtDef;\r
- m_pInfixOprtDef = a_Reader.m_pInfixOprtDef;\r
- m_pOprtDef = a_Reader.m_pOprtDef;\r
- m_bIgnoreUndefVar = a_Reader.m_bIgnoreUndefVar;\r
- m_vIdentFun = a_Reader.m_vIdentFun;\r
- m_pFactory = a_Reader.m_pFactory;\r
- m_pFactoryData = a_Reader.m_pFactoryData;\r
- m_iBrackets = a_Reader.m_iBrackets;\r
- m_cArgSep = a_Reader.m_cArgSep;\r
- m_fZero = a_Reader.m_fZero;\r
- m_lastTok = a_Reader.m_lastTok;\r
- }\r
-\r
- //---------------------------------------------------------------------------\r
- /** \brief Constructor. \r
- \r
- Create a Token reader and bind it to a parser object. \r
-\r
- \pre [assert] a_pParser may not be NULL\r
- \post #m_pParser==a_pParser\r
- \param a_pParent Parent parser object of the token reader.\r
- */\r
- ParserTokenReader::ParserTokenReader(ParserBase *a_pParent)\r
- :m_pParser(a_pParent)\r
- ,m_strFormula()\r
- ,m_iPos(0)\r
- ,m_iSynFlags(0)\r
- ,m_bIgnoreUndefVar(false)\r
- ,m_pFunDef(NULL)\r
- ,m_pPostOprtDef(NULL)\r
- ,m_pInfixOprtDef(NULL)\r
- ,m_pOprtDef(NULL)\r
- ,m_pConstDef(NULL)\r
- ,m_pStrVarDef(NULL)\r
- ,m_pVarDef(NULL)\r
- ,m_pFactory(NULL)\r
- ,m_pFactoryData(NULL)\r
- ,m_vIdentFun()\r
- ,m_UsedVar()\r
- ,m_fZero(0)\r
- ,m_iBrackets(0)\r
- ,m_lastTok()\r
- ,m_cArgSep(',')\r
- {\r
- assert(m_pParser);\r
- SetParent(m_pParser);\r
- }\r
- \r
- //---------------------------------------------------------------------------\r
- /** \brief Create instance of a ParserTokenReader identical with this \r
- and return its pointer. \r
-\r
- This is a factory method the calling function must take care of the object destruction.\r
-\r
- \return A new ParserTokenReader object.\r
- \throw nothrow\r
- */\r
- ParserTokenReader* ParserTokenReader::Clone(ParserBase *a_pParent) const\r
- {\r
- std::unique_ptr<ParserTokenReader> ptr(new ParserTokenReader(*this));\r
- ptr->SetParent(a_pParent);\r
- return ptr.release();\r
- }\r
-\r
- //---------------------------------------------------------------------------\r
- ParserTokenReader::token_type& ParserTokenReader::SaveBeforeReturn(const token_type &tok)\r
- {\r
- m_lastTok = tok;\r
- return m_lastTok;\r
- }\r
-\r
- //---------------------------------------------------------------------------\r
- void ParserTokenReader::AddValIdent(identfun_type a_pCallback)\r
- {\r
- // Use push_front is used to give user defined callbacks a higher priority than\r
- // the built in ones. Otherwise reading hex numbers would not work\r
- // since the "0" in "0xff" would always be read first making parsing of \r
- // the rest impossible.\r
- // reference:\r
- // http://sourceforge.net/projects/muparser/forums/forum/462843/topic/4824956\r
- m_vIdentFun.push_front(a_pCallback);\r
- }\r
-\r
- //---------------------------------------------------------------------------\r
- void ParserTokenReader::SetVarCreator(facfun_type a_pFactory, void *pUserData)\r
- {\r
- m_pFactory = a_pFactory;\r
- m_pFactoryData = pUserData;\r
- }\r
-\r
- //---------------------------------------------------------------------------\r
- /** \brief Return the current position of the token reader in the formula string. \r
-\r
- \return #m_iPos\r
- \throw nothrow\r
- */\r
- int ParserTokenReader::GetPos() const\r
- {\r
- return m_iPos;\r
- }\r
-\r
- //---------------------------------------------------------------------------\r
- /** \brief Return a reference to the formula. \r
-\r
- \return #m_strFormula\r
- \throw nothrow\r
- */\r
- const string_type& ParserTokenReader::GetExpr() const\r
- {\r
- return m_strFormula;\r
- }\r
-\r
- //---------------------------------------------------------------------------\r
- /** \brief Return a map containing the used variables only. */\r
- varmap_type& ParserTokenReader::GetUsedVar() \r
- {\r
- return m_UsedVar;\r
- }\r
-\r
- //---------------------------------------------------------------------------\r
- /** \brief Initialize the token Reader. \r
- \r
- Sets the formula position index to zero and set Syntax flags to default for initial formula parsing.\r
- \pre [assert] triggered if a_szFormula==0\r
- */\r
- void ParserTokenReader::SetFormula(const string_type &a_strFormula)\r
- {\r
- m_strFormula = a_strFormula;\r
- ReInit();\r
- }\r
-\r
- //---------------------------------------------------------------------------\r
- /** \brief Set Flag that contronls behaviour in case of undefined variables beeing found. \r
- \r
- If true, the parser does not throw an exception if an undefined variable is found. \r
- otherwise it does. This variable is used internally only!\r
- It supresses a "undefined variable" exception in GetUsedVar(). \r
- Those function should return a complete list of variables including \r
- those the are not defined by the time of it's call.\r
- */\r
- void ParserTokenReader::IgnoreUndefVar(bool bIgnore)\r
- {\r
- m_bIgnoreUndefVar = bIgnore;\r
- }\r
-\r
- //---------------------------------------------------------------------------\r
- /** \brief Reset the token reader to the start of the formula. \r
-\r
- The syntax flags will be reset to a value appropriate for the \r
- start of a formula.\r
- \post #m_iPos==0, #m_iSynFlags = noOPT | noBC | noPOSTOP | noSTR\r
- \throw nothrow\r
- \sa ESynCodes\r
- */\r
- void ParserTokenReader::ReInit()\r
- {\r
- m_iPos = 0;\r
- m_iSynFlags = sfSTART_OF_LINE;\r
- m_iBrackets = 0;\r
- m_UsedVar.clear();\r
- m_lastTok = token_type();\r
- }\r
-\r
- //---------------------------------------------------------------------------\r
- /** \brief Read the next token from the string. */ \r
- ParserTokenReader::token_type ParserTokenReader::ReadNextToken()\r
- {\r
- assert(m_pParser);\r
-\r
- const char_type *szFormula = m_strFormula.c_str();\r
- token_type tok;\r
-\r
- // Ignore all non printable characters when reading the expression\r
- while (szFormula[m_iPos]>0 && szFormula[m_iPos]<=0x20) \r
- ++m_iPos;\r
-\r
- if ( IsEOF(tok) ) return SaveBeforeReturn(tok); // Check for end of formula\r
- if ( IsOprt(tok) ) return SaveBeforeReturn(tok); // Check for user defined binary operator\r
- if ( IsFunTok(tok) ) return SaveBeforeReturn(tok); // Check for function token\r
- if ( IsBuiltIn(tok) ) return SaveBeforeReturn(tok); // Check built in operators / tokens\r
- if ( IsArgSep(tok) ) return SaveBeforeReturn(tok); // Check for function argument separators\r
- if ( IsValTok(tok) ) return SaveBeforeReturn(tok); // Check for values / constant tokens\r
- if ( IsVarTok(tok) ) return SaveBeforeReturn(tok); // Check for variable tokens\r
- if ( IsStrVarTok(tok) ) return SaveBeforeReturn(tok); // Check for string variables\r
- if ( IsString(tok) ) return SaveBeforeReturn(tok); // Check for String tokens\r
- if ( IsInfixOpTok(tok) ) return SaveBeforeReturn(tok); // Check for unary operators\r
- if ( IsPostOpTok(tok) ) return SaveBeforeReturn(tok); // Check for unary operators\r
-\r
- // Check String for undefined variable token. Done only if a \r
- // flag is set indicating to ignore undefined variables.\r
- // This is a way to conditionally avoid an error if \r
- // undefined variables occur. \r
- // (The GetUsedVar function must suppress the error for\r
- // undefined variables in order to collect all variable \r
- // names including the undefined ones.)\r
- if ( (m_bIgnoreUndefVar || m_pFactory) && IsUndefVarTok(tok) ) \r
- return SaveBeforeReturn(tok);\r
-\r
- // Check for unknown token\r
- // \r
- // !!! From this point on there is no exit without an exception possible...\r
- // \r
- string_type strTok;\r
- int iEnd = ExtractToken(m_pParser->ValidNameChars(), strTok, m_iPos);\r
- if (iEnd!=m_iPos)\r
- Error(ecUNASSIGNABLE_TOKEN, m_iPos, strTok);\r
-\r
- Error(ecUNASSIGNABLE_TOKEN, m_iPos, m_strFormula.substr(m_iPos));\r
- return token_type(); // never reached\r
- }\r
-\r
- //---------------------------------------------------------------------------\r
- void ParserTokenReader::SetParent(ParserBase *a_pParent)\r
- {\r
- m_pParser = a_pParent; \r
- m_pFunDef = &a_pParent->m_FunDef;\r
- m_pOprtDef = &a_pParent->m_OprtDef;\r
- m_pInfixOprtDef = &a_pParent->m_InfixOprtDef;\r
- m_pPostOprtDef = &a_pParent->m_PostOprtDef;\r
- m_pVarDef = &a_pParent->m_VarDef;\r
- m_pStrVarDef = &a_pParent->m_StrVarDef;\r
- m_pConstDef = &a_pParent->m_ConstDef;\r
- }\r
-\r
- //---------------------------------------------------------------------------\r
- /** \brief Extract all characters that belong to a certain charset.\r
-\r
- \param a_szCharSet [in] Const char array of the characters allowed in the token. \r
- \param a_strTok [out] The string that consists entirely of characters listed in a_szCharSet.\r
- \param a_iPos [in] Position in the string from where to start reading.\r
- \return The Position of the first character not listed in a_szCharSet.\r
- \throw nothrow\r
- */\r
- int ParserTokenReader::ExtractToken(const char_type *a_szCharSet, \r
- string_type &a_sTok, \r
- int a_iPos) const\r
- {\r
- int iEnd = (int)m_strFormula.find_first_not_of(a_szCharSet, a_iPos);\r
-\r
- if (iEnd==(int)string_type::npos)\r
- iEnd = (int)m_strFormula.length();\r
- \r
- // Assign token string if there was something found\r
- if (a_iPos!=iEnd)\r
- a_sTok = string_type( m_strFormula.begin()+a_iPos, m_strFormula.begin()+iEnd);\r
-\r
- return iEnd;\r
- }\r
-\r
- //---------------------------------------------------------------------------\r
- /** \brief Check Expression for the presence of a binary operator token.\r
- \r
- Userdefined binary operator "++" gives inconsistent parsing result for\r
- the equations "a++b" and "a ++ b" if alphabetic characters are allowed\r
- in operator tokens. To avoid this this function checks specifically\r
- for operator tokens.\r
- */\r
- int ParserTokenReader::ExtractOperatorToken(string_type &a_sTok, \r
- int a_iPos) const\r
- {\r
- // Changed as per Issue 6: https://code.google.com/p/muparser/issues/detail?id=6\r
- int iEnd = (int)m_strFormula.find_first_not_of(m_pParser->ValidOprtChars(), a_iPos);\r
- if (iEnd==(int)string_type::npos)\r
- iEnd = (int)m_strFormula.length();\r
-\r
- // Assign token string if there was something found\r
- if (a_iPos!=iEnd)\r
- {\r
- a_sTok = string_type( m_strFormula.begin() + a_iPos, m_strFormula.begin() + iEnd);\r
- return iEnd;\r
- }\r
- else\r
- {\r
- // There is still the chance of having to deal with an operator consisting exclusively\r
- // of alphabetic characters.\r
- return ExtractToken(MUP_CHARS, a_sTok, a_iPos);\r
- }\r
- }\r
-\r
- //---------------------------------------------------------------------------\r
- /** \brief Check if a built in operator or other token can be found\r
- \param a_Tok [out] Operator token if one is found. This can either be a binary operator or an infix operator token.\r
- \return true if an operator token has been found.\r
- */\r
- bool ParserTokenReader::IsBuiltIn(token_type &a_Tok)\r
- {\r
- const char_type **const pOprtDef = m_pParser->GetOprtDef(),\r
- *const szFormula = m_strFormula.c_str();\r
-\r
- // Compare token with function and operator strings\r
- // check string for operator/function\r
- for (int i=0; pOprtDef[i]; i++)\r
- {\r
- std::size_t len( std::char_traits<char_type>::length(pOprtDef[i]) );\r
- if ( string_type(pOprtDef[i]) == string_type(szFormula + m_iPos, szFormula + m_iPos + len) )\r
- {\r
- switch(i)\r
- {\r
- //case cmAND:\r
- //case cmOR:\r
- //case cmXOR:\r
- case cmLAND:\r
- case cmLOR:\r
- case cmLT:\r
- case cmGT:\r
- case cmLE:\r
- case cmGE:\r
- case cmNEQ: \r
- case cmEQ:\r
- case cmADD:\r
- case cmSUB:\r
- case cmMUL:\r
- case cmDIV:\r
- case cmPOW:\r
- case cmASSIGN:\r
- //if (len!=sTok.length())\r
- // continue;\r
-\r
- // The assignement operator need special treatment\r
- if (i==cmASSIGN && m_iSynFlags & noASSIGN)\r
- Error(ecUNEXPECTED_OPERATOR, m_iPos, pOprtDef[i]);\r
-\r
- if (!m_pParser->HasBuiltInOprt()) continue;\r
- if (m_iSynFlags & noOPT) \r
- {\r
- // Maybe its an infix operator not an operator\r
- // Both operator types can share characters in \r
- // their identifiers\r
- if ( IsInfixOpTok(a_Tok) ) \r
- return true;\r
-\r
- Error(ecUNEXPECTED_OPERATOR, m_iPos, pOprtDef[i]);\r
- }\r
-\r
- m_iSynFlags = noBC | noOPT | noARG_SEP | noPOSTOP | noASSIGN | noIF | noELSE | noEND;\r
- break;\r
-\r
- case cmBO:\r
- if (m_iSynFlags & noBO)\r
- Error(ecUNEXPECTED_PARENS, m_iPos, pOprtDef[i]);\r
- \r
- if (m_lastTok.GetCode()==cmFUNC)\r
- m_iSynFlags = noOPT | noEND | noARG_SEP | noPOSTOP | noASSIGN | noIF | noELSE;\r
- else\r
- m_iSynFlags = noBC | noOPT | noEND | noARG_SEP | noPOSTOP | noASSIGN| noIF | noELSE;\r
-\r
- ++m_iBrackets;\r
- break;\r
-\r
- case cmBC:\r
- if (m_iSynFlags & noBC)\r
- Error(ecUNEXPECTED_PARENS, m_iPos, pOprtDef[i]);\r
-\r
- m_iSynFlags = noBO | noVAR | noVAL | noFUN | noINFIXOP | noSTR | noASSIGN;\r
-\r
- if (--m_iBrackets<0)\r
- Error(ecUNEXPECTED_PARENS, m_iPos, pOprtDef[i]);\r
- break;\r
-\r
- case cmELSE:\r
- if (m_iSynFlags & noELSE)\r
- Error(ecUNEXPECTED_CONDITIONAL, m_iPos, pOprtDef[i]);\r
-\r
- m_iSynFlags = noBC | noPOSTOP | noEND | noOPT | noIF | noELSE;\r
- break;\r
-\r
- case cmIF:\r
- if (m_iSynFlags & noIF)\r
- Error(ecUNEXPECTED_CONDITIONAL, m_iPos, pOprtDef[i]);\r
-\r
- m_iSynFlags = noBC | noPOSTOP | noEND | noOPT | noIF | noELSE;\r
- break;\r
-\r
- default: // The operator is listed in c_DefaultOprt, but not here. This is a bad thing...\r
- Error(ecINTERNAL_ERROR);\r
- } // switch operator id\r
-\r
- m_iPos += (int)len;\r
- a_Tok.Set( (ECmdCode)i, pOprtDef[i] );\r
- return true;\r
- } // if operator string found\r
- } // end of for all operator strings\r
- \r
- return false;\r
- }\r
-\r
- //---------------------------------------------------------------------------\r
- bool ParserTokenReader::IsArgSep(token_type &a_Tok)\r
- {\r
- const char_type* szFormula = m_strFormula.c_str();\r
-\r
- if (szFormula[m_iPos]==m_cArgSep)\r
- {\r
- // copy the separator into null terminated string\r
- char_type szSep[2];\r
- szSep[0] = m_cArgSep;\r
- szSep[1] = 0;\r
-\r
- if (m_iSynFlags & noARG_SEP)\r
- Error(ecUNEXPECTED_ARG_SEP, m_iPos, szSep);\r
-\r
- m_iSynFlags = noBC | noOPT | noEND | noARG_SEP | noPOSTOP | noASSIGN;\r
- m_iPos++;\r
- a_Tok.Set(cmARG_SEP, szSep);\r
- return true;\r
- }\r
-\r
- return false;\r
- }\r
-\r
- //---------------------------------------------------------------------------\r
- /** \brief Check for End of Formula.\r
-\r
- \return true if an end of formula is found false otherwise.\r
- \param a_Tok [out] If an eof is found the corresponding token will be stored there.\r
- \throw nothrow\r
- \sa IsOprt, IsFunTok, IsStrFunTok, IsValTok, IsVarTok, IsString, IsInfixOpTok, IsPostOpTok\r
- */\r
- bool ParserTokenReader::IsEOF(token_type &a_Tok)\r
- {\r
- const char_type* szFormula = m_strFormula.c_str();\r
-\r
- // check for EOF\r
- if ( !szFormula[m_iPos] /*|| szFormula[m_iPos] == '\n'*/)\r
- {\r
- if ( m_iSynFlags & noEND )\r
- Error(ecUNEXPECTED_EOF, m_iPos);\r
-\r
- if (m_iBrackets>0)\r
- Error(ecMISSING_PARENS, m_iPos, _T(")"));\r
-\r
- m_iSynFlags = 0;\r
- a_Tok.Set(cmEND);\r
- return true;\r
- }\r
-\r
- return false;\r
- }\r
-\r
- //---------------------------------------------------------------------------\r
- /** \brief Check if a string position contains a unary infix operator. \r
- \return true if a function token has been found false otherwise.\r
- */\r
- bool ParserTokenReader::IsInfixOpTok(token_type &a_Tok)\r
- {\r
- string_type sTok;\r
- int iEnd = ExtractToken(m_pParser->ValidInfixOprtChars(), sTok, m_iPos);\r
- if (iEnd==m_iPos)\r
- return false;\r
-\r
- // iteraterate over all postfix operator strings\r
- funmap_type::const_reverse_iterator it = m_pInfixOprtDef->rbegin();\r
- for ( ; it!=m_pInfixOprtDef->rend(); ++it)\r
- {\r
- if (sTok.find(it->first)!=0)\r
- continue;\r
-\r
- a_Tok.Set(it->second, it->first);\r
- m_iPos += (int)it->first.length();\r
-\r
- if (m_iSynFlags & noINFIXOP) \r
- Error(ecUNEXPECTED_OPERATOR, m_iPos, a_Tok.GetAsString());\r
-\r
- m_iSynFlags = noPOSTOP | noINFIXOP | noOPT | noBC | noSTR | noASSIGN;\r
- return true;\r
- }\r
-\r
- return false;\r
-\r
-/*\r
- a_Tok.Set(item->second, sTok);\r
- m_iPos = (int)iEnd;\r
-\r
- if (m_iSynFlags & noINFIXOP) \r
- Error(ecUNEXPECTED_OPERATOR, m_iPos, a_Tok.GetAsString());\r
-\r
- m_iSynFlags = noPOSTOP | noINFIXOP | noOPT | noBC | noSTR | noASSIGN; \r
- return true;\r
-*/\r
- }\r
-\r
- //---------------------------------------------------------------------------\r
- /** \brief Check whether the token at a given position is a function token.\r
- \param a_Tok [out] If a value token is found it will be placed here.\r
- \throw ParserException if Syntaxflags do not allow a function at a_iPos\r
- \return true if a function token has been found false otherwise.\r
- \pre [assert] m_pParser!=0\r
- */\r
- bool ParserTokenReader::IsFunTok(token_type &a_Tok)\r
- {\r
- string_type strTok;\r
- int iEnd = ExtractToken(m_pParser->ValidNameChars(), strTok, m_iPos);\r
- if (iEnd==m_iPos)\r
- return false;\r
-\r
- funmap_type::const_iterator item = m_pFunDef->find(strTok);\r
- if (item==m_pFunDef->end())\r
- return false;\r
-\r
- // Check if the next sign is an opening bracket\r
- const char_type *szFormula = m_strFormula.c_str();\r
- if (szFormula[iEnd]!='(')\r
- return false;\r
-\r
- a_Tok.Set(item->second, strTok);\r
-\r
- m_iPos = (int)iEnd;\r
- if (m_iSynFlags & noFUN)\r
- Error(ecUNEXPECTED_FUN, m_iPos-(int)a_Tok.GetAsString().length(), a_Tok.GetAsString());\r
-\r
- m_iSynFlags = noANY ^ noBO;\r
- return true;\r
- }\r
-\r
- //---------------------------------------------------------------------------\r
- /** \brief Check if a string position contains a binary operator.\r
- \param a_Tok [out] Operator token if one is found. This can either be a binary operator or an infix operator token.\r
- \return true if an operator token has been found.\r
- */\r
- bool ParserTokenReader::IsOprt(token_type &a_Tok)\r
- {\r
- const char_type *const szExpr = m_strFormula.c_str();\r
- string_type strTok;\r
-\r
- int iEnd = ExtractOperatorToken(strTok, m_iPos);\r
- if (iEnd==m_iPos)\r
- return false;\r
-\r
- // Check if the operator is a built in operator, if so ignore it here\r
- const char_type **const pOprtDef = m_pParser->GetOprtDef();\r
- for (int i=0; m_pParser->HasBuiltInOprt() && pOprtDef[i]; ++i)\r
- {\r
- if (string_type(pOprtDef[i])==strTok)\r
- return false;\r
- }\r
-\r
- // Note:\r
- // All tokens in oprt_bin_maptype are have been sorted by their length\r
- // Long operators must come first! Otherwise short names (like: "add") that\r
- // are part of long token names (like: "add123") will be found instead \r
- // of the long ones.\r
- // Length sorting is done with ascending length so we use a reverse iterator here.\r
- funmap_type::const_reverse_iterator it = m_pOprtDef->rbegin();\r
- for ( ; it!=m_pOprtDef->rend(); ++it)\r
- {\r
- const string_type &sID = it->first;\r
- if ( sID == string_type(szExpr + m_iPos, szExpr + m_iPos + sID.length()) )\r
- {\r
- a_Tok.Set(it->second, strTok);\r
-\r
- // operator was found\r
- if (m_iSynFlags & noOPT) \r
- {\r
- // An operator was found but is not expected to occur at\r
- // this position of the formula, maybe it is an infix \r
- // operator, not a binary operator. Both operator types\r
- // can share characters in their identifiers.\r
- if ( IsInfixOpTok(a_Tok) ) \r
- return true;\r
- else\r
- {\r
- // nope, no infix operator\r
- return false;\r
- //Error(ecUNEXPECTED_OPERATOR, m_iPos, a_Tok.GetAsString()); \r
- }\r
-\r
- }\r
-\r
- m_iPos += (int)sID.length();\r
- m_iSynFlags = noBC | noOPT | noARG_SEP | noPOSTOP | noEND | noASSIGN;\r
- return true;\r
- }\r
- }\r
-\r
- return false;\r
- }\r
-\r
- //---------------------------------------------------------------------------\r
- /** \brief Check if a string position contains a unary post value operator. */\r
- bool ParserTokenReader::IsPostOpTok(token_type &a_Tok)\r
- {\r
- // <ibg 20110629> Do not check for postfix operators if they are not allowed at\r
- // the current expression index.\r
- //\r
- // This will fix the bug reported here: \r
- //\r
- // http://sourceforge.net/tracker/index.php?func=detail&aid=3343891&group_id=137191&atid=737979\r
- //\r
- if (m_iSynFlags & noPOSTOP)\r
- return false;\r
- // </ibg>\r
-\r
- // Tricky problem with equations like "3m+5":\r
- // m is a postfix operator, + is a valid sign for postfix operators and \r
- // for binary operators parser detects "m+" as operator string and \r
- // finds no matching postfix operator.\r
- // \r
- // This is a special case so this routine slightly differs from the other\r
- // token readers.\r
- \r
- // Test if there could be a postfix operator\r
- string_type sTok;\r
- int iEnd = ExtractToken(m_pParser->ValidOprtChars(), sTok, m_iPos);\r
- if (iEnd==m_iPos)\r
- return false;\r
-\r
- // iteraterate over all postfix operator strings\r
- funmap_type::const_reverse_iterator it = m_pPostOprtDef->rbegin();\r
- for ( ; it!=m_pPostOprtDef->rend(); ++it)\r
- {\r
- if (sTok.find(it->first)!=0)\r
- continue;\r
-\r
- a_Tok.Set(it->second, sTok);\r
- m_iPos += (int)it->first.length();\r
-\r
- m_iSynFlags = noVAL | noVAR | noFUN | noBO | noPOSTOP | noSTR | noASSIGN;\r
- return true;\r
- }\r
-\r
- return false;\r
- }\r
-\r
- //---------------------------------------------------------------------------\r
- /** \brief Check whether the token at a given position is a value token.\r
-\r
- Value tokens are either values or constants.\r
-\r
- \param a_Tok [out] If a value token is found it will be placed here.\r
- \return true if a value token has been found.\r
- */\r
- bool ParserTokenReader::IsValTok(token_type &a_Tok)\r
- {\r
- assert(m_pConstDef);\r
- assert(m_pParser);\r
-\r
- string_type strTok;\r
- value_type fVal(0);\r
- int iEnd(0);\r
- \r
- // 2.) Check for user defined constant\r
- // Read everything that could be a constant name\r
- iEnd = ExtractToken(m_pParser->ValidNameChars(), strTok, m_iPos);\r
- if (iEnd!=m_iPos)\r
- {\r
- valmap_type::const_iterator item = m_pConstDef->find(strTok);\r
- if (item!=m_pConstDef->end())\r
- {\r
- m_iPos = iEnd;\r
- a_Tok.SetVal(item->second, strTok);\r
-\r
- if (m_iSynFlags & noVAL)\r
- Error(ecUNEXPECTED_VAL, m_iPos - (int)strTok.length(), strTok);\r
-\r
- m_iSynFlags = noVAL | noVAR | noFUN | noBO | noINFIXOP | noSTR | noASSIGN; \r
- return true;\r
- }\r
- }\r
-\r
- // 3.call the value recognition functions provided by the user\r
- // Call user defined value recognition functions\r
- std::list<identfun_type>::const_iterator item = m_vIdentFun.begin();\r
- for (item = m_vIdentFun.begin(); item!=m_vIdentFun.end(); ++item)\r
- {\r
- int iStart = m_iPos;\r
- if ( (*item)(m_strFormula.c_str() + m_iPos, &m_iPos, &fVal)==1 )\r
- {\r
- // 2013-11-27 Issue 2: https://code.google.com/p/muparser/issues/detail?id=2\r
- strTok.assign(m_strFormula.c_str(), iStart, m_iPos-iStart);\r
-\r
- if (m_iSynFlags & noVAL)\r
- Error(ecUNEXPECTED_VAL, m_iPos - (int)strTok.length(), strTok);\r
-\r
- a_Tok.SetVal(fVal, strTok);\r
- m_iSynFlags = noVAL | noVAR | noFUN | noBO | noINFIXOP | noSTR | noASSIGN;\r
- return true;\r
- }\r
- }\r
-\r
- return false;\r
- }\r
-\r
- //---------------------------------------------------------------------------\r
- /** \brief Check wheter a token at a given position is a variable token. \r
- \param a_Tok [out] If a variable token has been found it will be placed here.\r
- \return true if a variable token has been found.\r
- */\r
- bool ParserTokenReader::IsVarTok(token_type &a_Tok)\r
- {\r
- if (m_pVarDef->empty())\r
- return false;\r
-\r
- string_type strTok;\r
- int iEnd = ExtractToken(m_pParser->ValidNameChars(), strTok, m_iPos);\r
- if (iEnd==m_iPos)\r
- return false;\r
-\r
- varmap_type::const_iterator item = m_pVarDef->find(strTok);\r
- if (item==m_pVarDef->end())\r
- return false;\r
-\r
- if (m_iSynFlags & noVAR)\r
- Error(ecUNEXPECTED_VAR, m_iPos, strTok);\r
-\r
- m_pParser->OnDetectVar(&m_strFormula, m_iPos, iEnd);\r
-\r
- m_iPos = iEnd;\r
- a_Tok.SetVar(item->second, strTok);\r
- m_UsedVar[item->first] = item->second; // Add variable to used-var-list\r
-\r
- m_iSynFlags = noVAL | noVAR | noFUN | noBO | noINFIXOP | noSTR;\r
-\r
-// Zur Info hier die SynFlags von IsVal():\r
-// m_iSynFlags = noVAL | noVAR | noFUN | noBO | noINFIXOP | noSTR | noASSIGN; \r
- return true;\r
- }\r
-\r
- //---------------------------------------------------------------------------\r
- bool ParserTokenReader::IsStrVarTok(token_type &a_Tok)\r
- {\r
- if (!m_pStrVarDef || m_pStrVarDef->empty())\r
- return false;\r
-\r
- string_type strTok;\r
- int iEnd = ExtractToken(m_pParser->ValidNameChars(), strTok, m_iPos);\r
- if (iEnd==m_iPos)\r
- return false;\r
-\r
- strmap_type::const_iterator item = m_pStrVarDef->find(strTok);\r
- if (item==m_pStrVarDef->end())\r
- return false;\r
-\r
- if (m_iSynFlags & noSTR)\r
- Error(ecUNEXPECTED_VAR, m_iPos, strTok);\r
-\r
- m_iPos = iEnd;\r
- if (!m_pParser->m_vStringVarBuf.size())\r
- Error(ecINTERNAL_ERROR);\r
-\r
- a_Tok.SetString(m_pParser->m_vStringVarBuf[item->second], m_pParser->m_vStringVarBuf.size() );\r
-\r
- m_iSynFlags = noANY ^ ( noBC | noOPT | noEND | noARG_SEP);\r
- return true;\r
- }\r
-\r
-\r
- //---------------------------------------------------------------------------\r
- /** \brief Check wheter a token at a given position is an undefined variable. \r
-\r
- \param a_Tok [out] If a variable tom_pParser->m_vStringBufken has been found it will be placed here.\r
- \return true if a variable token has been found.\r
- \throw nothrow\r
- */\r
- bool ParserTokenReader::IsUndefVarTok(token_type &a_Tok)\r
- {\r
- string_type strTok;\r
- int iEnd( ExtractToken(m_pParser->ValidNameChars(), strTok, m_iPos) );\r
- if ( iEnd==m_iPos )\r
- return false;\r
-\r
- if (m_iSynFlags & noVAR)\r
- {\r
- // <ibg/> 20061021 added token string strTok instead of a_Tok.GetAsString() as the \r
- // token identifier. \r
- // related bug report:\r
- // http://sourceforge.net/tracker/index.php?func=detail&aid=1578779&group_id=137191&atid=737979\r
- Error(ecUNEXPECTED_VAR, m_iPos - (int)a_Tok.GetAsString().length(), strTok);\r
- }\r
-\r
- // If a factory is available implicitely create new variables\r
- if (m_pFactory)\r
- {\r
- value_type *fVar = m_pFactory(strTok.c_str(), m_pFactoryData);\r
- a_Tok.SetVar(fVar, strTok );\r
-\r
- // Do not use m_pParser->DefineVar( strTok, fVar );\r
- // in order to define the new variable, it will clear the\r
- // m_UsedVar array which will kill previousely defined variables\r
- // from the list\r
- // This is safe because the new variable can never override an existing one\r
- // because they are checked first!\r
- (*m_pVarDef)[strTok] = fVar;\r
- m_UsedVar[strTok] = fVar; // Add variable to used-var-list\r
- }\r
- else\r
- {\r
- a_Tok.SetVar((value_type*)&m_fZero, strTok);\r
- m_UsedVar[strTok] = 0; // Add variable to used-var-list\r
- }\r
-\r
- m_iPos = iEnd;\r
-\r
- // Call the variable factory in order to let it define a new parser variable\r
- m_iSynFlags = noVAL | noVAR | noFUN | noBO | noPOSTOP | noINFIXOP | noSTR;\r
- return true;\r
- }\r
-\r
-\r
- //---------------------------------------------------------------------------\r
- /** \brief Check wheter a token at a given position is a string.\r
- \param a_Tok [out] If a variable token has been found it will be placed here.\r
- \return true if a string token has been found.\r
- \sa IsOprt, IsFunTok, IsStrFunTok, IsValTok, IsVarTok, IsEOF, IsInfixOpTok, IsPostOpTok\r
- \throw nothrow\r
- */\r
- bool ParserTokenReader::IsString(token_type &a_Tok)\r
- {\r
- if (m_strFormula[m_iPos]!='"') \r
- return false;\r
-\r
- string_type strBuf(&m_strFormula[m_iPos+1]);\r
- std::size_t iEnd(0), iSkip(0);\r
-\r
- // parser over escaped '\"' end replace them with '"'\r
- for(iEnd=(int)strBuf.find( _T('\"') ); iEnd!=0 && iEnd!=string_type::npos; iEnd=(int)strBuf.find( _T('\"'), iEnd))\r
- {\r
- if (strBuf[iEnd-1]!='\\') break;\r
- strBuf.replace(iEnd-1, 2, _T("\"") );\r
- iSkip++;\r
- }\r
-\r
- if (iEnd==string_type::npos)\r
- Error(ecUNTERMINATED_STRING, m_iPos, _T("\"") );\r
-\r
- string_type strTok(strBuf.begin(), strBuf.begin()+iEnd);\r
-\r
- if (m_iSynFlags & noSTR)\r
- Error(ecUNEXPECTED_STR, m_iPos, strTok);\r
-\r
- m_pParser->m_vStringBuf.push_back(strTok); // Store string in internal buffer\r
- a_Tok.SetString(strTok, m_pParser->m_vStringBuf.size());\r
-\r
- m_iPos += (int)strTok.length() + 2 + (int)iSkip; // +2 wg Anführungszeichen; +iSkip für entfernte escape zeichen\r
- m_iSynFlags = noANY ^ ( noARG_SEP | noBC | noOPT | noEND );\r
-\r
- return true;\r
- }\r
-\r
- //---------------------------------------------------------------------------\r
- /** \brief Create an error containing the parse error position.\r
-\r
- This function will create an Parser Exception object containing the error text and its position.\r
-\r
- \param a_iErrc [in] The error code of type #EErrorCodes.\r
- \param a_iPos [in] The position where the error was detected.\r
- \param a_strTok [in] The token string representation associated with the error.\r
- \throw ParserException always throws thats the only purpose of this function.\r
- */\r
- void ParserTokenReader::Error( EErrorCodes a_iErrc, \r
- int a_iPos, \r
- const string_type &a_sTok) const\r
- {\r
- m_pParser->Error(a_iErrc, a_iPos, a_sTok);\r
- }\r
-\r
- //---------------------------------------------------------------------------\r
- void ParserTokenReader::SetArgSep(char_type cArgSep)\r
- {\r
- m_cArgSep = cArgSep;\r
- }\r
-\r
- //---------------------------------------------------------------------------\r
- char_type ParserTokenReader::GetArgSep() const\r
- {\r
- return m_cArgSep;\r
- }\r
-} // namespace mu\r
-\r
-#######################################################################\r
-# #\r
-# #\r
-# __________ #\r
-# _____ __ __\______ \_____ _______ ______ ____ _______ #\r
-# / \ | | \| ___/\__ \ \_ __ \/ ___/_/ __ \\_ __ \ #\r
-# | Y Y \| | /| | / __ \_| | \/\___ \ \ ___/ | | \/ #\r
-# |__|_| /|____/ |____| (____ /|__| /____ > \___ >|__| #\r
-# \/ \/ \/ \/ #\r
-# Fast math parser Library #\r
-# #\r
-# Copyright (C) 2014 Ingo Berg #\r
-# #\r
-# Web: muparser.beltoforion.de #\r
-# e-mail: muparser@beltoforion.de #\r
-# #\r
-# #\r
-#######################################################################\r
- \r
-\r
-History:\r
---------\r
-\r
-Rev 2.2.4: 02.10.2014\r
----------------------\r
- Changes:\r
- * explicit positive sign allowed\r
-\r
- Bugfixes:\r
- * Fix for Issue 6 (https://code.google.com/p/muparser/issues/detail?id=6)\r
- * String constants did not work properly. Using more than a single one \r
- was impossible.\r
- * Project Files for VS2008 and VS2010 removed from the repository \r
- * Fix for Issue 4 (https://code.google.com/p/muparser/issues/detail?id=4)\r
- * Fix for VS2013 64 bit build option\r
- * return type of ParserError::GetPos changed to int\r
- * OpenMP support enabled in the VS2013 project files and precompiled windows DLL's\r
- * Bulkmode did not evaluate properly if "=" and "," operator was used in the expression\r
- \r
-Rev 2.2.3: 22.12.2012\r
----------------------\r
-\r
- Removed features:\r
- * build files for msvc2005, borland and watcom compiler were removed\r
-\r
- Bugfixes:\r
- * Bugfix for Intel Compilers added: The power operator did not work properly \r
- with Intel C++ composer XE 2011.\r
- (see https://sourceforge.net/projects/muparser/forums/forum/462843/topic/5117983/index/page/1)\r
- * Issue 3509860: Callbacks of functions with string parameters called twice\r
- (see http://sourceforge.net/tracker/?func=detail&aid=3509860&group_id=137191&atid=737979)\r
- * Issue 3570423: example1 shows slot number in hexadecimal\r
- (see https://sourceforge.net/tracker/?func=detail&aid=3570423&group_id=137191&atid=737979)\r
- * Fixes for compiling with the "MUP_MATH_EXCEPTIONS" macro definition:\r
- - division by zero in constant expressions was reported with the code "ec_GENERIC" \r
- instead of "ecDIV_BY_ZERO"\r
- - added throwing of "ecDOMAIN_ERROR" to sqrt and log functions\r
-\r
-\r
-Rev 2.2.2: 18.02.2012\r
----------------------\r
- Bugfixes:\r
- * Optimizer did'nt work properly for division:\r
- (see https://sourceforge.net/projects/muparser/forums/forum/462843/topic/5037825)\r
-\r
-Rev 2.2.1: 22.01.2012\r
----------------------\r
- Bugfixes:\r
- * Optimizer bug in 64 bit systems fixed\r
- (see https://sourceforge.net/projects/muparser/forums/forum/462843/topic/4977977/index/page/1)\r
- \r
-Rev 2.2.0: 22.01.2012\r
----------------------\r
- Improvements:\r
- * Optimizer rewritten and improved. In general: more optimizations are \r
- now applied to the bytecode. The downside is that callback Functions \r
- can no longer be flagged as non-optimizable. (The flag is still present \r
- but ignored) This is necessary since the optimizer had to call the \r
- functions in order to precalculate the result (see Bugfixes). These calls \r
- posed a problems for callback functions with side effects and if-then-else \r
- clauses in general since they undermined the shortcut evaluation prinziple.\r
-\r
- Bugfixes:\r
- * Infix operators where not properly detected in the presence of a constant \r
- name starting with an underscore which is a valid character for infix \r
- operators too (i.e. "-_pi").\r
- * Issue 3463353: Callback functions are called twice during the first call to eval.\r
- * Issue 3447007: GetUsedVar unnecessaryly executes callback functions.\r
-\r
-\r
-Rev 2.1.0: 19.11.2011\r
----------------------\r
- New feature:\r
- * Function atan2 added\r
-\r
- Bugfixes:\r
- * Issue 3438380: Changed behaviour of tellg with GCC >4.6 led to failures \r
- in value detection callbacks.\r
- * Issue 3438715: only "double" is a valid MUP_BASETYPE \r
- MUP_BASETYPE can now be any of:\r
- float, \r
- double, \r
- long double, \r
- short, \r
- unsigned short, \r
- unsigned int, \r
- long,\r
- unsigned long. \r
- Previousely only floating point types were allowed. \r
- Using "int" is still not allowed!\r
- * Compiler issues with GCC 4.6 fixed\r
- * Custom value recognition callbacks added with AddValIdent had lower\r
- priority than built in functions. This was causing problems with \r
- hex value recognition since detection of non hex values had priority\r
- over the detection of hex values. The "0" in the hex prefix "0x" would \r
- be read as a separate non-hex number leaving the rest of the expression\r
- unparseable.\r
-\r
-Rev 2.0.0: 04.09.2011\r
----------------------\r
-This release introduces a new version numbering scheme in order to make\r
-future changes in the ABI apparent to users of the library. The number is\r
-now based on the SONAME property as used by GNU/Linux. \r
-\r
- Changes:\r
- * Beginning with this version all version numbers will be SONAME compliant\r
- * Project files for MSVC2010 added\r
- * Project files for MSVC2003 removed\r
- * Bytecode parsing engine cleaned up and rewritten\r
- * Retrieving all results of expressions made up of comma separate \r
- subexpressions is now possible with a new Eval overload.\r
- * Callback functions with fixed number of arguments can now have up to 10 \r
- Parameters (previous limit was 5)\r
-\r
- New features:\r
- * ternary if-then-else operator added (C++ like; "(...) ? ... : ..." )\r
- * new intrinsic binary operators: "&&", "||" (logical and, or)\r
- * A new bulkmode allows submitting large arrays as variables to compute large \r
- numbers of expressions with a single call. This can drastically improve \r
- parsing performance when interfacing the library from managed languages like \r
- C#. (It doesn't bring any performance benefit for C++ users though...)\r
-\r
- Removed features:\r
- * intrinsic "and", "or" and "xor" operators have been removed. I'd like to let \r
- users the freedom of defining them on their own versions (either as logical or bitwise \r
- operators).\r
- * Implementation for complex numbers removed. This was merely a hack. If you \r
- need complex numbers try muParserX which provides native support for them.\r
- (see: http://beltoforion.de/muparserx/math_expression_parser_en.html)\r
-\r
- Bugfixes:\r
- * User defined operators could collide with built in operators that entirely \r
- contained their identifier. i.e. user defined "&" would not work with the built \r
- in "&&" operator since the user defined operator was detected with a higher \r
- priority resulting in a syntax error.\r
- * Detection of unknown variables did not work properly in case a postfix operator\r
- was defined which was part of the undefined variable.\r
- i.e. If a postfix operator "m" was defined expressions like "multi*1.0" did\r
- not detect "multi" as an undefined variable.\r
- (Reference: http://sourceforge.net/tracker/index.php?func=detail&aid=3343891&group_id=137191&atid=737979)\r
- * Postfix operators sharing the first few characters were causing bogus parsing exception.\r
- (Reference: https://sourceforge.net/tracker/?func=detail&aid=3358571&group_id=137191&atid=737979)\r
-\r
-Rev 1.34: 04.09.2010\r
---------------------\r
- Changes:\r
- * The prefix needed for parsing hex values is now "0x" and no longer "$". \r
- * AddValIdent reintroduced into the DLL interface\r
-\r
- New features:\r
- * The associativity of binary operators can now be changed. The pow operator \r
- is now right associative. (This is what Mathematica is using) \r
- * Seperator can now be used outside of functions. This allows compound \r
- expressions like:\r
- "a=10,b=20,c=a*b" The last "argument" will be taken as the return value\r
-\r
- Bugfixes: \r
- * The copy constructor did not copy binary operator definitions. Those were lost\r
- in the copied parser instance.\r
- * Mixing special characters and alphabetic characters in binary operator names \r
- led to inconsistent parsing behaviour when parsing expressions like "a ++ b" \r
- and "a++b" when "++" is defined as a binary operator. Binary operators must \r
- now consist entirely of special characters or of alphabetic ones.\r
- (original bug report: https://sourceforge.net/projects/muparser/forums/forum/462843/topic/3696881/index/page/1)\r
- * User defined operators were not exactly handled like built in operators. This \r
- led to inconsistencies in expression evaluation when using them. The results \r
- differed due to slightly different precedence rules.\r
- * Using empty string arguments ("") would cause a crash of muParser\r
-\r
-\r
-Rev 1.32: 29.01.2010 \r
---------------------\r
-\r
- Changes:\r
- * "example3" renamed to "example2"\r
- * Project/Makefiles files are now provided for:\r
- - msvc2003 \r
- - msvc2005 \r
- - msvc2008\r
- - watcom (makefile)\r
- - mingw (makefile)\r
- - bcc (makefile)\r
- * Project files for borland cpp builder were removed\r
-\r
-\r
- New features:\r
- * Added function returning muparsers version number\r
- * Added function for resetting the locale\r
-\r
-\r
- Bugfixes: \r
- * Changes example1 in order to fix issues with irritating memory leak reports. \r
- Added conditional code for memory leak detection with MSVC in example1.\r
- (see: http://www.codeproject.com/KB/recipes/FastMathParser.aspx?msg=3286367#xx3286367xx)\r
- * Fixed some warnings for gcc\r
-\r
-\r
-\r
-Rev 1.31cp: 15.01.2010 (Maintainance release for CodeProject)\r
-----------------------\r
-\r
- Changes:\r
- * Archive structure changed\r
- * C# wrapper added\r
- * Fixed issued that prevented compiling with VS2010 Beta2\r
- \r
-\r
-Rev 1.30: 09.06.2008\r
---------------------\r
-\r
- Changes:\r
- * Epsilon of the numerical differentiation algorithm changed to allow greater accuracy.\r
-\r
- New features:\r
- * Setting thousands separator and decimal separator is now possible\r
-\r
- Bugfixes:\r
- * The dll interface did not provide a callback for functions without any arguments. \r
- \r
-\r
-Rev 1.29: Januar 2008\r
----------------------\r
-\r
- Unrelease Version available only via SVN.\r
-\r
-\r
-Rev 1.28: 02. July, 2007\r
----------------------------\r
- \r
- Library changes:\r
- * Interface for the dynamic library changed and extended to create an interface \r
- using pure C functions only. \r
- * mupInit() removed\r
-\r
- Build system:\r
- * MSVC7 Project files removed in favor of MSVC8\r
-\r
- Bugfixes:\r
- * The dynamic library did not build on other systems than linux due to a misplaced \r
- preprocessor definition. This is fixed now.\r
-\r
-\r
-Rev 1.27:\r
----------------------------\r
-\r
- Build system:\r
- * Modified build\ directory layout introducing some subfolders \r
- for the various IDE supported\r
- * Project files for BCB and MSVC7 added\r
- * Switched to use bakefile 0.2.1 which now correctly creates the \r
- "make uninstall" target for autoconf's Makefile.in\r
- * Now the library debug builds are named "muparserd" instead of "muparser"\r
- to allow multiple mixed release/debug builds to coexist; so e.g. on Windows\r
- when building with DEBUG=1, you'll get "muparserd.lib" instead of "muparser.lib"\r
-\r
- New Features:\r
- * Factory functions can now take a user defined pointer\r
- * String functions can now be used with up to two additional \r
- double parameters\r
- * Support for UNICODE character types added\r
- * Infix operator priority can now be changed by the user\r
-\r
- Bugfixes:\r
- * An internal error was raised when evaluating an empty \r
- expressions\r
- * The error message raised in case of name collisions between \r
- implicitely defined variables and postfix operators did contain\r
- misleading data.\r
-\r
-\r
-Rev 1.26: (unofficial release)\r
-------------------------------\r
-\r
- New Features:\r
- * Unary operator precedence can now be changed by the user.\r
-\r
-\r
-Rev 1.25: 5. February, 2006\r
----------------------------\r
-\r
- Build system: (special thanks to Francesco Montorsi for implementing it!)\r
- * created a bakefile-based build system which adds support for the following win32 compilers:\r
- -> MS visual C++ (6 and .NET)\r
- -> BorlandC++ (5 or greater)\r
- -> Mingw32 (tested with gcc 3.2)\r
- -> Watcom (not tested)\r
- and for GCC on Unix (using a standard autoconf's configure script).\r
-\r
- Compatibility improvements:\r
- * fixed some small warnings when using -Wall with GCC on Unix\r
- * added inclusion guards for win32-specific portions of code\r
- * added fixes that remove compiler warnings on Intel C++ and the Solaris C++ compiler.\r
-\r
-\r
-Rev 1.24: 29. October, 2005\r
----------------------------\r
-\r
-Changes:\r
-\r
- Compatibility improvements:\r
- * parser now works on 64 bit compilers\r
- * (bytecode base datatype can now be changed freely)\r
-\r
-\r
-Rev 1.23: 19. October, 2005\r
----------------------------\r
-\r
-Changes:\r
-\r
- Bugfixes:\r
- * Variable factory examples in Example1.cpp and Example3.cpp contained a subtle bug.\r
-\r
- New features:\r
- * Added a MSVC6 project file and introduced muParserFixes.h in order to make it compile with MSVC6\r
-\r
-\r
-Rev 1.22: October, 2005\r
------------------------\r
-\r
-Release notes:\r
-\r
-All features of Version 1.22 are similar to Version 1.21. Version 1.22 fixes a compilation issue with\r
-gcc 4.0. In order to fix this issue I rewrote part of the library to remove some unnecessary templates. \r
-This should make the code cleaner. The Borland Project files were removed. If you want to use it \r
-with Borland either use the dll version or create your own project files. I can't support it since I don't \r
-have this compiler at hand.\r
-\r
-Changes:\r
-\r
- Project Changes:\r
- * Borland project files removed\r
- (The code should still compile with BCB but I cant provide you with project files)\r
-\r
- Internal Changes:\r
- * unnecessary template files have been removed:\r
- - new files: muParserError.cpp, muParserTokenReader.cpp, muParserCallback.cpp\r
- - removed Files: muIParserTypes.h\r
-\r
-\r
-Rev 1.2 / 1.21: April, 2005\r
----------------------------\r
-\r
-Release Notes:\r
-First of all the interface has changed so this version is not backwards compatible.\r
-After receiving a couple of questions about it, this version features support for\r
-user defined binary operators. Consequently the built in operators can now be \r
-turned off, thus you can deactivate them and write complete customized parser\r
-subclasses that only contain the functionality you want. Another new feature is \r
-the introduction of callback functions taking string arguments, implicit \r
-generation of variables and the Assignement operator.\r
-\r
- Functionality\r
- * New built in operator: xor; Logical xor.\r
- * New built in operator: Assignement operator; Defining variables in terms of \r
- other variables/constants\r
- * New feature: Strings as arguments for callback functions\r
- * New feature: User defined binary operators\r
- * New feature: ParserInt a class with a sample implementation for\r
- integer numbers.\r
- * New feature: Callbacks to value regognition functions.\r
-\r
- * Removed: all predefined postfix operators have been removed.\r
- * New project file: Now comes with a ready to use windows DLL.\r
- * New project file: Makefile for cygwin now included.\r
- * New example: Example3 shows usage of the DLL.\r
-\r
- Interface changes\r
- * New member function: DefineOprt For adding user defined binary operators.\r
- * New member function: EnableBuiltInOprt(bool) Enables/Disables built in \r
- binary operators.\r
- * New member function: AddValIdent(...) to add callbacks for custom value \r
- recognition functions.\r
- * Removed: SetVar(), SetConst().\r
- * Renamed: Most interface functions have been renamed\r
- * Changed: The type for multiargument callbacks multfun_type has changed. \r
- It no longer takes a std::vector as input.\r
-\r
- Internal changes\r
- * new class muParserTokenReader.h encapsulates the token identification \r
- and token assignement.\r
- * Internal handling of function callbacks unified as a result the performance \r
- of the bytecode evaluation increased.\r
-\r
-\r
-Rev 1.10 : December 30, 2004\r
-----------------------------\r
-\r
-Release Notes:\r
-This version does not contain major new feature compared to V1.07 but its internal structure has\r
-changed significantly. The String parsing routine is slower than the one of V1.07 but bytecode\r
-parsing is equally fast. On the other hand the error messages of V1.09 are more flexible and you\r
-can change its value datatype. It should work on 64-bit systems. For this reason I supply both\r
-versions for download. If you use V1.07 and are happy with it there is no need for updating\r
-your version.\r
-\r
- * New example program: Archive now contains two demo programs: One for standard C++ and one for\r
- managed C++.\r
- * New member function: RemoveVar(...) can be used for removing a single variable from the internal \r
- storage.\r
- * New member function: GetVar() can be used for querying the variable names and pointers of all\r
- variables defined in the parser.\r
- * New member function: GetConst() can be used for querying all defined constants and their values.\r
- * New member function: GetFunDef() can be used for querying all defined functions and the number of\r
- arguments they expect.\r
- * Internal structure changed; hanging base datatype at compile time is now possible.\r
- * Bugfix: Postfix operator parsing could fail in certain cases; This has been fixed now.\r
- * Bugfix: Variable names must will now be tested if they conflict with constant or function names.\r
- * Internal change: Removed most dependencies from the C-string libraries.\r
- * Internal change: Bytecode is now stored in a separate class: ParserByteCode.h\r
- * Internal change: GetUsedVar() does no longer require that variables are defined at time of call.\r
- * Internal change: Error treatment changed. ParserException is no longer derived from\r
- std::runtime_error; Internal treatment of Error messages changed.\r
- * New functions in Parser interface: ValidNameChars(), ValidOprtChars() and ValidPrefixOprtChars()\r
- they are used for defining the charset allowed for variable-, operator- and\r
- function names.\r
-\r
-\r
-Rev 1.09 : November 20, 2004\r
-----------------------------\r
-\r
- * New member function: RemoveVar(...) can be used for removing a single variable from the internal \r
- storage.\r
- * Internal structure changed; changing base datatype at compile time is now possible.\r
- * Bug fix: Postfix operator parsing could fail in certain cases; This has been fixed now.\r
- * Internal change: Removed most dependencies from the C-string libraries.\r
- * Internal change: Bytecode is now stored in a seperate class: ParserByteCode.h.\r
- * Internal change: GetUsedVar() does no longer require that variables are defined at time of call.\r
- * Internal change: Error treatment changed. ParserException is no longer derived from \r
- std::runtime_error; Internal treatment of Error messages changed.\r
- * New functions in Parser interface; ValidNameChars(), ValidOprtChars() and ValidPrefixOprtChars()\r
- they are used for defining the charset allowed for variable-, operator- and function names.\r
-\r
-\r
-Rev 1.08 : November, 2004 \r
--------------------------\r
-\r
- * unpublished; experimental template version with respect to data type and underlying string\r
- type (string <-> widestring). The idea was dropped...\r
-\r
-\r
-Rev 1.07 : September 4 2004\r
----------------------------\r
-\r
- * Improved portability; Changes to make life for MSVC 6 user easier, there are probably still some\r
- issues left.\r
- * Improved portability; Changes in order to allow compiling on BCB.\r
- * New function; value_type Diff(value_type *a_Var, value_type a_fPos) 4th order Differentiation with\r
- respect to a certain variable; added in muParser.h.\r
-\r
-\r
-Rev 1.06 : August 20 2004\r
--------------------------\r
-\r
- * Volatile functions added; All overloaded AddFun(...) functions can now take a third parameter\r
- indicating that the function can not be optimized.\r
- * Internal changes: muParserStack.h simplified; refactorings\r
- * Parser is now distributed under the MIT License; all comments changed accordingly.\r
-\r
-\r
-Rev 1.05 : August 20 2004\r
--------------------------\r
-\r
- * Variable/constant names will now be checked for invalid characters.\r
- * Querying the names of all variables used in an expression is now possible; new function: GetUsedVar().\r
- * Disabling bytecode parsing is now possible; new function: EnableByteCode(bool bStat).\r
- * Predefined functions with variable number of arguments added: sum, avg, min, max.\r
- * Unary prefix operators added; new functions: AddPrefixOp(...), ClearPrefixOp().\r
- * Postfix operator interface names changed; new function names: AddPostfixOp(...), ClearPostfixOp().\r
- * Hardcoded sign operators removed in favor of prefix operators; bytecode format changed accordingly.\r
- * Internal changes: static array removed in Command code calculation routine; misc. changes.\r
-\r
-\r
-Rev 1.04 : August 16 2004\r
--------------------------\r
-\r
- * Support for functions with variable number of arguments added.\r
- * Internal structure changed; new: ParserBase.h, ParserBase.cpp; removed: ParserException.h;\r
- changed: Parser.h, Parser.cpp.\r
- * Bug in the bytecode calculation function fixed (affected the unary minus operator).\r
- * Optimizer can be deactivated; new function: EnableOptimizer(bool bStat).\r
-\r
-\r
-Rev 1.03 : August 10 2004\r
--------------------------\r
-\r
- * Support for user-defined unary postfix operators added; new functions: AddPostOp(), InitPostOp(),\r
- ClearPostOp().\r
- * Minor changes to the bytecode parsing routine.\r
- * User defined functions can now have up to four parameters.\r
- * Performance optimized: simple formula optimization added; (precalculation of constant parts of the\r
- expression).\r
- * Bug fixes: Multi-arg function parameters, constant name lookup and unary minus did not work properly.\r
-\r
-\r
-Rev 1.02 : July 30 2004\r
------------------------\r
-\r
- * Support for user defined constants added; new functions: InitConst(), AddConst(), SetConst(),\r
- ClearConst().\r
- * Single variables can now be added using AddVar(); you have now the choice of adding them either\r
- one by one or all at the same time using SetVar(const varmap_type &a_vVar).\r
- * Internal handling of variables changed, is now similar to function handling.\r
- * Virtual destructor added; InitFun(), InitConst() are now virtual too thus making it possible to\r
- derive new parsers with a modified set of default functions and constants.\r
- * Support for user defined functions with 2 or 3 parameters added; bytecode format changed to hold\r
- function parameter count.\r
-\r
-\r
-Rev 1.01 : July 23 2004\r
------------------------\r
-\r
- * Support for user defined functions has been added; new functions: AddFun(), ClearFun(),\r
- InitFunctions().\r
- * Built in constants have been removed; the parser contained undocumented built in\r
- constants pi, e.\r
- There was the possibility of name conflicts with user defined variables.\r
- * Setting multiple variables with SetVar can now be done with a map of names and pointers as the only\r
- argument. For this reason, a new type Parser::varmap_type was added. The old version that took 3\r
- arguments (array of names, array of pointers, and array length) is now marked as deprecated.\r
- * The names of logarithm functions have changed. The new names are: log2 for base 2, log10 or log for\r
- base 10, and ln for base e.\r
-\r
-\r
-Rev 1.00 : July 21 2004\r
------------------------\r
-\r
- * Initial release\r
+
+ _____ __ _____________ _______ ______ ___________
+ / \| | \____ \__ \\_ __ \/ ___// __ \_ __ \
+ | 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
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/
--- /dev/null
+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.
--- /dev/null
+/*
+
+ _____ __ _____________ _______ ______ ___________
+ / \| | \____ \__ \\_ __ \/ ___// __ \_ __ \
+ | 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 <vector>
+
+//--- 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
+
--- /dev/null
+/*
+
+ _____ __ _____________ _______ ______ ___________
+ / \| | \____ \__ \\_ __ \/ ___// __ \_ __ \
+ | 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 <cmath>
+#include <string>
+#include <iostream>
+#include <map>
+#include <memory>
+#include <locale>
+#include <limits.h>
+
+//--- 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<value_type> valbuf_type;
+
+ /** \brief Type for a vector of strings. */
+ typedef std::vector<string_type> stringbuf_type;
+
+ /** \brief Typedef for the token reader. */
+ typedef ParserTokenReader token_reader_type;
+
+ /** \brief Type used for parser tokens. */
+ typedef ParserToken<value_type, string_type> 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<typename T>
+ 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<typename T>
+ 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<int>(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 TChar>
+ class change_dec_sep : public std::numpunct<TChar>
+ {
+ public:
+
+ explicit change_dec_sep(char_type cDecSep, char_type cThousandsSep = 0, int nGroup = 3)
+ :std::numpunct<TChar>()
+ ,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<token_type>& a_stOpt, std::stack<token_type>& a_stVal) const;
+ void ApplyBinOprt(std::stack<token_type>& a_stOpt, std::stack<token_type>& a_stVal) const;
+ void ApplyIfElse(std::stack<token_type>& a_stOpt, std::stack<token_type>& a_stVal) const;
+ void ApplyFunc(std::stack<token_type>& a_stOpt, std::stack<token_type>& a_stVal, int iArgCount) const;
+
+ token_type ApplyStrFunc(const token_type& a_FunTok, const std::vector<token_type>& 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<token_type >& a_stVal, const std::stack<token_type >& 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<token_reader_type> 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
--- /dev/null
+/*
+
+ _____ __ _____________ _______ ______ ___________
+ / \| | \____ \__ \\_ __ \/ ___// __ \_ __ \
+ | 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 <string>
+#include <stack>
+#include <vector>
+
+#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<value_type, string_type> token_type;
+
+ /** \brief Token vector for storing the RPN. */
+ typedef std::vector<SToken> 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
+
+
--- /dev/null
+/*
+
+ _____ __ _____________ _______ ______ ___________
+ / \| | \____ \__ \\_ __ \/ ___// __ \_ __ \
+ | 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<string_type, ParserCallback> funmap_type;
+
+} // namespace mu
+
+#endif
+
--- /dev/null
+/*
+
+ _____ __ _____________ _______ ______ ___________
+ / \| | \____ \__ \\_ __ \/ ___// __ \_ __ \
+ | 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 <iostream>
+#include <string>
+#include <sstream>
+#include <map>
+
+#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<char_type, std::char_traits<char_type>, std::allocator<char_type> > stringstream_type;
+
+ // Data container types
+
+ /** \brief Type used for storing variables. */
+ typedef std::map<string_type, value_type*> varmap_type;
+
+ /** \brief Type used for storing constants. */
+ typedef std::map<string_type, value_type> valmap_type;
+
+ /** \brief Type for assigning a string name to an index in the internal string table. */
+ typedef std::map<string_type, std::size_t> 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
+
--- /dev/null
+/*
+
+ _____ __ _____________ _______ ______ ___________
+ / \| | \____ \__ \\_ __ \/ ___// __ \_ __ \
+ | 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 <stdexcept>
+#include <string>
+#include <sstream>
+#include <vector>
+#include <memory>
+
+#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<string_type> 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
+
--- /dev/null
+/*
+
+ _____ __ _____________ _______ ______ ___________
+ / \| | \____ \__ \\_ __ \/ ___// __ \_ __ \
+ | 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
+
+
--- /dev/null
+/*
+
+ _____ __ _____________ _______ ______ ___________
+ / \| | \____ \__ \\_ __ \/ ___// __ \_ __ \
+ | 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 <vector>
+
+
+/** \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 TChar>
+ class change_dec_sep : public std::numpunct<TChar>
+ {
+ public:
+
+ explicit change_dec_sep(char_type cDecSep, char_type cThousandsSep = 0, int nGroup = 3)
+ :std::numpunct<TChar>()
+ , 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
+
--- /dev/null
+/*
+
+ _____ __ _____________ _______ ______ ___________
+ / \| | \____ \__ \\_ __ \/ ___// __ \_ __ \
+ | 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 <cmath>
+#include "muParserError.h"
+
+
+namespace mu
+{
+ //-----------------------------------------------------------------------------------------------
+ //
+ // Compile time type detection
+ //
+ //-----------------------------------------------------------------------------------------------
+
+ /** \brief A class singling out integer types at compile time using
+ template meta programming.
+ */
+ template<typename T>
+ struct TypeInfo
+ {
+ static bool IsInteger() { return false; }
+ };
+
+ template<>
+ struct TypeInfo<char>
+ {
+ static bool IsInteger() { return true; }
+ };
+
+ template<>
+ struct TypeInfo<short>
+ {
+ static bool IsInteger() { return true; }
+ };
+
+ template<>
+ struct TypeInfo<int>
+ {
+ static bool IsInteger() { return true; }
+ };
+
+ template<>
+ struct TypeInfo<long>
+ {
+ static bool IsInteger() { return true; }
+ };
+
+ template<>
+ struct TypeInfo<unsigned char>
+ {
+ static bool IsInteger() { return true; }
+ };
+
+ template<>
+ struct TypeInfo<unsigned short>
+ {
+ static bool IsInteger() { return true; }
+ };
+
+ template<>
+ struct TypeInfo<unsigned int>
+ {
+ static bool IsInteger() { return true; }
+ };
+
+ template<>
+ struct TypeInfo<unsigned long>
+ {
+ 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<typename T>
+ 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
--- /dev/null
+/*
+
+ _____ __ _____________ _______ ______ ___________
+ / \| | \____ \__ \\_ __ \/ ___// __ \_ __ \
+ | 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 <string>
+#include <cstdlib>
+#include <cstdint>
+#include <numeric> // 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<std::intptr_t>(data);
+ }
+
+ static value_type FunUd1(void* data, value_type v)
+ {
+ return reinterpret_cast<std::intptr_t>(data) + v;
+ }
+
+ static value_type FunUd2(void* data, value_type v1, value_type v2)
+ {
+ return reinterpret_cast<std::intptr_t>(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<std::intptr_t>(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<std::intptr_t>(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<std::intptr_t>(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<testfun_type> 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
+
--- /dev/null
+/*
+
+ _____ __ _____________ _______ ______ ___________
+ / \| | \____ \__ \\_ __ \/ ___// __ \_ __ \
+ | 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 <string>
+#include <stack>
+#include <vector>
+#include <memory>
+#include <utility>
+#include <type_traits>
+#include <cstddef>
+
+#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 <std::size_t NbParams> 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 <std::size_t NbParams, typename... Args>
+ 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<typename TplCallType<NbParams>::fun_type>(_pRawFun);
+ return (*fun_typed_ptr)(std::forward<Args>(args)...);
+ }
+ else
+ {
+ auto fun_userdata_typed_ptr = reinterpret_cast<typename TplCallType<NbParams>::fun_userdata_type>(_pRawFun);
+ return (*fun_userdata_typed_ptr)(_pUserData, std::forward<Args>(args)...);
+ }
+ }
+
+ template <std::size_t NbParams, typename... Args>
+ 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<typename TplCallType<NbParams>::bulkfun_type>(_pRawFun);
+ return (*bulkfun_typed_ptr)(std::forward<Args>(args)...);
+ } else {
+ auto bulkfun_userdata_typed_ptr = reinterpret_cast<typename TplCallType<NbParams>::bulkfun_userdata_type>(_pRawFun);
+ return (*bulkfun_userdata_typed_ptr)(_pUserData, std::forward<Args>(args)...);
+ }
+ }
+
+ value_type call_multfun(const value_type* a_afArg, int a_iArgc) const
+ {
+ if (_pUserData == nullptr) {
+ auto multfun_typed_ptr = reinterpret_cast<multfun_type>(_pRawFun);
+ return (*multfun_typed_ptr)(a_afArg, a_iArgc);
+ } else {
+ auto multfun_userdata_typed_ptr = reinterpret_cast<multfun_userdata_type>(_pRawFun);
+ return (*multfun_userdata_typed_ptr)(_pUserData, a_afArg, a_iArgc);
+ }
+ }
+
+ template <std::size_t NbParams, typename... Args>
+ 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<typename TplCallType<NbParams>::strfun_type>(_pRawFun);
+ return (*strfun_typed_ptr)(std::forward<Args>(args)...);
+ }
+ else
+ {
+ auto strfun_userdata_typed_ptr = reinterpret_cast<typename TplCallType<NbParams>::strfun_userdata_type>(_pRawFun);
+ return (*strfun_userdata_typed_ptr)(_pUserData, std::forward<Args>(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<generic_callable_type>::value, "generic_callable_type shall be trivial");
+ static_assert(std::is_standard_layout<generic_callable_type>::value, "generic_callable_type shall have standard layout");
+ // C++17: static_assert(std::is_aggregate<generic_callable_type>::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:
+ <ul>
+ <li>value</li>
+ <li>variable</li>
+ <li>function with numerical arguments</li>
+ <li>functions with a string as argument</li>
+ <li>prefix operators</li>
+ <li>infix operators</li>
+ <li>binary operator</li>
+ </ul>
+ */
+ template<typename TBase, typename TString>
+ 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<ParserCallback> 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<int>(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:
+ <ul>
+ <li>cmFUNC</li>
+ <li>cmSTRFUNC</li>
+ <li>cmPOSTOP</li>
+ <li>cmINFIXOP</li>
+ <li>cmOPRT_BIN</li>
+ </ul>
+ \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
--- /dev/null
+/*
+
+ _____ __ _____________ _______ ______ ___________
+ / \| | \____ \__ \\_ __ \/ ___// __ \_ __ \
+ | 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 <cstdio>
+#include <cstring>
+#include <list>
+#include <map>
+#include <memory>
+#include <stack>
+#include <string>
+
+#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<value_type, string_type> 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<identfun_type> m_vIdentFun; ///< Value token identification function
+ varmap_type m_UsedVar;
+ value_type m_fZero; ///< Dummy value of zero, referenced by undefined variables
+
+ std::stack<int> m_bracketStack;
+
+ token_type m_lastTok;
+ char_type m_cArgSep; ///< The character used for separating function arguments
+ };
+} // namespace mu
+
+#endif
+
+
--- /dev/null
+/*
+
+ _____ __ _____________ _______ ______ ___________
+ / \| | \____ \__ \\_ __ \/ ___// __ \_ __ \
+ | 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 <cmath>
+#include <algorithm>
+#include <numeric>
+
+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<mu::value_type>::IsInteger())
+ {
+ // When setting MUP_BASETYPE to an integer type
+ // Place functions for dealing with integer values here
+ // ...
+ // ...
+ // ...
+ }
+ else
+ {
+ // trigonometric functions
+ DefineFun(_T("sin"), MathImpl<value_type>::Sin);
+ DefineFun(_T("cos"), MathImpl<value_type>::Cos);
+ DefineFun(_T("tan"), MathImpl<value_type>::Tan);
+ // arcus functions
+ DefineFun(_T("asin"), MathImpl<value_type>::ASin);
+ DefineFun(_T("acos"), MathImpl<value_type>::ACos);
+ DefineFun(_T("atan"), MathImpl<value_type>::ATan);
+ DefineFun(_T("atan2"), MathImpl<value_type>::ATan2);
+ // hyperbolic functions
+ DefineFun(_T("sinh"), MathImpl<value_type>::Sinh);
+ DefineFun(_T("cosh"), MathImpl<value_type>::Cosh);
+ DefineFun(_T("tanh"), MathImpl<value_type>::Tanh);
+ // arcus hyperbolic functions
+ DefineFun(_T("asinh"), MathImpl<value_type>::ASinh);
+ DefineFun(_T("acosh"), MathImpl<value_type>::ACosh);
+ DefineFun(_T("atanh"), MathImpl<value_type>::ATanh);
+ // Logarithm functions
+ DefineFun(_T("log2"), MathImpl<value_type>::Log2);
+ DefineFun(_T("log10"), MathImpl<value_type>::Log10);
+ DefineFun(_T("log"), MathImpl<value_type>::Log);
+ DefineFun(_T("ln"), MathImpl<value_type>::Log);
+ // misc
+ DefineFun(_T("exp"), MathImpl<value_type>::Exp);
+ DefineFun(_T("sqrt"), MathImpl<value_type>::Sqrt);
+ DefineFun(_T("sign"), MathImpl<value_type>::Sign);
+ DefineFun(_T("rint"), MathImpl<value_type>::Rint);
+ DefineFun(_T("abs"), MathImpl<value_type>::Abs);
+ // Functions with variable number of arguments
+ DefineFun(_T("sum"), MathImpl<value_type>::Sum);
+ DefineFun(_T("avg"), MathImpl<value_type>::Avg);
+ DefineFun(_T("min"), MathImpl<value_type>::Min);
+ DefineFun(_T("max"), MathImpl<value_type>::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<value_type>::CONST_PI);
+ DefineConst(_T("_e"), MathImpl<value_type>::CONST_E);
+ }
+
+ //---------------------------------------------------------------------------
+ /** \brief Initialize operators.
+
+ By default only the unary minus operator is added.
+ */
+ void Parser::InitOprt()
+ {
+ DefineInfixOprt(_T("-"), MathImpl<value_type>::UnaryMinus);
+ DefineInfixOprt(_T("+"), MathImpl<value_type>::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<double>::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
--- /dev/null
+/*
+
+ _____ __ _____________ _______ ______ ___________
+ / \| | \____ \__ \\_ __ \/ ___// __ \_ __ \
+ | 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 <algorithm>
+#include <cmath>
+#include <memory>
+#include <vector>
+#include <deque>
+#include <sstream>
+#include <locale>
+#include <cassert>
+#include <cctype>
+
+#ifdef MUP_USE_OPENMP
+
+#include <omp.h>
+
+#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<char_type>('.'));
+
+ 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<char_type> >(s_locale).thousands_sep();
+ s_locale = std::locale(std::locale("C"), new change_dec_sep<char_type>(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<char_type> >(s_locale).decimal_point();
+ s_locale = std::locale(std::locale("C"), new change_dec_sep<char_type>(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<char_type>('.'));
+ 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<numpunct<char_type> >(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<token_type>& 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<token_type>& a_stOpt, std::stack<token_type>& 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<token_type> 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<token_type>& a_stOpt, std::stack<token_type>& 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<token_type>& a_stOpt, std::stack<token_type>& 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<token_type>& stOpt, std::stack<token_type>& 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<value_type>::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;
+
+ // <ibg 2020-06-08> 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);
+ // </ibg>
+
+ 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<token_type> stOpt, stVal;
+ std::stack<int> 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<value_type*>(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<token_type>& a_stVal, const std::stack<token_type>& a_stOprt) const
+ {
+ std::stack<token_type> stOprt(a_stOprt);
+ std::stack<token_type> 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
+
--- /dev/null
+/*
+
+ _____ __ _____________ _______ ______ ___________
+ / \| | \____ \__ \\_ __ \/ ___// __ \_ __ \
+ | 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 <algorithm>
+#include <string>
+#include <stack>
+#include <vector>
+#include <iostream>
+
+#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:
+ <ul>
+ <li>value array position of the value</li>
+ <li>the operator code according to ParserToken::cmVAL</li>
+ <li>the value stored in #mc_iSizeVal number of bytecode entries.</li>
+ </ul>
+
+ \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<value_type>::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:
+ <ul>
+ <li>value array position of the result</li>
+ <li>the operator code according to ParserToken::ECmdCode</li>
+ </ul>
+
+ \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:
+ <ul>
+ <li>cmASSIGN code</li>
+ <li>the pointer of the destination variable</li>
+ </ul>
+
+ \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)
+ {
+ // <ibg 2020-06-10/> Unary Plus is a no-op
+ if (a_pFun == generic_callable_type{(erased_fun_type)&MathImpl<value_type>::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<int> 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<void*>(m_vRPN[i].Fun.cb._pRawFun) << _T("]");
+ mu::console() << _T("[USERDATA: 0x") << std::hex << reinterpret_cast<void*>(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<void*>(m_vRPN[i].Fun.cb._pRawFun) << _T("]");
+ mu::console() << _T("[USERDATA: 0x") << std::hex << reinterpret_cast<void*>(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
--- /dev/null
+/*
+
+ _____ __ _____________ _______ ______ ___________
+ / \| | \____ \__ \\_ __ \/ ___// __ \_ __ \
+ | 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<void*>(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<void*>(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<void*>(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<void*>(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<void*>(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<void*>(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<void*>(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<void*>(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<void*>(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<void*>(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<void*>(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<void*>(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<void*>(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<void*>(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<void*>(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<void*>(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<void*>(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<void*>(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<void*>(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<void*>(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<void*>(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<void*>(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<void*>(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<void*>(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<void*>(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<void*>(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<void*>(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<void*>(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<void*>(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<CbWithUserData*>(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<CbWithUserData*>(m_pFun);
+ m_pFun = nullptr;
+ }
+
+ if (ref.m_iArgc & CALLBACK_INTERNAL_WITH_USER_DATA)
+ m_pFun = new CbWithUserData(*reinterpret_cast<CbWithUserData*>(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<CbWithUserData*>(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<CbWithUserData*>(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
--- /dev/null
+/*
+
+ _____ __ _____________ _______ ______ ___________
+ / \| | \____ \__ \\_ __ \/ ___// __ \_ __ \
+ | 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 <exception>
+
+#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
--- /dev/null
+/*
+
+ _____ __ _____________ _______ ______ ___________
+ / \| | \____ \__ \\_ __ \/ ___// __ \_ __ \
+ | 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 <cmath>
+#include <algorithm>
+#include <numeric>
+
+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
--- /dev/null
+/*
+
+ _____ __ _____________ _______ ______ ___________
+ / \| | \____ \__ \\_ __ \/ ___// __ \_ __ \
+ | 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 <cstdio>
+#include <cmath>
+#include <iostream>
+#include <limits>
+
+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("a<b"), 1, true);
+ iStat += EqnTest(_T("b>a"), 1, true);
+ iStat += EqnTest(_T("a>a"), 0, true);
+ iStat += EqnTest(_T("a<a"), 0, true);
+ iStat += EqnTest(_T("a>a"), 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("(a<b) && (b>a)"), 1, true);
+ iStat += EqnTest(_T("(a<b) && (a>b)"), 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("a<b && b>10"), 0, true);
+ iStat += EqnTestInt(_T("a<b && b<10"), 1, true);
+
+ iStat += EqnTestInt(_T("a + b << c"), 17, true);
+ iStat += EqnTestInt(_T("a << b + c"), 7, true);
+ iStat += EqnTestInt(_T("c * b < a"), 0, true);
+ iStat += EqnTestInt(_T("c * b == 6 * a"), 1, true);
+ iStat += EqnTestInt(_T("2^2^3"), 256, true);
+
+
+ if (iStat == 0)
+ mu::console() << _T("passed") << endl;
+ else
+ mu::console() << _T("\n failed with ") << iStat << _T(" errors") << endl;
+
+ return iStat;
+ }
+
+ //---------------------------------------------------------------------------------------------
+ /** \brief Check muParser name restriction enforcement. */
+ int ParserTester::TestNames()
+ {
+ int iStat = 0,
+ iErr = 0;
+
+ mu::console() << "testing name restriction enforcement...";
+
+ Parser p;
+
+#define PARSER_THROWCHECK(DOMAIN, FAIL, EXPR, ARG) \
+ iErr = 0; \
+ ParserTester::c_iCount++; \
+ try \
+ { \
+ p.Define##DOMAIN(EXPR, ARG); \
+ iErr = (FAIL) ? 0 : 1; \
+ } \
+ catch(...) \
+ { \
+ iErr = (!FAIL) ? 0 : 1; \
+ } \
+ iStat += iErr;
+
+ // constant names
+ PARSER_THROWCHECK(Const, false, _T("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"), 1)
+ PARSER_THROWCHECK(Const, false, _T("0a"), 1)
+ PARSER_THROWCHECK(Const, false, _T("9a"), 1)
+ PARSER_THROWCHECK(Const, false, _T("+a"), 1)
+ PARSER_THROWCHECK(Const, false, _T("-a"), 1)
+ PARSER_THROWCHECK(Const, false, _T("a-"), 1)
+ PARSER_THROWCHECK(Const, false, _T("a*"), 1)
+ PARSER_THROWCHECK(Const, false, _T("a?"), 1)
+ PARSER_THROWCHECK(Const, true, _T("a"), 1)
+ PARSER_THROWCHECK(Const, true, _T("a_min"), 1)
+ PARSER_THROWCHECK(Const, true, _T("a_min0"), 1)
+ PARSER_THROWCHECK(Const, true, _T("a_min9"), 1)
+
+ // variable names
+ value_type a;
+ p.ClearConst();
+ PARSER_THROWCHECK(Var, false, _T("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"), &a);
+ PARSER_THROWCHECK(Var, false, _T("123abc"), &a)
+ PARSER_THROWCHECK(Var, false, _T("9a"), &a)
+ PARSER_THROWCHECK(Var, false, _T("0a"), &a)
+ PARSER_THROWCHECK(Var, false, _T("+a"), &a)
+ PARSER_THROWCHECK(Var, false, _T("-a"), &a)
+ PARSER_THROWCHECK(Var, false, _T("?a"), &a)
+ PARSER_THROWCHECK(Var, false, _T("!a"), &a)
+ PARSER_THROWCHECK(Var, false, _T("a+"), &a)
+ PARSER_THROWCHECK(Var, false, _T("a-"), &a)
+ PARSER_THROWCHECK(Var, false, _T("a*"), &a)
+ PARSER_THROWCHECK(Var, false, _T("a?"), &a)
+ PARSER_THROWCHECK(Var, true, _T("a"), &a)
+ PARSER_THROWCHECK(Var, true, _T("a_min"), &a)
+ PARSER_THROWCHECK(Var, true, _T("a_min0"), &a)
+ PARSER_THROWCHECK(Var, true, _T("a_min9"), &a)
+ PARSER_THROWCHECK(Var, false, _T("a_min9"), 0)
+
+ // Postfix operators
+ // fail
+ PARSER_THROWCHECK(PostfixOprt, false, _T("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"), f1of1);
+ PARSER_THROWCHECK(PostfixOprt, false, _T("(k"), f1of1)
+ PARSER_THROWCHECK(PostfixOprt, false, _T("9+"), f1of1)
+ PARSER_THROWCHECK(PostfixOprt, false, _T("+"), 0)
+ // pass
+ PARSER_THROWCHECK(PostfixOprt, true, _T("-a"), f1of1)
+ PARSER_THROWCHECK(PostfixOprt, true, _T("?a"), f1of1)
+ PARSER_THROWCHECK(PostfixOprt, true, _T("_"), f1of1)
+ PARSER_THROWCHECK(PostfixOprt, true, _T("#"), f1of1)
+ PARSER_THROWCHECK(PostfixOprt, true, _T("&&"), f1of1)
+ PARSER_THROWCHECK(PostfixOprt, true, _T("||"), f1of1)
+ PARSER_THROWCHECK(PostfixOprt, true, _T("&"), f1of1)
+ PARSER_THROWCHECK(PostfixOprt, true, _T("|"), f1of1)
+ PARSER_THROWCHECK(PostfixOprt, true, _T("++"), f1of1)
+ PARSER_THROWCHECK(PostfixOprt, true, _T("--"), f1of1)
+ PARSER_THROWCHECK(PostfixOprt, true, _T("?>"), 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<double>::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("(a<b) ? (b<c) ? 1 : 2"), ecMISSING_ELSE_CLAUSE);
+ iStat += ThrowTest(_T("(a<b) ? 1"), ecMISSING_ELSE_CLAUSE);
+ iStat += ThrowTest(_T("(a<b) ? a"), ecMISSING_ELSE_CLAUSE);
+ iStat += ThrowTest(_T("(a<b) ? a+b"), ecMISSING_ELSE_CLAUSE);
+ iStat += ThrowTest(_T("a : b"), ecMISPLACED_COLON);
+ iStat += ThrowTest(_T("1 : 2"), ecMISPLACED_COLON);
+ iStat += ThrowTest(_T("(1) ? 1 : 2 : 3"), ecMISPLACED_COLON);
+ iStat += ThrowTest(_T("(true) ? 1 : 2 : 3"), ecUNASSIGNABLE_TOKEN);
+
+ // from oss-fzz.com: UNKNOWN READ; https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=22922#c1
+ iStat += ThrowTest(_T("1?2:0?(7:1)"), ecMISPLACED_COLON);
+
+ // from oss-fuzz.com: https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=22938
+ iStat += ThrowTest(_T("sum(0?1,0,0:3)"), ecUNEXPECTED_ARG_SEP);
+ iStat += ThrowTest(_T("sum(0?(1,0,0):3)"), ecUNEXPECTED_ARG);
+ iStat += ThrowTest(_T("sum(2>3?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("a<b ? 128 : 255"), 128, true);
+ iStat += EqnTest(_T("(a<b) ? 128 : 255"), 128, true);
+ iStat += EqnTest(_T("(1) ? 10 : 11"), 10, true);
+ iStat += EqnTest(_T("(0) ? 10 : 11"), 11, true);
+ iStat += EqnTest(_T("(1) ? a+b : c+d"), 3, true);
+ iStat += EqnTest(_T("(0) ? a+b : c+d"), 1, true);
+ iStat += EqnTest(_T("(1) ? 0 : 1"), 0, true);
+ iStat += EqnTest(_T("(0) ? 0 : 1"), 1, true);
+ iStat += EqnTest(_T("(a<b) ? 10 : 11"), 10, true);
+ iStat += EqnTest(_T("(a>b) ? 10 : 11"), 11, true);
+ iStat += EqnTest(_T("(a<b) ? c : d"), 3, true);
+ iStat += EqnTest(_T("(a>b) ? 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, (a<b) ? 3 : 10)"), 6, true);
+ iStat += EqnTest(_T("10*sum(3, (a>b) ? 3 : 10)"), 130, true);
+ iStat += EqnTest(_T("10*sum(3, (a<b) ? 3 : 10)"), 60, true);
+ iStat += EqnTest(_T("sum(3, (a>b) ? 3 : 10)*10"), 130, true);
+ iStat += EqnTest(_T("sum(3, (a<b) ? 3 : 10)*10"), 60, true);
+ iStat += EqnTest(_T("(a<b) ? sum(3, (a<b) ? 3 : 10)*10 : 99"), 60, true);
+ iStat += EqnTest(_T("(a>b) ? sum(3, (a<b) ? 3 : 10)*10 : 99"), 99, true);
+ iStat += EqnTest(_T("(a<b) ? sum(3, (a<b) ? 3 : 10,10,20)*10 : 99"), 360, true);
+ iStat += EqnTest(_T("(a>b) ? sum(3, (a<b) ? 3 : 10,10,20)*10 : 99"), 99, true);
+ iStat += EqnTest(_T("(a>b) ? sum(3, (a<b) ? 3 : 10,10,20)*10 : sum(3, (a<b) ? 3 : 10)*10"), 60, true);
+
+ // todo: also add for muParserX!
+ iStat += EqnTest(_T("(a<b)&&(a<b) ? 128 : 255"), 128, true);
+ iStat += EqnTest(_T("(a>b)&&(a<b) ? 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("((1<2)&&(1<2)) ? 128 : 255"), 128, true);
+ iStat += EqnTest(_T("((1>2)&&(1<2)) ? 128 : 255"), 255, true);
+ iStat += EqnTest(_T("((a<b)&&(a<b)) ? 128 : 255"), 128, true);
+ iStat += EqnTest(_T("((a>b)&&(a<b)) ? 128 : 255"), 255, true);
+
+ iStat += EqnTest(_T("1>0 ? 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<b)) ? 128 : 255, a"), 255, true);
+ iStat += EqnTest(_T("c=(a<b)&&(a<b) ? 128 : 255, c"), 128, true);
+ iStat += EqnTest(_T("0 ? a=a+1 : 666, a"), 1, true);
+ iStat += EqnTest(_T("1?a=10:a=20, a"), 10, true);
+ iStat += EqnTest(_T("0?a=10:a=20, a"), 20, true);
+ iStat += EqnTest(_T("0?a=sum(3,4):10, a"), 1, true); // a should not change its value due to lazy calculation
+
+ iStat += EqnTest(_T("a=1?b=1?3:4:5, a"), 3, true);
+ iStat += EqnTest(_T("a=1?b=1?3:4:5, b"), 3, true);
+ iStat += EqnTest(_T("a=0?b=1?3:4:5, a"), 5, true);
+ iStat += EqnTest(_T("a=0?b=1?3:4:5, b"), 2, true);
+
+ iStat += EqnTest(_T("a=1?5:b=1?3:4, a"), 5, true);
+ iStat += EqnTest(_T("a=1?5:b=1?3:4, b"), 2, true);
+ iStat += EqnTest(_T("a=0?5:b=1?3:4, a"), 3, true);
+ iStat += EqnTest(_T("a=0?5:b=1?3:4, b"), 3, true);
+
+ if (iStat == 0)
+ mu::console() << _T("passed") << endl;
+ else
+ mu::console() << _T("\n failed with ") << iStat << _T(" errors") << endl;
+
+ return iStat;
+ }
+
+ //---------------------------------------------------------------------------
+ int ParserTester::TestException()
+ {
+ int iStat = 0;
+ mu::console() << _T("testing error codes...");
+
+ iStat += ThrowTest(_T("3+"), ecUNEXPECTED_EOF);
+ iStat += ThrowTest(_T("3+)"), ecUNEXPECTED_PARENS);
+ iStat += ThrowTest(_T("()"), ecUNEXPECTED_PARENS);
+ iStat += ThrowTest(_T("3+()"), ecUNEXPECTED_PARENS);
+ iStat += ThrowTest(_T("sin(3,4)"), ecTOO_MANY_PARAMS);
+ iStat += ThrowTest(_T("sin()"), ecTOO_FEW_PARAMS);
+ iStat += ThrowTest(_T("(1+2"), ecMISSING_PARENS);
+ iStat += ThrowTest(_T("sin(3)3"), ecUNEXPECTED_VAL);
+ iStat += ThrowTest(_T("sin(3)xyz"), ecUNASSIGNABLE_TOKEN);
+ iStat += ThrowTest(_T("sin(3)cos(3)"), ecUNEXPECTED_FUN);
+ iStat += ThrowTest(_T("a+b+c=10"), ecUNEXPECTED_OPERATOR);
+ iStat += ThrowTest(_T("a=b=3"), ecUNEXPECTED_OPERATOR);
+
+ // functions without parameter
+ iStat += ThrowTest(_T("3+ping(2)"), ecTOO_MANY_PARAMS);
+ iStat += ThrowTest(_T("3+ping(a+2)"), ecTOO_MANY_PARAMS);
+ iStat += ThrowTest(_T("3+ping(sin(a)+2)"), ecTOO_MANY_PARAMS);
+ iStat += ThrowTest(_T("3+ping(1+sin(a))"), ecTOO_MANY_PARAMS);
+
+ // String function related
+ iStat += ThrowTest(_T("valueof(\"xxx\")"), 999, false);
+ iStat += ThrowTest(_T("valueof()"), ecUNEXPECTED_PARENS);
+ iStat += ThrowTest(_T("1+valueof(\"abc\""), ecMISSING_PARENS);
+ iStat += ThrowTest(_T("valueof(\"abc\""), ecMISSING_PARENS);
+ iStat += ThrowTest(_T("valueof(\"abc"), ecUNTERMINATED_STRING);
+ iStat += ThrowTest(_T("valueof(\"abc\",3)"), ecTOO_MANY_PARAMS);
+ iStat += ThrowTest(_T("valueof(3)"), ecSTRING_EXPECTED);
+ iStat += ThrowTest(_T("sin(\"abc\")"), ecVAL_EXPECTED);
+ iStat += ThrowTest(_T("valueof(\"\\\"abc\\\"\")"), 999, false);
+ iStat += ThrowTest(_T("\"hello world\""), ecSTR_RESULT);
+ iStat += ThrowTest(_T("(\"hello world\")"), ecSTR_RESULT);
+ iStat += ThrowTest(_T("\"abcd\"+100"), ecSTR_RESULT);
+ iStat += ThrowTest(_T("\"a\"+\"b\""), ecSTR_RESULT);
+ iStat += ThrowTest(_T("strfun1(\"100\",3)"), ecTOO_MANY_PARAMS);
+ iStat += ThrowTest(_T("strfun2(\"100\",3,5)"), ecTOO_MANY_PARAMS);
+ iStat += ThrowTest(_T("strfun3(\"100\",3,5,6)"), ecTOO_MANY_PARAMS);
+ iStat += ThrowTest(_T("strfun2(\"100\")"), ecTOO_FEW_PARAMS);
+ iStat += ThrowTest(_T("strfun3(\"100\",6)"), ecTOO_FEW_PARAMS);
+ iStat += ThrowTest(_T("strfun2(1,1)"), ecSTRING_EXPECTED);
+ iStat += ThrowTest(_T("strfun2(a,1)"), ecSTRING_EXPECTED);
+ iStat += ThrowTest(_T("strfun2(1,1,1)"), ecTOO_MANY_PARAMS);
+ iStat += ThrowTest(_T("strfun2(a,1,1)"), ecTOO_MANY_PARAMS);
+ iStat += ThrowTest(_T("strfun3(1,2,3)"), ecSTRING_EXPECTED);
+ iStat += ThrowTest(_T("strfun3(1, \"100\",3)"), ecSTRING_EXPECTED);
+ iStat += ThrowTest(_T("strfun3(\"1\", \"100\",3)"), ecVAL_EXPECTED);
+ iStat += ThrowTest(_T("strfun3(\"1\", 3, \"100\")"), ecVAL_EXPECTED);
+ iStat += ThrowTest(_T("strfun3(\"1\", \"100\", \"100\", \"100\")"), ecTOO_MANY_PARAMS);
+
+ // assignment operator
+ iStat += ThrowTest(_T("3=4"), ecUNEXPECTED_OPERATOR);
+ iStat += ThrowTest(_T("sin(8)=4"), ecUNEXPECTED_OPERATOR);
+ iStat += ThrowTest(_T("\"test\"=a"), ecSTR_RESULT);
+
+ // <ibg 20090529>
+ // this is now legal, for reference see:
+ // https://sourceforge.net/forum/message.php?msg_id=7411373
+ // iStat += ThrowTest( _T("sin=9"), ecUNEXPECTED_OPERATOR);
+ // </ibg>
+
+ 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<Parser> 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<value_type>::CONST_PI);
+ p1->DefineConst(_T("e"), MathImpl<value_type>::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<void*>(8));
+ p1->DefineFunUserData(_T("funud1_16"), FunUd1, reinterpret_cast<void*>(16));
+ p1->DefineFunUserData(_T("funud2_24"), FunUd2, reinterpret_cast<void*>(24));
+ p1->DefineFunUserData(_T("funud10_32"), FunUd10, reinterpret_cast<void*>(32));
+ p1->DefineFunUserData(_T("funud0_9"), FunUd0, reinterpret_cast<void*>(9));
+ p1->DefineFunUserData(_T("funud1_17"), FunUd1, reinterpret_cast<void*>(17));
+ p1->DefineFunUserData(_T("funud2_25"), FunUd2, reinterpret_cast<void*>(25));
+ p1->DefineFunUserData(_T("funud10_33"), FunUd10, reinterpret_cast<void*>(33));
+ p1->DefineFunUserData(_T("strfunud3_10"), StrFunUd3, reinterpret_cast<void*>(10));
+ p1->DefineFunUserData(_T("sumud_100"), SumUd, reinterpret_cast<void*>(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<mu::Parser> 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<value_type>::has_infinity)
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
+ {
+ bCloseEnough &= (fabs(fVal[i]) != numeric_limits<value_type>::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
--- /dev/null
+/*
+
+ _____ __ _____________ _______ ______ ___________
+ / \| | \____ \__ \\_ __ \/ ___// __ \_ __ \
+ | 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 <cstdio>
+#include <cstring>
+#include <map>
+#include <stack>
+#include <string>
+
+#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<ParserTokenReader> 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<int>();
+ 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<int>(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<int>(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<char_type>::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)
+ {
+ // <ibg 20110629> 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;
+ // </ibg>
+
+ // 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<identfun_type>::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)
+ {
+ // <ibg/> 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
"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/")
--- /dev/null
+Changed: The bundled version of muParser has been updated to version 2.3.3.
+<br>
+(David Wells, 2022/03/28)