From 1f4a944064bc42284c33e6b755353d191cf288e8 Mon Sep 17 00:00:00 2001 From: astrojhgu Date: Mon, 15 Dec 2008 07:26:12 +0000 Subject: git-svn-id: file:///home/svn/opt_utilities@1 ed2142bd-67ad-457f-ba7c-d818d4011675 --- muparser/makefile | 16 + muparser/muParser.cpp | 259 ++++++++ muparser/muParser.h | 104 +++ muparser/muParserBase.cpp | 1364 ++++++++++++++++++++++++++++++++++++++ muparser/muParserBase.h | 324 +++++++++ muparser/muParserBytecode.cpp | 396 +++++++++++ muparser/muParserBytecode.h | 148 +++++ muparser/muParserCallback.cpp | 198 ++++++ muparser/muParserCallback.h | 94 +++ muparser/muParserDLL.cpp | 657 ++++++++++++++++++ muparser/muParserDLL.h | 123 ++++ muparser/muParserDef.h | 239 +++++++ muparser/muParserError.cpp | 300 +++++++++ muparser/muParserError.h | 160 +++++ muparser/muParserFixes.h | 196 ++++++ muparser/muParserInt.cpp | 264 ++++++++ muparser/muParserInt.h | 93 +++ muparser/muParserStack.h | 120 ++++ muparser/muParserTest.cpp | 1125 +++++++++++++++++++++++++++++++ muparser/muParserTest.h | 176 +++++ muparser/muParserToken.h | 464 +++++++++++++ muparser/muParserTokenReader.cpp | 822 +++++++++++++++++++++++ muparser/muParserTokenReader.h | 156 +++++ 23 files changed, 7798 insertions(+) create mode 100644 muparser/makefile create mode 100644 muparser/muParser.cpp create mode 100644 muparser/muParser.h create mode 100644 muparser/muParserBase.cpp create mode 100644 muparser/muParserBase.h create mode 100644 muparser/muParserBytecode.cpp create mode 100644 muparser/muParserBytecode.h create mode 100644 muparser/muParserCallback.cpp create mode 100644 muparser/muParserCallback.h create mode 100644 muparser/muParserDLL.cpp create mode 100644 muparser/muParserDLL.h create mode 100644 muparser/muParserDef.h create mode 100644 muparser/muParserError.cpp create mode 100644 muparser/muParserError.h create mode 100644 muparser/muParserFixes.h create mode 100644 muparser/muParserInt.cpp create mode 100644 muparser/muParserInt.h create mode 100644 muparser/muParserStack.h create mode 100644 muparser/muParserTest.cpp create mode 100644 muparser/muParserTest.h create mode 100644 muparser/muParserToken.h create mode 100644 muparser/muParserTokenReader.cpp create mode 100644 muparser/muParserTokenReader.h (limited to 'muparser') diff --git a/muparser/makefile b/muparser/makefile new file mode 100644 index 0000000..83d2cd5 --- /dev/null +++ b/muparser/makefile @@ -0,0 +1,16 @@ +MUP_OBJ=muParserBase.o muParser.o muParserInt.o\ + muParserBytecode.o muParserDLL.o\ + muParserTest.o\ + muParserCallback.o muParserError.o\ + muParserTokenReader.o + +libmuparser.a:$(MUP_OBJ) + $(AR) $@ *.o + +.cpp.o: + $(CPP) $< $(CPPFLAGS) + +clean: + $(RM) *.o + + diff --git a/muparser/muParser.cpp b/muparser/muParser.cpp new file mode 100644 index 0000000..e92b1a2 --- /dev/null +++ b/muparser/muParser.cpp @@ -0,0 +1,259 @@ +/* + __________ + _____ __ __\______ \_____ _______ ______ ____ _______ + / \ | | \| ___/\__ \ \_ __ \/ ___/_/ __ \\_ __ \ + | Y Y \| | /| | / __ \_| | \/\___ \ \ ___/ | | \/ + |__|_| /|____/ |____| (____ /|__| /____ > \___ >|__| + \/ \/ \/ \/ + Copyright (C) 2004-2006 Ingo Berg + + Permission is hereby granted, free of charge, to any person obtaining a copy of this + software and associated documentation files (the "Software"), to deal in the Software + without restriction, including without limitation the rights to use, copy, modify, + merge, publish, distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all copies or + substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT + NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ +#include "muParser.h" + +#include +#include +#include + +/** \brief Pi (what else?). */ +#define PARSER_CONST_PI 3.141592653589793238462643 + +/** \brief The eulerian number. */ +#define PARSER_CONST_E 2.718281828459045235360287 + +using namespace std; + + +/** \brief Namespace for mathematical applications. */ +namespace mu +{ + +//--------------------------------------------------------------------------- +// Trigonometric function +value_type Parser::Sin(value_type v) { return sin(v); } +value_type Parser::Cos(value_type v) { return cos(v); } +value_type Parser::Tan(value_type v) { return tan(v); } +value_type Parser::ASin(value_type v) { return asin(v); } +value_type Parser::ACos(value_type v) { return acos(v); } +value_type Parser::ATan(value_type v) { return atan(v); } +value_type Parser::Sinh(value_type v) { return sinh(v); } +value_type Parser::Cosh(value_type v) { return cosh(v); } +value_type Parser::Tanh(value_type v) { return tanh(v); } +value_type Parser::ASinh(value_type v) { return log(v + sqrt(v * v + 1)); } +value_type Parser::ACosh(value_type v) { return log(v + sqrt(v * v - 1)); } +value_type Parser::ATanh(value_type v) { return ((value_type)0.5 * log((1 + v) / (1 - v))); } + +//--------------------------------------------------------------------------- +// Logarithm functions +value_type Parser::Log2(value_type v) { return log(v)/log((value_type)2); } // Logarithm base 2 +value_type Parser::Log10(value_type v) { return log10(v); } // Logarithm base 10 +value_type Parser::Ln(value_type v) { return log(v); } // Logarithm base e (natural logarithm) + +//--------------------------------------------------------------------------- +// misc +value_type Parser::Exp(value_type v) { return exp(v); } +value_type Parser::Abs(value_type v) { return fabs(v); } +value_type Parser::Sqrt(value_type v) { return sqrt(v); } +value_type Parser::Rint(value_type v) { return floor(v + (value_type)0.5); } +value_type Parser::Sign(value_type v) { return (value_type)((v<0) ? -1 : (v>0) ? 1 : 0); } + +//--------------------------------------------------------------------------- +// Conditional (if then else) +value_type Parser::Ite(value_type v1, value_type v2, value_type v3) { return (v1==1) ? v2 : v3; } + +//--------------------------------------------------------------------------- +// Unary operator Callbacks: Infix operators +value_type Parser::UnaryMinus(value_type v) { return -v; } + +//--------------------------------------------------------------------------- +// Functions with variable number of arguments +// sum +value_type Parser::Sum(const value_type *a_afArg, int a_iArgc) +{ + if (!a_iArgc) + throw exception_type(_T("too few arguments for function sum.")); + + value_type fRes=0; + for (int i=0; i> fVal; + int iEnd = stream.tellg(); // Position after reading +#endif + + if (iEnd==-1) + return false; + + a_iPos += iEnd; + a_fVal = fVal; + return true; +} + +//--------------------------------------------------------------------------- +/** \brief Constructor. + + Call ParserBase class constructor and trigger Function, Operator and Constant initialization. +*/ +Parser::Parser() + :ParserBase() + ,m_fEpsilon((value_type)1e-7) +{ + AddValIdent(IsVal); + + InitCharSets(); + InitFun(); + InitConst(); + InitOprt(); +} + +//--------------------------------------------------------------------------- +/** Define the character sets. */ +void Parser::InitCharSets() +{ + DefineNameChars( _T("0123456789_abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ") ); + DefineOprtChars( _T("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ+-*^/?<>=#!$%&|~'_") ); + DefineInfixOprtChars( _T("/+-*^?<>=#!$%&|~'_") ); +} + +//--------------------------------------------------------------------------- +/** \brief Initialize the default functions. */ +void Parser::InitFun() +{ + // trigonometric functions + DefineFun(_T("sin"), Sin); + DefineFun(_T("cos"), Cos); + DefineFun(_T("tan"), Tan); + // arcus functions + DefineFun(_T("asin"), ASin); + DefineFun(_T("acos"), ACos); + DefineFun(_T("atan"), ATan); + // hyperbolic functions + DefineFun(_T("sinh"), Sinh); + DefineFun(_T("cosh"), Cosh); + DefineFun(_T("tanh"), Tanh); + // arcus hyperbolic functions + DefineFun(_T("asinh"), ASinh); + DefineFun(_T("acosh"), ACosh); + DefineFun(_T("atanh"), ATanh); + // Logarithm functions + DefineFun(_T("log2"), Log2); + DefineFun(_T("log10"), Log10); + DefineFun(_T("log"), Log10); + DefineFun(_T("ln"), Ln); + // misc + DefineFun(_T("exp"), Exp); + DefineFun(_T("sqrt"), Sqrt); + DefineFun(_T("sign"), Sign); + DefineFun(_T("rint"), Rint); + DefineFun(_T("abs"), Abs); + DefineFun(_T("if"), Ite); + // Functions with variable number of arguments + DefineFun(_T("sum"), Sum); + DefineFun(_T("avg"), Avg); + DefineFun(_T("min"), Min); + DefineFun(_T("max"), Max); +} + +//--------------------------------------------------------------------------- +/** \brief Initialize operators. */ +void Parser::InitConst() +{ + DefineConst(_T("_pi"), (value_type)PARSER_CONST_PI); + DefineConst(_T("_e"), (value_type)PARSER_CONST_E); +} + +//--------------------------------------------------------------------------- +/** \brief Initialize operators. */ +void Parser::InitOprt() +{ + DefineInfixOprt(_T("-"), UnaryMinus); +} + +//--------------------------------------------------------------------------- +/** \brief Numerically differentiate with regard to a variable. */ +value_type Parser::Diff(value_type *a_Var, value_type a_fPos) const +{ + assert(m_fEpsilon); + value_type fEpsilon( (a_fPos==0) ? (value_type)1e-10 : m_fEpsilon * a_fPos ), + fRes(0), fBuf(*a_Var), f[4] = {0,0,0,0}; + + *a_Var = a_fPos+2*fEpsilon; f[0] = Eval(); + *a_Var = a_fPos+1*fEpsilon; f[1] = Eval(); + *a_Var = a_fPos-1*fEpsilon; f[2] = Eval(); + *a_Var = a_fPos-2*fEpsilon; f[3] = Eval(); + *a_Var = fBuf; // restore variable + + fRes = (-f[0] + 8*f[1] - 8*f[2] + f[3]) / (12*fEpsilon); + return fRes; +} + +} // namespace mu diff --git a/muparser/muParser.h b/muparser/muParser.h new file mode 100644 index 0000000..5aff62d --- /dev/null +++ b/muparser/muParser.h @@ -0,0 +1,104 @@ +/* + __________ + _____ __ __\______ \_____ _______ ______ ____ _______ + / \ | | \| ___/\__ \ \_ __ \/ ___/_/ __ \\_ __ \ + | Y Y \| | /| | / __ \_| | \/\___ \ \ ___/ | | \/ + |__|_| /|____/ |____| (____ /|__| /____ > \___ >|__| + \/ \/ \/ \/ + Copyright (C) 2004-2006 Ingo Berg + + Permission is hereby granted, free of charge, to any person obtaining a copy of this + software and associated documentation files (the "Software"), to deal in the Software + without restriction, including without limitation the rights to use, copy, modify, + merge, publish, distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all copies or + substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT + NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ +#ifndef MU_PARSER_H +#define MU_PARSER_H + +#include "muParserBase.h" +#include + + +namespace mu +{ + +/** \brief Mathematical expressions parser (reference implementation). + + Standard implementation of the mathematical expressions parser. + Can be used as a reference implementation for subclassing the parser. + + + (C) 2004-2006 Ingo Berg
+ ingo_berg(at)gmx.de +
+*/ +class Parser : public ParserBase +{ +private: + // Trigonometric functions + static value_type Sin(value_type); + static value_type Cos(value_type); + static value_type Tan(value_type); + // arcus functions + static value_type ASin(value_type); + static value_type ACos(value_type); + static value_type ATan(value_type); + // hyperbolic functions + static value_type Sinh(value_type); + static value_type Cosh(value_type); + static value_type Tanh(value_type); + // arcus hyperbolic functions + static value_type ASinh(value_type); + static value_type ACosh(value_type); + static value_type ATanh(value_type); + // Logarithm functions + static value_type Log2(value_type); // Logarithm Base 2 + static value_type Log10(value_type); // Logarithm Base 10 + static value_type Ln(value_type); // Logarithm Base e (natural logarithm) + // misc + static value_type Exp(value_type); + static value_type Abs(value_type); + static value_type Sqrt(value_type); + static value_type Rint(value_type); + static value_type Sign(value_type); + static value_type Ite(value_type, value_type, value_type); + + // Prefix operators + // !!! Unary Minus is a MUST if you want to use negative signs !!! + static value_type UnaryMinus(value_type); + + // Functions with variable number of arguments + static value_type Sum(const value_type*, int); // sum + static value_type Avg(const value_type*, int); // mean value + static value_type Min(const value_type*, int); // minimum + static value_type Max(const value_type*, int); // maximum + + static bool IsVal(const char_type *a_szExpr, int &a_iPos, value_type &a_fVal); + + value_type m_fEpsilon; ///< Epsilon used for numerical differentiation. + +public: + Parser(); + + virtual void InitCharSets(); + virtual void InitFun(); + virtual void InitConst(); + virtual void InitOprt(); + + value_type Diff(value_type *a_Var, value_type a_fPos) const; +}; + +} // namespace mu + +#endif + diff --git a/muparser/muParserBase.cpp b/muparser/muParserBase.cpp new file mode 100644 index 0000000..e81e53e --- /dev/null +++ b/muparser/muParserBase.cpp @@ -0,0 +1,1364 @@ +/* + __________ + _____ __ __\______ \_____ _______ ______ ____ _______ + / \ | | \| ___/\__ \ \_ __ \/ ___/_/ __ \\_ __ \ + | Y Y \| | /| | / __ \_| | \/\___ \ \ ___/ | | \/ + |__|_| /|____/ |____| (____ /|__| /____ > \___ >|__| + \/ \/ \/ \/ + Copyright (C) 2004-2006 Ingo Berg + + Permission is hereby granted, free of charge, to any person obtaining a copy of this + software and associated documentation files (the "Software"), to deal in the Software + without restriction, including without limitation the rights to use, copy, modify, + merge, publish, distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all copies or + substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT + NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#include "muParser.h" + +#include +#include +#include +#include +#include + +using namespace std; + + +namespace mu +{ + +//------------------------------------------------------------------------------ +/** \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("and"), + _T("or"), _T("xor"), _T("="), + _T("("), _T(")"), _T(","), 0 +}; + +//------------------------------------------------------------------------------ +/** \brief Constructor. + \param a_szFormula the formula to interpret. + \throw ParserException if a_szFormula is null. +*/ +ParserBase::ParserBase() + :m_pParseFormula(&ParserBase::ParseString) + ,m_pCmdCode(0) + ,m_vByteCode() + ,m_vStringBuf() + ,m_pTokenReader() + ,m_FunDef() + ,m_PostOprtDef() + ,m_InfixOprtDef() + ,m_OprtDef() + ,m_ConstDef() + ,m_StrVarDef() + ,m_VarDef() + ,m_bOptimize(true) + ,m_bUseByteCode(true) + ,m_bBuiltInOp(true) + ,m_sNameChars() + ,m_sOprtChars() + ,m_sInfixOprtChars() +{ + InitTokenReader(); +} + +//--------------------------------------------------------------------------- +/** \brief Copy constructor. + + Implemented by calling Assign(a_Parser) +*/ +ParserBase::ParserBase(const ParserBase &a_Parser) + :m_pParseFormula(&ParserBase::ParseString) + ,m_pCmdCode(0) + ,m_vByteCode() + ,m_vStringBuf() + ,m_pTokenReader() + ,m_FunDef() + ,m_PostOprtDef() + ,m_InfixOprtDef() + ,m_OprtDef() + ,m_ConstDef() + ,m_StrVarDef() + ,m_VarDef() + ,m_bOptimize(true) + ,m_bUseByteCode(true) + ,m_bBuiltInOp(true) +{ + m_pTokenReader.reset(new token_reader_type(this)); + Assign(a_Parser); +} + +//--------------------------------------------------------------------------- +/** \brief Assignement operator. + + Implemented by calling Assign(a_Parser). Self assignement is suppressed. + \param a_Parser Object to copy to this. + \return *this + \throw nothrow +*/ +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_bOptimize = a_Parser.m_bOptimize; + m_bUseByteCode = a_Parser.m_bUseByteCode; + m_bBuiltInOp = a_Parser.m_bBuiltInOp; + m_vStringBuf = a_Parser.m_vStringBuf; + m_pTokenReader.reset(a_Parser.m_pTokenReader->Clone(this)); + m_StrVarDef = a_Parser.m_StrVarDef; + m_vStringVarBuf = a_Parser.m_vStringVarBuf; + + // 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_sNameChars = a_Parser.m_sNameChars; + m_sOprtChars = a_Parser.m_sOprtChars; + m_sInfixOprtChars = a_Parser.m_sInfixOprtChars; +} + +//--------------------------------------------------------------------------- +/** \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_vByteCode.clear(); + m_pTokenReader->ReInit(); +} + +//--------------------------------------------------------------------------- +void ParserBase::AddValIdent(identfun_type a_pCallback) +{ + m_pTokenReader->AddValIdent(a_pCallback); +} + +//--------------------------------------------------------------------------- +void ParserBase::SetVarFactory(facfun_type a_pFactory, void *pUserData) +{ + m_pTokenReader->SetVarCreator(a_pFactory, pUserData); +} + +//--------------------------------------------------------------------------- +/** \brief Add a function or operator callback to the parser. +*/ +void ParserBase::AddCallback( const string_type &a_strName, + const ParserCallback &a_Callback, + funmap_type &a_Storage, + const char_type *a_szCharSet ) +{ + if (a_Callback.GetAddr()==0) + Error(ecINVALID_FUN_PTR); + + const funmap_type *pFunMap = &a_Storage; + + // Check for conflicting operator or function names + if ( pFunMap!=&m_FunDef && m_FunDef.find(a_strName)!=m_FunDef.end() ) + Error(ecNAME_CONFLICT); + + if ( pFunMap!=&m_PostOprtDef && m_PostOprtDef.find(a_strName)!=m_PostOprtDef.end() ) + Error(ecNAME_CONFLICT); + + if ( pFunMap!=&m_InfixOprtDef && pFunMap!=&m_OprtDef && m_InfixOprtDef.find(a_strName)!=m_InfixOprtDef.end() ) + Error(ecNAME_CONFLICT); + + if ( pFunMap!=&m_InfixOprtDef && pFunMap!=&m_OprtDef && m_OprtDef.find(a_strName)!=m_OprtDef.end() ) + Error(ecNAME_CONFLICT); + + CheckName(a_strName, a_szCharSet); + a_Storage[a_strName] = a_Callback; + ReInit(); +} + +//--------------------------------------------------------------------------- +/** \brief Check if a name contains invalid characters. + + \throw ParserException if the name contains invalid charakters. +*/ +void ParserBase::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. + Triggers first time calculation thus the creation of the bytecode and + scanning of used variables. + + \param a_strFormula Formula as string_type + \throw ParserException in case of syntax errors. +*/ +void ParserBase::SetExpr(const string_type &a_sExpr) +{ + // 20060222: Bugfix for Borland-Kylix: + // adding a space to the expression will keep Borlands KYLIX from going wild + // when calling tellg on a stringstream created from the expression after + // reading a value at the end of an expression. (mu::Parser::IsVal function) + // (tellg returns -1 otherwise causing the parser to ignore the value) + string_type sBuf(a_sExpr + _T(" ") ); + m_pTokenReader->SetFormula(sBuf); + ReInit(); +} + +//--------------------------------------------------------------------------- +/** \brief Add a user defined operator. + \post Will reset the Parser to string parsing mode. +*/ +void ParserBase::DefinePostfixOprt(const string_type &a_sName, + fun_type1 a_pFun, + bool a_bAllowOpt) +{ + AddCallback( a_sName, + ParserCallback(a_pFun, a_bAllowOpt, prPOSTFIX, cmOPRT_POSTFIX), + m_PostOprtDef, + ValidOprtChars() ); +} + +//--------------------------------------------------------------------------- +/** \brief Add a user defined operator. + \post Will reset the Parser to string parsing mode. + \param a_sName [in] operator Identifier + \param a_pFun [in] Operator callback function + \param a_iPrec [in] Operator Precedence (default=prSIGN) + \param a_bAllowOpt [in] True if operator is volatile (default=false) + + \sa EPrec +*/ +void ParserBase::DefineInfixOprt(const string_type &a_sName, + fun_type1 a_pFun, + int a_iPrec, + bool a_bAllowOpt) +{ + AddCallback( a_sName, + ParserCallback(a_pFun, a_bAllowOpt, a_iPrec, cmOPRT_INFIX), + m_InfixOprtDef, + ValidOprtChars() ); +} + +//--------------------------------------------------------------------------- +void ParserBase::DefineOprt( const string_type &a_sName, + fun_type2 a_pFun, + unsigned a_iPrec, + bool a_bAllowOpt ) +{ + // Check for conflicts with built in operator names + for (int i=0; m_bBuiltInOp && iIgnoreUndefVar(true); + ParseString(); // implicitely create or update the map with the + // used variables stored in the token reader if not already done + m_pTokenReader->IgnoreUndefVar(false); + } + catch(exception_type &e) + { + m_pTokenReader->IgnoreUndefVar(false); + throw e; + } + + // Make sure to stay in string parse mode, dont call ReInit() + // because it deletes the array with the used variables + m_pParseFormula = &ParserBase::ParseString; + + 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. + + 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. + \return #m_FunDef + \sa FunProt + \throw nothrow +*/ +const funmap_type& ParserBase::GetFunDef() const +{ + return m_FunDef; +} + +//--------------------------------------------------------------------------- +/** \brief Retrieve the formula. */ +const string_type& ParserBase::GetExpr() const +{ + return m_pTokenReader->GetFormula(); +} + +//--------------------------------------------------------------------------- +ParserBase::token_type ParserBase::ApplyNumFunc( const token_type &a_FunTok, + const std::vector &a_vArg) const +{ + token_type valTok; + int iArgCount = (unsigned)a_vArg.size(); + void *pFunc = a_FunTok.GetFuncAddr(); + assert(pFunc); + + // Collect the function arguments from the value stack + switch(a_FunTok.GetArgCount()) + { + case -1: + // Function with variable argument count + // copy arguments into a vector + { + /** \todo remove the unnecessary argument vector by changing order in stArg. */ + std::vector vArg; + for (int i=0; i &a_vArg) const +{ + if (a_vArg.back().GetCode()!=cmSTRING) + Error(ecSTRING_EXPECTED, m_pTokenReader->GetPos(), a_FunTok.GetAsString()); + + token_type valTok; + int iArgCount = (unsigned)a_vArg.size(); + void *pFunc = a_FunTok.GetFuncAddr(); + assert(pFunc); + + try + { + // Collect the function arguments from the value stack + switch(a_FunTok.GetArgCount()) + { + case 0: valTok.SetVal( ((strfun_type1)pFunc)(a_vArg[0].GetAsString().c_str()) ); break; + case 1: valTok.SetVal( ((strfun_type2)pFunc)(a_vArg[1].GetAsString().c_str(), + a_vArg[0].GetVal()) ); break; + case 2: valTok.SetVal( ((strfun_type3)pFunc)(a_vArg[2].GetAsString().c_str(), + a_vArg[1].GetVal(), + a_vArg[0].GetVal()) ); break; + default: Error(ecINTERNAL_ERROR); + } + } + catch(ParserError& /*e*/) + { + Error(ecVAL_EXPECTED, m_pTokenReader->GetPos(), a_FunTok.GetAsString()); + } + + // Find out if the result will depend on a variable + /** \todo remove this loop, put content in the loop that takes the argument values. + + (Attention: SetVal will reset Flags.) + */ + bool bVolatile = a_FunTok.IsFlagSet(token_type::flVOLATILE); + for (int i=0; (bVolatile==false) && (i &a_stOpt, + ParserStack &a_stVal, + int a_iArgCount) const +{ + assert(m_pTokenReader.get()); + + // Operator stack empty or does not contain tokens with callback functions + if (a_stOpt.empty() || a_stOpt.top().GetFuncAddr()==0 ) + return; + + token_type funTok = a_stOpt.pop(); + assert(funTok.GetFuncAddr()); + + // Binary operators must rely on their internal operator number + // since counting of operators relies on commas for function arguments + // binary operators do not have commas in their expression + int iArgCount = ( funTok.GetCode()==cmOPRT_BIN ) ? funTok.GetArgCount() : a_iArgCount; + + if (funTok.GetArgCount()>0 && iArgCount>funTok.GetArgCount()) + Error(ecTOO_MANY_PARAMS, m_pTokenReader->GetPos()-1, funTok.GetAsString()); + + if ( funTok.GetCode()!=cmOPRT_BIN && iArgCountGetPos()-1, funTok.GetAsString()); + + if ( funTok.GetCode()==cmFUNC_STR && iArgCount>funTok.GetArgCount() ) + Error(ecTOO_MANY_PARAMS, m_pTokenReader->GetPos()-1, funTok.GetAsString()); + + // Collect the numeric function arguments from the value stack and store them + // in a vector + std::vector stArg; + for (int i=0; iGetPos(), funTok.GetAsString()); + } + + // for string functions add the string argument + if (funTok.GetCode()==cmFUNC_STR) + { + stArg.push_back( a_stVal.pop() ); + if ( stArg.back().GetType()==tpSTR && funTok.GetType()!=tpSTR ) + Error(ecVAL_EXPECTED, m_pTokenReader->GetPos(), funTok.GetAsString()); + } + + // String functions accept only one parameter + if (funTok.GetType()==tpSTR) + { + token_type token( ApplyStrFunc(funTok, stArg) ); + a_stVal.push( token ); + } + else + { + token_type token( ApplyNumFunc(funTok, stArg) ); + a_stVal.push( token ); + } +/* +#ifdef __BORLANDC__ + // Borland C++ Compiler does not support taking references on + // unnamed temporaries + if (funTok.GetType()==tpSTR) + { + ParserToken pt( ApplyStrFunc(funTok, stArg.back()) ); + a_stVal.push(pt); + } + else + { + ParserToken pt( ApplyNumFunc(funTok, stArg) ); + a_stVal.push(pt); + } +#else + // String functions accept only one parameter + a_stVal.push( (funTok.GetType()==tpSTR) ? ApplyStrFunc(funTok, stArg) : + ApplyNumFunc(funTok, stArg) ); +#endif // __BORLANDC__ +*/ +} + +//--------------------------------------------------------------------------- +void ParserBase::ApplyBinOprt( ParserStack &a_stOpt, + ParserStack &a_stVal) const +{ + assert(a_stOpt.size()); + + // user defined binary operator + if (a_stOpt.top().GetCode()==cmOPRT_BIN) + { + ApplyFunc(a_stOpt, a_stVal, 2); + } + else + { + // internal binary operator + MUP_ASSERT(a_stVal.size()>=2); + + token_type valTok1 = a_stVal.pop(), + valTok2 = a_stVal.pop(), + optTok = a_stOpt.pop(), + resTok; + + if ( valTok1.GetType()!=valTok2.GetType() || + (valTok1.GetType()==tpSTR && valTok2.GetType()==tpSTR) ) + Error(ecOPRT_TYPE_CONFLICT, m_pTokenReader->GetPos(), optTok.GetAsString()); + + value_type x = valTok2.GetVal(), + y = valTok1.GetVal(); + + switch (optTok.GetCode()) + { + // built in binary operators + case cmAND: resTok.SetVal( (int)x & (int)y ); break; + case cmOR: resTok.SetVal( (int)x | (int)y ); break; + case cmXOR: resTok.SetVal( (int)x ^ (int)y ); break; + case cmLT: resTok.SetVal( x < y ); break; + case cmGT: resTok.SetVal( x > y ); break; + case cmLE: resTok.SetVal( x <= y ); break; + case cmGE: resTok.SetVal( x >= y ); break; + case cmNEQ: resTok.SetVal( x != y ); break; + case cmEQ: resTok.SetVal( x == y ); break; + case cmADD: resTok.SetVal( x + y ); break; + case cmSUB: resTok.SetVal( x - y ); break; + case cmMUL: resTok.SetVal( x * y ); break; + case cmDIV: resTok.SetVal( x / y ); break; + case cmPOW: resTok.SetVal(pow(x, y)); break; + + case cmASSIGN: + // The assignement operator needs special treatment + // it uses a different format when stored in the bytecode! + { + if (valTok2.GetCode()!=cmVAR) + Error(ecINTERNAL_ERROR, 7); + + value_type *pVar = valTok2.GetVar(); + resTok.SetVal( *pVar = y ); + a_stVal.push( resTok ); + + m_vByteCode.AddAssignOp(pVar); + return; // we must return since the following + // stuff does not apply + } + + default: Error(ecINTERNAL_ERROR, 8); + } + + // Create the bytecode entries + if (!m_bOptimize) + { + // Optimization flag is not set + m_vByteCode.AddOp(optTok.GetCode()); + } + else if ( valTok1.IsFlagSet(token_type::flVOLATILE) || + valTok2.IsFlagSet(token_type::flVOLATILE) ) + { + // Optimization flag is not set, but one of the value + // depends on a variable + m_vByteCode.AddOp(optTok.GetCode()); + resTok.AddFlags(token_type::flVOLATILE); + } + else + { + // operator call can be optimized; If optimization is possible + // the two previous tokens must be value tokens / they will be removed + // and replaced with the result of the pending operation. + m_vByteCode.RemoveValEntries(2); + m_vByteCode.AddVal(resTok.GetVal()); + } + + a_stVal.push( resTok ); + } +} + +//--------------------------------------------------------------------------- +/** \brief Parse the command code. + + 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. + + \sa ParseString(), ParseValue() +*/ +value_type ParserBase::ParseCmdCode() const +{ +#if defined(_MSC_VER) + #pragma warning( disable : 4312 ) +#endif + + value_type Stack[99]; + ECmdCode iCode; + bytecode_type idx(0); + int i(0); + + __start: + + idx = m_pCmdCode[i]; + iCode = (ECmdCode)m_pCmdCode[i+1]; + i += 2; + +#ifdef _DEBUG + if (idx>=99) + throw exception_type(ecGENERIC, _T(""), m_pTokenReader->GetFormula(), -1); +#endif + + switch (iCode) + { + // built in binary operators + case cmAND: Stack[idx] = (int)Stack[idx] & (int)Stack[idx+1]; goto __start; + case cmOR: Stack[idx] = (int)Stack[idx] | (int)Stack[idx+1]; goto __start; + case cmXOR: Stack[idx] = (int)Stack[idx] ^ (int)Stack[idx+1]; goto __start; + case cmLE: Stack[idx] = Stack[idx] <= Stack[idx+1]; goto __start; + case cmGE: Stack[idx] = Stack[idx] >= Stack[idx+1]; goto __start; + case cmNEQ: Stack[idx] = Stack[idx] != Stack[idx+1]; goto __start; + case cmEQ: Stack[idx] = Stack[idx] == Stack[idx+1]; goto __start; + case cmLT: Stack[idx] = Stack[idx] < Stack[idx+1]; goto __start; + case cmGT: Stack[idx] = Stack[idx] > Stack[idx+1]; goto __start; + case cmADD: Stack[idx] += Stack[1+idx]; goto __start; + case cmSUB: Stack[idx] -= Stack[1+idx]; goto __start; + case cmMUL: Stack[idx] *= Stack[1+idx]; goto __start; + case cmDIV: Stack[idx] /= Stack[1+idx]; goto __start; + case cmPOW: Stack[idx] = pow(Stack[idx], Stack[1+idx]); goto __start; + + // Assignement needs special treatment + case cmASSIGN: + { + // next is a pointer to the target + value_type **pDest = (value_type**)(&m_pCmdCode[i]); + + // advance index according to pointer size + i += m_vByteCode.GetPtrSize(); + // assign the value + Stack[idx] = **pDest = Stack[idx+1]; + } + goto __start; + + // user defined binary operators + case cmOPRT_BIN: + Stack[idx] = (**(fun_type2**)(&m_pCmdCode[i]))(Stack[idx], Stack[idx+1]); + ++i; + goto __start; + + // variable tokens + case cmVAR: + Stack[idx] = **(value_type**)(&m_pCmdCode[i]); + i += m_vByteCode.GetValSize(); + goto __start; + + // value tokens + case cmVAL: + Stack[idx] = *(value_type*)(&m_pCmdCode[i]); + i += m_vByteCode.GetValSize(); + goto __start; + + // Next is treatment of string functions + case cmFUNC_STR: + { + // The function argument count + int iArgCount = (int)m_pCmdCode[ i++ ]; + + // The index of the string argument in the string table + int iIdxStack = (int)m_pCmdCode[ i++ ]; + MUP_ASSERT( iIdxStack>=0 && iIdxStack<(int)m_vStringBuf.size() ); + + switch(iArgCount) // switch according to argument count + { + case 0: Stack[idx] = (*(strfun_type1*)(&m_pCmdCode[i]))(m_vStringBuf[iIdxStack].c_str()); break; + case 1: Stack[idx] = (*(strfun_type2*)(&m_pCmdCode[i]))(m_vStringBuf[iIdxStack].c_str(), Stack[idx]); break; + case 2: Stack[idx] = (*(strfun_type3*)(&m_pCmdCode[i]))(m_vStringBuf[iIdxStack].c_str(), Stack[idx], Stack[idx+1]); break; + } + i += m_vByteCode.GetPtrSize(); + } + goto __start; + + // Next is treatment of numeric functions + case cmFUNC: + { + int iArgCount = (int)m_pCmdCode[i++]; + + switch(iArgCount) // switch according to argument count + { + case 1: Stack[idx] = (*(fun_type1*)(&m_pCmdCode[i]))(Stack[idx]); break; + case 2: Stack[idx] = (*(fun_type2*)(&m_pCmdCode[i]))(Stack[idx], Stack[idx+1]); break; + case 3: Stack[idx] = (*(fun_type3*)(&m_pCmdCode[i]))(Stack[idx], Stack[idx+1], Stack[idx+2]); break; + case 4: Stack[idx] = (*(fun_type4*)(&m_pCmdCode[i]))(Stack[idx], Stack[idx+1], Stack[idx+2], Stack[idx+3]); break; + case 5: Stack[idx] = (*(fun_type5*)(&m_pCmdCode[i]))(Stack[idx], Stack[idx+1], Stack[idx+2], Stack[idx+3], Stack[idx+4]); break; + default: + if (iArgCount>0) // function with variable arguments store the number as a negative value + Error(ecINTERNAL_ERROR, 1); + + Stack[idx] =(*(multfun_type*)(&m_pCmdCode[i]))(&Stack[idx], -iArgCount); + break; + } + i += m_vByteCode.GetPtrSize(); + } + goto __start; + + case cmEND: + return Stack[1]; + + default: + Error(ecINTERNAL_ERROR, 2); + return 0; + } + +#if defined(_MSC_VER) + #pragma warning( default : 4312 ) +#endif +} + +//--------------------------------------------------------------------------- +/** \brief Return result for constant functions. + + Seems pointless, but for parser functions that are made up of only a value, which occur + in real world applications, this speeds up things by removing the parser overhead almost + completely. +*/ +value_type ParserBase::ParseValue() const +{ + return *(value_type*)(&m_pCmdCode[2]); +} + +//--------------------------------------------------------------------------- +/** \brief One of the two main parse functions. + + 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. + + \sa ParseCmdCode(), ParseValue() +*/ +value_type ParserBase::ParseString() const +{ +#if defined(_MSC_VER) + #pragma warning( disable : 4311 ) +#endif + if (!m_pTokenReader->GetFormula().length()) + Error(ecUNEXPECTED_EOF, 0); + + ParserStack stOpt, stVal; + ParserStack stArgCount; + token_type opta, opt; // for storing operators + token_type val, tval; // for storing value + string_type strBuf; // buffer for string function arguments + + ReInit(); + + for(;;) + { + opt = m_pTokenReader->ReadNextToken(); + + switch (opt.GetCode()) + { + // + // Next three are different kind of value entries + // + case cmSTRING: + opt.SetIdx((int)m_vStringBuf.size()); // Assign buffer index to token + stVal.push(opt); + m_vStringBuf.push_back(opt.GetAsString()); // Store string in internal buffer + break; + + case cmVAR: + stVal.push(opt); + m_vByteCode.AddVar( static_cast(opt.GetVar()) ); + break; + + case cmVAL: + stVal.push(opt); + m_vByteCode.AddVal( opt.GetVal() ); + break; + + case cmCOMMA: + if (stArgCount.empty()) + Error(ecUNEXPECTED_COMMA, m_pTokenReader->GetPos()); + ++stArgCount.top(); // Record number of arguments + // fall through... + case cmEND: + case cmBC: + { + while ( stOpt.size() && stOpt.top().GetCode() != cmBO) + { + if (stOpt.top().GetCode()==cmOPRT_INFIX) + ApplyFunc(stOpt, stVal, 1); // infix operator + else + ApplyBinOprt(stOpt, stVal); + } + + // 20060218 infix operator treatment moved here + if (stOpt.size() && stOpt.top().GetCode()==cmOPRT_INFIX) + ApplyFunc(stOpt, stVal, 1); // infix operator + + if ( opt.GetCode()!=cmBC || stOpt.size()==0 || stOpt.top().GetCode()!=cmBO ) + break; + + // if opt is ")" and opta is "(" the bracket has been evaluated, now its time to check + // if there is either a function or a sign pending + // neither the opening nor the closing bracket will be pushed back to + // the operator stack + // Check if a function is standing in front of the opening bracket, + // if yes evaluate it afterwards check for infix operators + assert(stArgCount.size()); + int iArgCount = stArgCount.pop(); + + stOpt.pop(); // Take opening bracket from stack + + if (iArgCount>1 && ( stOpt.size()==0 || + (stOpt.top().GetCode()!=cmFUNC && + stOpt.top().GetCode()!=cmFUNC_STR) ) ) + Error(ecUNEXPECTED_ARG, m_pTokenReader->GetPos()); + + if (stOpt.size() && stOpt.top().GetCode()!=cmOPRT_INFIX) + ApplyFunc(stOpt, stVal, iArgCount); + } // if bracket content is evaluated + break; + + // + // Next are the binary operator entries + // + case cmAND: // built in binary operators + case cmOR: + case cmXOR: + case 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) + { + if (GetOprtPri(stOpt.top()) < GetOprtPri(opt)) + break; + + if (stOpt.top().GetCode()==cmOPRT_INFIX) + ApplyFunc(stOpt, stVal, 1); // infix operator + else + ApplyBinOprt(stOpt, stVal); + } // while ( ... ) + + // The operator can't be evaluated right now, push back to the operator stack + stOpt.push(opt); + break; + + // + // Last section contains functions and operators implicitely mapped to functions + // + case cmBO: + stArgCount.push( (stOpt.size() && stOpt.top().GetCode()==cmFUNC_STR) ? 0 : 1 ); + stOpt.push(opt); + break; + + case cmFUNC_STR: + case cmFUNC: + case cmOPRT_INFIX: + 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 + + if ( opt.GetCode() == cmEND ) + { + m_vByteCode.Finalize(); + break; + } + +#if defined(MUP_DUMP_STACK) + StackDump(stVal, stOpt); + m_vByteCode.AsciiDump(); +#endif + } // while (true) + + // Store pointer to start of bytecode + m_pCmdCode = m_vByteCode.GetRawData(); + +#if defined(MUP_DUMP_CMDCODE) + m_vByteCode.AsciiDump(); +#endif + + // get the last value (= final result) from the stack + if (stVal.size()!=1) + Error(ecEMPTY_EXPRESSION); + + if (stVal.top().GetType()!=tpDBL) + Error(ecSTR_RESULT); + + // no error, so change the function pointer for the main parse routine + value_type fVal = stVal.top().GetVal(); // Result from String parsing + + if (m_bUseByteCode) + { + m_pParseFormula = (m_pCmdCode[1]==cmVAL && m_pCmdCode[6]==cmEND) ? + &ParserBase::ParseValue : + &ParserBase::ParseCmdCode; + } + + return fVal; + +#if defined(_MSC_VER) + #pragma warning( default : 4311 ) +#endif +} + + +//--------------------------------------------------------------------------- +/** \brief Create an error containing the parse error position. + + This function will create an Parser Exception object containing the error text and + its position. + + \param a_iErrc [in] The error code of type #EErrorCodes. + \param a_iPos [in] The position where the error was detected. + \param a_strTok [in] The token string representation associated with the error. + \throw ParserException always throws thats the only purpose of this function. +*/ +void ParserBase::Error(EErrorCodes a_iErrc, int a_iPos, const string_type &a_sTok) const +{ + throw exception_type(a_iErrc, a_sTok, m_pTokenReader->GetFormula(), a_iPos); +} + +//------------------------------------------------------------------------------ +/** \brief Clear all user defined variables. + + Resets the parser to string parsing mode by calling #ReInit. + \throw nothrow +*/ +void ParserBase::ClearVar() +{ + m_VarDef.clear(); + ReInit(); +} + +//------------------------------------------------------------------------------ +/** \brief Remove a variable from internal storage. + +Removes a variable if it exists. If the Variable does not exist nothing will be done. + +\throw nothrow +*/ +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 the formula. + +Clear the formula and existing bytecode. + +\post Resets the parser to string parsing mode. +\throw nothrow +*/ +void ParserBase::ClearFormula() +{ + m_vByteCode.clear(); + m_pCmdCode = 0; + m_pTokenReader->SetFormula(_T("")); + 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_bOptimize = a_bIsOn; + ReInit(); +} + +//------------------------------------------------------------------------------ +/** \brief Enable or disable parsing from Bytecode. + + \attention There is no reason to disable bytecode. It will + drastically decrease parsing speed. +*/ +void ParserBase::EnableByteCode(bool a_bIsOn) +{ + m_bUseByteCode = a_bIsOn; + if (!a_bIsOn) + ReInit(); +} + +//------------------------------------------------------------------------------ +/** \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; +} + +#if defined(MUP_DUMP_STACK) | defined(MUP_DUMP_CMDCODE) + +//------------------------------------------------------------------------------ +/** \brief Dump stack content. + + This function is used for debugging only. +*/ +void ParserBase::StackDump( const ParserStack &a_stVal, + const ParserStack &a_stOprt ) const +{ + ParserStack stOprt(a_stOprt), + stVal(a_stVal); + + mu::console() << _T("\nValue stack:\n"); + while ( !stVal.empty() ) + { + token_type val = stVal.pop(); + if (val.GetType()==tpSTR) + mu::console() << _T(" \"") << val.GetAsString() << _T("\" "); + else + mu::console() << _T(" ") << val.GetVal() << _T(" "); + } + mu::console() << "\nOperator stack:\n"; + + while ( !stOprt.empty() ) + { + if (stOprt.top().GetCode()<=cmASSIGN) + { + mu::console() << _T("OPRT_INTRNL \"") + << ParserBase::c_DefaultOprt[stOprt.top().GetCode()] + << _T("\" \n"); + } + else + { + switch(stOprt.top().GetCode()) + { + case cmVAR: mu::console() << _T("VAR\n"); break; + case cmVAL: mu::console() << _T("VAL\n"); break; + case cmFUNC: mu::console() << _T("FUNC_NUM \"") + << 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; + default: mu::console() << stOprt.top().GetType() << _T(" "); break; + } + } + stOprt.pop(); + } + + mu::console() << dec << endl; +} + +#endif // defined(MUP_DUMP_STACK) | defined(MUP_DUMP_CMDCODE) + +} // namespace mu + + diff --git a/muparser/muParserBase.h b/muparser/muParserBase.h new file mode 100644 index 0000000..c4cc433 --- /dev/null +++ b/muparser/muParserBase.h @@ -0,0 +1,324 @@ +/* + __________ + _____ __ __\______ \_____ _______ ______ ____ _______ + / \ | | \| ___/\__ \ \_ __ \/ ___/_/ __ \\_ __ \ + | Y Y \| | /| | / __ \_| | \/\___ \ \ ___/ | | \/ + |__|_| /|____/ |____| (____ /|__| /____ > \___ >|__| + \/ \/ \/ \/ + Copyright (C) 2004-2006 Ingo Berg + + Permission is hereby granted, free of charge, to any person obtaining a copy of this + software and associated documentation files (the "Software"), to deal in the Software + without restriction, including without limitation the rights to use, copy, modify, + merge, publish, distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all copies or + substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT + NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ +#ifndef MU_PARSER_BASE_H +#define MU_PARSER_BASE_H + +#include +#include +#include +#include +#include + +#include "muParserDef.h" +#include "muParserStack.h" +#include "muParserTokenReader.h" +#include "muParserBytecode.h" +#include "muParserError.h" + + +namespace mu +{ + +/** \brief Mathematical expressions parser (base parser engine). + + Version 1.27 (20061201) + + 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. + + \author (C) 2004-2006 Ingo Berg +*/ +class ParserBase +{ +friend class ParserTokenReader; + +private: + typedef value_type (ParserBase::*ParseFunction)() const; + typedef ParserToken token_type; + typedef std::vector stringbuf_type; + typedef ParserTokenReader token_reader_type; + + static const char_type *c_DefaultOprt[]; + + public: + /** \brief Type of the error class. + + Included for backwards compatibility. + */ + typedef ParserError exception_type; + + ParserBase(); + ParserBase( const ParserBase &a_Parser ); + ParserBase& operator=(const ParserBase &a_Parser); + + //--------------------------------------------------------------------------- + /** \brief Destructor. (trivial) + + \throw nothrow + */ + virtual ~ParserBase() + {} + + //--------------------------------------------------------------------------- + /** \brief Calculate the result. + + A note on const correctness: + I consider it important that Calc is a const function. + Due to caching operations Calc changes only the state of internal variables with one exception + m_UsedVar this is reset during string parsing and accessible from the outside. Instead of making + Calc non const GetUsedVar is non const because it explicitely calls Eval() forcing this update. + + \pre A formula must be set. + \pre Variables must have been set (if needed) + + \sa #m_pParseFormula + \return The evaluation result + \throw ParseException if no Formula is set or in case of any other error related to the formula. + */ + inline value_type Eval() const + { + return (this->*m_pParseFormula)(); + } + + void SetExpr(const string_type &a_sExpr); + void SetVarFactory(facfun_type a_pFactory, void *pUserData = NULL); + + void EnableOptimizer(bool a_bIsOn=true); + void EnableByteCode(bool a_bIsOn=true); + void EnableBuiltInOprt(bool a_bIsOn=true); + + bool HasBuiltInOprt() const; + void AddValIdent(identfun_type a_pCallback); + +#define MUP_DEFINE_FUNC(TYPE) \ + inline void DefineFun(const string_type &a_strName, TYPE a_pFun, bool a_bAllowOpt = true) \ + { \ + AddCallback( a_strName, ParserCallback(a_pFun, a_bAllowOpt), \ + m_FunDef, ValidNameChars() ); \ + } + + MUP_DEFINE_FUNC(fun_type1) + MUP_DEFINE_FUNC(fun_type2) + MUP_DEFINE_FUNC(fun_type3) + MUP_DEFINE_FUNC(fun_type4) + MUP_DEFINE_FUNC(fun_type5) + MUP_DEFINE_FUNC(multfun_type) + MUP_DEFINE_FUNC(strfun_type1) + MUP_DEFINE_FUNC(strfun_type2) + MUP_DEFINE_FUNC(strfun_type3) +#undef MUP_DEFINE_FUNC + + void DefineOprt(const string_type &a_strName, fun_type2 a_pFun, unsigned a_iPri=0, 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; + + //--------------------------------------------------------------------------- + /** \brief Return the strings of all Operator identifiers. + + GetOprt is a const function returning a pinter to an array of const char pointers. + + \return Returns a pointer to the c_DefaultOprt array of const char *. + \throw nothrow + */ + const char_type ** 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 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 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 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* ValidNameChars() const + { + assert(m_sNameChars.size()); + return m_sNameChars.c_str(); + } + + //--------------------------------------------------------------------------- + /** \brief Virtual function that defines the characters allowed in operator definitions. + \sa #ValidNameChars, #ValidPrefixOprtChars + */ + const char_type* ValidOprtChars() const + { + assert(m_sOprtChars.size()); + return m_sOprtChars.c_str(); + } + + //--------------------------------------------------------------------------- + /** \brief Virtual function that defines the characters allowed in infix operator definitions. + \sa #ValidNameChars, #ValidOprtChars + */ + const char_type* ValidInfixOprtChars() const + { + assert(m_sInfixOprtChars.size()); + return m_sInfixOprtChars.c_str(); + } + + void Error( EErrorCodes a_iErrc, + int a_iPos = (int)mu::string_type::npos, + const string_type &a_strTok = string_type() ) const; + + protected: + + //--------------------------------------------------------------------------- + /** \brief Initialize user defined functions. + + Calls the virtual functions InitFun(), InitConst() and InitOprt(). + */ + void Init() + { + InitCharSets(); + InitFun(); + InitConst(); + InitOprt(); + } + + //--------------------------------------------------------------------------- + virtual void InitCharSets() = 0; + virtual void InitFun() = 0; + virtual void InitConst() = 0; + virtual void InitOprt() = 0; + + 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 ApplyBinOprt(ParserStack &a_stOpt, + ParserStack &a_stVal) const; + + void ApplyFunc(ParserStack &a_stOpt, + ParserStack &a_stVal, + int iArgCount) const; + + token_type ApplyNumFunc(const token_type &a_FunTok, + const std::vector &a_vArg) const; + + token_type ApplyStrFunc(const token_type &a_FunTok, + const std::vector &a_vArg) const; + + int GetOprtPri(const token_type &a_Tok) const; + + value_type ParseString() const; + value_type ParseCmdCode() const; + value_type ParseValue() const; + + void ClearFormula(); + void CheckName(const string_type &a_strName, const string_type &a_CharSet) const; + +#if defined(MUP_DUMP_STACK) | defined(MUP_DUMP_CMDCODE) + void StackDump(const ParserStack &a_stVal, + const ParserStack &a_stOprt) const; +#endif + + /** \brief Pointer to the parser function. + + Eval() calls the function whose address is stored there. + */ + mutable ParseFunction m_pParseFormula; + mutable const ParserByteCode::map_type *m_pCmdCode; ///< Formula converted to bytecode, points to the data of the bytecode class. + mutable ParserByteCode m_vByteCode; ///< The Bytecode class. + mutable stringbuf_type m_vStringBuf; ///< String buffer, used for storing string function arguments + stringbuf_type m_vStringVarBuf; + + /** \brief Managed pointer to the token reader object. */ + std::auto_ptr m_pTokenReader; + + 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_bOptimize; ///< Flag that indicates if the optimizer is on or off. + bool m_bUseByteCode; ///< Flag that indicates if bytecode parsing is on or off. + 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 +}; + +} // namespace mu + +#endif + diff --git a/muparser/muParserBytecode.cpp b/muparser/muParserBytecode.cpp new file mode 100644 index 0000000..7c8db24 --- /dev/null +++ b/muparser/muParserBytecode.cpp @@ -0,0 +1,396 @@ +/* + __________ + _____ __ __\______ \_____ _______ ______ ____ _______ + / \ | | \| ___/\__ \ \_ __ \/ ___/_/ __ \\_ __ \ + | Y Y \| | /| | / __ \_| | \/\___ \ \ ___/ | | \/ + |__|_| /|____/ |____| (____ /|__| /____ > \___ >|__| + \/ \/ \/ \/ + Copyright (C) 2004-2006 Ingo Berg + + Permission is hereby granted, free of charge, to any person obtaining a copy of this + software and associated documentation files (the "Software"), to deal in the Software + without restriction, including without limitation the rights to use, copy, modify, + merge, publish, distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all copies or + substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT + NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#include "muParserBytecode.h" + +#include +#include +#include +#include +#include + +#include "muParserDef.h" +#include "muParserError.h" +#include "muParserToken.h" + + +namespace mu +{ + + //--------------------------------------------------------------------------- + /** \brief Bytecode default constructor. + + \pre [assert] sizeof(value_type)>=sizeof(map_type) + \pre [assert] sizeof(value_type*)>=sizeof(map_type) + */ + ParserByteCode::ParserByteCode() + :m_iStackPos(0) + ,m_vBase() + ,mc_iSizeVal( sizeof(value_type) / sizeof(map_type) ) + ,mc_iSizePtr( std::max( (int)sizeof(value_type*) / + (int)sizeof(map_type), 1 ) ) + ,mc_iSizeValEntry( 2 + mc_iSizeVal) + { + m_vBase.reserve(1000); + assert( sizeof(value_type)>=sizeof(map_type) ); + } + + //--------------------------------------------------------------------------- + /** \brief Destructor (trivial).*/ + ParserByteCode::~ParserByteCode() + {} + + //--------------------------------------------------------------------------- + /** \brief Copy constructor. + + Implemented in Terms of Assign(const ParserByteCode &a_ByteCode) + */ + ParserByteCode::ParserByteCode(const ParserByteCode &a_ByteCode) + :mc_iSizeVal( sizeof(value_type)/sizeof(map_type) ) + ,mc_iSizePtr( sizeof(value_type*) / sizeof(map_type) ) + ,mc_iSizeValEntry( 2 + mc_iSizeVal) + { + 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; + } + + //--------------------------------------------------------------------------- + /** \brief Store an address in bytecode. + + \param a_pAddr Address to be stored. + \throw nothrow + */ + void ParserByteCode::StorePtr(void *a_pAddr) + { + #if defined(_MSC_VER) + #pragma warning( disable : 4311 ) + #endif + + // demo code for packing / unpacking pointers into bytecode +// void *ptr(NULL); +// double **pVal; +// double fVal; +// map_type dbg[2]; +// dbg[0] = *( reinterpret_cast(&a_pAddr) ), +// dbg[1] = *( reinterpret_cast(&a_pAddr) + 1 ); +// Version 1: +// *( (map_type*)&ptr+0) = dbg[0]; +// *( (map_type*)&ptr+1) = dbg[1]; +// Version 2: +// memcpy(&ptr, dbg, sizeof(dbg)); +// Version 3: +// pVal = (double**)dbg; +// fVal = **(double**)dbg; + + for (int i=0; i(&a_pAddr) + i ) ); + } + + #if defined(_MSC_VER) + #pragma warning( default : 4311 ) + #endif + } + + //--------------------------------------------------------------------------- + /** \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_vBase = a_ByteCode.m_vBase; + } + + //--------------------------------------------------------------------------- + /** \brief Add a Variable pointer to bytecode. + \param a_pVar Pointer to be added. + \throw nothrow + */ + void ParserByteCode::AddVar(value_type *a_pVar) + { + m_vBase.push_back( ++m_iStackPos ); + m_vBase.push_back( cmVAR ); + + StorePtr(a_pVar); + + int iSize = GetValSize()-GetPtrSize(); + assert(iSize>=0); + + // Make sure variable entries have the same size like value entries. + // (necessary for optimization; fill with zeros) + for (int i=0; i +
  • value array position of the value
  • +
  • the operator code according to ParserToken::cmVAL
  • +
  • the value stored in #mc_iSizeVal number of bytecode entries.
  • + + + \param a_pVal Value to be added. + \throw nothrow + */ + void ParserByteCode::AddVal(value_type a_fVal) + { + m_vBase.push_back( ++m_iStackPos ); + m_vBase.push_back( cmVAL ); + + for (int i=0; i(&a_fVal) + i) ); + } + + //--------------------------------------------------------------------------- + /** \brief Add an operator identifier to bytecode. + + Operator entries in byte code consist of: +
      +
    • value array position of the result
    • +
    • the operator code according to ParserToken::ECmdCode
    • +
    + + \sa ParserToken::ECmdCode + */ + void ParserByteCode::AddOp(ECmdCode a_Oprt) + { + m_vBase.push_back(--m_iStackPos); + m_vBase.push_back(a_Oprt); + } + + //--------------------------------------------------------------------------- + /** \brief Add an assignement operator + + Operator entries in byte code consist of: +
      +
    • cmASSIGN code
    • +
    • the pointer of the destination variable
    • +
    + + \sa ParserToken::ECmdCode + */ + void ParserByteCode::AddAssignOp(value_type *a_pVar) + { + m_vBase.push_back(--m_iStackPos); + m_vBase.push_back(cmASSIGN); + StorePtr(a_pVar); + } + + //--------------------------------------------------------------------------- + /** \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(void *a_pFun, int a_iArgc) + { + if (a_iArgc>=0) + { + m_iStackPos = m_iStackPos - a_iArgc + 1; + } + else + { + m_iStackPos = m_iStackPos + a_iArgc + 1; + } + + m_vBase.push_back(m_iStackPos); + m_vBase.push_back(cmFUNC); + m_vBase.push_back(a_iArgc); + + StorePtr(a_pFun); + } + + //--------------------------------------------------------------------------- + /** \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(void *a_pFun, int a_iArgc, int a_iIdx) + { + m_iStackPos = m_iStackPos - a_iArgc + 1; + m_vBase.push_back(m_iStackPos); + m_vBase.push_back(cmFUNC_STR); + m_vBase.push_back(a_iArgc); + m_vBase.push_back(a_iIdx); + + StorePtr(a_pFun); + } + + //--------------------------------------------------------------------------- + /** \brief Add end marker to bytecode. + + \throw nothrow + */ + void ParserByteCode::Finalize() + { + // yes we need the end code three times!! (I forgot why) + m_vBase.push_back(cmEND); + m_vBase.push_back(cmEND); + m_vBase.push_back(cmEND); + + // shrink bytecode vector to fit + storage_type(m_vBase).swap(m_vBase); + } + + //--------------------------------------------------------------------------- + /** \brief Get Pointer to bytecode data storage. */ + const ParserByteCode::map_type* ParserByteCode::GetRawData() const + { + assert(m_vBase.size()); + return &m_vBase[0]; + } + + //--------------------------------------------------------------------------- + /** \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_vBase.clear(); + m_iStackPos = 0; + } + + //--------------------------------------------------------------------------- + /** \brief Remove a value number of entries from the bytecode. + + \attention Currently I don't test if the entries are really value entries. + */ + void ParserByteCode::RemoveValEntries(unsigned a_iNumber) + { + unsigned iSize = a_iNumber * mc_iSizeValEntry; + assert( m_vBase.size() >= iSize ); + m_vBase.resize(m_vBase.size()-iSize); + + assert(m_iStackPos >= a_iNumber); + m_iStackPos -= (a_iNumber); + } + + //--------------------------------------------------------------------------- + /** \brief Dump bytecode (for debugging only!). */ + void ParserByteCode::AsciiDump() + { + if (!m_vBase.size()) + { + std::cout << "No bytecode available\n"; + return; + } + + std::cout << "Entries:" << (int)m_vBase.size() + << " (ValSize:" << mc_iSizeVal + << " entries, PtrSize:" << mc_iSizePtr + << " entries, MapSize:" << sizeof(map_type) + << " byte)\n"; + int i = 0; + + while ( m_vBase[i] != cmEND && i<(int)m_vBase.size()) + { + std::cout << "IDX[" << m_vBase[i++] << "]\t"; + switch (m_vBase[i]) + { + case cmVAL: std::cout << "VAL "; ++i; + std::cout << "[" << *( reinterpret_cast(&m_vBase[i]) ) << "]\n"; + i += mc_iSizeVal; + break; + + case cmVAR: std::cout << "VAR "; ++i; + std::cout << "[ADDR: 0x" << std::hex << *(value_type**)&m_vBase[i] << "]\n"; + i += mc_iSizePtr; + + // Variable entries have the same size like value entries + // the remaining spave must be skipped + i+= std::max(mc_iSizeVal - mc_iSizePtr, 0); + break; + + case cmFUNC: + std::cout << "CALL\t"; ++i; + std::cout << "[ARG:" << std::dec << m_vBase[i] << "]"; ++i; + std::cout << "[ADDR: 0x" << std::hex << *(value_type**)&m_vBase[i] << "]\n"; + i += mc_iSizePtr; + break; + + case cmFUNC_STR: + std::cout << "CALL STRFUNC\t"; ++i; + std::cout << "[ARG:" << std::dec << m_vBase[i] << "]"; ++i; + std::cout << "[IDX:" << std::dec << m_vBase[i] << "]"; ++i; + std::cout << "[ADDR: 0x" << *(value_type**)&m_vBase[i] << "]\n"; + i += mc_iSizePtr; + break; + + case cmLT: std::cout << "LT\n"; ++i; break; + case cmGT: std::cout << "GT\n"; ++i; break; + case cmLE: std::cout << "LE\n"; ++i; break; + case cmGE: std::cout << "GE\n"; ++i; break; + case cmEQ: std::cout << "EQ\n"; ++i; break; + case cmNEQ: std::cout << "NEQ\n"; ++i; break; + case cmADD: std::cout << "ADD\n"; ++i; break; + case cmAND: std::cout << "AND\n"; ++i; break; + case cmOR: std::cout << "OR\n"; ++i; break; + case cmXOR: std::cout << "XOR\n"; ++i; break; + case cmSUB: std::cout << "SUB\n"; ++i; break; + case cmMUL: std::cout << "MUL\n"; ++i; break; + case cmDIV: std::cout << "DIV\n"; ++i; break; + case cmPOW: std::cout << "POW\n"; ++i; break; + + case cmASSIGN: + std::cout << "ASSIGN\t"; ++i; + std::cout << "[ADDR: 0x" << *(value_type**)&m_vBase[i] << "]\n"; + i += mc_iSizePtr; + break; + + default: std::cout << "(unknown code: " << m_vBase[i] << ")\n"; + ++i; + break; + } // switch cmdCode + } // while bytecode + + std::cout << "END" << std::endl; + } +} // namespace mu diff --git a/muparser/muParserBytecode.h b/muparser/muParserBytecode.h new file mode 100644 index 0000000..d425990 --- /dev/null +++ b/muparser/muParserBytecode.h @@ -0,0 +1,148 @@ +/* + __________ + _____ __ __\______ \_____ _______ ______ ____ _______ + / \ | | \| ___/\__ \ \_ __ \/ ___/_/ __ \\_ __ \ + | Y Y \| | /| | / __ \_| | \/\___ \ \ ___/ | | \/ + |__|_| /|____/ |____| (____ /|__| /____ > \___ >|__| + \/ \/ \/ \/ + Copyright (C) 2004-2006 Ingo Berg + + Permission is hereby granted, free of charge, to any person obtaining a copy of this + software and associated documentation files (the "Software"), to deal in the Software + without restriction, including without limitation the rights to use, copy, modify, + merge, publish, distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all copies or + substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT + NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ +#ifndef MU_PARSER_BYTECODE_H +#define MU_PARSER_BYTECODE_H + +#include +#include +#include +#include + +#include "muParserDef.h" +#include "muParserError.h" +#include "muParserToken.h" + + +namespace mu +{ + + +/** \brief Bytecode implementation of the Math Parser. + + The bytecode contains the formula converted to revers polish notation stored in a continious + memory area. Associated with this data are operator codes, variable pointers, constant + values and function pointers. Those are necessary in order to calculate the result. + All those data items will be casted to the underlying datatype of the bytecode. + + \author (C) 2004, 2005 Ingo Berg +*/ +class ParserByteCode +{ +public: + /** \brief Underlying type of the container. + + The bytecode is a vector of this type containing control codes, + values and pointers. Values and pointer will be casted to this + type before their storage. + */ + typedef bytecode_type map_type; + +private: + + /** \brief Token type for internal use only. */ + typedef ParserToken token_type; + + /** \brief Core type of the bytecode. */ + typedef std::vector storage_type; + + /** \brief Position in the Calculation array. */ + unsigned m_iStackPos; + + /** \brief Core type of the bytecode. */ + storage_type m_vBase; + + /** \brief Size of a value entry in the bytecode, relative to TMapType size. */ + const int mc_iSizeVal; + + /** \brief Size of a pointer, relative to size of underlying TMapType. + + \attention The size is related to the size of TMapType not bytes! + */ + const int mc_iSizePtr; + + /** \brief A value entry requires that much entires in the bytecode. + + Value entry consists of: +
      +
    • One entry for Stack index
    • +
    • One entry for Token identifier
    • +
    • mc_iSizeVal entries for the value
    • +
        + + \sa AddVal(TBaseData a_fVal) + */ + const int mc_iSizeValEntry; + + void StorePtr(void *a_pAddr); + +public: + ParserByteCode(); + ~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 AddAssignOp(value_type *a_pVar); + void AddFun(void *a_pFun, int a_iArgc); + void AddStrFun(void *a_pFun, int a_iArgc, int a_iIdx); + + void Finalize(); + void clear(); + const map_type* GetRawData() const; + + /** \brief Return size of a value entry. + + That many bytecode entries are necessary to store a value. + + \sa mc_iSizeVal + */ + unsigned GetValSize() const + { + return mc_iSizeVal; + } + + /** \brief Return size of a pointer entry. + + That many bytecode entries are necessary to store a pointer. + + \sa mc_iSizePtr + */ + unsigned GetPtrSize() const + { + return mc_iSizePtr; + } + + void RemoveValEntries(unsigned a_iNumber); + void AsciiDump(); +}; + +} // namespace mu + +#endif + + diff --git a/muparser/muParserCallback.cpp b/muparser/muParserCallback.cpp new file mode 100644 index 0000000..1312ad7 --- /dev/null +++ b/muparser/muParserCallback.cpp @@ -0,0 +1,198 @@ +/* + __________ + _____ __ __\______ \_____ _______ ______ ____ _______ + / \ | | \| ___/\__ \ \_ __ \/ ___/_/ __ \\_ __ \ + | Y Y \| | /| | / __ \_| | \/\___ \ \ ___/ | | \/ + |__|_| /|____/ |____| (____ /|__| /____ > \___ >|__| + \/ \/ \/ \/ + Copyright (C) 2004-2006 Ingo Berg + + Permission is hereby granted, free of charge, to any person obtaining a copy of this + software and associated documentation files (the "Software"), to deal in the Software + without restriction, including without limitation the rights to use, copy, modify, + merge, publish, distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all copies or + substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT + NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#include "muParserCallback.h" + + +namespace mu +{ + + 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_iCode(a_iCode) + ,m_iType(tpDBL) + ,m_bAllowOpti(a_bAllowOpti) + {} + + + ParserCallback::ParserCallback( fun_type2 a_pFun, bool a_bAllowOpti, int a_iPrec, ECmdCode a_iCode) + :m_pFun((void*)a_pFun) + ,m_iArgc(2) + ,m_iPri(a_iPrec) + ,m_iCode(a_iCode) + ,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_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_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_iCode(cmFUNC) + ,m_iType(tpDBL) + ,m_bAllowOpti(a_bAllowOpti) + {} + + ParserCallback::ParserCallback(multfun_type a_pFun, bool a_bAllowOpti) + :m_pFun((void*)a_pFun) + ,m_iArgc(-1) + ,m_iPri(-1) + ,m_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_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_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_iCode(cmFUNC_STR) + ,m_iType(tpSTR) + ,m_bAllowOpti(a_bAllowOpti) + {} + + /** \brief Default constructor. + + \throw nothrow + */ + ParserCallback::ParserCallback() + :m_pFun(0) + ,m_iArgc(0) + ,m_iCode(cmUNKNOWN) + ,m_iType(tpVOID) + ,m_bAllowOpti(0) + {} + + /** \brief Copy constructor. + + \throw nothrow + */ + ParserCallback::ParserCallback(const ParserCallback &a_Fun) + { + m_pFun = a_Fun.m_pFun; + m_iArgc = a_Fun.m_iArgc; + m_bAllowOpti = a_Fun.m_bAllowOpti; + m_iCode = a_Fun.m_iCode; + m_iType = a_Fun.m_iType; + m_iPri = a_Fun.m_iPri; + } + + /** \brief Clone this instance and return a pointer to the new instance. */ + ParserCallback* ParserCallback::Clone() const + { + return new ParserCallback(*this); + } + + + /** \brief Return tru if the function is conservative. + + Conservative functions return always the same result for the same argument. + \throw nothrow + */ + bool ParserCallback::IsOptimizable() const + { + return m_bAllowOpti; + } + + /** \brief Get the callback address for the parser function. + + The type of the address is void. It needs to be recasted according to the + argument number to the right type. + + \throw nothrow + \return #pFun + */ + void* ParserCallback::GetAddr() const + { + return m_pFun; + } + + /** \brief Return the callback code. */ + ECmdCode ParserCallback::GetCode() const + { + return m_iCode; + } + + + ETypeCode ParserCallback::GetType() const + { + return m_iType; + } + + /** \brief Return the operator priority. + + Only valid if the callback token is an operator token (binary or infix). + */ + int ParserCallback::GetPri() const + { + return m_iPri; + } + + /** \brief Returns the number of function Arguments. */ + int ParserCallback::GetArgc() const + { + return m_iArgc; + } +} // namespace mu diff --git a/muparser/muParserCallback.h b/muparser/muParserCallback.h new file mode 100644 index 0000000..9fecddb --- /dev/null +++ b/muparser/muParserCallback.h @@ -0,0 +1,94 @@ +/* + __________ + _____ __ __\______ \_____ _______ ______ ____ _______ + / \ | | \| ___/\__ \ \_ __ \/ ___/_/ __ \\_ __ \ + | Y Y \| | /| | / __ \_| | \/\___ \ \ ___/ | | \/ + |__|_| /|____/ |____| (____ /|__| /____ > \___ >|__| + \/ \/ \/ \/ + Copyright (C) 2004-2006 Ingo Berg + + Permission is hereby granted, free of charge, to any person obtaining a copy of this + software and associated documentation files (the "Software"), to deal in the Software + without restriction, including without limitation the rights to use, copy, modify, + merge, publish, distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all copies or + substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT + NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#ifndef MU_PARSER_CALLBACK_H +#define MU_PARSER_CALLBACK_H + +#include "muParserDef.h" + + +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. + This class is not used for string function prototyping. + + \author (C) 2004-2006 Ingo Berg +*/ +class ParserCallback +{ +public: + ParserCallback(fun_type1 a_pFun, bool a_bAllowOpti, int a_iPrec = -1, ECmdCode a_iCode=cmFUNC); + ParserCallback(fun_type2 a_pFun, bool a_bAllowOpti, int a_iPrec = -1, ECmdCode a_iCode=cmFUNC); + 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(multfun_type a_pFun, bool a_bAllowOpti); + ParserCallback(strfun_type1 a_pFun, bool a_bAllowOpti); + ParserCallback(strfun_type2 a_pFun, bool a_bAllowOpti); + ParserCallback(strfun_type3 a_pFun, bool a_bAllowOpti); + ParserCallback(); + ParserCallback(const ParserCallback &a_Fun); + + ParserCallback* Clone() const; + + bool IsOptimizable() const; + void* GetAddr() const; + ECmdCode GetCode() const; + ETypeCode GetType() const; + int GetPri() const; + int GetArgc() const; + +private: + void *m_pFun; ///< Pointer to the callback function, casted to void + + /** \brief Number of numeric function arguments + + This number is negative for functions with variable number of arguments. in this cases + they represent the actual number of arguments found. + */ + int m_iArgc; + int m_iPri; ///< Valid only for binary and infix operators; Operator precedence. + ECmdCode m_iCode; + ETypeCode m_iType; + bool m_bAllowOpti; ///< Flag indication optimizeability +}; + +//------------------------------------------------------------------------------ +/** \brief Container for Callback objects. */ +typedef std::map funmap_type; + +} // namespace mu + +#endif + diff --git a/muparser/muParserDLL.cpp b/muparser/muParserDLL.cpp new file mode 100644 index 0000000..0f85cb7 --- /dev/null +++ b/muparser/muParserDLL.cpp @@ -0,0 +1,657 @@ +/* + __________ + _____ __ __\______ \_____ _______ ______ ____ _______ + / \ | | \| ___/\__ \ \_ __ \/ ___/_/ __ \\_ __ \ + | Y Y \| | /| | / __ \_| | \/\___ \ \ ___/ | | \/ + |__|_| /|____/ |____| (____ /|__| /____ > \___ >|__| + \/ \/ \/ \/ + Copyright (C) 2004-2006 Ingo Berg + + Permission is hereby granted, free of charge, to any person obtaining a copy of this + software and associated documentation files (the "Software"), to deal in the Software + without restriction, including without limitation the rights to use, copy, modify, + merge, publish, distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all copies or + substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT + NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + + +#if defined(MUPARSER_DLL) && defined(_WIN32) + +#include "muParserDLL.h" +#include "muParser.h" +#include "muParserError.h" + + +#define MU_PARSER_TRY \ + try \ + { + +#define MU_PARSER_CATCH \ + } \ + catch(exception_type &e) \ + { \ + g_bError = true; \ + g_ParserError = e; \ + if (g_pErrHandler) \ + g_pErrHandler(); \ + } \ + catch(...) \ + { \ + g_bError = true; \ + g_ParserError = exception_type(mu::ecINTERNAL_ERROR); \ + if (g_pErrHandler) \ + g_pErrHandler(); \ + } + +//--------------------------------------------------------------------------- +typedef mu::ParserBase::exception_type exception_type; +typedef mu::ParserBase* parser_type; + +#if !defined(_UNICODE) + typedef std::string string_type; +#else + typedef std::wstring string_type; +#endif + +typedef string_type::value_type char_type; + +//--------------------------------------------------------------------------- +// +// +// unexported variables +// +// +//--------------------------------------------------------------------------- + +/** \brief The last exception that was caught. +*/ +exception_type g_ParserError; +errhandler_type g_pErrHandler; + +//--------------------------------------------------------------------------- +/** \brief Flags indicating an error occured. +*/ +bool g_bError; + +//--------------------------------------------------------------------------- +// +// +// unexported functions +// +// +//--------------------------------------------------------------------------- + +parser_type GetPtr(parser_handle a_hParser) +{ + return static_cast(a_hParser); +} + +//--------------------------------------------------------------------------- +/** \brief DLL entry point. +*/ +BOOL APIENTRY DllMain( HANDLE /*hModule*/, DWORD ul_reason_for_call, LPVOID /*lpReserved*/ ) +{ + switch (ul_reason_for_call) + { + case DLL_PROCESS_ATTACH: + case DLL_THREAD_ATTACH: + g_pErrHandler = 0; + g_bError = false; + break; + + case DLL_THREAD_DETACH: + case DLL_PROCESS_DETACH: + break; + } + + return TRUE; +} + +//--------------------------------------------------------------------------- +// +// +// exported functions +// +// +//--------------------------------------------------------------------------- + +MU_PARSER_API void mupSetErrorHandler(errhandler_type a_pHandler) +{ + g_pErrHandler = a_pHandler; +} + +//--------------------------------------------------------------------------- +MU_PARSER_API void mupSetVarFactory(parser_handle a_hParser, facfun_type a_pFactory, void *pUserData) +{ + parser_type p(GetPtr(a_hParser)); + p->SetVarFactory(a_pFactory, pUserData); +} + +//--------------------------------------------------------------------------- +/** \brief Create a new Parser instance and return its handle. +*/ +MU_PARSER_API parser_handle mupInit() +{ + return (void*)(new mu::Parser()); +} + +//--------------------------------------------------------------------------- +/** \brief Release the parser instance related with a parser handle. +*/ +MU_PARSER_API void mupRelease(parser_handle a_hParser) +{ + MU_PARSER_TRY + parser_type p(GetPtr(a_hParser)); + delete p; + MU_PARSER_CATCH +} + +//--------------------------------------------------------------------------- +/** \brief Evaluate the expression. +*/ +MU_PARSER_API double mupEval(parser_handle a_hParser) +{ + MU_PARSER_TRY + parser_type p(GetPtr(a_hParser)); + return p->Eval(); + MU_PARSER_CATCH + + return 0; +} + +//--------------------------------------------------------------------------- +MU_PARSER_API void mupSetExpr(parser_handle a_hParser, const char *a_szExpr) +{ + MU_PARSER_TRY + parser_type p(GetPtr(a_hParser)); + p->SetExpr(a_szExpr); + MU_PARSER_CATCH +} + +//--------------------------------------------------------------------------- +MU_PARSER_API void mupRemoveVar(parser_handle a_hParser, const char *a_szName) +{ + MU_PARSER_TRY + parser_type p(GetPtr(a_hParser)); + p->RemoveVar( string_type(a_szName) ); + MU_PARSER_CATCH +} + +//--------------------------------------------------------------------------- +/** \brief Release all parser variables. + \param a_hParser Handle to the parser instance. +*/ +MU_PARSER_API void mupClearVar(parser_handle a_hParser) +{ + MU_PARSER_TRY + parser_type p(GetPtr(a_hParser)); + p->ClearVar(); + MU_PARSER_CATCH +} + +//--------------------------------------------------------------------------- +/** \brief Release all parser variables. + \param a_hParser Handle to the parser instance. +*/ +MU_PARSER_API void mupClearConst(parser_handle a_hParser) +{ + MU_PARSER_TRY + parser_type p(GetPtr(a_hParser)); + p->ClearConst(); + MU_PARSER_CATCH +} + +//--------------------------------------------------------------------------- +/** \brief Clear all user defined operators. + \param a_hParser Handle to the parser instance. +*/ +MU_PARSER_API void mupClearOprt(parser_handle a_hParser) +{ + MU_PARSER_TRY + parser_type p(GetPtr(a_hParser)); + p->ClearOprt(); + MU_PARSER_CATCH +} + +//--------------------------------------------------------------------------- +MU_PARSER_API void mupDefineFun1(parser_handle a_hParser, const char *a_szName, fun_type1 a_pFun, bool a_bAllowOpt) +{ + MU_PARSER_TRY + parser_type p(GetPtr(a_hParser)); + p->DefineFun(a_szName, a_pFun, a_bAllowOpt); + MU_PARSER_CATCH +} + +//--------------------------------------------------------------------------- +MU_PARSER_API void mupDefineFun2(parser_handle a_hParser, const char *a_szName, fun_type2 a_pFun, bool a_bAllowOpt) +{ + MU_PARSER_TRY + parser_type p(GetPtr(a_hParser)); + p->DefineFun(a_szName, a_pFun, a_bAllowOpt); + MU_PARSER_CATCH +} + +//--------------------------------------------------------------------------- +MU_PARSER_API void mupDefineFun3(parser_handle a_hParser, const char *a_szName, fun_type3 a_pFun, bool a_bAllowOpt) +{ + MU_PARSER_TRY + parser_type p(GetPtr(a_hParser)); + p->DefineFun(a_szName, a_pFun, a_bAllowOpt); + MU_PARSER_CATCH +} + +//--------------------------------------------------------------------------- +MU_PARSER_API void mupDefineFun4(parser_handle a_hParser, const char *a_szName, fun_type4 a_pFun, bool a_bAllowOpt) +{ + MU_PARSER_TRY + parser_type p(GetPtr(a_hParser)); + p->DefineFun(a_szName, a_pFun, a_bAllowOpt); + MU_PARSER_CATCH +} + +//--------------------------------------------------------------------------- +MU_PARSER_API void mupDefineFun5(parser_handle a_hParser, const char *a_szName, fun_type5 a_pFun, bool a_bAllowOpt) +{ + MU_PARSER_TRY + parser_type p(GetPtr(a_hParser)); + p->DefineFun(a_szName, a_pFun, a_bAllowOpt); + MU_PARSER_CATCH +} + +//--------------------------------------------------------------------------- +MU_PARSER_API void mupDefineStrFun1(parser_handle a_hParser, const char *a_szName, strfun_type1 a_pFun, bool a_bAllowOpt) +{ + MU_PARSER_TRY + parser_type p(GetPtr(a_hParser)); + p->DefineFun(a_szName, a_pFun, a_bAllowOpt); + MU_PARSER_CATCH +} + +//--------------------------------------------------------------------------- +MU_PARSER_API void mupDefineStrFun2(parser_handle a_hParser, const char *a_szName, strfun_type2 a_pFun, bool a_bAllowOpt) +{ + MU_PARSER_TRY + parser_type p(GetPtr(a_hParser)); + p->DefineFun(a_szName, a_pFun, a_bAllowOpt); + MU_PARSER_CATCH +} + +//--------------------------------------------------------------------------- +MU_PARSER_API void mupDefineStrFun3(parser_handle a_hParser, const char *a_szName, strfun_type3 a_pFun, bool a_bAllowOpt) +{ + MU_PARSER_TRY + parser_type p(GetPtr(a_hParser)); + p->DefineFun(a_szName, a_pFun, a_bAllowOpt); + MU_PARSER_CATCH +} + +//--------------------------------------------------------------------------- +MU_PARSER_API void mupDefineMultFun(parser_handle a_hParser, const char *a_szName, multfun_type a_pFun, bool a_bAllowOpt) +{ + MU_PARSER_TRY + parser_type p(GetPtr(a_hParser)); + p->DefineFun(a_szName, a_pFun, a_bAllowOpt); + MU_PARSER_CATCH +} + +//--------------------------------------------------------------------------- +MU_PARSER_API void mupDefineOprt(parser_handle a_hParser, const char *a_szName, fun_type2 a_pFun, int a_iPri, bool a_bAllowOpt) +{ + MU_PARSER_TRY + parser_type p(GetPtr(a_hParser)); + p->DefineOprt(a_szName, a_pFun, a_iPri, a_bAllowOpt); + MU_PARSER_CATCH +} + +//--------------------------------------------------------------------------- +MU_PARSER_API void mupDefineVar(parser_handle a_hParser, const char *a_szName, double *a_pVar) +{ + MU_PARSER_TRY + parser_type p(GetPtr(a_hParser)); + p->DefineVar(a_szName, a_pVar); + MU_PARSER_CATCH +} + +//--------------------------------------------------------------------------- +MU_PARSER_API void mupDefineConst(parser_handle a_hParser, const char *a_szName, double a_fVal) +{ + MU_PARSER_TRY + parser_type p(GetPtr(a_hParser)); + p->DefineConst(a_szName, a_fVal); + MU_PARSER_CATCH +} + +//--------------------------------------------------------------------------- +MU_PARSER_API void mupDefineStrConst(parser_handle a_hParser, const char *a_szName, const char *a_szVal) +{ + MU_PARSER_TRY + parser_type p(GetPtr(a_hParser)); + p->DefineStrConst(a_szName, a_szVal); + MU_PARSER_CATCH +} + +//--------------------------------------------------------------------------- +MU_PARSER_API const char* mupGetExpr(parser_handle a_hParser) +{ + MU_PARSER_TRY + parser_type p(GetPtr(a_hParser)); + return p->GetExpr().c_str(); + MU_PARSER_CATCH + + return ""; +} + +//--------------------------------------------------------------------------- +MU_PARSER_API void mupDefinePostfixOprt(parser_handle a_hParser, + const char *a_szName, + fun_type1 a_pOprt, + bool a_bAllowOpt) +{ + MU_PARSER_TRY + parser_type p(GetPtr(a_hParser)); + p->DefinePostfixOprt(a_szName, a_pOprt, a_bAllowOpt); + MU_PARSER_CATCH +} + +//--------------------------------------------------------------------------- +MU_PARSER_API void mupDefineInfixOprt(parser_handle a_hParser, + const char *a_szName, + fun_type1 a_pOprt, + bool a_bAllowOpt) +{ + MU_PARSER_TRY + parser_type p(GetPtr(a_hParser)); + p->DefineInfixOprt(a_szName, a_pOprt, a_bAllowOpt); + MU_PARSER_CATCH +} + +// Define character sets for identifiers +//--------------------------------------------------------------------------- +MU_PARSER_API void mupDefineNameChars(parser_handle a_hParser, const char *a_szCharset) +{ + parser_type p(GetPtr(a_hParser)); + p->DefineNameChars(a_szCharset); +} + +//--------------------------------------------------------------------------- +MU_PARSER_API void mupDefineOprtChars(parser_handle a_hParser, const char *a_szCharset) +{ + parser_type p(GetPtr(a_hParser)); + p->DefineOprtChars(a_szCharset); +} + +//--------------------------------------------------------------------------- +MU_PARSER_API void mupDefineInfixOprtChars(parser_handle a_hParser, const char *a_szCharset) +{ + parser_type p(GetPtr(a_hParser)); + p->DefineInfixOprtChars(a_szCharset); +} + +//--------------------------------------------------------------------------- +/** \brief Get the number of variables defined in the parser. + \param a_hParser [in] Must be a valid parser handle. + \return The number of used variables. + \sa mupGetExprVar +*/ +MU_PARSER_API int mupGetVarNum(parser_handle a_hParser) +{ + MU_PARSER_TRY + parser_type p(GetPtr(a_hParser)); + const mu::varmap_type VarMap = p->GetVar(); + return (int)VarMap.size(); + MU_PARSER_CATCH + + return 0; // never reached +} + +//--------------------------------------------------------------------------- +/** \brief Return a variable that is used in an expression. + + Prior to calling this function call mupGetExprVarNum in order to get the + number of variables in the expression. If the parameter a_iVar is greater + than the number of variables both a_szName and a_pVar will be set to zero. + As a side effect this function will trigger an internal calculation of the + expression undefined variables will be set to zero during this calculation. + During the calculation user defined callback functions present in the expression + will be called, this is unavoidable. + + \param a_hParser [in] A valid parser handle. + \param a_iVar [in] The index of the variable to return. + \param a_szName [out] Pointer to the variable name. + \param a_pVar [out] Pointer to the variable. + \throw nothrow +*/ +MU_PARSER_API void mupGetVar(parser_handle a_hParser, unsigned a_iVar, const char **a_szName, double **a_pVar) +{ + // A static buffer is needed for the name since i cant return the + // pointer from the map. + static char szName[1024]; + + MU_PARSER_TRY + parser_type p(GetPtr(a_hParser)); + const mu::varmap_type VarMap = p->GetVar(); + + if (a_iVar>=VarMap.size()) + { + *a_szName = 0; + *a_pVar = 0; + return; + } + mu::varmap_type::const_iterator item; + + item = VarMap.begin(); + for (unsigned i=0; ifirst.c_str(), sizeof(szName)); + szName[sizeof(szName)-1] = 0; + + *a_szName = &szName[0]; + *a_pVar = item->second; + return; + + MU_PARSER_CATCH + + *a_szName = 0; + *a_pVar = 0; +} + +//--------------------------------------------------------------------------- +/** \brief Get the number of variables used in the expression currently set in the parser. + \param a_hParser [in] Must be a valid parser handle. + \return The number of used variables. + \sa mupGetExprVar +*/ +MU_PARSER_API int mupGetExprVarNum(parser_handle a_hParser) +{ + MU_PARSER_TRY + parser_type p(GetPtr(a_hParser)); + const mu::varmap_type VarMap = p->GetUsedVar(); + return (int)VarMap.size(); + MU_PARSER_CATCH + + return 0; // never reached +} + +//--------------------------------------------------------------------------- +/** \brief Return a variable that is used in an expression. + + Prior to calling this function call mupGetExprVarNum in order to get the + number of variables in the expression. If the parameter a_iVar is greater + than the number of variables both a_szName and a_pVar will be set to zero. + As a side effect this function will trigger an internal calculation of the + expression undefined variables will be set to zero during this calculation. + During the calculation user defined callback functions present in the expression + will be called, this is unavoidable. + + \param a_hParser [in] A valid parser handle. + \param a_iVar [in] The index of the variable to return. + \param a_szName [out] Pointer to the variable name. + \param a_pVar [out] Pointer to the variable. + \throw nothrow +*/ +MU_PARSER_API void mupGetExprVar(parser_handle a_hParser, unsigned a_iVar, const char **a_szName, double **a_pVar) +{ + // A static buffer is needed for the name since i cant return the + // pointer from the map. + static char szName[1024]; + + MU_PARSER_TRY + parser_type p(GetPtr(a_hParser)); + const mu::varmap_type VarMap = p->GetUsedVar(); + + if (a_iVar>=VarMap.size()) + { + *a_szName = 0; + *a_pVar = 0; + return; + } + mu::varmap_type::const_iterator item; + + item = VarMap.begin(); + for (unsigned i=0; ifirst.c_str(), sizeof(szName)); + szName[sizeof(szName)-1] = 0; + + *a_szName = &szName[0]; + *a_pVar = item->second; + return; + + MU_PARSER_CATCH + + *a_szName = 0; + *a_pVar = 0; +} + +//--------------------------------------------------------------------------- +/** \brief Return the number of constants defined in a parser. */ +MU_PARSER_API int mupGetConstNum(parser_handle a_hParser) +{ + MU_PARSER_TRY + parser_type p(GetPtr(a_hParser)); + const mu::valmap_type ValMap = p->GetConst(); + return (int)ValMap.size(); + MU_PARSER_CATCH + + return 0; // never reached +} + +//--------------------------------------------------------------------------- +/** \brief Retrieve name and value of a single parser constant. + \param a_hParser [in] a valid parser handle + \param a_iVar [in] Index of the constant to query + \param a_pszName [out] pointer to a null terminated string with the constant name + \param [out] The constant value +*/ +MU_PARSER_API void mupGetConst(parser_handle a_hParser, unsigned a_iVar, + const char **a_pszName, double &a_fVal) +{ + // A static buffer is needed for the name since i cant return the + // pointer from the map. + static char szName[1024]; + + MU_PARSER_TRY + parser_type p(GetPtr(a_hParser)); + const mu::valmap_type ValMap = p->GetConst(); + + if (a_iVar>=ValMap.size()) + { + *a_pszName = 0; + a_fVal = 0; + return; + } + + mu::valmap_type::const_iterator item; + item = ValMap.begin(); + for (unsigned i=0; ifirst.c_str(), sizeof(szName)); + szName[sizeof(szName)-1] = 0; + + *a_pszName = &szName[0]; + a_fVal = item->second; + return; + + MU_PARSER_CATCH + + *a_pszName = 0; + a_fVal = 0; +} + +//--------------------------------------------------------------------------- +/** \brief Add a custom value regognition function. +*/ +MU_PARSER_API void mupAddValIdent(parser_handle a_hParser, identfun_type a_pFun) +{ + MU_PARSER_TRY + parser_type p(GetPtr(a_hParser)); + p->AddValIdent(a_pFun); + MU_PARSER_CATCH +} + +//--------------------------------------------------------------------------- +/** \brief Query if an error occured. + + After querying the internal error bit will be reset. So a consecutive call + will return false. +*/ +MU_PARSER_API bool mupError() +{ + bool bError(g_bError); + g_bError = false; + return bError; +} + +//--------------------------------------------------------------------------- +/** \brief Reset the internal error flag. +*/ +MU_PARSER_API void mupErrorReset() +{ + g_bError = false; +} + +//--------------------------------------------------------------------------- +/** \brief Return the message associated with the last error. +*/ +MU_PARSER_API const char* mupGetErrorMsg() +{ + return g_ParserError.GetMsg().c_str(); +} + +//--------------------------------------------------------------------------- +/** \brief Return the message associated with the last error. +*/ +MU_PARSER_API const char* mupGetErrorToken() +{ + return g_ParserError.GetToken().c_str(); +} + +//--------------------------------------------------------------------------- +/** \brief Return the code associated with the last error. +*/ +MU_PARSER_API int mupGetErrorCode() +{ + return g_ParserError.GetCode(); +} + +//--------------------------------------------------------------------------- +/** \brief Return the postion associated with the last error. */ +MU_PARSER_API int mupGetErrorPos() +{ + return (int)g_ParserError.GetPos(); +} + + +#endif // MUPARSER_DLL diff --git a/muparser/muParserDLL.h b/muparser/muParserDLL.h new file mode 100644 index 0000000..20e2db8 --- /dev/null +++ b/muparser/muParserDLL.h @@ -0,0 +1,123 @@ +/* + __________ + _____ __ __\______ \_____ _______ ______ ____ _______ + / \ | | \| ___/\__ \ \_ __ \/ ___/_/ __ \\_ __ \ + | Y Y \| | /| | / __ \_| | \/\___ \ \ ___/ | | \/ + |__|_| /|____/ |____| (____ /|__| /____ > \___ >|__| + \/ \/ \/ \/ + Copyright (C) 2004-2006 Ingo Berg + + Permission is hereby granted, free of charge, to any person obtaining a copy of this + software and associated documentation files (the "Software"), to deal in the Software + without restriction, including without limitation the rights to use, copy, modify, + merge, publish, distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all copies or + substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT + NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +// Folgender ifdef-Block ist die Standardmethode zum Erstellen von Makros, die das Exportieren +// aus einer DLL vereinfachen. Alle Dateien in der DLL werden mit dem MUPARSERLIB_EXPORTS-Symbol +// kompiliert, das in der Befehlszeile definiert wurde. Das Symbol darf nicht für ein Projekt definiert werden, +// das diese DLL verwendet. Alle anderen Projekte, deren Quelldateien diese Datei beinhalten, erkennen +// MUPARSERLIB_API-Funktionen als aus einer DLL importiert, während die DLL mit diesem Makro +// definierte Symbole als exportiert ansieht. +#ifndef MU_PARSER_DLL_H +#define MU_PARSER_DLL_H + +#ifdef MUPARSERLIB_EXPORTS +#define MU_PARSER_API __declspec(dllexport) +#else +#define MU_PARSER_API __declspec(dllimport) +#endif + + +#define WIN32_LEAN_AND_MEAN +#include + +typedef void* parser_handle; +typedef double (*fun_type1)(double); +typedef double (*fun_type2)(double, double); +typedef double (*fun_type3)(double, double, double); +typedef double (*fun_type4)(double, double, double, double); +typedef double (*fun_type5)(double, double, double, double, double); +typedef double (*multfun_type)(const double*, int); +typedef double (*strfun_type1)(const char*); +typedef double (*strfun_type2)(const char*, double); +typedef double (*strfun_type3)(const char*, double, double); +typedef void (*errhandler_type)(); +typedef double* (*facfun_type)(const char*, void *); +typedef bool (*identfun_type)(const char*, int&, double&); + +extern "C" +{ + +// Basic operations / initialization +MU_PARSER_API parser_handle mupInit(); +MU_PARSER_API void mupRelease(parser_handle a_hParser); +MU_PARSER_API const char* mupGetExpr(parser_handle a_hParser); +MU_PARSER_API void mupSetExpr(parser_handle a_hParser, const char *a_szExpr); +MU_PARSER_API void mupSetErrorHandler(errhandler_type a_pErrHandler); +MU_PARSER_API void mupSetVarFactory(parser_handle a_hParser, facfun_type a_pFactory, void *pUserData); + +MU_PARSER_API double mupEval(parser_handle a_hParser); + +// Defining callbacks / variables / constants +MU_PARSER_API void mupDefineFun1(parser_handle a_hParser, const char *a_szName, fun_type1 a_pFun, bool a_bAllowOpt = true); +MU_PARSER_API void mupDefineFun2(parser_handle a_hParser, const char *a_szName, fun_type2 a_pFun, bool a_bAllowOpt = true); +MU_PARSER_API void mupDefineFun3(parser_handle a_hParser, const char *a_szName, fun_type3 a_pFun, bool a_bAllowOpt = true); +MU_PARSER_API void mupDefineFun4(parser_handle a_hParser, const char *a_szName, fun_type4 a_pFun, bool a_bAllowOpt = true); +MU_PARSER_API void mupDefineFun5(parser_handle a_hParser, const char *a_szName, fun_type5 a_pFun, bool a_bAllowOpt = true); +// string functions +MU_PARSER_API void mupDefineStrFun1(parser_handle a_hParser, const char *a_szName, strfun_type1 a_pFun, bool a_bAllowOpt = true); +MU_PARSER_API void mupDefineStrFun2(parser_handle a_hParser, const char *a_szName, strfun_type2 a_pFun, bool a_bAllowOpt = true); +MU_PARSER_API void mupDefineStrFun3(parser_handle a_hParser, const char *a_szName, strfun_type3 a_pFun, bool a_bAllowOpt = true); + +MU_PARSER_API void mupDefineMultFun(parser_handle a_hParser, const char *a_szName, multfun_type a_pFun, bool a_bAllowOpt = true); +MU_PARSER_API void mupDefineOprt(parser_handle a_hParser, const char *a_szName, fun_type2 a_pFun, int a_iPri = 0, bool a_bAllowOpt = true); +MU_PARSER_API void mupDefineConst(parser_handle a_hParser, const char *a_szName, double a_fVal); +MU_PARSER_API void mupDefineStrConst(parser_handle a_hParser, const char *a_szName, const char *a_sVal); +MU_PARSER_API void mupDefineVar(parser_handle a_hParser, const char *a_szName, double *a_fVar); +MU_PARSER_API void mupDefinePostfixOprt(parser_handle a_hParser, const char *a_szName, fun_type1 a_pOprt, bool a_bAllowOpt = true); +MU_PARSER_API void mupDefineInfixOprt(parser_handle a_hParser, const char *a_szName, fun_type1 a_pOprt, bool a_bAllowOpt=true); + +// Define character sets for identifiers +MU_PARSER_API void mupDefineNameChars(parser_handle a_hParser, const char *a_szCharset); +MU_PARSER_API void mupDefineOprtChars(parser_handle a_hParser, const char *a_szCharset); +MU_PARSER_API void mupDefineInfixOprtChars(parser_handle a_hParser, const char *a_szCharset); + +// Remove all / single variables +MU_PARSER_API void mupRemoveVar(parser_handle a_hParser, const char *a_szName); +MU_PARSER_API void mupClearVar(parser_handle a_hParser); +MU_PARSER_API void mupClearConst(parser_handle a_hParser); +MU_PARSER_API void mupClearOprt(parser_handle a_hParser); + +// Querying variables / expression variables / constants +MU_PARSER_API int mupGetExprVarNum(parser_handle a_hParser); +MU_PARSER_API int mupGetVarNum(parser_handle a_hParser); +MU_PARSER_API int mupGetConstNum(parser_handle a_hParser); +MU_PARSER_API void mupGetExprVar(parser_handle a_hParser, unsigned a_iVar, const char **a_pszName, double **a_pVar); +MU_PARSER_API void mupGetVar(parser_handle a_hParser, unsigned a_iVar, const char **a_pszName, double **a_pVar); +MU_PARSER_API void mupGetConst(parser_handle a_hParser, unsigned a_iVar, const char **a_pszName, double &a_pVar); + +// Add value recognition callbacks +MU_PARSER_API void mupAddValIdent(parser_handle a_hParser, identfun_type); + +// Error handling +MU_PARSER_API bool mupError(); +MU_PARSER_API void mupErrorReset(); +MU_PARSER_API const char* mupGetErrorMsg(); +MU_PARSER_API int mupGetErrorCode(); +MU_PARSER_API int mupGetErrorPos(); +MU_PARSER_API const char* mupGetErrorToken(); + +} // extern "C" + +#endif diff --git a/muparser/muParserDef.h b/muparser/muParserDef.h new file mode 100644 index 0000000..164b882 --- /dev/null +++ b/muparser/muParserDef.h @@ -0,0 +1,239 @@ +/* + __________ + _____ __ __\______ \_____ _______ ______ ____ _______ + / \ | | \| ___/\__ \ \_ __ \/ ___/_/ __ \\_ __ \ + | Y Y \| | /| | / __ \_| | \/\___ \ \ ___/ | | \/ + |__|_| /|____/ |____| (____ /|__| /____ > \___ >|__| + \/ \/ \/ \/ + Copyright (C) 2004-2006 Ingo Berg + + Permission is hereby granted, free of charge, to any person obtaining a copy of this + software and associated documentation files (the "Software"), to deal in the Software + without restriction, including without limitation the rights to use, copy, modify, + merge, publish, distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all copies or + substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT + NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ +#ifndef MUP_DEF_H +#define MUP_DEF_H + +#include +#include +#include +#include + +#include "muParserFixes.h" + +/** \brief Define the base datatype for values. + + This datatype must be a built in value type. You can not use custom classes. + It has been tested with float, double and long double types, int should + work as well. +*/ +#define MUP_BASETYPE double + + +/** \brief Definition of the basic bytecode datatype. */ +#define MUP_BYTECODE_TYPE long + +/** \brief Maybe I use this for unicode support later. */ +#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 + #endif + + /** \brief Definition of the basic parser string type. */ + #define MUP_STRING_TYPE std::string +#endif + +#if defined(_DEBUG) + /** \brief Debug macro to force an abortion of the programm with a certain message. + */ + #define MUP_FAIL(MSG) \ + bool MSG=false; \ + assert(MSG); + + #ifndef _UNICODE + /** \brief An assertion that does not kill the program. + + This macro is neutralised in UNICODE builds. It's + too difficult to translate. + */ + #define MUP_ASSERT(COND) \ + if (!(COND)) \ + { \ + stringstream_type ss; \ + ss << "Assertion \""#COND"\" failed: " \ + << __FILE__ << " line " \ + << __LINE__ << "."; \ + throw ParserError( ss.str() ); \ + } + #else + #define MUP_ASSERT(COND) + #endif // _UNICODE +#else + #define MUP_FAIL(MSG) + #define MUP_ASSERT(COND) +#endif + +//------------------------------------------------------------------------------ +// +// do not change anything beyond this point... +// +// !!! This section is devoted to macros that are used for debugging +// !!! or for features that are not fully implemented yet. +// +//#define MUP_DUMP_STACK +//#define MUP_DUMP_CMDCODE + + +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. */ + inline std::ostream& console() + { + return std::cout; + } + + /** \brief Encapsulate cin. */ + 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 ... + cmAND = 11, ///< Operator item: logical and + cmOR = 12, ///< Operator item: logical or + cmXOR = 13, ///< Operator item: logical xor + cmASSIGN = 14, ///< Operator item: Assignment operator + cmBO = 15, ///< Operator item: opening bracket + cmBC = 16, ///< Operator item: closing bracket + cmCOMMA = 17, ///< Operator item: comma + cmVAR = 18, ///< variable item + cmSTRVAR = 19, + cmVAL = 20, ///< value item + + cmFUNC = 21, ///< Code for a function item + cmFUNC_STR = 22, ///< Code for a function with a string parameter + + cmSTRING = 23, ///< Code for a string token + cmOPRT_BIN = 24, ///< user defined binary operator + cmOPRT_POSTFIX = 25, ///< code for postfix operators + cmOPRT_INFIX = 26, ///< code for infix operators + cmEND = 27, ///< end of formula + cmUNKNOWN = 28 ///< 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. + }; + + //------------------------------------------------------------------------------ + /** \brief Parser operator precedence values. */ + enum EPrec + { + // binary operators + prLOGIC = 1, ///> logic operators + prCMP = 2, ///> comparsion operators + prADD_SUB = 3, ///> addition + prMUL_DIV = 4, ///> multiplication/division + prPOW = 5, ///> power operator priority (highest) + + // infix operators + prINFIX = 4, ///> Signs have a higher priority than ADD_SUB, but lower than power operator + prPOSTFIX = 4 ///> Postfix operator priority (currently unused) + }; + + //------------------------------------------------------------------------------ + // basic types + typedef MUP_BASETYPE value_type; + typedef MUP_STRING_TYPE string_type; + typedef MUP_BYTECODE_TYPE bytecode_type; + typedef string_type::value_type char_type; + typedef std::basic_stringstream, + std::allocator > stringstream_type; + + // Data container types + typedef std::map varmap_type; + typedef std::map valmap_type; + typedef std::map strmap_type; + + // Parser callbacks + typedef value_type (*fun_type1)(value_type); + typedef value_type (*fun_type2)(value_type, value_type); + typedef value_type (*fun_type3)(value_type, value_type, value_type); + typedef value_type (*fun_type4)(value_type, value_type, value_type, value_type); + typedef value_type (*fun_type5)(value_type, value_type, value_type, value_type, value_type); + typedef value_type (*multfun_type)(const value_type*, int); + typedef value_type (*strfun_type1)(const char_type*); + typedef value_type (*strfun_type2)(const char_type*, value_type); + typedef value_type (*strfun_type3)(const char_type*, value_type, value_type); + + // Parser utility callback functions (unrelated to the math callbacks) + typedef bool (*identfun_type)(const char_type*, int&, value_type&); + typedef value_type* (*facfun_type)(const char_type*, void*); +} // end fo namespace + +#endif + diff --git a/muparser/muParserError.cpp b/muparser/muParserError.cpp new file mode 100644 index 0000000..237f529 --- /dev/null +++ b/muparser/muParserError.cpp @@ -0,0 +1,300 @@ +/* + __________ + _____ __ __\______ \_____ _______ ______ ____ _______ + / \ | | \| ___/\__ \ \_ __ \/ ___/_/ __ \\_ __ \ + | Y Y \| | /| | / __ \_| | \/\___ \ \ ___/ | | \/ + |__|_| /|____/ |____| (____ /|__| /____ > \___ >|__| + \/ \/ \/ \/ + Copyright (C) 2004-2006 Ingo Berg + + Permission is hereby granted, free of charge, to any person obtaining a copy of this + software and associated documentation files (the "Software"), to deal in the Software + without restriction, including without limitation the rights to use, copy, modify, + merge, publish, distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all copies or + substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT + NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ +#include "muParserError.h" + + +namespace mu +{ + const ParserErrorMsg ParserErrorMsg::m_Instance; + + //------------------------------------------------------------------------------ + const ParserErrorMsg& ParserErrorMsg::Instance() + { + return m_Instance; + } + + //------------------------------------------------------------------------------ + string_type ParserErrorMsg::operator[](unsigned a_iIdx) const + { + return (a_iIdx \___ >|__| + \/ \/ \/ \/ + Copyright (C) 2004-2006 Ingo Berg + + Permission is hereby granted, free of charge, to any person obtaining a copy of this + software and associated documentation files (the "Software"), to deal in the Software + without restriction, including without limitation the rights to use, copy, modify, + merge, publish, distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all copies or + substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT + NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#ifndef MU_PARSER_ERROR_H +#define MU_PARSER_ERROR_H + +#include +#include +#include +#include +#include +#include + +#include "muParserDef.h" + + +namespace mu +{ + +/** \brief Error codes. */ +enum EErrorCodes +{ + // Formula syntax errors + ecUNEXPECTED_OPERATOR = 0, ///< Unexpected binary operator found + ecUNASSIGNABLE_TOKEN = 1, ///< Token cant be identified. + ecUNEXPECTED_EOF = 2, ///< Unexpected end of formula. (Example: "2+sin(") + ecUNEXPECTED_COMMA = 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. + ecBUILTIN_OVERLOAD = 19, ///< Trying to overload builtin operator + ecINVALID_FUN_PTR = 20, ///< Invalid callback function pointer + ecINVALID_VAR_PTR = 21, ///< Invalid variable pointer + ecEMPTY_EXPRESSION = 22, ///< The Expression is empty + ecNAME_CONFLICT = 23, ///< Name conflict + ecOPT_PRI = 24, ///< Invalid operator priority + // + ecDOMAIN_ERROR = 25, ///< catch division by zero, sqrt(-1), log(0) (currently unused) + ecDIV_BY_ZERO = 26, ///< Division by zero (currently unused) + ecGENERIC = 27, ///< Generic error + + // internal errors + ecINTERNAL_ERROR = 28, ///< 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 +}; + +//--------------------------------------------------------------------------- +class ParserErrorMsg +{ +public: + typedef ParserErrorMsg self_type; + + ParserErrorMsg& operator=(const ParserErrorMsg &); + ParserErrorMsg(const ParserErrorMsg&); + ParserErrorMsg(); + + ~ParserErrorMsg(); + + static const ParserErrorMsg& Instance(); + string_type operator[](unsigned a_iIdx) const; + +private: + std::vector m_vErrMsg; + static const self_type m_Instance; +}; + +//--------------------------------------------------------------------------- +/** \brief Error class of the parser. + + Part of the math parser package. + + \author Ingo Berg +*/ +/* final */ class ParserError +{ +private: + //------------------------------------------------------------------------------ + /** \brief Replace all ocuurences of a substring with another string. */ + void ReplaceSubString( string_type &strSource, + const string_type &strFind, + const string_type &strReplaceWith); + void Reset(); + +public: + ParserError(); + explicit ParserError(EErrorCodes a_iErrc); + explicit ParserError(const string_type &sMsg); + ParserError( EErrorCodes a_iErrc, + const string_type &sTok, + const string_type &sFormula = string_type(_T("(formula is not available)")), + 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; + std::size_t GetPos() const; + const string_type& GetToken() const; + EErrorCodes GetCode() const; + +private: + string_type m_strMsg; ///< The message string + string_type m_strFormula; ///< Formula string + string_type m_strTok; ///< Token related with the error + int m_iPos; ///< Formula position related to the error + EErrorCodes m_iErrc; ///< Error code + const ParserErrorMsg &m_ErrMsg; +}; + +} // namespace mu + +#endif + diff --git a/muparser/muParserFixes.h b/muparser/muParserFixes.h new file mode 100644 index 0000000..02ca007 --- /dev/null +++ b/muparser/muParserFixes.h @@ -0,0 +1,196 @@ +/* + __________ + _____ __ __\______ \_____ _______ ______ ____ _______ + / \ | | \| ___/\__ \ \_ __ \/ ___/_/ __ \\_ __ \ + | Y Y \| | /| | / __ \_| | \/\___ \ \ ___/ | | \/ + |__|_| /|____/ |____| (____ /|__| /____ > \___ >|__| + \/ \/ \/ \/ + Copyright (C) 2004-2006 Ingo Berg + + Permission is hereby granted, free of charge, to any person obtaining a copy of this + software and associated documentation files (the "Software"), to deal in the Software + without restriction, including without limitation the rights to use, copy, modify, + merge, publish, distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all copies or + substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT + NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#ifndef MU_PARSER_FIXES_H +#define MU_PARSER_FIXES_H + +// +// Compatibility fixes +// + +//--------------------------------------------------------------------------- +// +// Intel Compiler +// +//--------------------------------------------------------------------------- + +#ifdef __INTEL_COMPILER + +// remark #981: operands are evaluated in unspecified order +// disabled -> completely pointless if the functions do not have side effects +// +#pragma warning(disable:981) + +// remark #383: value copied to temporary, reference to temporary used +#pragma warning(disable:383) + +// remark #1572: floating-point equality and inequality comparisons are unreliable +// disabled -> everyone knows it, the parser passes this problem +// deliberately to the user +#pragma warning(disable:1572) + +#endif + + +//--------------------------------------------------------------------------- +// +// MSVC6 +// +//--------------------------------------------------------------------------- + + +#if _MSC_VER==1200 + +/** \brief Macro to replace the MSVC6 auto_ptr with the _my_auto_ptr class. + + Hijack auto_ptr and replace it with a version that actually does + what an auto_ptr normally does. If you use std::auto_ptr in your other code + it might either explode or work much better. The original crap created + by Microsoft, called auto_ptr and bundled with MSVC6 is not standard compliant. +*/ +#define auto_ptr _my_auto_ptr + +// This is another stupidity that needs to be undone in order to de-pollute +// the global namespace! +#undef min +#undef max + + +namespace std +{ + typedef ::size_t size_t; + + //--------------------------------------------------------------------------- + /** \brief MSVC6 fix: Dummy function to put rand into namespace std. + + This is a hack for MSVC6 only. It's dirty, it's ugly and it works, provided + inlining is enabled. Necessary because I will not pollute or change my + code in order to adopt it to MSVC6 interpretation of how C++ should look like! + */ + inline int rand(void) + { + return ::rand(); + } + + //--------------------------------------------------------------------------- + /** \brief MSVC6 fix: Dummy function to put strlen into namespace std. + + This is a hack for MSVC6 only. It's dirty, it's ugly and it works, provided + inlining is enabled. Necessary because I will not pollute or change my + code in order to adopt it to MSVC6 interpretation of how C++ should look like! + */ + inline size_t strlen(const char *szMsg) + { + return ::strlen(szMsg); + } + + //--------------------------------------------------------------------------- + /** \brief MSVC6 fix: Dummy function to put strncmp into namespace std. + + This is a hack for MSVC6 only. It's dirty, it's ugly and it works, provided + inlining is enabled. Necessary because I will not pollute or change my + code in order to adopt it to MSVC6 interpretation of how C++ should look like! + */ + inline int strncmp(const char *a, const char *b, size_t len) + { + return ::strncmp(a,b,len); + } + + //--------------------------------------------------------------------------- + template + T max(T a, T b) + { + return (a>b) ? a : b; + } + + //--------------------------------------------------------------------------- + template + T min(T a, T b) + { + return (a + class _my_auto_ptr + { + public: + typedef _Ty element_type; + + explicit _my_auto_ptr(_Ty *_Ptr = 0) + :_Myptr(_Ptr) + {} + + _my_auto_ptr(_my_auto_ptr<_Ty>& _Right) + :_Myptr(_Right.release()) + {} + + template + operator _my_auto_ptr<_Other>() + { + return (_my_auto_ptr<_Other>(*this)); + } + + template + _my_auto_ptr<_Ty>& operator=(_my_auto_ptr<_Other>& _Right) + { + reset(_Right.release()); + return (*this); + } + + ~auto_ptr() { delete _Myptr; } + _Ty& operator*() const { return (*_Myptr); } + _Ty *operator->() const { return (&**this); } + _Ty *get() const { return (_Myptr); } + + _Ty *release() + { + _Ty *_Tmp = _Myptr; + _Myptr = 0; + return (_Tmp); + } + + void reset(_Ty* _Ptr = 0) + { + if (_Ptr != _Myptr) + delete _Myptr; + _Myptr = _Ptr; + } + + private: + _Ty *_Myptr; + }; // class _my_auto_ptr +} // namespace std + +#endif // Microsoft Visual Studio Version 6.0 + +#endif // include guard + + diff --git a/muparser/muParserInt.cpp b/muparser/muParserInt.cpp new file mode 100644 index 0000000..1b072bc --- /dev/null +++ b/muparser/muParserInt.cpp @@ -0,0 +1,264 @@ +/* + __________ + _____ __ __\______ \_____ _______ ______ ____ _______ + / \ | | \| ___/\__ \ \_ __ \/ ___/_/ __ \\_ __ \ + | Y Y \| | /| | / __ \_| | \/\___ \ \ ___/ | | \/ + |__|_| /|____/ |____| (____ /|__| /____ > \___ >|__| + \/ \/ \/ \/ + Copyright (C) 2004-2006 Ingo Berg + + Permission is hereby granted, free of charge, to any person obtaining a copy of this + software and associated documentation files (the "Software"), to deal in the Software + without restriction, including without limitation the rights to use, copy, modify, + merge, publish, distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all copies or + substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT + NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#include "muParserInt.h" + +#include +#include +#include + +using namespace std; + + +/** \brief Namespace for mathematical applications. */ +namespace mu +{ + +value_type ParserInt::Abs(value_type v) { return Round(fabs(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::LogXor(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); } + +//--------------------------------------------------------------------------- +// Unary operator Callbacks: Infix operators +value_type ParserInt::UnaryMinus(value_type v) +{ + return -Round(v); +} + +//--------------------------------------------------------------------------- +value_type ParserInt::Sum(const value_type* a_afArg, int a_iArgc) +{ + if (!a_iArgc) + throw ParserError(_T("too few arguments for function sum.")); + + value_type fRes=0; + for (int i=0; i> iVal; + int iEnd = stream.tellg(); // Position after reading + + if (iEnd==-1) + return false; + + a_iPos += iEnd; + a_fVal = (value_type)iVal; + return true; +} + +//--------------------------------------------------------------------------- +bool ParserInt::IsHexVal(const char_type *a_szExpr, int &a_iPos, value_type &a_fVal) +{ + if (a_szExpr[0]!='$') + return false; + + unsigned iVal(0); + +// New code based on streams for UNICODE compliance: + stringstream_type::pos_type nPos(0); + stringstream_type ss(a_szExpr+1); + ss >> std::hex >> iVal; + nPos = ss.tellg(); + + if (nPos==(stringstream_type::pos_type)0) + return false; + + a_iPos += 1 + nPos; + +/* + // original code based on sscanf. Working but not UNICODE compliant! + int iLen(0); + if (sscanf(a_szExpr+1, "%x%n", &iVal, &iLen)==0) + return false; + + a_iPos += iLen+1; +*/ + + a_fVal = iVal; + return true; +} + +//--------------------------------------------------------------------------- +bool ParserInt::IsBinVal(const char_type *a_szExpr, int &a_iPos, value_type &a_fVal) +{ + if (a_szExpr[0]!='#') + return false; + + unsigned iVal(0), + iBits(sizeof(iVal)*8), + i(0); + + for (i=0; (a_szExpr[i+1]=='0' || a_szExpr[i+1]=='1') && i> (iBits-i) ); + a_iPos += i+1; + + return true; +} + +//--------------------------------------------------------------------------- +/** \brief Constructor. + + Call ParserBase class constructor and trigger Function, Operator and Constant initialization. +*/ +ParserInt::ParserInt() +:ParserBase() +{ + AddValIdent(IsVal); + AddValIdent(IsHexVal); + AddValIdent(IsBinVal); + + InitCharSets(); + InitFun(); + InitOprt(); +} + +//--------------------------------------------------------------------------- +void ParserInt::InitConst() +{ +} + +//--------------------------------------------------------------------------- +void ParserInt::InitCharSets() +{ + DefineNameChars( _T("0123456789_abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ") ); + DefineOprtChars( _T("+-*^/?<>=!%&|~'_") ); + DefineInfixOprtChars( _T("/+-*^?<>=!%&|~'_") ); +} + +//--------------------------------------------------------------------------- +/** \brief Initialize the default functions. */ +void ParserInt::InitFun() +{ + DefineFun( _T("sign"), Sign); + DefineFun( _T("abs"), Abs); + DefineFun( _T("if"), Ite); + DefineFun( _T("sum"), Sum); + DefineFun( _T("min"), Min); + DefineFun( _T("max"), Max); +} + +//--------------------------------------------------------------------------- +/** \brief Initialize operators. */ +void ParserInt::InitOprt() +{ + // disable all built in operators, not all of them usefull for integer numbers + // (they don't do rounding of values) + EnableBuiltInOprt(false); + + // Disable all built in operators, they wont work with integer numbers + // since they are designed for floating point numbers + DefineInfixOprt( _T("-"), UnaryMinus); + DefineInfixOprt( _T("!"), Not); + + DefineOprt( _T("&"), LogAnd, prLOGIC); + DefineOprt( _T("|"), LogOr, prLOGIC); + DefineOprt( _T("^"), LogXor, 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(">>"), Shr, prMUL_DIV+1); + DefineOprt( _T("<<"), Shl, prMUL_DIV+1); +} + +} // namespace mu diff --git a/muparser/muParserInt.h b/muparser/muParserInt.h new file mode 100644 index 0000000..008792d --- /dev/null +++ b/muparser/muParserInt.h @@ -0,0 +1,93 @@ +/* + __________ + _____ __ __\______ \_____ _______ ______ ____ _______ + / \ | | \| ___/\__ \ \_ __ \/ ___/_/ __ \\_ __ \ + | Y Y \| | /| | / __ \_| | \/\___ \ \ ___/ | | \/ + |__|_| /|____/ |____| (____ /|__| /____ > \___ >|__| + \/ \/ \/ \/ + Copyright (C) 2004-2006 Ingo Berg + + Permission is hereby granted, free of charge, to any person obtaining a copy of this + software and associated documentation files (the "Software"), to deal in the Software + without restriction, including without limitation the rights to use, copy, modify, + merge, publish, distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all copies or + substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT + NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#ifndef MU_PARSER_INT_H +#define MU_PARSER_INT_H + +#include "muParserBase.h" +#include + + +namespace mu +{ + +/** \brief Mathematical expressions parser. + + This version of the parser handles only integer numbers. It disables the built in operators thus it is + slower than muParser. Integer values are stored in the double value_type and converted if needed. +*/ +class ParserInt : public ParserBase +{ +private: + static int Round(value_type v) { return (int)(v + ((v>=0) ? 0.5 : -0.5) ); }; + + static value_type Abs(value_type); + static value_type Sign(value_type); + static value_type Ite(value_type, value_type, value_type); + // !! The unary Minus is a MUST, otherwise you cant use negative signs !! + static value_type UnaryMinus(value_type); + // Functions with variable number of arguments + static value_type Sum(const value_type* a_afArg, int a_iArgc); // sum + static value_type Min(const value_type* a_afArg, int a_iArgc); // minimum + static value_type Max(const value_type* a_afArg, int a_iArgc); // maximum + // binary operator callbacks + static value_type Add(value_type v1, value_type v2); + static value_type Sub(value_type v1, value_type v2); + static value_type Mul(value_type v1, value_type v2); + static value_type Div(value_type v1, value_type v2); + static value_type Mod(value_type v1, value_type v2); + static value_type 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 LogXor(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 bool IsHexVal(const char_type *a_szExpr, int &a_iPos, value_type &a_iVal); + static bool IsBinVal(const char_type *a_szExpr, int &a_iPos, value_type &a_iVal); + static bool IsVal(const char_type *a_szExpr, int &a_iPos, value_type &a_iVal); + +public: + ParserInt(); + + virtual void InitFun(); + virtual void InitOprt(); + virtual void InitConst(); + virtual void InitCharSets(); +}; + +} // namespace mu + +#endif + diff --git a/muparser/muParserStack.h b/muparser/muParserStack.h new file mode 100644 index 0000000..e035582 --- /dev/null +++ b/muparser/muParserStack.h @@ -0,0 +1,120 @@ +/* + __________ + _____ __ __\______ \_____ _______ ______ ____ _______ + / \ | | \| ___/\__ \ \_ __ \/ ___/_/ __ \\_ __ \ + | Y Y \| | /| | / __ \_| | \/\___ \ \ ___/ | | \/ + |__|_| /|____/ |____| (____ /|__| /____ > \___ >|__| + \/ \/ \/ \/ + Copyright (C) 2004-2006 Ingo Berg + + Permission is hereby granted, free of charge, to any person obtaining a copy of this + software and associated documentation files (the "Software"), to deal in the Software + without restriction, including without limitation the rights to use, copy, modify, + merge, publish, distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all copies or + substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT + NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#ifndef MU_PARSER_STACK_H +#define MU_PARSER_STACK_H + +#include +#include +#include +#include + +#include "muParserError.h" +#include "muParserToken.h" + + +namespace mu +{ + + /** \brief Parser stack implementation. + + Stack implementation based on a std::stack. The behaviour of pop() had been + slightly changed in order to get an error code if the stack is empty. + The stack is used within the Parser both as a value stack and as an operator stack. + + \author (C) 2004, 2005 Ingo Berg + */ + template + class ParserStack + { + private: + /** \brief Type of the underlying stack implementation. */ + typedef std::stack > impl_type; + impl_type m_Stack; + + public: + + //--------------------------------------------------------------------------- + ParserStack() + :m_Stack() + {} + + //--------------------------------------------------------------------------- + virtual ~ParserStack() + {} + + //--------------------------------------------------------------------------- + /** \brief Pop a value from the stack. + + Unlike the standard implementation this function will return the value that + is going to be taken from the stack. + + \throw ParserException in case the stack is empty. + \sa pop(int &a_iErrc) + */ + TValueType pop() + { + if (empty()) + throw ParserError( _T("stack is empty.") ); + + TValueType el = top(); + m_Stack.pop(); + return el; + } + + /** \brief Push an object into the stack. + + \param a_Val object to push into the stack. + \throw nothrow + */ + void push(const TValueType& a_Val) + { + m_Stack.push(a_Val); + } + + /** \brief Return the number of stored elements. */ + unsigned size() const + { + return (unsigned)m_Stack.size(); + } + + /** \brief Returns true if stack is empty false otherwise. */ + bool empty() const + { + return m_Stack.size()==0; + } + + /** \brief Return reference to the top object in the stack. + + The top object is the one pushed most recently. + */ + TValueType& top() + { + return m_Stack.top(); + } + }; +} // namespace MathUtils + +#endif diff --git a/muparser/muParserTest.cpp b/muparser/muParserTest.cpp new file mode 100644 index 0000000..eed6cc4 --- /dev/null +++ b/muparser/muParserTest.cpp @@ -0,0 +1,1125 @@ +/* + __________ + _____ __ __\______ \_____ _______ ______ ____ _______ + / \ | | \| ___/\__ \ \_ __ \/ ___/_/ __ \\_ __ \ + | Y Y \| | /| | / __ \_| | \/\___ \ \ ___/ | | \/ + |__|_| /|____/ |____| (____ /|__| /____ > \___ >|__| + \/ \/ \/ \/ + Copyright (C) 2004-2006 Ingo Berg + + Permission is hereby granted, free of charge, to any person obtaining a copy of this + software and associated documentation files (the "Software"), to deal in the Software + without restriction, including without limitation the rights to use, copy, modify, + merge, publish, distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all copies or + substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT + NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#include "muParserTest.h" + +#include +#include +#include + +#define PARSER_CONST_PI 3.141592653589793238462643 +#define PARSER_CONST_E 2.718281828459045235360287 + +using namespace std; + + +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::TestVolatile); + AddTest(&ParserTester::TestMultiArg); + AddTest(&ParserTester::TestFormula); + AddTest(&ParserTester::TestInterface); + AddTest(&ParserTester::TestBinOprt); + AddTest(&ParserTester::TestException); + AddTest(&ParserTester::TestStrArg); + + ParserTester::c_iCount = 0; + } + + //--------------------------------------------------------------------------- + int ParserTester::TestInterface() + { + int iStat = 0; + mu::console() << _T("testing member functions..."); + + // Test RemoveVar + value_type afVal[3] = {1,2,3}; + Parser p; + + try + { + p.DefineVar( _T("a"), &afVal[0]); + p.DefineVar( _T("b"), &afVal[1]); + p.DefineVar( _T("c"), &afVal[2]); + p.SetExpr( _T("a+b+c") ); + p.Eval(); + } + catch(...) + { + iStat += 1; // this is not supposed to happen + } + + try + { + p.RemoveVar( _T("c") ); + p.Eval(); + iStat += 1; // not supposed to reach this, nonexisting variable "c" deleted... + } + catch(...) + { + // failure is expected... + } + + if (iStat==0) + mu::console() << _T("passed") << endl; + else + mu::console() << _T("\n failed with ") << iStat << _T(" errors") << endl; + + return iStat; + } + + //--------------------------------------------------------------------------- + int ParserTester::TestStrArg() + { + int iStat = 0; + mu::console() << _T("testing string arguments..."); + + iStat += EqnTest(_T("valueof(\"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); + + 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("1 xor 2"), 3, true); + iStat += EqnTest(_T("a xor b"), 3, true); // with a=1 and b=2 + iStat += EqnTest(_T("1 xor 2 xor 3"), 0, true); + iStat += EqnTest(_T("a xor b xor 3"), 0, true); // with a=1 and b=2 + iStat += EqnTest(_T("a xor b xor c"), 0, true); // with a=1 and b=2 + iStat += EqnTest(_T("(1 xor 2) xor 3"), 0, true); + iStat += EqnTest(_T("(a xor b) xor c"), 0, true); // with a=1 and b=2 + iStat += EqnTest(_T("(a) xor (b) xor c"), 0, true); // with a=1 and b=2 + iStat += EqnTest(_T("1 or 2"), 3, true); + iStat += EqnTest(_T("a or b"), 3, true); // with a=1 and b=2 + + // Assignement operator + iStat += EqnTest(_T("a = b"), 2, true); + iStat += EqnTest(_T("a = sin(b)"), 0.909297, true); + iStat += EqnTest(_T("a = 1+sin(b)"), 1.909297, true); + + // 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("10 ^ 10"), 0, true); + iStat += EqnTestInt(_T("10 * 10 ^ 99"), 7, 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); +// incorrect: '^' is yor here, not power +// iStat += EqnTestInt("-(1+2)^2", -9, true); +// iStat += EqnTestInt("-1^3", -1, true); + + // Test precedence + // a=1, b=2, c=3 + iStat += EqnTestInt(_T("a + b * c"), 7, true); + iStat += EqnTestInt(_T("a * b + c"), 5, true); + iStat += EqnTestInt(_T("a10"), 0, true); + iStat += EqnTestInt(_T("a"), f1of1) + PARSER_THROWCHECK(PostfixOprt, true, _T("?<"), f1of1) + PARSER_THROWCHECK(PostfixOprt, true, _T("**"), f1of1) + PARSER_THROWCHECK(PostfixOprt, true, _T("xor"), f1of1) + PARSER_THROWCHECK(PostfixOprt, true, _T("and"), f1of1) + PARSER_THROWCHECK(PostfixOprt, true, _T("or"), f1of1) + PARSER_THROWCHECK(PostfixOprt, true, _T("not"), f1of1) + PARSER_THROWCHECK(PostfixOprt, true, _T("!"), f1of1) + // Binary operator + // The following must fail with builtin operators activated + // p.EnableBuiltInOp(true); -> this is the default + 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) + #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 += EqnTest(_T("(1+ 2*a)"), 3, true); // Spaces within formula + 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 + 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 name recognition..."); + + // 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); + + // 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; + + 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; + + 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..."); + + // 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("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); + // correct calculation of arguments + 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(1,2,3)"), 6, true); + iStat += EqnTest( _T("2*sum(1,2,3)"), 12, true); + iStat += EqnTest( _T("2*sum(1,2,3)+2"), 14, true); + iStat += EqnTest( _T("2*sum(-1,2,3)+2"), 10, true); + iStat += EqnTest( _T("2*sum(-1,2,-(-a))+2"), 6, true); + iStat += EqnTest( _T("2*sum(-1,10,-a)+2"), 18, true); + iStat += EqnTest( _T("2*sum(1,2,3)*2"), 24, true); + iStat += EqnTest( _T("sum(1,-max(1,2),3)*2"), 4, true); + iStat += EqnTest( _T("sum(1*3, 4, a+2)"), 10, true); + iStat += EqnTest( _T("sum(1*3, 2*sum(1,2,2), a+2)"), 16, true); + iStat += EqnTest( _T("sum(1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2)"), 24, true); + + // some failures + iStat += EqnTest( _T("sum()"), 0, false); + iStat += EqnTest( _T("sum(,)"), 0, false); + iStat += EqnTest( _T("sum(1,2,)"), 0, false); + iStat += EqnTest( _T("sum(,1,2)"), 0, false); + + if (iStat==0) + mu::console() << _T("passed") << endl; + else + mu::console() << _T("\n failed with ") << iStat << _T(" errors") << endl; + + return iStat; + } + + + //--------------------------------------------------------------------------- + int ParserTester::TestInfixOprt() + { + int iStat = 0; + mu::console() << "testing infix operators..."; + + iStat += EqnTest( _T("-1"), -1, true); + iStat += EqnTest( _T("-(-1)"), 1, true); + iStat += EqnTest( _T("-(-1)*2"), 2, true); + iStat += EqnTest( _T("-(-2)*sqrt(4)"), 4, true); + iStat += EqnTest( _T("-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); + + // 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 + // tiwth 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); + + 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("3m+5"), 5.003, true); + iStat += EqnTest( _T("1000m"), 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("-2m"), -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); + // some incorrect results + iStat += EqnTest( _T("1000m"), 0.1, false); + iStat += EqnTest( _T("(a)m"), 2, false); + // failure due to syntax checking + iStat += EqnTest( _T("a m"), 0, false); + iStat += EqnTest( _T("4 + m"), 0, false); + iStat += EqnTest( _T("m4"), 0, false); + iStat += EqnTest( _T("sin(m)"), 0, false); + iStat += EqnTest( _T("m m"), 0, false); + iStat += EqnTest( _T("m(8)"), 0, false); + iStat += EqnTest( _T("4,m"), 0, false); + iStat += EqnTest( _T("-m"), 0, false); + iStat += EqnTest( _T("2(-m)"), 0, false); + iStat += EqnTest( _T("2(m)"), 0, false); + + if (iStat==0) + mu::console() << _T("passed") << endl; + else + mu::console() << _T("\n failed with ") << iStat << _T(" errors") << endl; + + return iStat; + } + + //--------------------------------------------------------------------------- + /** \brief Test volatile (nonoptimizeable functions). */ + int ParserTester::TestVolatile() + { + int iStat = 0; + mu::console() << "testing volatile/nonvolatile functions..."; + + // First test with volatile flag turned on + try + { + mu::Parser p; + p.DefineFun( _T("rnd"), Rnd, false); + p.DefineFun( _T("valueof"), RndWithString, false); + + // 1st test, compare results from sucessive calculations + p.SetExpr( _T("3+rnd(8)") ); + if (p.Eval()==p.Eval()) iStat += 1; + + // 2nd test, force bytecode creation, compare two results both + // calculated from bytecode + p.SetExpr( _T("3+rnd(8)") ); + p.Eval(); //<- Force bytecode creation + if (p.Eval()==p.Eval()) iStat += 1; + + p.SetExpr( _T("3*rnd(8)+3") ); + p.Eval(); //<- Force bytecode creation + if (p.Eval()==p.Eval()) iStat += 1; + + p.SetExpr( _T("10+3*sin(rnd(8))-1") ); + p.Eval(); //<- Force bytecode creation + if (p.Eval()==p.Eval()) iStat += 1; + + p.SetExpr( _T("3+rnd(rnd(8))*2") ); + p.Eval(); //<- Force bytecode creation + if (p.Eval()==p.Eval()) iStat += 1; + + p.SetExpr( _T("valueof(\"Das ist ein Test\")") ); + p.Eval(); //<- Force bytecode creation + if (p.Eval()==p.Eval()) iStat += 1; + } + catch(Parser::exception_type &e) + { + mu::console() << _T("\n ") << e.GetExpr() << _T(" : ") << e.GetMsg(); + iStat += 1; + } + + // Second test with volatile flag turned off + try + { + mu::Parser p; + p.DefineFun( _T("rnd"), Rnd); + p.DefineFun( _T("valueof"), RndWithString); + + // compare string parsing with bytecode + p.SetExpr( _T("3+rnd(8)") ); + if (p.Eval()!=p.Eval()) iStat += 1; + + p.SetExpr( _T("3+rnd(8)") ); + p.Eval(); //<- Force bytecode creation + if (p.Eval()!=p.Eval()) iStat += 1; + + p.SetExpr( _T("3*rnd(8)+3") ); + p.Eval(); //<- Force bytecode creation + if (p.Eval()!=p.Eval()) iStat += 1; + + p.SetExpr( _T("10+3*sin(rnd(8))-1") ); + p.Eval(); //<- Force bytecode creation + if (p.Eval()!=p.Eval()) iStat += 1; + + p.SetExpr( _T("3+rnd(rnd(8))*2") ); + p.Eval(); //<- Force bytecode creation + if (p.Eval()!=p.Eval()) iStat += 1; + } + catch(Parser::exception_type &e) + { + mu::console() << _T("\n ") << e.GetExpr() << _T(" : ") << e.GetMsg(); + 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::TestFormula() + { + int iStat = 0; + mu::console() << _T("testing sample formulas..."); + + // operator precedencs + iStat += EqnTest( _T("1+2-3*4/5^6"), 2.99923, true); + iStat += EqnTest( _T("1^2/3*4-5+6"), 2.3333, 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)"), 5, 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); + +#if !defined(_UNICODE) + // I can't translate the following two tests to unicode without loosing + // readability. + + // long formula (Reference: Matlab) + iStat += EqnTest( + "(((-9))-e/(((((((pi-(((-7)+(-3)/4/e))))/(((-5))-2)-((pi+(-0))*(sqrt((e+e))*(-8))*(((-pi)+(-pi)-(-9)*(6*5))" + "/(-e)-e))/2)/((((sqrt(2/(-e)+6)-(4-2))+((5/(-2))/(1*(-pi)+3))/8)*pi*((pi/((-2)/(-6)*1*(-1))*(-6)+(-e)))))/" + "((e+(-2)+(-e)*((((-3)*9+(-e)))+(-9)))))))-((((e-7+(((5/pi-(3/1+pi)))))/e)/(-5))/(sqrt((((((1+(-7))))+((((-" + "e)*(-e)))-8))*(-5)/((-e)))*(-6)-((((((-2)-(-9)-(-e)-1)/3))))/(sqrt((8+(e-((-6))+(9*(-9))))*(((3+2-8))*(7+6" + "+(-5))+((0/(-e)*(-pi))+7)))+(((((-e)/e/e)+((-6)*5)*e+(3+(-5)/pi))))+pi))/sqrt((((9))+((((pi))-8+2))+pi))/e" + "*4)*((-5)/(((-pi))*(sqrt(e)))))-(((((((-e)*(e)-pi))/4+(pi)*(-9)))))))+(-pi)", -12.23016549, true); + + // long formula (Reference: Matlab) + iStat += EqnTest( + "(atan(sin((((((((((((((((pi/cos((a/((((0.53-b)-pi)*e)/b))))+2.51)+a)-0.54)/0.98)+b)*b)+e)/a)+b)+a)+b)+pi)/e" + ")+a)))*2.77)", -2.16995656, true); +#endif + + // 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::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("sin(3,4)"), ecTOO_MANY_PARAMS); + iStat += ThrowTest(_T("3,4"), ecUNEXPECTED_COMMA); + iStat += ThrowTest(_T("if(3)"), 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); + + // 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"), ecOPRT_TYPE_CONFLICT); + iStat += ThrowTest( _T("\"a\"+\"b\""), ecOPRT_TYPE_CONFLICT); + 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); + + // assignement operator +// iStat += ThrowTest("maxspec=0", 0, false); + iStat += ThrowTest( _T("3=4"), ecUNEXPECTED_OPERATOR); + iStat += ThrowTest( _T("sin(8)=4"), ecUNEXPECTED_OPERATOR); + iStat += ThrowTest( _T("\"test\"=a"), ecUNEXPECTED_OPERATOR); + iStat += ThrowTest( _T("sin=9"), ecUNEXPECTED_OPERATOR); + iStat += ThrowTest( _T("(8)=5"), ecUNEXPECTED_OPERATOR); + iStat += ThrowTest( _T("(a)=5"), ecUNEXPECTED_OPERATOR); + iStat += ThrowTest( _T("a=\"tttt\""), ecOPRT_TYPE_CONFLICT); + + if (iStat==0) + mu::console() << _T("passed") << endl; + else + mu::console() << _T("\n failed with ") << iStat << _T(" errors") << endl; + + return iStat; + } + + + //--------------------------------------------------------------------------- + void ParserTester::AddTest(testfun_type a_pFun) + { + m_vTestFun.push_back(a_pFun); + } + + //--------------------------------------------------------------------------- + void ParserTester::Run() + { + int iStat = 0; + try + { + for (int i=0; i<(int)m_vTestFun.size(); ++i) + iStat += (this->*m_vTestFun[i])(); + } + catch(Parser::exception_type &e) + { + mu::console() << "\n" << e.GetMsg() << endl; + mu::console() << e.GetToken() << endl; + Abort(); + } + catch(std::exception &e) + { + mu::console() << e.what() << endl; + Abort(); + } + catch(...) + { + mu::console() << "Internal error"; + Abort(); + } + + if (iStat==0) + { + mu::console() << "Test passed (" << ParserTester::c_iCount << " expressions)" << endl; + } + else + { + mu::console() << "Test failed with " << iStat + << " errors (" << ParserTester::c_iCount + << " expressions)" << endl; + } + ParserTester::c_iCount = 0; + } + + + //--------------------------------------------------------------------------- + int ParserTester::ThrowTest(const string_type &a_str, int a_iErrc, bool a_bFail) + { + ParserTester::c_iCount++; + + try + { + double fVal=0; + Parser p; + + p.DefineVar( _T("a"), &fVal); + p.DefineFun( _T("valueof"), ValueOf); + p.DefineFun( _T("strfun1"), StrFun1); + p.DefineFun( _T("strfun2"), StrFun2); + p.DefineFun( _T("strfun3"), StrFun3); + p.SetExpr(a_str); + p.Eval(); + } + catch(Parser::exception_type &e) + { + // output the formula in case of an failed test + if (a_bFail==true && a_iErrc!=e.GetCode() ) + { + mu::console() << _T("\n ") + << _T("Expression: ") << a_str + << _T(" Code:") << e.GetCode() + << _T(" Expected:") << a_iErrc; + } + + return (a_iErrc==e.GetCode()) ? 0 : 1; + } + + // if a_bFail==false no exception is expected + bool bRet((a_bFail==false) ? 0 : 1); + if (bRet==1) + { + mu::console() << _T("\n ") + << _T("Expression: ") << a_str + << _T(" did evaluate; Expected error:") << a_iErrc; + } + + return bRet; + } + + //--------------------------------------------------------------------------- + /** \brief Evaluate a tet expression. + + \return 1 in case of a failure, 0 otherwise. + */ + int ParserTester::EqnTest(const string_type &a_str, double a_fRes, bool a_fPass) + { + ParserTester::c_iCount++; + int iRet(0); + + try + { + Parser *p1, p2, p3; // three parser objects + // they will be used for testing copy and assihnment operators + // p1 is a pointer since i'm going to delete it in order to test if + // parsers after copy construction still refer to members of it. + // !! If this is the case this function will crash !! + + p1 = new mu::Parser(); + // Add constants + p1->DefineConst( _T("pi"), (value_type)PARSER_CONST_PI); + p1->DefineConst( _T("e"), (value_type)PARSER_CONST_E); + p1->DefineConst( _T("const"), 1); + p1->DefineConst( _T("const1"), 2); + p1->DefineConst( _T("const2"), 3); + // 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]); + // functions + 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); + // 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); + + // infix / postfix operator + // (identifiers used here do not have any meaning or make any sense at all) + p1->DefineInfixOprt( _T("$"), sign, prPOW+1); // sign with high priority + p1->DefineInfixOprt( _T("~"), plus2); // high priority + p1->DefinePostfixOprt( _T("m"), Milli); + 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 + value_type fVal[4] = {-999, -998, -997, -996}; // initially should be different + 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 corrupt.") ); + + // Test copy and assignement operators + try + { + // Test copy constructor + std::vector vParser; + vParser.push_back(*p1); + mu::Parser p2 = vParser[0]; // take parser from vector + + // destroy the originals from p2 + vParser.clear(); // delete the vector + delete p1; // delete the original + p1 = 0; + + fVal[2] = p2.Eval(); + + // Test assignement operator + // additionally disable Optimizer this time + mu::Parser p3; + p3 = p2; + p3.EnableOptimizer(false); + fVal[3] = p3.Eval(); + } + catch(std::exception &e) + { + mu::console() << _T("\n ") << e.what() << _T("\n"); + } + + // limited floating point accuracy requires the following test + bool bCloseEnough(true); + for (int i=0; i<4; ++i) + { + bCloseEnough &= (fabs(a_fRes-fVal[i]) <= fabs(fVal[i]*0.0001)); + } + + iRet = ((bCloseEnough && a_fPass) || (!bCloseEnough && !a_fPass)) ? 0 : 1; + if (iRet==1) + { + mu::console() << "\n fail: " << a_str.c_str() + << " (incorrect result; expected: " << a_fRes + << " ;calculated: " << fVal[0]<< ")."; + } + } + catch(Parser::exception_type &e) + { + if (a_fPass) + { + 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 + value_type fVal[2] = {-99, -999}; // results: initially should be different + int iRet(0); + + try + { + 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 ") << e.GetExpr() << _T(" : ") << e.GetMsg(); + } + catch(...) + { + mu::console() << "\n fail: " << a_str.c_str() << " (unexpected exception)"; + return 1; // exceptions other than ParserException are not allowed + } + + return iRet; + } + + //--------------------------------------------------------------------------- + /** \brief Internal error in test class Test is going to be aborted. */ + void ParserTester::Abort() const + { + mu::console() << _T("Test failed (internal error in test class)") << endl; + while (!getchar()); + exit(-1); + } + } // namespace test +} // namespace mu + + + diff --git a/muparser/muParserTest.h b/muparser/muParserTest.h new file mode 100644 index 0000000..fe9bfdd --- /dev/null +++ b/muparser/muParserTest.h @@ -0,0 +1,176 @@ +/* + __________ + _____ __ __\______ \_____ _______ ______ ____ _______ + / \ | | \| ___/\__ \ \_ __ \/ ___/_/ __ \\_ __ \ + | Y Y \| | /| | / __ \_| | \/\___ \ \ ___/ | | \/ + |__|_| /|____/ |____| (____ /|__| /____ > \___ >|__| + \/ \/ \/ \/ + Copyright (C) 2004-2006 Ingo Berg + + Permission is hereby granted, free of charge, to any person obtaining a copy of this + software and associated documentation files (the "Software"), to deal in the Software + without restriction, including without limitation the rights to use, copy, modify, + merge, publish, distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all copies or + substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT + NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#ifndef MU_PARSER_TEST_H +#define MU_PARSER_TEST_H + +#include +#include // for accumulate +#include "muParser.h" +#include "muParserInt.h" + + +namespace mu +{ + /** \brief Namespace for test cases. */ + namespace Test + { + //------------------------------------------------------------------------------ + /** \brief Test cases for unit testing. + + (C) 2004-2006 Ingo Berg + */ + class ParserTester // final + { + private: + // Multiarg callbacks + static value_type f1of1(value_type v) { return v;}; + + static value_type f1of2(value_type v, value_type ) {return v;}; + static value_type f2of2(value_type , value_type v) {return v;}; + + static value_type f1of3(value_type v, value_type , value_type ) {return v;}; + static value_type f2of3(value_type , value_type v, value_type ) {return v;}; + static value_type f3of3(value_type , value_type , value_type v) {return v;}; + + static value_type f1of4(value_type v, value_type, value_type , value_type ) {return v;} + static value_type f2of4(value_type , value_type v, value_type , value_type ) {return v;} + static value_type f3of4(value_type , value_type, value_type v, value_type ) {return v;} + static value_type f4of4(value_type , value_type, value_type , value_type v) {return v;} + + static value_type f1of5(value_type v, value_type, value_type , value_type , value_type ) { return v; } + static value_type f2of5(value_type , value_type v, value_type , value_type , value_type ) { return v; } + static value_type f3of5(value_type , value_type, value_type v, value_type , value_type ) { return v; } + static value_type f4of5(value_type , value_type, value_type , value_type v, value_type ) { return v; } + static value_type f5of5(value_type , value_type, value_type , value_type , value_type v) { return v; } + + static value_type Min(value_type a_fVal1, value_type a_fVal2) { return (a_fVal1a_fVal2) ? a_fVal1 : a_fVal2; } + + static value_type plus2(value_type v1) { return v1+2; } + static value_type times3(value_type v1) { return v1*3; } + static value_type sqr(value_type v1) { return v1*v1; } + + static value_type sign(value_type v) { return -v; } + + static value_type Sum(const value_type* a_afArg, int a_iArgc) + { + if (!a_iArgc) + throw mu::Parser::exception_type( _T("too few arguments for function sum.") ); + + value_type fRes=0; + for (int i=0; i> val; + return val; + } + + static value_type StrFun2(const char_type* v1, value_type v2) + { + int val(0); + stringstream_type(v1) >> val; + return val + v2; + } + + static value_type StrFun3(const char_type* v1, value_type v2, value_type v3) + { + int val(0); + stringstream_type(v1) >> val; + return val + v2 + v3; + } + + static value_type StrToFloat(const char_type* a_szMsg) + { + double val(0); + stringstream_type(a_szMsg) >> val; + return val; + +// using namespace std; // atof is for some compilers in std for some not... +// return atof(a_szMsg); + } + + // postfix operator callback + static value_type Milli(value_type v) { return v/(value_type)1e3; } + + static int c_iCount; + + int TestNames(); + int TestSyntax(); + int TestMultiArg(); + int TestVolatile(); + int TestPostFix(); + int TestFormula(); + int TestInfixOprt(); + int TestBinOprt(); + int TestVarConst(); + int TestInterface(); + int TestException(); + int TestStrArg(); + + void Abort() const; + + public: + typedef int (ParserTester::*testfun_type)(); + + ParserTester(); + void Run(); + + private: + std::vector m_vTestFun; + void AddTest(testfun_type a_pFun); + + // Test Double Parser + int EqnTest(const string_type& a_str, double a_fRes, bool a_fPass); + int 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); + }; + } // namespace Test +} // namespace mu + +#endif + + diff --git a/muparser/muParserToken.h b/muparser/muParserToken.h new file mode 100644 index 0000000..b4c7e54 --- /dev/null +++ b/muparser/muParserToken.h @@ -0,0 +1,464 @@ +/* + __________ + _____ __ __\______ \_____ _______ ______ ____ _______ + / \ | | \| ___/\__ \ \_ __ \/ ___/_/ __ \\_ __ \ + | Y Y \| | /| | / __ \_| | \/\___ \ \ ___/ | | \/ + |__|_| /|____/ |____| (____ /|__| /____ > \___ >|__| + \/ \/ \/ \/ + Copyright (C) 2004-2006 Ingo Berg + + Permission is hereby granted, free of charge, to any person obtaining a copy of this + software and associated documentation files (the "Software"), to deal in the Software + without restriction, including without limitation the rights to use, copy, modify, + merge, publish, distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all copies or + substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT + NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#ifndef MU_PARSER_TOKEN_H +#define MU_PARSER_TOKEN_H + +#include +#include +#include +#include +#include + +#include "muParserError.h" +#include "muParserCallback.h" + + +namespace mu +{ + +/** \brief Encapsulation of the data for a single formula token. + + Formula token implementation. Part of the Math Parser Package. + Formula tokens can be either one of the following: +
          +
        • value
        • +
        • variable
        • +
        • function with numerical arguments
        • +
        • functions with a string as argument
        • +
        • prefix operators
        • +
        • infix operators
        • +
        • binary operator
        • +
        + + \author (C) 2004 Ingo Berg +*/ +template +class ParserToken +{ +public: + /** \brief Additional token flags. */ + enum ETokFlags + { + flVOLATILE = 1 ///< Mark a token that depends on a variable or a function that is not conservative + }; + +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_iFlags; ///< Additional flags for the token. + 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; + std::auto_ptr m_pCallback; + +public: + + //--------------------------------------------------------------------------- + /** \brief Constructor (default). + + Sets token to an neutral state of type cmUNKNOWN. + \throw nothrow + \sa ECmdCode + */ + ParserToken() + :m_iCode(cmUNKNOWN) + ,m_iType(tpVOID) + ,m_pTok(0) + ,m_iFlags(0) + ,m_iIdx(-1) + ,m_strTok() + ,m_pCallback() + {} + + //------------------------------------------------------------------------------ + /** \brief Create token from another one. + + Implemented by calling Assign(...) + \throw nothrow + \post m_iType==cmUNKNOWN + \sa #Assign + */ + ParserToken(const ParserToken &a_Tok) + { + Assign(a_Tok); + } + + //------------------------------------------------------------------------------ + /** \brief Assignement operator. + + Copy token state from another token and return this. + Implemented by calling Assign(...). + \throw nothrow + */ + ParserToken& operator=(const ParserToken &a_Tok) + { + Assign(a_Tok); + return *this; + } + + //------------------------------------------------------------------------------ + /** \brief Copy token information from argument. + + \throw nothrow + */ + void Assign(const ParserToken &a_Tok) + { + m_iCode = a_Tok.m_iCode; + m_pTok = a_Tok.m_pTok; + m_iFlags = a_Tok.m_iFlags; + 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 Add additional flags to the token. + + Flags are currently used to mark volatile (non optimizeable) functions. + \sa m_iFlags, ETokFlags + */ + void AddFlags(int a_iFlags) + { + m_iFlags |= a_iFlags; + } + + //------------------------------------------------------------------------------ + /** \brief Check if a certain flag ist set. + + \throw nothrow + */ + bool IsFlagSet(int a_iFlags) const + { + #if defined(_MSC_VER) + #pragma warning( disable : 4800 ) + #endif + + return (bool)(m_iFlags & a_iFlags); + + #if defined(_MSC_VER) + #pragma warning( default : 4800 ) // int: Variable set to boolean value (may degrade performance) + #endif + } + + //------------------------------------------------------------------------------ + /** \brief Assign a token type. + + Token may not be of type value, variable or function. Those have seperate set functions. + + \pre [assert] a_iType!=cmVAR + \pre [assert] a_iType!=cmVAL + \pre [assert] a_iType!=cmFUNC + \post m_fVal = 0 + \post m_pTok = 0 + */ + ParserToken& Set(ECmdCode a_iType, const TString &a_strTok=TString()) + { + // The following types cant be set this way, they have special Set functions + assert(a_iType!=cmVAR); + assert(a_iType!=cmVAL); + assert(a_iType!=cmFUNC); + + m_iCode = a_iType; + m_iType = tpVOID; + m_pTok = 0; + m_iFlags = 0; + m_strTok = a_strTok; + m_iIdx = -1; + + return *this; + } + + //------------------------------------------------------------------------------ + /** \brief Set Callback type. */ + ParserToken& Set(const ParserCallback &a_pCallback, const TString &a_sTok) + { + assert(a_pCallback.GetAddr()); + + m_iCode = a_pCallback.GetCode(); + m_iType = tpVOID; + m_strTok = a_sTok; + m_pCallback.reset(new ParserCallback(a_pCallback)); + + m_pTok = 0; + m_iFlags = 0; + m_iIdx = -1; + + if (!m_pCallback->IsOptimizable()) + AddFlags(flVOLATILE); + + 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_iFlags = 0; + 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_iFlags = 0; + m_strTok = a_strTok; + m_iIdx = -1; + m_pTok = (void*)a_pVar; + m_pCallback.reset(0); + + AddFlags(ParserToken::flVOLATILE); + 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; // cmSTRVAR; + m_iType = tpSTR; + m_iFlags = 0; + m_strTok = a_strTok; + m_iIdx = static_cast(a_iSize); + + m_pTok = 0; + m_pCallback.reset(0); + + AddFlags(ParserToken::flVOLATILE); + return *this; + } + + //------------------------------------------------------------------------------ + /** \brief Make token a string token. + + String tokens are used to store the value of string function arguments. + \param a_strTok the string content. + \post #m_iType==cmSTRING, #m_strTok==a_strTok + \throw nothrow + */ + ParserToken& SetString(const string_type &a_strTok) + { + m_iCode = cmSTRING; + m_iType = tpSTR; + m_iFlags = 0; + m_iIdx = -1; + + m_pTok = 0; + m_pCallback.reset(0); + + m_strTok = a_strTok; + + 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(); + } + + //------------------------------------------------------------------------------ + /** \brief Return the address of the callback function assoziated with + function and operator tokens. + + \return The pointer stored in #m_pTok. + \throw exception_type if token type is non of: +
          +
        • cmFUNC
        • +
        • cmSTRFUNC
        • +
        • cmPOSTOP
        • +
        • cmINFIXOP
        • +
        • cmOPRT_BIN
        • +
        + \sa ECmdCode + */ + void* GetFuncAddr() const + { + return (m_pCallback.get()) ? m_pCallback->GetAddr() : 0; + } + + //------------------------------------------------------------------------------ + /** \biref Get value of the token. + + Only applicable to variable and value tokens. + \throw exception_type if token is no value/variable token. + */ + TBase GetVal() const + { + switch (m_iCode) + { + case cmVAL: return m_fVal; + case cmVAR: return *((TBase*)m_pTok); + default: throw ParserError(ecVAL_EXPECTED); + } + } + + //------------------------------------------------------------------------------ + /** \brief Get address of a variable token. + + Valid only if m_iType==CmdVar. + \throw exception_type if token is no variable token. + */ + TBase* GetVar() const + { + if (m_iCode!=cmVAR) + throw ParserError(ecINTERNAL_ERROR); + + return (TBase*)m_pTok; + } + + //------------------------------------------------------------------------------ + /** \brief Return the number of function arguments. + + Valid only if m_iType==CmdFUNC. + */ + int GetArgCount() const + { + assert(m_pCallback.get()); + + if (!m_pCallback->GetAddr()) + throw ParserError(ecINTERNAL_ERROR); + + return m_pCallback->GetArgc(); + } + + //------------------------------------------------------------------------------ + /** \brief Return the token identifier. + + If #m_iType is cmSTRING the token identifier is the value of the string argument + for a string function. + \return #m_strTok + \throw nothrow + \sa m_strTok + */ + const TString& GetAsString() const + { + return m_strTok; + } +}; + +} // namespace mu + +#endif + + diff --git a/muparser/muParserTokenReader.cpp b/muparser/muParserTokenReader.cpp new file mode 100644 index 0000000..99c0a0a --- /dev/null +++ b/muparser/muParserTokenReader.cpp @@ -0,0 +1,822 @@ +/* + __________ + _____ __ __\______ \_____ _______ ______ ____ _______ + / \ | | \| ___/\__ \ \_ __ \/ ___/_/ __ \\_ __ \ + | Y Y \| | /| | / __ \_| | \/\___ \ \ ___/ | | \/ + |__|_| /|____/ |____| (____ /|__| /____ > \___ >|__| + \/ \/ \/ \/ + Copyright (C) 2004-2006 Ingo Berg + + Permission is hereby granted, free of charge, to any person obtaining a copy of this + software and associated documentation files (the "Software"), to deal in the Software + without restriction, including without limitation the rights to use, copy, modify, + merge, publish, distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all copies or + substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT + NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#include +#include +#include +#include +#include +#include + +#include "muParserTokenReader.h" +#include "muParserBase.h" + + +namespace mu +{ + + // Forward declaration + class ParserBase; + + //--------------------------------------------------------------------------- + /** \brief Copy constructor. + + \sa Assign + \throw nothrow + */ + ParserTokenReader::ParserTokenReader(const ParserTokenReader &a_Reader) + { + Assign(a_Reader); + } + + //--------------------------------------------------------------------------- + /** \brief Assignement operator. + + Self assignement will be suppressed otherwise #Assign is called. + + \param a_Reader Object to copy to this token reader. + \throw nothrow + */ + ParserTokenReader& ParserTokenReader::operator=(const ParserTokenReader &a_Reader) + { + if (&a_Reader!=this) + Assign(a_Reader); + + return *this; + } + + //--------------------------------------------------------------------------- + /** \brief Assign state of a token reader to this token reader. + + \param a_Reader Object from which the state should be copied. + \throw nothrow + */ + void ParserTokenReader::Assign(const ParserTokenReader &a_Reader) + { + m_pParser = a_Reader.m_pParser; + m_strFormula = a_Reader.m_strFormula; + m_iPos = a_Reader.m_iPos; + m_iSynFlags = a_Reader.m_iSynFlags; + + m_UsedVar = a_Reader.m_UsedVar; + m_pFunDef = a_Reader.m_pFunDef; + m_pConstDef = a_Reader.m_pConstDef; + m_pVarDef = a_Reader.m_pVarDef; + m_pStrVarDef = a_Reader.m_pStrVarDef; + m_pPostOprtDef = a_Reader.m_pPostOprtDef; + m_pInfixOprtDef = a_Reader.m_pInfixOprtDef; + m_pOprtDef = a_Reader.m_pOprtDef; + m_bIgnoreUndefVar = a_Reader.m_bIgnoreUndefVar; + m_vIdentFun = a_Reader.m_vIdentFun; + m_pFactory = a_Reader.m_pFactory; + m_pFactoryData = a_Reader.m_pFactoryData; + m_iBrackets = a_Reader.m_iBrackets; + } + + //--------------------------------------------------------------------------- + /** \brief Constructor. + + Create a Token reader and bind it to a parser object. + + \pre [assert] a_pParser may not be NULL + \post #m_pParser==a_pParser + \param a_pParent Parent parser object of the token reader. + */ + ParserTokenReader::ParserTokenReader(ParserBase *a_pParent) + :m_pParser(a_pParent) + ,m_strFormula() + ,m_iPos(0) + ,m_iSynFlags(0) + ,m_bIgnoreUndefVar(false) + ,m_pFunDef(NULL) + ,m_pPostOprtDef(NULL) + ,m_pInfixOprtDef(NULL) + ,m_pOprtDef(NULL) + ,m_pConstDef(NULL) + ,m_pStrVarDef(NULL) + ,m_pVarDef(NULL) + ,m_pFactory(NULL) + ,m_pFactoryData(NULL) + ,m_vIdentFun() + ,m_UsedVar() + ,m_fZero(0) + ,m_iBrackets(0) + { + assert(m_pParser); + SetParent(m_pParser); + } + + //--------------------------------------------------------------------------- + /** \brief Destructor (trivial). + + \throw nothrow + */ + ParserTokenReader::~ParserTokenReader() + {} + + //--------------------------------------------------------------------------- + /** \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::auto_ptr ptr(new ParserTokenReader(*this)); + ptr->SetParent(a_pParent); + return ptr.release(); + } + + //--------------------------------------------------------------------------- + void ParserTokenReader::AddValIdent(identfun_type a_pCallback) + { + m_vIdentFun.push_back(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::GetFormula() const + { + return m_strFormula; + } + + //--------------------------------------------------------------------------- + /** \brief Return a map containing the used variables only. */ + const varmap_type& ParserTokenReader::GetUsedVar() const + { + 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(); + } + + //--------------------------------------------------------------------------- + void ParserTokenReader::SetDefs( const funmap_type *a_pFunDef, + const funmap_type *a_pOprtDef, + const funmap_type *a_pInfixOprtDef, + const funmap_type *a_pPostOprtDef, + varmap_type *a_pVarDef, + const strmap_type *a_pStrVarDef, + const valmap_type *a_pConstDef ) + { + m_pFunDef = a_pFunDef; + m_pOprtDef = a_pOprtDef; + m_pInfixOprtDef = a_pInfixOprtDef; + m_pPostOprtDef = a_pPostOprtDef; + m_pVarDef = a_pVarDef; + m_pStrVarDef = a_pStrVarDef; + m_pConstDef = a_pConstDef; + } + + //--------------------------------------------------------------------------- + /** \brief Set Flag that contronls behaviour in case of undefined variables beeing found. + + If true, the parser does not throw an exception if an undefined variable is found. + otherwise it does. This variable is used internally only! + It supresses a "undefined variable" exception in GetUsedVar(). + Those function should return a complete list of variables including + those the are not defined by the time of it's call. + */ + void ParserTokenReader::IgnoreUndefVar(bool bIgnore) + { + m_bIgnoreUndefVar = bIgnore; + } + + //--------------------------------------------------------------------------- + /** \brief Reset the token reader to the start of the formula. + + The syntax flags will be reset to a value appropriate for the + start of a formula. + \post #m_iPos==0, #m_iSynFlags = noOPT | noBC | noPOSTOP | noSTR + \throw nothrow + \sa ESynCodes + */ + void ParserTokenReader::ReInit() + { + m_iPos = 0; + m_iSynFlags = noOPT | noBC | noPOSTOP | noASSIGN; + m_iBrackets = 0; + m_UsedVar.clear(); + } + + //--------------------------------------------------------------------------- + /** \brief Read the next token from the string. */ + ParserTokenReader::token_type ParserTokenReader::ReadNextToken() + { + assert(m_pParser); + + std::stack FunArgs; + const char_type *szFormula = m_strFormula.c_str(); + token_type tok; + + while (szFormula[m_iPos]==' ') + ++m_iPos; + + if ( IsEOF(tok) ) return tok; // Check for end of formula + if ( IsOprt(tok) ) return tok; // Check for user defined binary operator + if ( IsBuiltIn(tok) ) return tok; // Check built in operators / tokens + if ( IsFunTok(tok) ) return tok; // Check for function token + if ( IsValTok(tok) ) return tok; // Check for values / constant tokens + if ( IsVarTok(tok) ) return tok; // Check for variable tokens + if ( IsStrVarTok(tok) ) return tok; // Check for string variables + if ( IsString(tok) ) return tok; // Check for String tokens + if ( IsInfixOpTok(tok) ) return tok; // Check for unary operators + if ( IsPostOpTok(tok) ) return tok; // Check for unary operators + + // Check String for undefined variable token. Done only if a + // flag is set indicating to ignore undefined variables. + // This is a way to conditionally avoid an error if + // undefined variables occur. + // The GetUsedVar function must supress the error for + // undefined variables in order to collect all variable + // names including the undefined ones. + if ( (m_bIgnoreUndefVar || m_pFactory) && IsUndefVarTok(tok) ) return tok; + + // Check for unknown token + // + // !!! From this point on there is no exit without an exception possible... + // + string_type strTok; + int iEnd = ExtractToken(m_pParser->ValidNameChars(), strTok, m_iPos); + if (iEnd!=m_iPos) + Error(ecUNASSIGNABLE_TOKEN, m_iPos, strTok); + + Error(ecUNASSIGNABLE_TOKEN, m_iPos, m_strFormula.substr(m_iPos)); + return token_type(); // never reached + } + + //--------------------------------------------------------------------------- + void ParserTokenReader::SetParent(ParserBase *a_pParent) + { + m_pParser = a_pParent; + m_pFunDef = &a_pParent->m_FunDef; + m_pOprtDef = &a_pParent->m_OprtDef; + m_pInfixOprtDef = &a_pParent->m_InfixOprtDef; + m_pPostOprtDef = &a_pParent->m_PostOprtDef; + m_pVarDef = &a_pParent->m_VarDef; + m_pStrVarDef = &a_pParent->m_StrVarDef; + m_pConstDef = &a_pParent->m_ConstDef; + } + + //--------------------------------------------------------------------------- + /** \brief Extract all characters that belong to a certain charset. + + \param a_szCharSet [in] Const char array of the characters allowed in the token. + \param a_strTok [out] The string that consists entirely of characters listed in a_szCharSet. + \param a_iPos [in] Position in the string from where to start reading. + \return The Position of the first character not listed in a_szCharSet. + \throw nothrow + */ + int ParserTokenReader::ExtractToken( const char_type *a_szCharSet, + string_type &a_sTok, int a_iPos ) const + { + int iEnd = (int)m_strFormula.find_first_not_of(a_szCharSet, a_iPos); + + if (iEnd==(int)string_type::npos) + iEnd = (int)m_strFormula.length(); + + a_sTok = string_type( m_strFormula.begin()+a_iPos, m_strFormula.begin()+iEnd); + a_iPos = iEnd; + return iEnd; + } + + //--------------------------------------------------------------------------- + /** \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 **pOprtDef = m_pParser->GetOprtDef(); + const char_type* szFormula = m_strFormula.c_str(); + + // Compare token with function and operator strings + // check string for operator/function + for (int i=0; pOprtDef[i]; i++) + { +#if !defined _UNICODE + std::size_t len = std::strlen( pOprtDef[i] ); + if ( !std::strncmp(&szFormula[m_iPos], pOprtDef[i], len) ) +#else + // this would work for both UNICODE and char but it's so god damn ugly!! + // apart from this this cant be fast + std::size_t len( std::char_traits::length(pOprtDef[i]) ); + if ( string_type(pOprtDef[i]) == string_type(szFormula + m_iPos, szFormula + m_iPos + len) ) +#endif + { + switch(i) + { + case cmAND: + case cmOR: + case cmXOR: + 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 assignement operator need special treatment + if (i==cmASSIGN && m_iSynFlags & noASSIGN) + Error(ecUNEXPECTED_OPERATOR, m_iPos, pOprtDef[i]); + + if (!m_pParser->HasBuiltInOprt()) continue; + if (m_iSynFlags & noOPT) + { + // Maybe its an infix operator not an operator + // Both operator types can share characters in + // their identifiers + if ( IsInfixOpTok(a_Tok) ) + return true; + + Error(ecUNEXPECTED_OPERATOR, m_iPos, pOprtDef[i]); + } + + m_iSynFlags = noBC | noOPT | noCOMMA | noPOSTOP | noASSIGN; + m_iSynFlags |= ( (i != cmEND) && ( i != cmBC) ) ? noEND : 0; + break; + + case cmCOMMA: + if (m_iSynFlags & noCOMMA) + Error(ecUNEXPECTED_COMMA, m_iPos, pOprtDef[i]); + + m_iSynFlags = noBC | noOPT | noEND | noCOMMA | noPOSTOP | noASSIGN; + break; + + case cmBO: + if (m_iSynFlags & noBO) + Error(ecUNEXPECTED_PARENS, m_iPos, pOprtDef[i]); + + m_iSynFlags = noBC | noOPT | noEND | noCOMMA | noPOSTOP | noASSIGN; + ++m_iBrackets; + break; + + case cmBC: + if (m_iSynFlags & noBC) + Error(ecUNEXPECTED_PARENS, m_iPos, pOprtDef[i]); + + m_iSynFlags = noBO | noVAR | noVAL | noFUN | noINFIXOP | noSTR | noASSIGN; + + if (--m_iBrackets<0) + Error(ecUNEXPECTED_PARENS, m_iPos, pOprtDef[i]); + break; + + 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; + } + + //--------------------------------------------------------------------------- + /** \brief Check for End of Formula. + + \return true if an end of formula is found false otherwise. + \param a_Tok [out] If an eof is found the corresponding token will be stored there. + \throw nothrow + \sa IsOprt, IsFunTok, IsStrFunTok, IsValTok, IsVarTok, IsString, IsInfixOpTok, IsPostOpTok + */ + bool ParserTokenReader::IsEOF(token_type &a_Tok) + { + const char_type* szFormula = m_strFormula.c_str(); + + // check for EOF + if ( !szFormula[m_iPos] || szFormula[m_iPos] == '\n') + { + if ( m_iSynFlags & noEND ) + Error(ecUNEXPECTED_EOF, m_iPos); + + if (m_iBrackets>0) + Error(ecMISSING_PARENS, m_iPos, _T(")")); + + m_iSynFlags = 0; + a_Tok.Set(cmEND); + return true; + } + + return false; + } + + //--------------------------------------------------------------------------- + /** \brief Check if a string position contains a unary infix operator. + \return true if a function token has been found false otherwise. + */ + bool ParserTokenReader::IsInfixOpTok(token_type &a_Tok) + { + string_type sTok; + int iEnd = ExtractToken(m_pParser->ValidInfixOprtChars(), sTok, m_iPos); + if (iEnd==m_iPos) + return false; + + funmap_type::const_iterator item = m_pInfixOprtDef->find(sTok); + if (item==m_pInfixOprtDef->end()) + return false; + + a_Tok.Set(item->second, sTok); + m_iPos = (int)iEnd; + + if (m_iSynFlags & noINFIXOP) + Error(ecUNEXPECTED_OPERATOR, m_iPos, a_Tok.GetAsString()); + + m_iSynFlags = noPOSTOP | noINFIXOP | noOPT | noBC | noSTR | noASSIGN; + return true; + } + + //--------------------------------------------------------------------------- + /** \brief Check whether the token at a given position is a function token. + \param a_Tok [out] If a value token is found it will be placed here. + \throw ParserException if Syntaxflags do not allow a function at a_iPos + \return true if a function token has been found false otherwise. + \pre [assert] m_pParser!=0 + */ + bool ParserTokenReader::IsFunTok(token_type &a_Tok) + { + string_type strTok; + int iEnd = ExtractToken(m_pParser->ValidNameChars(), strTok, m_iPos); + if (iEnd==m_iPos) + return false; + + funmap_type::const_iterator item = m_pFunDef->find(strTok); + if (item==m_pFunDef->end()) + return false; + + 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) + { + string_type strTok; + int iEnd = ExtractToken(m_pParser->ValidOprtChars(), strTok, m_iPos); + if (iEnd==m_iPos) + return false; + + funmap_type::const_iterator item = m_pOprtDef->find(strTok); + if (item==m_pOprtDef->end()) + return false; + + a_Tok.Set(item->second, strTok); + + 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; + // nope, no infix operator + Error(ecUNEXPECTED_OPERATOR, m_iPos, a_Tok.GetAsString()); + } + + m_iPos = (int)iEnd; + m_iSynFlags = noBC | noOPT | noCOMMA | noPOSTOP | noEND | noBC | noASSIGN; + return true; + } + + //--------------------------------------------------------------------------- + /** \brief Check if a string position contains a unary post value operator. */ + bool ParserTokenReader::IsPostOpTok(token_type &a_Tok) + { + // Tricky problem with equations like "3m+5": + // m is a postfix operator, + is a valid sign for postfix operators and + // for binary operators parser detects "m+" as operator string and + // finds no matching postfix operator. + // + // This is a special case so this routine slightly differs from the other + // token readers. + + // Test if there could be a postfix operator + string_type sTok; + int iEnd = ExtractToken(m_pParser->ValidOprtChars(), sTok, m_iPos); + if (iEnd==m_iPos) + return false; + + // iteraterate over all postfix operator strings + funmap_type::const_iterator item = m_pPostOprtDef->begin(); + for (item=m_pPostOprtDef->begin(); item!=m_pPostOprtDef->end(); ++item) + { + if (sTok.find(item->first)!=0) + continue; + + a_Tok.Set(item->second, sTok); + m_iPos += (int)item->first.length(); + + if (m_iSynFlags & noPOSTOP) + Error(ecUNEXPECTED_OPERATOR, m_iPos-(int)item->first.length(), item->first); + + m_iSynFlags = noVAL | noVAR | noFUN | noBO | noPOSTOP | noSTR | noASSIGN; + return true; + } + + return false; + } + + //--------------------------------------------------------------------------- + /** \brief Check whether the token at a given position is a value token. + + Value tokens are either values or constants. + + \param a_Tok [out] If a value token is found it will be placed here. + \return true if a value token has been found. + */ + bool ParserTokenReader::IsValTok(token_type &a_Tok) + { + assert(m_pConstDef); + assert(m_pParser); + + #if defined(_MSC_VER) + #pragma warning( disable : 4244 ) + #endif + + string_type strTok; + value_type fVal(0); + int iEnd(0); + + // 2.) Check for user defined constant + // Read everything that could be a constant name + iEnd = ExtractToken(m_pParser->ValidNameChars(), strTok, m_iPos); + if (iEnd!=m_iPos) + { + valmap_type::const_iterator item = m_pConstDef->find(strTok); + if (item!=m_pConstDef->end()) + { + m_iPos = iEnd; + a_Tok.SetVal(item->second, strTok); + + if (m_iSynFlags & noVAL) + Error(ecUNEXPECTED_VAL, m_iPos - (int)strTok.length(), strTok); + + m_iSynFlags = noVAL | noVAR | noFUN | noBO | noINFIXOP | noSTR | noASSIGN; + return true; + } + } + + // 3.call the value recognition functions provided by the user + // Call user defined value recognition functions + std::vector::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) ) + { + strTok.assign(m_strFormula.c_str(), iStart, m_iPos); + 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; + + #if defined(_MSC_VER) + #pragma warning( default : 4244 ) + #endif + } + + //--------------------------------------------------------------------------- + /** \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->size()) + return false; + + string_type strTok; + int iEnd = ExtractToken(m_pParser->ValidNameChars(), strTok, m_iPos); + if (iEnd==m_iPos) + return false; + + varmap_type::const_iterator item = m_pVarDef->find(strTok); + if (item==m_pVarDef->end()) + return false; + + if (m_iSynFlags & noVAR) + Error(ecUNEXPECTED_VAR, m_iPos, strTok); + + m_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 | noPOSTOP | noINFIXOP | noSTR; + return true; + } + + //--------------------------------------------------------------------------- + bool ParserTokenReader::IsStrVarTok(token_type &a_Tok) + { + if (!m_pStrVarDef || !m_pStrVarDef->size()) + return false; + + string_type strTok; + int iEnd = ExtractToken(m_pParser->ValidNameChars(), strTok, m_iPos); + if (iEnd==m_iPos) + return false; + + strmap_type::const_iterator item = m_pStrVarDef->find(strTok); + if (item==m_pStrVarDef->end()) + return false; + + if (m_iSynFlags & noSTR) + Error(ecUNEXPECTED_VAR, m_iPos, strTok); + + m_iPos = iEnd; + if (!m_pParser->m_vStringVarBuf.size()) + Error(ecINTERNAL_ERROR); + + a_Tok.SetString(m_pParser->m_vStringVarBuf[item->second], m_pParser->m_vStringVarBuf.size() ); + + m_iSynFlags = m_iSynFlags = noANY ^ ( noBC | noOPT | noEND | noCOMMA); + return true; + } + + //--------------------------------------------------------------------------- + /** \brief Check wheter a token at a given position is an undefined variable. + + \param a_Tok [out] If a variable tom_pParser->m_vStringBufken has been found it will be placed here. + \return true if a variable token has been found. + \throw nothrow + */ + bool ParserTokenReader::IsUndefVarTok(token_type &a_Tok) + { + string_type strTok; + int iEnd = ExtractToken(m_pParser->ValidNameChars(), strTok, m_iPos); + if (iEnd==m_iPos) + return false; + + if (m_iSynFlags & noVAR) + { + // 20061021 added token string strTok instead of a_Tok.GetAsString() as the + // token identifier. + Error(ecUNEXPECTED_VAR, m_iPos - (int)a_Tok.GetAsString().length(), strTok); + } + + // If a factory is available implicitely create new variables + if (m_pFactory) + { + value_type *fVar = m_pFactory(strTok.c_str(), m_pFactoryData); + a_Tok.SetVar(fVar, strTok ); + + // Do not use m_pParser->DefineVar( strTok, fVar ); + // in order to define the new variable, it will clear the + // m_UsedVar array which will kill previousely defined variables + // from the list + // This is safe because the new variable can never override an existing one + // because they are checked first! + (*m_pVarDef)[strTok] = fVar; + m_UsedVar[strTok] = fVar; // Add variable to used-var-list + } + else + { + a_Tok.SetVar((value_type*)&m_fZero, strTok); + m_UsedVar[strTok] = 0; // Add variable to used-var-list + } + + m_iPos = iEnd; + + // Call the variable factory in order to let it define a new parser variable + m_iSynFlags = noVAL | noVAR | noFUN | noBO | noPOSTOP | noINFIXOP | noSTR; + return true; + } + + //--------------------------------------------------------------------------- + /** \brief Check wheter a token at a given position is a string. + + \param a_Tok [out] If a variable token has been found it will be placed here. + \return true if a string token has been found. + \sa IsOprt, IsFunTok, IsStrFunTok, IsValTok, IsVarTok, IsEOF, IsInfixOpTok, IsPostOpTok + \throw nothrow + */ + bool ParserTokenReader::IsString(token_type &a_Tok) + { + if (m_strFormula[m_iPos]!='"') + return false; + + string_type strBuf(&m_strFormula[m_iPos+1]); + std::size_t iEnd(0), iSkip(0); + + // parser over escaped '\"' end replace them with '"' + for(iEnd=(int)strBuf.find( _T("\"") ); iEnd!=string_type::npos; iEnd=(int)strBuf.find( _T("\""), iEnd)) + { + if (strBuf[iEnd-1]!='\\') break; + strBuf.replace(iEnd-1, 2, _T("\"") ); + iSkip++; + } + + if (iEnd==string_type::npos) + Error(ecUNTERMINATED_STRING, m_iPos, _T("\"") ); + + string_type strTok(strBuf.begin(), strBuf.begin()+iEnd); + + if (m_iSynFlags & noSTR) + Error(ecUNEXPECTED_STR, m_iPos, strTok); + + m_pParser->m_vStringBuf.push_back(strTok); // Store string in internal buffer + a_Tok.SetString(strTok, m_pParser->m_vStringBuf.size()); + + m_iPos += (int)strTok.length() + 2 + (int)iSkip; // +2 wg Anführungszeichen; +iSkip für entfernte escape zeichen + m_iSynFlags = m_iSynFlags = noANY ^ ( noCOMMA | 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); + } +} // namespace mu + diff --git a/muparser/muParserTokenReader.h b/muparser/muParserTokenReader.h new file mode 100644 index 0000000..122586e --- /dev/null +++ b/muparser/muParserTokenReader.h @@ -0,0 +1,156 @@ +/* + __________ + _____ __ __\______ \_____ _______ ______ ____ _______ + / \ | | \| ___/\__ \ \_ __ \/ ___/_/ __ \\_ __ \ + | Y Y \| | /| | / __ \_| | \/\___ \ \ ___/ | | \/ + |__|_| /|____/ |____| (____ /|__| /____ > \___ >|__| + \/ \/ \/ \/ + Copyright (C) 2004-2006 Ingo Berg + + Permission is hereby granted, free of charge, to any person obtaining a copy of this + software and associated documentation files (the "Software"), to deal in the Software + without restriction, including without limitation the rights to use, copy, modify, + merge, publish, distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all copies or + substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT + NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#ifndef MU_PARSER_TOKEN_READER_H +#define MU_PARSER_TOKEN_READER_H + +#include +#include +#include +#include +#include +#include +#include + +#include "muParserDef.h" +#include "muParserToken.h" + + +namespace mu +{ + + // Forward declaration + class ParserBase; + + /** \brief Token reader for the ParserBase class. + + */ + class ParserTokenReader + { + private: + typedef ParserToken token_type; + + private: + 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::vector m_vIdentFun; ///< Value token identification function + varmap_type m_UsedVar; + value_type m_fZero; ///< Dummy value of zero, referenced by undefined variables + int m_iBrackets; + + // + // private Functions + // + 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" + noCOMMA = 1 << 4, ///< to avoid i.e. ",," or "+," ... + noFUN = 1 << 5, ///< to avoid i.e. "sqrt cos" or "(1)sin" + noOPT = 1 << 6, ///< to avoid i.e. "(+)" + noPOSTOP = 1 << 7, ///< to avoid i.e. "(5!!)" "sin!" + noINFIXOP = 1 << 8, ///< to avoid i.e. "++4" "!!4" + noEND = 1 << 9, ///< to avoid unexpected end of formula + noSTR = 1 << 10, ///< to block numeric arguments on string functions + noASSIGN = 1 << 11, ///< to block assignement to constant i.e. "4=7" + 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); + + public: + ParserTokenReader(ParserBase *a_pParent); + ~ParserTokenReader(); + ParserTokenReader* Clone(ParserBase *a_pParent) const; + + void AddValIdent(identfun_type a_pCallback); + void SetVarCreator(facfun_type a_pFactory, void *pUserData); + int GetPos() const; + const string_type& GetFormula() const; + const varmap_type& GetUsedVar() const; + void SetFormula(const string_type &a_strFormula); + void SetDefs( const funmap_type *a_pFunDef, + const funmap_type *a_pOprtDef, + const funmap_type *a_pInfixOprtDef, + const funmap_type *a_pPostOprtDef, + varmap_type *a_pVarDef, + const strmap_type *a_pStrVarDef, + const valmap_type *a_pConstDef ); + void IgnoreUndefVar(bool bIgnore); + void ReInit(); + token_type ReadNextToken(); + + // + // private functions + // + private: + + void SetParent(ParserBase *a_pParent); + int ExtractToken( const char_type *a_szCharSet, + string_type &a_strTok, int a_iPos ) const; + bool IsBuiltIn(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; + }; +} // namespace mu + +#endif + + -- cgit v1.2.2