2014-04-25 14:58:14 +02:00
|
|
|
/***************************************************************************************************
|
|
|
|
**
|
|
|
|
** Original work Copyright (C) 2013 Ingo Berg
|
|
|
|
** Modified work Copyright 2014 Roman Telezhinsky <dismine@gmail.com>
|
|
|
|
**
|
|
|
|
** Permission is hereby granted, free of charge, to any person obtaining a copy of this
|
|
|
|
** software and associated documentation files (the "Software"), to deal in the Software
|
|
|
|
** without restriction, including without limitation the rights to use, copy, modify,
|
|
|
|
** merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
|
|
|
|
** permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
|
|
**
|
|
|
|
** The above copyright notice and this permission notice shall be included in all copies or
|
|
|
|
** substantial portions of the Software.
|
|
|
|
**
|
|
|
|
** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT
|
|
|
|
** NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
|
|
** NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
|
|
|
** DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
|
|
** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
|
|
**
|
|
|
|
******************************************************************************************************/
|
|
|
|
|
|
|
|
#include "qmuparsertest.h"
|
|
|
|
|
|
|
|
#include <cstdio>
|
|
|
|
#include <cmath>
|
|
|
|
#include <iostream>
|
|
|
|
#include <limits>
|
|
|
|
|
|
|
|
#define PARSER_CONST_PI 3.141592653589793238462643
|
|
|
|
#define PARSER_CONST_E 2.718281828459045235360287
|
|
|
|
|
|
|
|
using namespace std;
|
|
|
|
|
|
|
|
/** \file
|
|
|
|
\brief This file contains the implementation of parser test cases.
|
|
|
|
*/
|
|
|
|
|
|
|
|
namespace qmu
|
|
|
|
{
|
|
|
|
namespace Test
|
|
|
|
{
|
|
|
|
int QmuParserTester::c_iCount = 0;
|
|
|
|
|
|
|
|
//---------------------------------------------------------------------------------------------
|
|
|
|
QmuParserTester::QmuParserTester()
|
|
|
|
:m_vTestFun()
|
|
|
|
{
|
|
|
|
AddTest(&QmuParserTester::TestNames);
|
|
|
|
AddTest(&QmuParserTester::TestSyntax);
|
|
|
|
AddTest(&QmuParserTester::TestPostFix);
|
|
|
|
AddTest(&QmuParserTester::TestInfixOprt);
|
|
|
|
AddTest(&QmuParserTester::TestVarConst);
|
|
|
|
AddTest(&QmuParserTester::TestMultiArg);
|
|
|
|
AddTest(&QmuParserTester::TestExpression);
|
|
|
|
AddTest(&QmuParserTester::TestIfThenElse);
|
|
|
|
AddTest(&QmuParserTester::TestInterface);
|
|
|
|
AddTest(&QmuParserTester::TestBinOprt);
|
|
|
|
AddTest(&QmuParserTester::TestException);
|
|
|
|
AddTest(&QmuParserTester::TestStrArg);
|
|
|
|
|
|
|
|
QmuParserTester::c_iCount = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------------------------------------------
|
2014-04-25 17:01:23 +02:00
|
|
|
int QmuParserTester::IsHexVal(const char_type *a_szExpr, int *a_iPos, qreal *a_fVal)
|
2014-04-25 14:58:14 +02:00
|
|
|
{
|
|
|
|
if (a_szExpr[1]==0 || (a_szExpr[0]!='0' || a_szExpr[1]!='x') )
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
unsigned iVal(0);
|
|
|
|
|
|
|
|
// New code based on streams for UNICODE compliance:
|
|
|
|
stringstream_type::pos_type nPos(0);
|
|
|
|
stringstream_type ss(a_szExpr + 2);
|
|
|
|
ss >> std::hex >> iVal;
|
|
|
|
nPos = ss.tellg();
|
|
|
|
|
|
|
|
if (nPos==(stringstream_type::pos_type)0)
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
*a_iPos += (int)(2 + nPos);
|
2014-04-25 17:01:23 +02:00
|
|
|
*a_fVal = (qreal)iVal;
|
2014-04-25 14:58:14 +02:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------------------------------------------
|
|
|
|
int QmuParserTester::TestInterface()
|
|
|
|
{
|
|
|
|
int iStat = 0;
|
2014-04-26 09:23:50 +02:00
|
|
|
qmu::console() << "testing member functions...";
|
2014-04-25 14:58:14 +02:00
|
|
|
|
|
|
|
// Test RemoveVar
|
2014-04-25 17:01:23 +02:00
|
|
|
qreal afVal[3] = {1,2,3};
|
2014-04-25 14:58:14 +02:00
|
|
|
QmuParser p;
|
|
|
|
|
|
|
|
try
|
|
|
|
{
|
2014-04-25 17:01:23 +02:00
|
|
|
p.DefineVar( "a", &afVal[0]);
|
|
|
|
p.DefineVar( "b", &afVal[1]);
|
|
|
|
p.DefineVar( "c", &afVal[2]);
|
|
|
|
p.SetExpr( "a+b+c" );
|
2014-04-25 14:58:14 +02:00
|
|
|
p.Eval();
|
|
|
|
}
|
|
|
|
catch(...)
|
|
|
|
{
|
|
|
|
iStat += 1; // this is not supposed to happen
|
|
|
|
}
|
|
|
|
|
|
|
|
try
|
|
|
|
{
|
2014-04-25 17:01:23 +02:00
|
|
|
p.RemoveVar( "c" );
|
2014-04-25 14:58:14 +02:00
|
|
|
p.Eval();
|
|
|
|
iStat += 1; // not supposed to reach this, nonexisting variable "c" deleted...
|
|
|
|
}
|
|
|
|
catch(...)
|
|
|
|
{
|
|
|
|
// failure is expected...
|
|
|
|
}
|
|
|
|
|
|
|
|
if (iStat==0)
|
2014-04-26 09:23:50 +02:00
|
|
|
qmu::console() << "passed" << endl;
|
2014-04-25 14:58:14 +02:00
|
|
|
else
|
2014-04-26 09:23:50 +02:00
|
|
|
qmu::console() << "\n failed with " << iStat << " errors" << endl;
|
2014-04-25 14:58:14 +02:00
|
|
|
|
|
|
|
return iStat;
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------------------------------------------
|
|
|
|
int QmuParserTester::TestStrArg()
|
|
|
|
{
|
|
|
|
int iStat = 0;
|
2014-04-26 09:23:50 +02:00
|
|
|
qmu::console() << "testing string arguments...";
|
2014-04-25 14:58:14 +02:00
|
|
|
|
2014-04-25 17:01:23 +02:00
|
|
|
iStat += EqnTest("valueof(\"\")", 123, true); // empty string arguments caused a crash
|
|
|
|
iStat += EqnTest("valueof(\"aaa\")+valueof(\"bbb\") ", 246, true);
|
|
|
|
iStat += EqnTest("2*(valueof(\"aaa\")-23)+valueof(\"bbb\")", 323, true);
|
2014-04-25 14:58:14 +02:00
|
|
|
// use in expressions with variables
|
2014-04-25 17:01:23 +02:00
|
|
|
iStat += EqnTest("a*(atof(\"10\")-b)", 8, true);
|
|
|
|
iStat += EqnTest("a-(atof(\"10\")*b)", -19, true);
|
2014-04-25 14:58:14 +02:00
|
|
|
// string + numeric arguments
|
2014-04-25 17:01:23 +02:00
|
|
|
iStat += EqnTest("strfun1(\"100\")", 100, true);
|
|
|
|
iStat += EqnTest("strfun2(\"100\",1)", 101, true);
|
|
|
|
iStat += EqnTest("strfun3(\"99\",1,2)", 102, true);
|
2014-04-25 14:58:14 +02:00
|
|
|
|
|
|
|
if (iStat==0)
|
2014-04-26 09:23:50 +02:00
|
|
|
qmu::console() << "passed" << endl;
|
2014-04-25 14:58:14 +02:00
|
|
|
else
|
2014-04-26 09:23:50 +02:00
|
|
|
qmu::console() << "\n failed with " << iStat << " errors" << endl;
|
2014-04-25 14:58:14 +02:00
|
|
|
|
|
|
|
return iStat;
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------------------------------------------
|
|
|
|
int QmuParserTester::TestBinOprt()
|
|
|
|
{
|
|
|
|
int iStat = 0;
|
2014-04-26 09:23:50 +02:00
|
|
|
qmu::console() << "testing binary operators...";
|
2014-04-25 14:58:14 +02:00
|
|
|
|
|
|
|
// built in operators
|
|
|
|
// xor operator
|
2014-04-25 17:01:23 +02:00
|
|
|
//iStat += EqnTest("1 xor 2", 3, true);
|
|
|
|
//iStat += EqnTest("a xor b", 3, true); // with a=1 and b=2
|
|
|
|
//iStat += EqnTest("1 xor 2 xor 3", 0, true);
|
|
|
|
//iStat += EqnTest("a xor b xor 3", 0, true); // with a=1 and b=2
|
|
|
|
//iStat += EqnTest("a xor b xor c", 0, true); // with a=1 and b=2
|
|
|
|
//iStat += EqnTest("(1 xor 2) xor 3", 0, true);
|
|
|
|
//iStat += EqnTest("(a xor b) xor c", 0, true); // with a=1 and b=2
|
|
|
|
//iStat += EqnTest("(a) xor (b) xor c", 0, true); // with a=1 and b=2
|
|
|
|
//iStat += EqnTest("1 or 2"), 3, true;
|
|
|
|
//iStat += EqnTest("a or b"), 3, true; // with a=1 and b=2
|
|
|
|
iStat += EqnTest("a++b", 3, true);
|
|
|
|
iStat += EqnTest("a ++ b", 3, true);
|
|
|
|
iStat += EqnTest("1++2", 3, true);
|
|
|
|
iStat += EqnTest("1 ++ 2", 3, true);
|
|
|
|
iStat += EqnTest("a add b", 3, true);
|
|
|
|
iStat += EqnTest("1 add 2", 3, true);
|
|
|
|
iStat += EqnTest("a<b", 1, true);
|
|
|
|
iStat += EqnTest("b>a", 1, true);
|
|
|
|
iStat += EqnTest("a>a", 0, true);
|
|
|
|
iStat += EqnTest("a<a", 0, true);
|
|
|
|
iStat += EqnTest("a>a", 0, true);
|
|
|
|
iStat += EqnTest("a<=a", 1, true);
|
|
|
|
iStat += EqnTest("a<=b", 1, true);
|
|
|
|
iStat += EqnTest("b<=a", 0, true);
|
|
|
|
iStat += EqnTest("a>=a", 1, true);
|
|
|
|
iStat += EqnTest("b>=a", 1, true);
|
|
|
|
iStat += EqnTest("a>=b", 0, true);
|
2014-04-25 14:58:14 +02:00
|
|
|
|
|
|
|
// Test logical operators, expecially if user defined "&" and the internal "&&" collide
|
2014-04-25 17:01:23 +02:00
|
|
|
iStat += EqnTest("1 && 1", 1, true);
|
|
|
|
iStat += EqnTest("1 && 0", 0, true);
|
|
|
|
iStat += EqnTest("(a<b) && (b>a)", 1, true);
|
|
|
|
iStat += EqnTest("(a<b) && (a>b)", 0, true);
|
|
|
|
//iStat += EqnTest("12 and 255", 12, true);
|
|
|
|
//iStat += EqnTest("12 and 0", 0, true);
|
|
|
|
iStat += EqnTest("12 & 255", 12, true);
|
|
|
|
iStat += EqnTest("12 & 0", 0, true);
|
|
|
|
iStat += EqnTest("12&255", 12, true);
|
|
|
|
iStat += EqnTest("12&0", 0, true);
|
2014-04-25 14:58:14 +02:00
|
|
|
|
|
|
|
// Assignement operator
|
2014-04-25 17:01:23 +02:00
|
|
|
iStat += EqnTest("a = b", 2, true);
|
|
|
|
iStat += EqnTest("a = sin(b)", 0.909297, true);
|
|
|
|
iStat += EqnTest("a = 1+sin(b)", 1.909297, true);
|
|
|
|
iStat += EqnTest("(a=b)*2", 4, true);
|
|
|
|
iStat += EqnTest("2*(a=b)", 4, true);
|
|
|
|
iStat += EqnTest("2*(a=b+1)", 6, true);
|
|
|
|
iStat += EqnTest("(a=b+1)*2", 6, true);
|
2014-04-25 14:58:14 +02:00
|
|
|
|
2014-04-25 17:01:23 +02:00
|
|
|
iStat += EqnTest("2^2^3", 256, true);
|
|
|
|
iStat += EqnTest("1/2/3", 1.0/6.0, true);
|
2014-04-25 14:58:14 +02:00
|
|
|
|
|
|
|
// reference: http://www.wolframalpha.com/input/?i=3%2B4*2%2F%281-5%29^2^3
|
2014-04-25 17:01:23 +02:00
|
|
|
iStat += EqnTest("3+4*2/(1-5)^2^3", 3.0001220703125, true);
|
2014-04-25 14:58:14 +02:00
|
|
|
|
|
|
|
if (iStat==0)
|
2014-04-26 09:23:50 +02:00
|
|
|
qmu::console() << "passed" << endl;
|
2014-04-25 14:58:14 +02:00
|
|
|
else
|
2014-04-26 09:23:50 +02:00
|
|
|
qmu::console() << "\n failed with " << iStat << " errors" << endl;
|
2014-04-25 14:58:14 +02:00
|
|
|
|
|
|
|
return iStat;
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------------------------------------------
|
|
|
|
/** \brief Check muParser name restriction enforcement. */
|
|
|
|
int QmuParserTester::TestNames()
|
|
|
|
{
|
|
|
|
int iStat= 0,
|
|
|
|
iErr = 0;
|
|
|
|
|
2014-04-26 09:23:50 +02:00
|
|
|
qmu::console() << "testing name restriction enforcement...";
|
2014-04-25 14:58:14 +02:00
|
|
|
|
|
|
|
QmuParser p;
|
|
|
|
|
|
|
|
#define PARSER_THROWCHECK(DOMAIN, FAIL, EXPR, ARG) \
|
|
|
|
iErr = 0; \
|
|
|
|
QmuParserTester::c_iCount++; \
|
|
|
|
try \
|
|
|
|
{ \
|
|
|
|
p.Define##DOMAIN(EXPR, ARG); \
|
|
|
|
} \
|
|
|
|
catch(QmuParser::exception_type&) \
|
|
|
|
{ \
|
|
|
|
iErr = (FAIL==false) ? 0 : 1; \
|
|
|
|
} \
|
|
|
|
iStat += iErr;
|
|
|
|
|
|
|
|
// constant names
|
2014-04-25 17:01:23 +02:00
|
|
|
PARSER_THROWCHECK(Const, false, "0a", 1)
|
|
|
|
PARSER_THROWCHECK(Const, false, "9a", 1)
|
|
|
|
PARSER_THROWCHECK(Const, false, "+a", 1)
|
|
|
|
PARSER_THROWCHECK(Const, false, "-a", 1)
|
|
|
|
PARSER_THROWCHECK(Const, false, "a-", 1)
|
|
|
|
PARSER_THROWCHECK(Const, false, "a*", 1)
|
|
|
|
PARSER_THROWCHECK(Const, false, "a?", 1)
|
|
|
|
PARSER_THROWCHECK(Const, true, "a", 1)
|
|
|
|
PARSER_THROWCHECK(Const, true, "a_min", 1)
|
|
|
|
PARSER_THROWCHECK(Const, true, "a_min0", 1)
|
|
|
|
PARSER_THROWCHECK(Const, true, "a_min9", 1)
|
2014-04-25 14:58:14 +02:00
|
|
|
// variable names
|
2014-04-25 17:01:23 +02:00
|
|
|
qreal a;
|
2014-04-25 14:58:14 +02:00
|
|
|
p.ClearConst();
|
2014-04-25 17:01:23 +02:00
|
|
|
PARSER_THROWCHECK(Var, false, "123abc", &a)
|
|
|
|
PARSER_THROWCHECK(Var, false, "9a", &a)
|
|
|
|
PARSER_THROWCHECK(Var, false, "0a", &a)
|
|
|
|
PARSER_THROWCHECK(Var, false, "+a", &a)
|
|
|
|
PARSER_THROWCHECK(Var, false, "-a", &a)
|
|
|
|
PARSER_THROWCHECK(Var, false, "?a", &a)
|
|
|
|
PARSER_THROWCHECK(Var, false, "!a", &a)
|
|
|
|
PARSER_THROWCHECK(Var, false, "a+", &a)
|
|
|
|
PARSER_THROWCHECK(Var, false, "a-", &a)
|
|
|
|
PARSER_THROWCHECK(Var, false, "a*", &a)
|
|
|
|
PARSER_THROWCHECK(Var, false, "a?", &a)
|
|
|
|
PARSER_THROWCHECK(Var, true, "a", &a)
|
|
|
|
PARSER_THROWCHECK(Var, true, "a_min", &a)
|
|
|
|
PARSER_THROWCHECK(Var, true, "a_min0", &a)
|
|
|
|
PARSER_THROWCHECK(Var, true, "a_min9", &a)
|
|
|
|
PARSER_THROWCHECK(Var, false, "a_min9", 0)
|
2014-04-25 14:58:14 +02:00
|
|
|
// Postfix operators
|
|
|
|
// fail
|
2014-04-25 17:01:23 +02:00
|
|
|
PARSER_THROWCHECK(PostfixOprt, false, "(k", f1of1)
|
|
|
|
PARSER_THROWCHECK(PostfixOprt, false, "9+", f1of1)
|
|
|
|
PARSER_THROWCHECK(PostfixOprt, false, "+", 0)
|
2014-04-25 14:58:14 +02:00
|
|
|
// pass
|
2014-04-25 17:01:23 +02:00
|
|
|
PARSER_THROWCHECK(PostfixOprt, true, "-a", f1of1)
|
|
|
|
PARSER_THROWCHECK(PostfixOprt, true, "?a", f1of1)
|
|
|
|
PARSER_THROWCHECK(PostfixOprt, true, "_", f1of1)
|
|
|
|
PARSER_THROWCHECK(PostfixOprt, true, "#", f1of1)
|
|
|
|
PARSER_THROWCHECK(PostfixOprt, true, "&&", f1of1)
|
|
|
|
PARSER_THROWCHECK(PostfixOprt, true, "||", f1of1)
|
|
|
|
PARSER_THROWCHECK(PostfixOprt, true, "&", f1of1)
|
|
|
|
PARSER_THROWCHECK(PostfixOprt, true, "|", f1of1)
|
|
|
|
PARSER_THROWCHECK(PostfixOprt, true, "++", f1of1)
|
|
|
|
PARSER_THROWCHECK(PostfixOprt, true, "--", f1of1)
|
|
|
|
PARSER_THROWCHECK(PostfixOprt, true, "?>", f1of1)
|
|
|
|
PARSER_THROWCHECK(PostfixOprt, true, "?<", f1of1)
|
|
|
|
PARSER_THROWCHECK(PostfixOprt, true, "**", f1of1)
|
|
|
|
PARSER_THROWCHECK(PostfixOprt, true, "xor", f1of1)
|
|
|
|
PARSER_THROWCHECK(PostfixOprt, true, "and", f1of1)
|
|
|
|
PARSER_THROWCHECK(PostfixOprt, true, "or", f1of1)
|
|
|
|
PARSER_THROWCHECK(PostfixOprt, true, "not", f1of1)
|
|
|
|
PARSER_THROWCHECK(PostfixOprt, true, "!", f1of1)
|
2014-04-25 14:58:14 +02:00
|
|
|
// Binary operator
|
|
|
|
// The following must fail with builtin operators activated
|
|
|
|
// p.EnableBuiltInOp(true); -> this is the default
|
|
|
|
p.ClearPostfixOprt();
|
2014-04-25 17:01:23 +02:00
|
|
|
PARSER_THROWCHECK(Oprt, false, "+", f1of2)
|
|
|
|
PARSER_THROWCHECK(Oprt, false, "-", f1of2)
|
|
|
|
PARSER_THROWCHECK(Oprt, false, "*", f1of2)
|
|
|
|
PARSER_THROWCHECK(Oprt, false, "/", f1of2)
|
|
|
|
PARSER_THROWCHECK(Oprt, false, "^", f1of2)
|
|
|
|
PARSER_THROWCHECK(Oprt, false, "&&", f1of2)
|
|
|
|
PARSER_THROWCHECK(Oprt, false, "||", f1of2)
|
2014-04-25 14:58:14 +02:00
|
|
|
// without activated built in operators it should work
|
|
|
|
p.EnableBuiltInOprt(false);
|
2014-04-25 17:01:23 +02:00
|
|
|
PARSER_THROWCHECK(Oprt, true, "+", f1of2)
|
|
|
|
PARSER_THROWCHECK(Oprt, true, "-", f1of2)
|
|
|
|
PARSER_THROWCHECK(Oprt, true, "*", f1of2)
|
|
|
|
PARSER_THROWCHECK(Oprt, true, "/", f1of2)
|
|
|
|
PARSER_THROWCHECK(Oprt, true, "^", f1of2)
|
|
|
|
PARSER_THROWCHECK(Oprt, true, "&&", f1of2)
|
|
|
|
PARSER_THROWCHECK(Oprt, true, "||", f1of2)
|
2014-04-25 14:58:14 +02:00
|
|
|
#undef PARSER_THROWCHECK
|
|
|
|
|
|
|
|
if (iStat==0)
|
2014-04-26 09:23:50 +02:00
|
|
|
qmu::console() << "passed" << endl;
|
2014-04-25 14:58:14 +02:00
|
|
|
else
|
2014-04-26 09:23:50 +02:00
|
|
|
qmu::console() << "\n failed with " << iStat << " errors" << endl;
|
2014-04-25 14:58:14 +02:00
|
|
|
|
|
|
|
return iStat;
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
int QmuParserTester::TestSyntax()
|
|
|
|
{
|
|
|
|
int iStat = 0;
|
2014-04-26 09:23:50 +02:00
|
|
|
qmu::console() << "testing syntax engine...";
|
2014-04-25 17:01:23 +02:00
|
|
|
|
|
|
|
iStat += ThrowTest("1,", ecUNEXPECTED_EOF); // incomplete hex definition
|
|
|
|
iStat += ThrowTest("a,", ecUNEXPECTED_EOF); // incomplete hex definition
|
|
|
|
iStat += ThrowTest("sin(8),", ecUNEXPECTED_EOF); // incomplete hex definition
|
|
|
|
iStat += ThrowTest("(sin(8)),", ecUNEXPECTED_EOF); // incomplete hex definition
|
|
|
|
iStat += ThrowTest("a{m},", ecUNEXPECTED_EOF); // incomplete hex definition
|
|
|
|
|
|
|
|
iStat += EqnTest("(1+ 2*a)", 3, true); // Spaces within formula
|
|
|
|
iStat += EqnTest("sqrt((4))", 2, true); // Multiple brackets
|
|
|
|
iStat += EqnTest("sqrt((2)+2)", 2, true);// Multiple brackets
|
|
|
|
iStat += EqnTest("sqrt(2+(2))", 2, true);// Multiple brackets
|
|
|
|
iStat += EqnTest("sqrt(a+(3))", 2, true);// Multiple brackets
|
|
|
|
iStat += EqnTest("sqrt((3)+a)", 2, true);// Multiple brackets
|
|
|
|
iStat += EqnTest("order(1,2)", 1, true); // May not cause name collision with operator "or"
|
|
|
|
iStat += EqnTest("(2+", 0, false); // missing closing bracket
|
|
|
|
iStat += EqnTest("2++4", 0, false); // unexpected operator
|
|
|
|
iStat += EqnTest("2+-4", 0, false); // unexpected operator
|
|
|
|
iStat += EqnTest("(2+)", 0, false); // unexpected closing bracket
|
|
|
|
iStat += EqnTest("--2", 0, false); // double sign
|
|
|
|
iStat += EqnTest("ksdfj", 0, false); // unknown token
|
|
|
|
iStat += EqnTest("()", 0, false); // empty bracket without a function
|
|
|
|
iStat += EqnTest("5+()", 0, false); // empty bracket without a function
|
|
|
|
iStat += EqnTest("sin(cos)", 0, false); // unexpected function
|
|
|
|
iStat += EqnTest("5t6", 0, false); // unknown token
|
|
|
|
iStat += EqnTest("5 t 6", 0, false); // unknown token
|
|
|
|
iStat += EqnTest("8*", 0, false); // unexpected end of formula
|
|
|
|
iStat += EqnTest(",3", 0, false); // unexpected comma
|
|
|
|
iStat += EqnTest("3,5", 0, false); // unexpected comma
|
|
|
|
iStat += EqnTest("sin(8,8)", 0, false); // too many function args
|
|
|
|
iStat += EqnTest("(7,8)", 0, false); // too many function args
|
|
|
|
iStat += EqnTest("sin)", 0, false); // unexpected closing bracket
|
|
|
|
iStat += EqnTest("a)", 0, false); // unexpected closing bracket
|
|
|
|
iStat += EqnTest("pi)", 0, false); // unexpected closing bracket
|
|
|
|
iStat += EqnTest("sin(())", 0, false); // unexpected closing bracket
|
|
|
|
iStat += EqnTest("sin()", 0, false); // unexpected closing bracket
|
2014-04-25 14:58:14 +02:00
|
|
|
|
|
|
|
if (iStat==0)
|
2014-04-26 09:23:50 +02:00
|
|
|
qmu::console() << "passed" << endl;
|
2014-04-25 14:58:14 +02:00
|
|
|
else
|
2014-04-26 09:23:50 +02:00
|
|
|
qmu::console() << "\n failed with " << iStat << " errors" << endl;
|
2014-04-25 14:58:14 +02:00
|
|
|
|
|
|
|
return iStat;
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
int QmuParserTester::TestVarConst()
|
|
|
|
{
|
|
|
|
int iStat = 0;
|
2014-04-26 09:23:50 +02:00
|
|
|
qmu::console() << "testing variable/constant detection...";
|
2014-04-25 14:58:14 +02:00
|
|
|
|
|
|
|
// Test if the result changes when a variable changes
|
2014-04-25 17:01:23 +02:00
|
|
|
iStat += EqnTestWithVarChange( "a", 1, 1, 2, 2 );
|
|
|
|
iStat += EqnTestWithVarChange( "2*a", 2, 4, 3, 6 );
|
2014-04-25 14:58:14 +02:00
|
|
|
|
|
|
|
// distinguish constants with same basename
|
2014-04-25 17:01:23 +02:00
|
|
|
iStat += EqnTest( "const", 1, true);
|
|
|
|
iStat += EqnTest( "const1", 2, true);
|
|
|
|
iStat += EqnTest( "const2", 3, true);
|
|
|
|
iStat += EqnTest( "2*const", 2, true);
|
|
|
|
iStat += EqnTest( "2*const1", 4, true);
|
|
|
|
iStat += EqnTest( "2*const2", 6, true);
|
|
|
|
iStat += EqnTest( "2*const+1", 3, true);
|
|
|
|
iStat += EqnTest( "2*const1+1", 5, true);
|
|
|
|
iStat += EqnTest( "2*const2+1", 7, true);
|
|
|
|
iStat += EqnTest( "const", 0, false);
|
|
|
|
iStat += EqnTest( "const1", 0, false);
|
|
|
|
iStat += EqnTest( "const2", 0, false);
|
2014-04-25 14:58:14 +02:00
|
|
|
|
|
|
|
// distinguish variables with same basename
|
2014-04-25 17:01:23 +02:00
|
|
|
iStat += EqnTest( "a", 1, true);
|
|
|
|
iStat += EqnTest( "aa", 2, true);
|
|
|
|
iStat += EqnTest( "2*a", 2, true);
|
|
|
|
iStat += EqnTest( "2*aa", 4, true);
|
|
|
|
iStat += EqnTest( "2*a-1", 1, true);
|
|
|
|
iStat += EqnTest( "2*aa-1", 3, true);
|
2014-04-25 14:58:14 +02:00
|
|
|
|
|
|
|
// custom value recognition
|
2014-04-25 17:01:23 +02:00
|
|
|
iStat += EqnTest( "0xff", 255, true);
|
|
|
|
iStat += EqnTest( "0x97 + 0xff", 406, true);
|
2014-04-25 14:58:14 +02:00
|
|
|
|
|
|
|
// Finally test querying of used variables
|
|
|
|
try
|
|
|
|
{
|
|
|
|
int idx;
|
|
|
|
qmu::QmuParser p;
|
2014-04-25 17:01:23 +02:00
|
|
|
qreal vVarVal[] = { 1, 2, 3, 4, 5};
|
|
|
|
p.DefineVar( "a", &vVarVal[0]);
|
|
|
|
p.DefineVar( "b", &vVarVal[1]);
|
|
|
|
p.DefineVar( "c", &vVarVal[2]);
|
|
|
|
p.DefineVar( "d", &vVarVal[3]);
|
|
|
|
p.DefineVar( "e", &vVarVal[4]);
|
2014-04-25 14:58:14 +02:00
|
|
|
|
|
|
|
// Test lookup of defined variables
|
|
|
|
// 4 used variables
|
2014-04-25 17:01:23 +02:00
|
|
|
p.SetExpr( "a+b+c+d" );
|
2014-04-26 09:23:50 +02:00
|
|
|
qmu::varmap_type UsedVar = p.GetUsedVar();
|
2014-04-25 14:58:14 +02:00
|
|
|
int iCount = (int)UsedVar.size();
|
|
|
|
if (iCount!=4)
|
|
|
|
throw false;
|
|
|
|
|
|
|
|
// the next check will fail if the parser
|
|
|
|
// erroneousely creates new variables internally
|
|
|
|
if (p.GetVar().size()!=5)
|
|
|
|
throw false;
|
|
|
|
|
2014-04-26 09:23:50 +02:00
|
|
|
qmu::varmap_type::const_iterator item = UsedVar.begin();
|
2014-04-25 14:58:14 +02:00
|
|
|
for (idx=0; item!=UsedVar.end(); ++item)
|
|
|
|
{
|
|
|
|
if (&vVarVal[idx++]!=item->second)
|
|
|
|
throw false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Test lookup of undefined variables
|
2014-04-25 17:01:23 +02:00
|
|
|
p.SetExpr( "undef1+undef2+undef3" );
|
2014-04-25 14:58:14 +02:00
|
|
|
UsedVar = p.GetUsedVar();
|
|
|
|
iCount = (int)UsedVar.size();
|
|
|
|
if (iCount!=3)
|
|
|
|
throw false;
|
|
|
|
|
|
|
|
// the next check will fail if the parser
|
|
|
|
// erroneousely creates new variables internally
|
|
|
|
if (p.GetVar().size()!=5)
|
|
|
|
throw false;
|
|
|
|
|
|
|
|
for (item = UsedVar.begin(); item!=UsedVar.end(); ++item)
|
|
|
|
{
|
|
|
|
if (item->second!=0)
|
|
|
|
throw false; // all pointers to undefined variables must be null
|
|
|
|
}
|
|
|
|
|
|
|
|
// 1 used variables
|
2014-04-25 17:01:23 +02:00
|
|
|
p.SetExpr( "a+b" );
|
2014-04-25 14:58:14 +02:00
|
|
|
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)
|
2014-04-26 09:23:50 +02:00
|
|
|
qmu::console() << "passed" << endl;
|
2014-04-25 14:58:14 +02:00
|
|
|
else
|
2014-04-26 09:23:50 +02:00
|
|
|
qmu::console() << "\n failed with " << iStat << " errors" << endl;
|
2014-04-25 14:58:14 +02:00
|
|
|
|
|
|
|
return iStat;
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
int QmuParserTester::TestMultiArg()
|
|
|
|
{
|
|
|
|
int iStat = 0;
|
2014-04-26 09:23:50 +02:00
|
|
|
qmu::console() << "testing multiarg functions...";
|
2014-04-25 14:58:14 +02:00
|
|
|
|
|
|
|
// Compound expressions
|
2014-04-25 17:01:23 +02:00
|
|
|
iStat += EqnTest( "1,2,3", 3, true);
|
|
|
|
iStat += EqnTest( "a,b,c", 3, true);
|
|
|
|
iStat += EqnTest( "a=10,b=20,c=a*b", 200, true);
|
|
|
|
iStat += EqnTest( "1,\n2,\n3", 3, true);
|
|
|
|
iStat += EqnTest( "a,\nb,\nc", 3, true);
|
|
|
|
iStat += EqnTest( "a=10,\nb=20,\nc=a*b", 200, true);
|
|
|
|
iStat += EqnTest( "1,\r\n2,\r\n3", 3, true);
|
|
|
|
iStat += EqnTest( "a,\r\nb,\r\nc", 3, true);
|
|
|
|
iStat += EqnTest( "a=10,\r\nb=20,\r\nc=a*b", 200, true);
|
2014-04-25 14:58:14 +02:00
|
|
|
|
|
|
|
// picking the right argument
|
2014-04-25 17:01:23 +02:00
|
|
|
iStat += EqnTest( "f1of1(1)", 1, true);
|
|
|
|
iStat += EqnTest( "f1of2(1, 2)", 1, true);
|
|
|
|
iStat += EqnTest( "f2of2(1, 2)", 2, true);
|
|
|
|
iStat += EqnTest( "f1of3(1, 2, 3)", 1, true);
|
|
|
|
iStat += EqnTest( "f2of3(1, 2, 3)", 2, true);
|
|
|
|
iStat += EqnTest( "f3of3(1, 2, 3)", 3, true);
|
|
|
|
iStat += EqnTest( "f1of4(1, 2, 3, 4)", 1, true);
|
|
|
|
iStat += EqnTest( "f2of4(1, 2, 3, 4)", 2, true);
|
|
|
|
iStat += EqnTest( "f3of4(1, 2, 3, 4)", 3, true);
|
|
|
|
iStat += EqnTest( "f4of4(1, 2, 3, 4)", 4, true);
|
|
|
|
iStat += EqnTest( "f1of5(1, 2, 3, 4, 5)", 1, true);
|
|
|
|
iStat += EqnTest( "f2of5(1, 2, 3, 4, 5)", 2, true);
|
|
|
|
iStat += EqnTest( "f3of5(1, 2, 3, 4, 5)", 3, true);
|
|
|
|
iStat += EqnTest( "f4of5(1, 2, 3, 4, 5)", 4, true);
|
|
|
|
iStat += EqnTest( "f5of5(1, 2, 3, 4, 5)", 5, true);
|
2014-04-25 14:58:14 +02:00
|
|
|
// Too few arguments / Too many arguments
|
2014-04-25 17:01:23 +02:00
|
|
|
iStat += EqnTest( "1+ping()", 11, true);
|
|
|
|
iStat += EqnTest( "ping()+1", 11, true);
|
|
|
|
iStat += EqnTest( "2*ping()", 20, true);
|
|
|
|
iStat += EqnTest( "ping()*2", 20, true);
|
|
|
|
iStat += EqnTest( "ping(1,2)", 0, false);
|
|
|
|
iStat += EqnTest( "1+ping(1,2)", 0, false);
|
|
|
|
iStat += EqnTest( "f1of1(1,2)", 0, false);
|
|
|
|
iStat += EqnTest( "f1of1()", 0, false);
|
|
|
|
iStat += EqnTest( "f1of2(1, 2, 3)", 0, false);
|
|
|
|
iStat += EqnTest( "f1of2(1)", 0, false);
|
|
|
|
iStat += EqnTest( "f1of3(1, 2, 3, 4)", 0, false);
|
|
|
|
iStat += EqnTest( "f1of3(1)", 0, false);
|
|
|
|
iStat += EqnTest( "f1of4(1, 2, 3, 4, 5)", 0, false);
|
|
|
|
iStat += EqnTest( "f1of4(1)", 0, false);
|
|
|
|
iStat += EqnTest( "(1,2,3)", 0, false);
|
|
|
|
iStat += EqnTest( "1,2,3", 0, false);
|
|
|
|
iStat += EqnTest( "(1*a,2,3)", 0, false);
|
|
|
|
iStat += EqnTest( "1,2*a,3", 0, false);
|
2014-04-25 14:58:14 +02:00
|
|
|
|
|
|
|
// correct calculation of arguments
|
2014-04-25 17:01:23 +02:00
|
|
|
iStat += EqnTest( "min(a, 1)", 1, true);
|
|
|
|
iStat += EqnTest( "min(3*2, 1)", 1, true);
|
|
|
|
iStat += EqnTest( "min(3*2, 1)", 6, false);
|
|
|
|
iStat += EqnTest( "firstArg(2,3,4)", 2, true);
|
|
|
|
iStat += EqnTest( "lastArg(2,3,4)", 4, true);
|
|
|
|
iStat += EqnTest( "min(3*a+1, 1)", 1, true);
|
|
|
|
iStat += EqnTest( "max(3*a+1, 1)", 4, true);
|
|
|
|
iStat += EqnTest( "max(3*a+1, 1)*2", 8, true);
|
|
|
|
iStat += EqnTest( "2*max(3*a+1, 1)+2", 10, true);
|
2014-04-25 14:58:14 +02:00
|
|
|
|
|
|
|
// functions with Variable argument count
|
2014-04-25 17:01:23 +02:00
|
|
|
iStat += EqnTest( "sum(a)", 1, true);
|
|
|
|
iStat += EqnTest( "sum(1,2,3)", 6, true);
|
|
|
|
iStat += EqnTest( "sum(a,b,c)", 6, true);
|
|
|
|
iStat += EqnTest( "sum(1,-max(1,2),3)*2", 4, true);
|
|
|
|
iStat += EqnTest( "2*sum(1,2,3)", 12, true);
|
|
|
|
iStat += EqnTest( "2*sum(1,2,3)+2", 14, true);
|
|
|
|
iStat += EqnTest( "2*sum(-1,2,3)+2", 10, true);
|
|
|
|
iStat += EqnTest( "2*sum(-1,2,-(-a))+2", 6, true);
|
|
|
|
iStat += EqnTest( "2*sum(-1,10,-a)+2", 18, true);
|
|
|
|
iStat += EqnTest( "2*sum(1,2,3)*2", 24, true);
|
|
|
|
iStat += EqnTest( "sum(1,-max(1,2),3)*2", 4, true);
|
|
|
|
iStat += EqnTest( "sum(1*3, 4, a+2)", 10, true);
|
|
|
|
iStat += EqnTest( "sum(1*3, 2*sum(1,2,2), a+2)", 16, true);
|
|
|
|
iStat += EqnTest( "sum(1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2)", 24, true);
|
2014-04-25 14:58:14 +02:00
|
|
|
|
|
|
|
// some failures
|
2014-04-25 17:01:23 +02:00
|
|
|
iStat += EqnTest( "sum()", 0, false);
|
|
|
|
iStat += EqnTest( "sum(,)", 0, false);
|
|
|
|
iStat += EqnTest( "sum(1,2,)", 0, false);
|
|
|
|
iStat += EqnTest( "sum(,1,2)", 0, false);
|
2014-04-25 14:58:14 +02:00
|
|
|
|
|
|
|
if (iStat==0)
|
2014-04-26 09:23:50 +02:00
|
|
|
qmu::console() << "passed" << endl;
|
2014-04-25 14:58:14 +02:00
|
|
|
else
|
2014-04-26 09:23:50 +02:00
|
|
|
qmu::console() << "\n failed with " << iStat << " errors" << endl;
|
2014-04-25 14:58:14 +02:00
|
|
|
|
|
|
|
return iStat;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
int QmuParserTester::TestInfixOprt()
|
|
|
|
{
|
|
|
|
int iStat(0);
|
2014-04-26 09:23:50 +02:00
|
|
|
qmu::console() << "testing infix operators...";
|
2014-04-25 14:58:14 +02:00
|
|
|
|
2014-04-25 17:01:23 +02:00
|
|
|
iStat += EqnTest( "-1", -1, true);
|
|
|
|
iStat += EqnTest( "-(-1)", 1, true);
|
|
|
|
iStat += EqnTest( "-(-1)*2", 2, true);
|
|
|
|
iStat += EqnTest( "-(-2)*sqrt(4)", 4, true);
|
|
|
|
iStat += EqnTest( "-_pi", -PARSER_CONST_PI, true);
|
|
|
|
iStat += EqnTest( "-a", -1, true);
|
|
|
|
iStat += EqnTest( "-(a)", -1, true);
|
|
|
|
iStat += EqnTest( "-(-a)", 1, true);
|
|
|
|
iStat += EqnTest( "-(-a)*2", 2, true);
|
|
|
|
iStat += EqnTest( "-(8)", -8, true);
|
|
|
|
iStat += EqnTest( "-8", -8, true);
|
|
|
|
iStat += EqnTest( "-(2+1)", -3, true);
|
|
|
|
iStat += EqnTest( "-(f1of1(1+2*3)+1*2)", -9, true);
|
|
|
|
iStat += EqnTest( "-(-f1of1(1+2*3)+1*2)", 5, true);
|
|
|
|
iStat += EqnTest( "-sin(8)", -0.989358, true);
|
|
|
|
iStat += EqnTest( "3-(-a)", 4, true);
|
|
|
|
iStat += EqnTest( "3--a", 4, true);
|
|
|
|
iStat += EqnTest( "-1*3", -3, true);
|
2014-04-25 14:58:14 +02:00
|
|
|
|
|
|
|
// Postfix / infix priorities
|
2014-04-25 17:01:23 +02:00
|
|
|
iStat += EqnTest( "~2#", 8, true);
|
|
|
|
iStat += EqnTest( "~f1of1(2)#", 8, true);
|
|
|
|
iStat += EqnTest( "~(b)#", 8, true);
|
|
|
|
iStat += EqnTest( "(~b)#", 12, true);
|
|
|
|
iStat += EqnTest( "~(2#)", 8, true);
|
|
|
|
iStat += EqnTest( "~(f1of1(2)#)", 8, true);
|
2014-04-25 14:58:14 +02:00
|
|
|
//
|
2014-04-25 17:01:23 +02:00
|
|
|
iStat += EqnTest( "-2^2",-4, true);
|
|
|
|
iStat += EqnTest( "-(a+b)^2",-9, true);
|
|
|
|
iStat += EqnTest( "(-3)^2",9, true);
|
|
|
|
iStat += EqnTest( "-(-2^2)",4, true);
|
|
|
|
iStat += EqnTest( "3+-3^2",-6, true);
|
2014-04-25 14:58:14 +02:00
|
|
|
// The following assumes use of sqr as postfix operator ("") together
|
|
|
|
// with a sign operator of low priority:
|
2014-04-25 17:01:23 +02:00
|
|
|
iStat += EqnTest( "-2'", -4, true);
|
|
|
|
iStat += EqnTest( "-(1+1)'",-4, true);
|
|
|
|
iStat += EqnTest( "2+-(1+1)'",-2, true);
|
|
|
|
iStat += EqnTest( "2+-2'", -2, true);
|
2014-04-25 14:58:14 +02:00
|
|
|
// This is the classic behaviour of the infix sign operator (here: "$") which is
|
|
|
|
// now deprecated:
|
2014-04-25 17:01:23 +02:00
|
|
|
iStat += EqnTest( "$2^2",4, true);
|
|
|
|
iStat += EqnTest( "$(a+b)^2",9, true);
|
|
|
|
iStat += EqnTest( "($3)^2",9, true);
|
|
|
|
iStat += EqnTest( "$($2^2)",-4, true);
|
|
|
|
iStat += EqnTest( "3+$3^2",12, true);
|
2014-04-25 14:58:14 +02:00
|
|
|
|
|
|
|
// infix operators sharing the first few characters
|
2014-04-25 17:01:23 +02:00
|
|
|
iStat += EqnTest( "~ 123", 123+2, true);
|
|
|
|
iStat += EqnTest( "~~ 123", 123+2, true);
|
2014-04-25 14:58:14 +02:00
|
|
|
|
|
|
|
if (iStat==0)
|
2014-04-26 09:23:50 +02:00
|
|
|
qmu::console() << "passed" << endl;
|
2014-04-25 14:58:14 +02:00
|
|
|
else
|
2014-04-26 09:23:50 +02:00
|
|
|
qmu::console() << "\n failed with " << iStat << " errors" << endl;
|
2014-04-25 14:58:14 +02:00
|
|
|
|
|
|
|
return iStat;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
int QmuParserTester::TestPostFix()
|
|
|
|
{
|
|
|
|
int iStat = 0;
|
2014-04-26 09:23:50 +02:00
|
|
|
qmu::console() << "testing postfix operators...";
|
2014-04-25 14:58:14 +02:00
|
|
|
|
|
|
|
// application
|
2014-04-25 17:01:23 +02:00
|
|
|
iStat += EqnTest( "3{m}+5", 5.003, true);
|
|
|
|
iStat += EqnTest( "1000{m}", 1, true);
|
|
|
|
iStat += EqnTest( "1000 {m}", 1, true);
|
|
|
|
iStat += EqnTest( "(a){m}", 1e-3, true);
|
|
|
|
iStat += EqnTest( "a{m}", 1e-3, true);
|
|
|
|
iStat += EqnTest( "a {m}", 1e-3, true);
|
|
|
|
iStat += EqnTest( "-(a){m}", -1e-3, true);
|
|
|
|
iStat += EqnTest( "-2{m}", -2e-3, true);
|
|
|
|
iStat += EqnTest( "-2 {m}", -2e-3, true);
|
|
|
|
iStat += EqnTest( "f1of1(1000){m}", 1, true);
|
|
|
|
iStat += EqnTest( "-f1of1(1000){m}", -1, true);
|
|
|
|
iStat += EqnTest( "-f1of1(-1000){m}", 1, true);
|
|
|
|
iStat += EqnTest( "f4of4(0,0,0,1000){m}", 1, true);
|
|
|
|
iStat += EqnTest( "2+(a*1000){m}", 3, true);
|
2014-04-25 14:58:14 +02:00
|
|
|
|
|
|
|
// can postfix operators "m" und "meg" be told apart properly?
|
2014-04-25 17:01:23 +02:00
|
|
|
iStat += EqnTest( "2*3000meg+2", 2*3e9+2, true);
|
2014-04-25 14:58:14 +02:00
|
|
|
|
|
|
|
// some incorrect results
|
2014-04-25 17:01:23 +02:00
|
|
|
iStat += EqnTest( "1000{m}", 0.1, false);
|
|
|
|
iStat += EqnTest( "(a){m}", 2, false);
|
2014-04-25 14:58:14 +02:00
|
|
|
// failure due to syntax checking
|
2014-04-25 17:01:23 +02:00
|
|
|
iStat += ThrowTest("0x", ecUNASSIGNABLE_TOKEN); // incomplete hex definition
|
|
|
|
iStat += ThrowTest("3+", ecUNEXPECTED_EOF);
|
|
|
|
iStat += ThrowTest( "4 + {m}", ecUNASSIGNABLE_TOKEN);
|
|
|
|
iStat += ThrowTest( "{m}4", ecUNASSIGNABLE_TOKEN);
|
|
|
|
iStat += ThrowTest( "sin({m})", ecUNASSIGNABLE_TOKEN);
|
|
|
|
iStat += ThrowTest( "{m} {m}", ecUNASSIGNABLE_TOKEN);
|
|
|
|
iStat += ThrowTest( "{m}(8)", ecUNASSIGNABLE_TOKEN);
|
|
|
|
iStat += ThrowTest( "4,{m}", ecUNASSIGNABLE_TOKEN);
|
|
|
|
iStat += ThrowTest( "-{m}", ecUNASSIGNABLE_TOKEN);
|
|
|
|
iStat += ThrowTest( "2(-{m})", ecUNEXPECTED_PARENS);
|
|
|
|
iStat += ThrowTest( "2({m})", ecUNEXPECTED_PARENS);
|
2014-04-25 14:58:14 +02:00
|
|
|
|
2014-04-25 17:01:23 +02:00
|
|
|
iStat += ThrowTest( "multi*1.0", ecUNASSIGNABLE_TOKEN);
|
2014-04-25 14:58:14 +02:00
|
|
|
|
|
|
|
if (iStat==0)
|
2014-04-26 09:23:50 +02:00
|
|
|
qmu::console() << "passed" << endl;
|
2014-04-25 14:58:14 +02:00
|
|
|
else
|
2014-04-26 09:23:50 +02:00
|
|
|
qmu::console() << "\n failed with " << iStat << " errors" << endl;
|
2014-04-25 14:58:14 +02:00
|
|
|
|
|
|
|
return iStat;
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
int QmuParserTester::TestExpression()
|
|
|
|
{
|
|
|
|
int iStat = 0;
|
2014-04-26 09:23:50 +02:00
|
|
|
qmu::console() << "testing expression samples...";
|
2014-04-25 14:58:14 +02:00
|
|
|
|
2014-04-25 17:01:23 +02:00
|
|
|
qreal b = 2;
|
2014-04-25 14:58:14 +02:00
|
|
|
|
|
|
|
// Optimization
|
2014-04-25 17:01:23 +02:00
|
|
|
iStat += EqnTest( "2*b*5", 20, true);
|
|
|
|
iStat += EqnTest( "2*b*5 + 4*b", 28, true);
|
|
|
|
iStat += EqnTest( "2*a/3", 2.0/3.0, true);
|
2014-04-25 14:58:14 +02:00
|
|
|
|
|
|
|
// Addition auf cmVARMUL
|
2014-04-25 17:01:23 +02:00
|
|
|
iStat += EqnTest( "3+b", b+3, true);
|
|
|
|
iStat += EqnTest( "b+3", b+3, true);
|
|
|
|
iStat += EqnTest( "b*3+2", b*3+2, true);
|
|
|
|
iStat += EqnTest( "3*b+2", b*3+2, true);
|
|
|
|
iStat += EqnTest( "2+b*3", b*3+2, true);
|
|
|
|
iStat += EqnTest( "2+3*b", b*3+2, true);
|
|
|
|
iStat += EqnTest( "b+3*b", b+3*b, true);
|
|
|
|
iStat += EqnTest( "3*b+b", b+3*b, true);
|
2014-04-25 14:58:14 +02:00
|
|
|
|
2014-04-25 17:01:23 +02:00
|
|
|
iStat += EqnTest( "2+b*3+b", 2+b*3+b, true);
|
|
|
|
iStat += EqnTest( "b+2+b*3", b+2+b*3, true);
|
2014-04-25 14:58:14 +02:00
|
|
|
|
2014-04-25 17:01:23 +02:00
|
|
|
iStat += EqnTest( "(2*b+1)*4", (2*b+1)*4, true);
|
|
|
|
iStat += EqnTest( "4*(2*b+1)", (2*b+1)*4, true);
|
2014-04-25 14:58:14 +02:00
|
|
|
|
|
|
|
// operator precedencs
|
2014-04-25 17:01:23 +02:00
|
|
|
iStat += EqnTest( "1+2-3*4/5^6", 2.99923, true);
|
|
|
|
iStat += EqnTest( "1^2/3*4-5+6", 2.33333333, true);
|
|
|
|
iStat += EqnTest( "1+2*3", 7, true);
|
|
|
|
iStat += EqnTest( "1+2*3", 7, true);
|
|
|
|
iStat += EqnTest( "(1+2)*3", 9, true);
|
|
|
|
iStat += EqnTest( "(1+2)*(-3)", -9, true);
|
|
|
|
iStat += EqnTest( "2/4", 0.5, true);
|
|
|
|
|
|
|
|
iStat += EqnTest( "exp(ln(7))", 7, true);
|
|
|
|
iStat += EqnTest( "e^ln(7)", 7, true);
|
|
|
|
iStat += EqnTest( "e^(ln(7))", 7, true);
|
|
|
|
iStat += EqnTest( "(e^(ln(7)))", 7, true);
|
|
|
|
iStat += EqnTest( "1-(e^(ln(7)))", -6, true);
|
|
|
|
iStat += EqnTest( "2*(e^(ln(7)))", 14, true);
|
|
|
|
iStat += EqnTest( "10^log(5)", 5, true);
|
|
|
|
iStat += EqnTest( "10^log10(5)", 5, true);
|
|
|
|
iStat += EqnTest( "2^log2(4)", 4, true);
|
|
|
|
iStat += EqnTest( "-(sin(0)+1)", -1, true);
|
|
|
|
iStat += EqnTest( "-(2^1.1)", -2.14354692, true);
|
|
|
|
|
|
|
|
iStat += EqnTest( "(cos(2.41)/b)", -0.372056, true);
|
|
|
|
iStat += EqnTest( "(1*(2*(3*(4*(5*(6*(a+b)))))))", 2160, true);
|
|
|
|
iStat += EqnTest( "(1*(2*(3*(4*(5*(6*(7*(a+b))))))))", 15120, true);
|
|
|
|
iStat += EqnTest( "(a/((((b+(((e*(((((pi*((((3.45*((pi+a)+pi))+b)+b)*a))+0.68)+e)+a)/a))+a)+b))+b)*a)-pi))", 0.00377999, true);
|
2014-04-25 14:58:14 +02:00
|
|
|
|
|
|
|
// long formula (Reference: Matlab)
|
|
|
|
iStat += EqnTest(
|
2014-04-25 17:01:23 +02:00
|
|
|
"(((-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);
|
2014-04-25 14:58:14 +02:00
|
|
|
|
|
|
|
// long formula (Reference: Matlab)
|
|
|
|
iStat += EqnTest(
|
2014-04-25 17:01:23 +02:00
|
|
|
"(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);
|
2014-04-25 14:58:14 +02:00
|
|
|
|
|
|
|
// long formula (Reference: Matlab)
|
2014-04-25 17:01:23 +02:00
|
|
|
iStat += EqnTest( "1+2-3*4/5^6*(2*(1-5+(3*7^9)*(4+6*7-3)))+12", -7995810.09926, true);
|
2014-04-25 14:58:14 +02:00
|
|
|
|
|
|
|
if (iStat==0)
|
2014-04-26 09:23:50 +02:00
|
|
|
qmu::console() << "passed" << endl;
|
2014-04-25 14:58:14 +02:00
|
|
|
else
|
2014-04-26 09:23:50 +02:00
|
|
|
qmu::console() << "\n failed with " << iStat << " errors" << endl;
|
2014-04-25 14:58:14 +02:00
|
|
|
|
|
|
|
return iStat;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
int QmuParserTester::TestIfThenElse()
|
|
|
|
{
|
|
|
|
int iStat = 0;
|
2014-04-26 09:23:50 +02:00
|
|
|
qmu::console() << "testing if-then-else operator...";
|
2014-04-25 14:58:14 +02:00
|
|
|
|
|
|
|
// Test error detection
|
2014-04-25 17:01:23 +02:00
|
|
|
iStat += ThrowTest(":3", ecUNEXPECTED_CONDITIONAL);
|
|
|
|
iStat += ThrowTest("? 1 : 2", ecUNEXPECTED_CONDITIONAL);
|
|
|
|
iStat += ThrowTest("(a<b) ? (b<c) ? 1 : 2", ecMISSING_ELSE_CLAUSE);
|
|
|
|
iStat += ThrowTest("(a<b) ? 1", ecMISSING_ELSE_CLAUSE);
|
|
|
|
iStat += ThrowTest("(a<b) ? a", ecMISSING_ELSE_CLAUSE);
|
|
|
|
iStat += ThrowTest("(a<b) ? a+b", ecMISSING_ELSE_CLAUSE);
|
|
|
|
iStat += ThrowTest("a : b", ecMISPLACED_COLON);
|
|
|
|
iStat += ThrowTest("1 : 2", ecMISPLACED_COLON);
|
|
|
|
iStat += ThrowTest("(1) ? 1 : 2 : 3", ecMISPLACED_COLON);
|
|
|
|
iStat += ThrowTest("(true) ? 1 : 2 : 3", ecUNASSIGNABLE_TOKEN);
|
|
|
|
|
|
|
|
iStat += EqnTest("1 ? 128 : 255", 128, true);
|
|
|
|
iStat += EqnTest("1<2 ? 128 : 255", 128, true);
|
|
|
|
iStat += EqnTest("a<b ? 128 : 255", 128, true);
|
|
|
|
iStat += EqnTest("(a<b) ? 128 : 255", 128, true);
|
|
|
|
iStat += EqnTest("(1) ? 10 : 11", 10, true);
|
|
|
|
iStat += EqnTest("(0) ? 10 : 11", 11, true);
|
|
|
|
iStat += EqnTest("(1) ? a+b : c+d", 3, true);
|
|
|
|
iStat += EqnTest("(0) ? a+b : c+d", 1, true);
|
|
|
|
iStat += EqnTest("(1) ? 0 : 1", 0, true);
|
|
|
|
iStat += EqnTest("(0) ? 0 : 1", 1, true);
|
|
|
|
iStat += EqnTest("(a<b) ? 10 : 11", 10, true);
|
|
|
|
iStat += EqnTest("(a>b) ? 10 : 11", 11, true);
|
|
|
|
iStat += EqnTest("(a<b) ? c : d", 3, true);
|
|
|
|
iStat += EqnTest("(a>b) ? c : d", -2, true);
|
|
|
|
|
|
|
|
iStat += EqnTest("(a>b) ? 1 : 0", 0, true);
|
|
|
|
iStat += EqnTest("((a>b) ? 1 : 0) ? 1 : 2", 2, true);
|
|
|
|
iStat += EqnTest("((a>b) ? 1 : 0) ? 1 : sum((a>b) ? 1 : 2)", 2, true);
|
|
|
|
iStat += EqnTest("((a>b) ? 0 : 1) ? 1 : sum((a>b) ? 1 : 2)", 1, true);
|
|
|
|
|
|
|
|
iStat += EqnTest("sum((a>b) ? 1 : 2)", 2, true);
|
|
|
|
iStat += EqnTest("sum((1) ? 1 : 2)", 1, true);
|
|
|
|
iStat += EqnTest("sum((a>b) ? 1 : 2, 100)", 102, true);
|
|
|
|
iStat += EqnTest("sum((1) ? 1 : 2, 100)", 101, true);
|
|
|
|
iStat += EqnTest("sum(3, (a>b) ? 3 : 10)", 13, true);
|
|
|
|
iStat += EqnTest("sum(3, (a<b) ? 3 : 10)", 6, true);
|
|
|
|
iStat += EqnTest("10*sum(3, (a>b) ? 3 : 10)", 130, true);
|
|
|
|
iStat += EqnTest("10*sum(3, (a<b) ? 3 : 10)", 60, true);
|
|
|
|
iStat += EqnTest("sum(3, (a>b) ? 3 : 10)*10", 130, true);
|
|
|
|
iStat += EqnTest("sum(3, (a<b) ? 3 : 10)*10", 60, true);
|
|
|
|
iStat += EqnTest("(a<b) ? sum(3, (a<b) ? 3 : 10)*10 : 99", 60, true);
|
|
|
|
iStat += EqnTest("(a>b) ? sum(3, (a<b) ? 3 : 10)*10 : 99", 99, true);
|
|
|
|
iStat += EqnTest("(a<b) ? sum(3, (a<b) ? 3 : 10,10,20)*10 : 99", 360, true);
|
|
|
|
iStat += EqnTest("(a>b) ? sum(3, (a<b) ? 3 : 10,10,20)*10 : 99", 99, true);
|
|
|
|
iStat += EqnTest("(a>b) ? sum(3, (a<b) ? 3 : 10,10,20)*10 : sum(3, (a<b) ? 3 : 10)*10", 60, true);
|
2014-04-25 14:58:14 +02:00
|
|
|
|
|
|
|
// todo: auch fr muParserX hinzufgen!
|
2014-04-25 17:01:23 +02:00
|
|
|
iStat += EqnTest("(a<b)&&(a<b) ? 128 : 255", 128, true);
|
|
|
|
iStat += EqnTest("(a>b)&&(a<b) ? 128 : 255", 255, true);
|
|
|
|
iStat += EqnTest("(1<2)&&(1<2) ? 128 : 255", 128, true);
|
|
|
|
iStat += EqnTest("(1>2)&&(1<2) ? 128 : 255", 255, true);
|
|
|
|
iStat += EqnTest("((1<2)&&(1<2)) ? 128 : 255", 128, true);
|
|
|
|
iStat += EqnTest("((1>2)&&(1<2)) ? 128 : 255", 255, true);
|
|
|
|
iStat += EqnTest("((a<b)&&(a<b)) ? 128 : 255", 128, true);
|
|
|
|
iStat += EqnTest("((a>b)&&(a<b)) ? 128 : 255", 255, true);
|
|
|
|
|
|
|
|
iStat += EqnTest("1>0 ? 1>2 ? 128 : 255 : 1>0 ? 32 : 64", 255, true);
|
|
|
|
iStat += EqnTest("1>0 ? 1>2 ? 128 : 255 :(1>0 ? 32 : 64)", 255, true);
|
|
|
|
iStat += EqnTest("1>0 ? 1>0 ? 128 : 255 : 1>2 ? 32 : 64", 128, true);
|
|
|
|
iStat += EqnTest("1>0 ? 1>0 ? 128 : 255 :(1>2 ? 32 : 64)", 128, true);
|
|
|
|
iStat += EqnTest("1>2 ? 1>2 ? 128 : 255 : 1>0 ? 32 : 64", 32, true);
|
|
|
|
iStat += EqnTest("1>2 ? 1>0 ? 128 : 255 : 1>2 ? 32 : 64", 64, true);
|
|
|
|
iStat += EqnTest("1>0 ? 50 : 1>0 ? 128 : 255", 50, true);
|
|
|
|
iStat += EqnTest("1>0 ? 50 : (1>0 ? 128 : 255)", 50, true);
|
|
|
|
iStat += EqnTest("1>0 ? 1>0 ? 128 : 255 : 50", 128, true);
|
|
|
|
iStat += EqnTest("1>2 ? 1>2 ? 128 : 255 : 1>0 ? 32 : 1>2 ? 64 : 16", 32, true);
|
|
|
|
iStat += EqnTest("1>2 ? 1>2 ? 128 : 255 : 1>0 ? 32 :(1>2 ? 64 : 16)", 32, true);
|
|
|
|
iStat += EqnTest("1>0 ? 1>2 ? 128 : 255 : 1>0 ? 32 :1>2 ? 64 : 16", 255, true);
|
|
|
|
iStat += EqnTest("1>0 ? 1>2 ? 128 : 255 : (1>0 ? 32 :1>2 ? 64 : 16)", 255, true);
|
|
|
|
iStat += EqnTest("1 ? 0 ? 128 : 255 : 1 ? 32 : 64", 255, true);
|
2014-04-25 14:58:14 +02:00
|
|
|
|
|
|
|
// assignment operators
|
2014-04-25 17:01:23 +02:00
|
|
|
iStat += EqnTest("a= 0 ? 128 : 255, a", 255, true);
|
|
|
|
iStat += EqnTest("a=((a>b)&&(a<b)) ? 128 : 255, a", 255, true);
|
|
|
|
iStat += EqnTest("c=(a<b)&&(a<b) ? 128 : 255, c", 128, true);
|
|
|
|
iStat += EqnTest("0 ? a=a+1 : 666, a", 1, true);
|
|
|
|
iStat += EqnTest("1?a=10:a=20, a", 10, true);
|
|
|
|
iStat += EqnTest("0?a=10:a=20, a", 20, true);
|
|
|
|
iStat += EqnTest("0?a=sum(3,4):10, a", 1, true); // a should not change its value due to lazy calculation
|
2014-04-25 14:58:14 +02:00
|
|
|
|
2014-04-25 17:01:23 +02:00
|
|
|
iStat += EqnTest("a=1?b=1?3:4:5, a", 3, true);
|
|
|
|
iStat += EqnTest("a=1?b=1?3:4:5, b", 3, true);
|
|
|
|
iStat += EqnTest("a=0?b=1?3:4:5, a", 5, true);
|
|
|
|
iStat += EqnTest("a=0?b=1?3:4:5, b", 2, true);
|
2014-04-25 14:58:14 +02:00
|
|
|
|
2014-04-25 17:01:23 +02:00
|
|
|
iStat += EqnTest("a=1?5:b=1?3:4, a", 5, true);
|
|
|
|
iStat += EqnTest("a=1?5:b=1?3:4, b", 2, true);
|
|
|
|
iStat += EqnTest("a=0?5:b=1?3:4, a", 3, true);
|
|
|
|
iStat += EqnTest("a=0?5:b=1?3:4, b", 3, true);
|
2014-04-25 14:58:14 +02:00
|
|
|
|
|
|
|
if (iStat==0)
|
2014-04-26 09:23:50 +02:00
|
|
|
qmu::console() << "passed" << endl;
|
2014-04-25 14:58:14 +02:00
|
|
|
else
|
2014-04-26 09:23:50 +02:00
|
|
|
qmu::console() << "\n failed with " << iStat << " errors" << endl;
|
2014-04-25 14:58:14 +02:00
|
|
|
|
|
|
|
return iStat;
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
int QmuParserTester::TestException()
|
|
|
|
{
|
|
|
|
int iStat = 0;
|
2014-04-26 09:23:50 +02:00
|
|
|
qmu::console() << "testing error codes...";
|
2014-04-25 17:01:23 +02:00
|
|
|
|
|
|
|
iStat += ThrowTest("3+", ecUNEXPECTED_EOF);
|
|
|
|
iStat += ThrowTest("3+)", ecUNEXPECTED_PARENS);
|
|
|
|
iStat += ThrowTest("()", ecUNEXPECTED_PARENS);
|
|
|
|
iStat += ThrowTest("3+()", ecUNEXPECTED_PARENS);
|
|
|
|
iStat += ThrowTest("sin(3,4)", ecTOO_MANY_PARAMS);
|
|
|
|
iStat += ThrowTest("sin()", ecTOO_FEW_PARAMS);
|
|
|
|
iStat += ThrowTest("(1+2", ecMISSING_PARENS);
|
|
|
|
iStat += ThrowTest("sin(3)3", ecUNEXPECTED_VAL);
|
|
|
|
iStat += ThrowTest("sin(3)xyz", ecUNASSIGNABLE_TOKEN);
|
|
|
|
iStat += ThrowTest("sin(3)cos(3)", ecUNEXPECTED_FUN);
|
|
|
|
iStat += ThrowTest("a+b+c=10", ecUNEXPECTED_OPERATOR);
|
|
|
|
iStat += ThrowTest("a=b=3", ecUNEXPECTED_OPERATOR);
|
2014-04-25 14:58:14 +02:00
|
|
|
|
|
|
|
#if defined(MUP_MATH_EXCEPTIONS)
|
|
|
|
// divide by zero whilst constant folding
|
2014-04-25 17:01:23 +02:00
|
|
|
iStat += ThrowTest("1/0", ecDIV_BY_ZERO);
|
2014-04-25 14:58:14 +02:00
|
|
|
// square root of a negative number
|
2014-04-25 17:01:23 +02:00
|
|
|
iStat += ThrowTest("sqrt(-1)", ecDOMAIN_ERROR);
|
2014-04-25 14:58:14 +02:00
|
|
|
// logarithms of zero
|
2014-04-25 17:01:23 +02:00
|
|
|
iStat += ThrowTest("ln(0)", ecDOMAIN_ERROR);
|
|
|
|
iStat += ThrowTest("log2(0)", ecDOMAIN_ERROR);
|
|
|
|
iStat += ThrowTest("log10(0)", ecDOMAIN_ERROR);
|
|
|
|
iStat += ThrowTest("log(0)", ecDOMAIN_ERROR);
|
2014-04-25 14:58:14 +02:00
|
|
|
// logarithms of negative values
|
2014-04-25 17:01:23 +02:00
|
|
|
iStat += ThrowTest("ln(-1)", ecDOMAIN_ERROR);
|
|
|
|
iStat += ThrowTest("log2(-1)", ecDOMAIN_ERROR);
|
|
|
|
iStat += ThrowTest("log10(-1)", ecDOMAIN_ERROR);
|
|
|
|
iStat += ThrowTest("log(-1)", ecDOMAIN_ERROR);
|
2014-04-25 14:58:14 +02:00
|
|
|
#endif
|
|
|
|
|
|
|
|
// functions without parameter
|
2014-04-25 17:01:23 +02:00
|
|
|
iStat += ThrowTest( "3+ping(2)", ecTOO_MANY_PARAMS);
|
|
|
|
iStat += ThrowTest( "3+ping(a+2)", ecTOO_MANY_PARAMS);
|
|
|
|
iStat += ThrowTest( "3+ping(sin(a)+2)", ecTOO_MANY_PARAMS);
|
|
|
|
iStat += ThrowTest( "3+ping(1+sin(a))", ecTOO_MANY_PARAMS);
|
2014-04-25 14:58:14 +02:00
|
|
|
|
|
|
|
// String function related
|
2014-04-25 17:01:23 +02:00
|
|
|
iStat += ThrowTest( "valueof(\"xxx\")", 999, false);
|
|
|
|
iStat += ThrowTest( "valueof()", ecUNEXPECTED_PARENS);
|
|
|
|
iStat += ThrowTest( "1+valueof(\"abc\"", ecMISSING_PARENS);
|
|
|
|
iStat += ThrowTest( "valueof(\"abc\"", ecMISSING_PARENS);
|
|
|
|
iStat += ThrowTest( "valueof(\"abc", ecUNTERMINATED_STRING);
|
|
|
|
iStat += ThrowTest( "valueof(\"abc\",3)", ecTOO_MANY_PARAMS);
|
|
|
|
iStat += ThrowTest( "valueof(3)", ecSTRING_EXPECTED);
|
|
|
|
iStat += ThrowTest( "sin(\"abc\")", ecVAL_EXPECTED);
|
|
|
|
iStat += ThrowTest( "valueof(\"\\\"abc\\\"\")", 999, false);
|
|
|
|
iStat += ThrowTest( "\"hello world\"", ecSTR_RESULT);
|
|
|
|
iStat += ThrowTest( "(\"hello world\")", ecSTR_RESULT);
|
|
|
|
iStat += ThrowTest( "\"abcd\"+100", ecOPRT_TYPE_CONFLICT);
|
|
|
|
iStat += ThrowTest( "\"a\"+\"b\"", ecOPRT_TYPE_CONFLICT);
|
|
|
|
iStat += ThrowTest( "strfun1(\"100\",3)", ecTOO_MANY_PARAMS);
|
|
|
|
iStat += ThrowTest( "strfun2(\"100\",3,5)", ecTOO_MANY_PARAMS);
|
|
|
|
iStat += ThrowTest( "strfun3(\"100\",3,5,6)", ecTOO_MANY_PARAMS);
|
|
|
|
iStat += ThrowTest( "strfun2(\"100\")", ecTOO_FEW_PARAMS);
|
|
|
|
iStat += ThrowTest( "strfun3(\"100\",6)", ecTOO_FEW_PARAMS);
|
|
|
|
iStat += ThrowTest( "strfun2(1,1)", ecSTRING_EXPECTED);
|
|
|
|
iStat += ThrowTest( "strfun2(a,1)", ecSTRING_EXPECTED);
|
|
|
|
iStat += ThrowTest( "strfun2(1,1,1)", ecTOO_MANY_PARAMS);
|
|
|
|
iStat += ThrowTest( "strfun2(a,1,1)", ecTOO_MANY_PARAMS);
|
|
|
|
iStat += ThrowTest( "strfun3(1,2,3)", ecSTRING_EXPECTED);
|
|
|
|
iStat += ThrowTest( "strfun3(1, \"100\",3)", ecSTRING_EXPECTED);
|
|
|
|
iStat += ThrowTest( "strfun3(\"1\", \"100\",3)", ecVAL_EXPECTED);
|
|
|
|
iStat += ThrowTest( "strfun3(\"1\", 3, \"100\")", ecVAL_EXPECTED);
|
|
|
|
iStat += ThrowTest( "strfun3(\"1\", \"100\", \"100\", \"100\")", ecTOO_MANY_PARAMS);
|
2014-04-25 14:58:14 +02:00
|
|
|
|
|
|
|
// assignement operator
|
2014-04-25 17:01:23 +02:00
|
|
|
iStat += ThrowTest( "3=4", ecUNEXPECTED_OPERATOR);
|
|
|
|
iStat += ThrowTest( "sin(8)=4", ecUNEXPECTED_OPERATOR);
|
|
|
|
iStat += ThrowTest( "\"test\"=a", ecUNEXPECTED_OPERATOR);
|
2014-04-25 14:58:14 +02:00
|
|
|
|
|
|
|
// <ibg 20090529>
|
|
|
|
// this is now legal, for reference see:
|
|
|
|
// https://sourceforge.net/forum/message.php?msg_id=7411373
|
2014-04-25 17:01:23 +02:00
|
|
|
// iStat += ThrowTest( "sin=9"), ecUNEXPECTED_OPERATOR);
|
2014-04-25 14:58:14 +02:00
|
|
|
// </ibg>
|
|
|
|
|
2014-04-25 17:01:23 +02:00
|
|
|
iStat += ThrowTest( "(8)=5", ecUNEXPECTED_OPERATOR);
|
|
|
|
iStat += ThrowTest( "(a)=5", ecUNEXPECTED_OPERATOR);
|
|
|
|
iStat += ThrowTest( "a=\"tttt\"", ecOPRT_TYPE_CONFLICT);
|
2014-04-25 14:58:14 +02:00
|
|
|
|
|
|
|
if (iStat==0)
|
2014-04-26 09:23:50 +02:00
|
|
|
qmu::console() << "passed" << endl;
|
2014-04-25 14:58:14 +02:00
|
|
|
else
|
2014-04-26 09:23:50 +02:00
|
|
|
qmu::console() << "\n failed with " << iStat << " errors" << endl;
|
2014-04-25 14:58:14 +02:00
|
|
|
|
|
|
|
return iStat;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
void QmuParserTester::AddTest(testfun_type a_pFun)
|
|
|
|
{
|
|
|
|
m_vTestFun.push_back(a_pFun);
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
void QmuParserTester::Run()
|
|
|
|
{
|
|
|
|
int iStat = 0;
|
|
|
|
try
|
|
|
|
{
|
|
|
|
for (int i=0; i<(int)m_vTestFun.size(); ++i)
|
|
|
|
iStat += (this->*m_vTestFun[i])();
|
|
|
|
}
|
|
|
|
catch(QmuParser::exception_type &e)
|
|
|
|
{
|
2014-04-26 09:23:50 +02:00
|
|
|
qmu::console() << "\n" << e.GetMsg() << endl;
|
|
|
|
qmu::console() << e.GetToken() << endl;
|
2014-04-25 14:58:14 +02:00
|
|
|
Abort();
|
|
|
|
}
|
|
|
|
catch(std::exception &e)
|
|
|
|
{
|
2014-04-26 09:23:50 +02:00
|
|
|
qmu::console() << e.what() << endl;
|
2014-04-25 14:58:14 +02:00
|
|
|
Abort();
|
|
|
|
}
|
|
|
|
catch(...)
|
|
|
|
{
|
2014-04-26 09:23:50 +02:00
|
|
|
qmu::console() << "Internal error";
|
2014-04-25 14:58:14 +02:00
|
|
|
Abort();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (iStat==0)
|
|
|
|
{
|
2014-04-26 09:23:50 +02:00
|
|
|
qmu::console() << "Test passed (" << QmuParserTester::c_iCount << " expressions)" << endl;
|
2014-04-25 14:58:14 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2014-04-26 09:23:50 +02:00
|
|
|
qmu::console() << "Test failed with " << iStat
|
2014-04-25 14:58:14 +02:00
|
|
|
<< " errors (" << QmuParserTester::c_iCount
|
|
|
|
<< " expressions)" << endl;
|
|
|
|
}
|
|
|
|
QmuParserTester::c_iCount = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
int QmuParserTester::ThrowTest(const string_type &a_str, int a_iErrc, bool a_bFail)
|
|
|
|
{
|
|
|
|
QmuParserTester::c_iCount++;
|
|
|
|
|
|
|
|
try
|
|
|
|
{
|
2014-04-25 17:01:23 +02:00
|
|
|
qreal fVal[] = {1,1,1};
|
2014-04-25 14:58:14 +02:00
|
|
|
QmuParser p;
|
|
|
|
|
2014-04-25 17:01:23 +02:00
|
|
|
p.DefineVar( "a", &fVal[0]);
|
|
|
|
p.DefineVar( "b", &fVal[1]);
|
|
|
|
p.DefineVar( "c", &fVal[2]);
|
|
|
|
p.DefinePostfixOprt( "{m}", Milli);
|
|
|
|
p.DefinePostfixOprt( "m", Milli);
|
|
|
|
p.DefineFun( "ping", Ping);
|
|
|
|
p.DefineFun( "valueof", ValueOf);
|
|
|
|
p.DefineFun( "strfun1", StrFun1);
|
|
|
|
p.DefineFun( "strfun2", StrFun2);
|
|
|
|
p.DefineFun( "strfun3", StrFun3);
|
2014-04-25 14:58:14 +02:00
|
|
|
p.SetExpr(a_str);
|
|
|
|
p.Eval();
|
|
|
|
}
|
|
|
|
catch(QmuParserError &e)
|
|
|
|
{
|
|
|
|
// output the formula in case of an failed test
|
|
|
|
if (a_bFail==false || (a_bFail==true && a_iErrc!=e.GetCode()) )
|
|
|
|
{
|
2014-04-26 09:23:50 +02:00
|
|
|
qmu::console() << "\n "
|
2014-04-25 17:01:23 +02:00
|
|
|
<< "Expression: " << a_str
|
|
|
|
<< " Code:" << e.GetCode() << "(" << e.GetMsg() << ")"
|
|
|
|
<< " Expected:" << a_iErrc;
|
2014-04-25 14:58:14 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
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)
|
|
|
|
{
|
2014-04-26 09:23:50 +02:00
|
|
|
qmu::console() << "\n "
|
2014-04-25 17:01:23 +02:00
|
|
|
<< "Expression: " << a_str
|
|
|
|
<< " did evaluate; Expected error:" << a_iErrc;
|
2014-04-25 14:58:14 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return bRet;
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
/** \brief Evaluate a tet expression.
|
|
|
|
|
|
|
|
\return 1 in case of a failure, 0 otherwise.
|
|
|
|
*/
|
|
|
|
int QmuParserTester::EqnTestWithVarChange(const string_type &a_str,
|
|
|
|
double a_fVar1,
|
|
|
|
double a_fRes1,
|
|
|
|
double a_fVar2,
|
|
|
|
double a_fRes2)
|
|
|
|
{
|
|
|
|
QmuParserTester::c_iCount++;
|
2014-04-25 17:01:23 +02:00
|
|
|
qreal fVal[2] = {-999, -999 }; // should be equalinitially
|
2014-04-25 14:58:14 +02:00
|
|
|
|
|
|
|
try
|
|
|
|
{
|
|
|
|
QmuParser p;
|
|
|
|
|
|
|
|
// variable
|
2014-04-25 17:01:23 +02:00
|
|
|
qreal var = 0;
|
|
|
|
p.DefineVar( "a", &var);
|
2014-04-25 14:58:14 +02:00
|
|
|
p.SetExpr(a_str);
|
|
|
|
|
|
|
|
var = a_fVar1;
|
|
|
|
fVal[0] = p.Eval();
|
|
|
|
|
|
|
|
var = a_fVar2;
|
|
|
|
fVal[1] = p.Eval();
|
|
|
|
|
|
|
|
if ( fabs(a_fRes1-fVal[0]) > 0.0000000001)
|
|
|
|
throw std::runtime_error("incorrect result (first pass)");
|
|
|
|
|
|
|
|
if ( fabs(a_fRes2-fVal[1]) > 0.0000000001)
|
|
|
|
throw std::runtime_error("incorrect result (second pass)");
|
|
|
|
}
|
|
|
|
catch(QmuParser::exception_type &e)
|
|
|
|
{
|
2014-04-26 09:23:50 +02:00
|
|
|
qmu::console() << "\n fail: " << a_str.c_str() << " (" << e.GetMsg() << ")";
|
2014-04-25 14:58:14 +02:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
catch(std::exception &e)
|
|
|
|
{
|
2014-04-26 09:23:50 +02:00
|
|
|
qmu::console() << "\n fail: " << a_str.c_str() << " (" << e.what() << ")";
|
2014-04-25 14:58:14 +02:00
|
|
|
return 1; // always return a failure since this exception is not expected
|
|
|
|
}
|
|
|
|
catch(...)
|
|
|
|
{
|
2014-04-26 09:23:50 +02:00
|
|
|
qmu::console() << "\n fail: " << a_str.c_str() << " (unexpected exception)";
|
2014-04-25 14:58:14 +02:00
|
|
|
return 1; // exceptions other than ParserException are not allowed
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
/** \brief Evaluate a tet expression.
|
|
|
|
|
|
|
|
\return 1 in case of a failure, 0 otherwise.
|
|
|
|
*/
|
|
|
|
int QmuParserTester::EqnTest(const string_type &a_str, double a_fRes, bool a_fPass)
|
|
|
|
{
|
|
|
|
QmuParserTester::c_iCount++;
|
|
|
|
int iRet(0);
|
2014-04-25 17:01:23 +02:00
|
|
|
qreal fVal[5] = {-999, -998, -997, -996, -995}; // initially should be different
|
2014-04-25 14:58:14 +02:00
|
|
|
|
|
|
|
try
|
|
|
|
{
|
|
|
|
std::auto_ptr<QmuParser> p1;
|
|
|
|
QmuParser p2, p3; // three parser objects
|
|
|
|
// they will be used for testing copy and assihnment operators
|
|
|
|
// p1 is a pointer since i'm going to delete it in order to test if
|
|
|
|
// parsers after copy construction still refer to members of it.
|
|
|
|
// !! If this is the case this function will crash !!
|
|
|
|
|
|
|
|
p1.reset(new qmu::QmuParser());
|
|
|
|
// Add constants
|
2014-04-25 17:01:23 +02:00
|
|
|
p1->DefineConst( "pi", (qreal)PARSER_CONST_PI);
|
|
|
|
p1->DefineConst( "e", (qreal)PARSER_CONST_E);
|
|
|
|
p1->DefineConst( "const", 1);
|
|
|
|
p1->DefineConst( "const1", 2);
|
|
|
|
p1->DefineConst( "const2", 3);
|
2014-04-25 14:58:14 +02:00
|
|
|
// variables
|
2014-04-25 17:01:23 +02:00
|
|
|
qreal vVarVal[] = { 1, 2, 3, -2};
|
|
|
|
p1->DefineVar( "a", &vVarVal[0]);
|
|
|
|
p1->DefineVar( "aa", &vVarVal[1]);
|
|
|
|
p1->DefineVar( "b", &vVarVal[1]);
|
|
|
|
p1->DefineVar( "c", &vVarVal[2]);
|
|
|
|
p1->DefineVar( "d", &vVarVal[3]);
|
2014-04-25 14:58:14 +02:00
|
|
|
|
|
|
|
// custom value ident functions
|
|
|
|
p1->AddValIdent(&QmuParserTester::IsHexVal);
|
|
|
|
|
|
|
|
// functions
|
2014-04-25 17:01:23 +02:00
|
|
|
p1->DefineFun( "ping", Ping);
|
|
|
|
p1->DefineFun( "f1of1", f1of1); // one parameter
|
|
|
|
p1->DefineFun( "f1of2", f1of2); // two parameter
|
|
|
|
p1->DefineFun( "f2of2", f2of2);
|
|
|
|
p1->DefineFun( "f1of3", f1of3); // three parameter
|
|
|
|
p1->DefineFun( "f2of3", f2of3);
|
|
|
|
p1->DefineFun( "f3of3", f3of3);
|
|
|
|
p1->DefineFun( "f1of4", f1of4); // four parameter
|
|
|
|
p1->DefineFun( "f2of4", f2of4);
|
|
|
|
p1->DefineFun( "f3of4", f3of4);
|
|
|
|
p1->DefineFun( "f4of4", f4of4);
|
|
|
|
p1->DefineFun( "f1of5", f1of5); // five parameter
|
|
|
|
p1->DefineFun( "f2of5", f2of5);
|
|
|
|
p1->DefineFun( "f3of5", f3of5);
|
|
|
|
p1->DefineFun( "f4of5", f4of5);
|
|
|
|
p1->DefineFun( "f5of5", f5of5);
|
2014-04-25 14:58:14 +02:00
|
|
|
|
|
|
|
// binary operators
|
2014-04-25 17:01:23 +02:00
|
|
|
p1->DefineOprt( "add", add, 0);
|
|
|
|
p1->DefineOprt( "++", add, 0);
|
|
|
|
p1->DefineOprt( "&", land, prLAND);
|
2014-04-25 14:58:14 +02:00
|
|
|
|
|
|
|
// sample functions
|
2014-04-25 17:01:23 +02:00
|
|
|
p1->DefineFun( "min", Min);
|
|
|
|
p1->DefineFun( "max", Max);
|
|
|
|
p1->DefineFun( "sum", Sum);
|
|
|
|
p1->DefineFun( "valueof", ValueOf);
|
|
|
|
p1->DefineFun( "atof", StrToFloat);
|
|
|
|
p1->DefineFun( "strfun1", StrFun1);
|
|
|
|
p1->DefineFun( "strfun2", StrFun2);
|
|
|
|
p1->DefineFun( "strfun3", StrFun3);
|
|
|
|
p1->DefineFun( "lastArg", LastArg);
|
|
|
|
p1->DefineFun( "firstArg", FirstArg);
|
|
|
|
p1->DefineFun( "order", FirstArg);
|
2014-04-25 14:58:14 +02:00
|
|
|
|
|
|
|
// infix / postfix operator
|
|
|
|
// Note: Identifiers used here do not have any meaning
|
|
|
|
// they are mere placeholders to test certain features.
|
2014-04-25 17:01:23 +02:00
|
|
|
p1->DefineInfixOprt( "$", sign, prPOW+1); // sign with high priority
|
|
|
|
p1->DefineInfixOprt( "~", plus2); // high priority
|
|
|
|
p1->DefineInfixOprt( "~~", plus2);
|
|
|
|
p1->DefinePostfixOprt( "{m}", Milli);
|
|
|
|
p1->DefinePostfixOprt( "{M}", Mega);
|
|
|
|
p1->DefinePostfixOprt( "m", Milli);
|
|
|
|
p1->DefinePostfixOprt( "meg", Mega);
|
|
|
|
p1->DefinePostfixOprt( "#", times3);
|
|
|
|
p1->DefinePostfixOprt( "'", sqr);
|
2014-04-25 14:58:14 +02:00
|
|
|
p1->SetExpr(a_str);
|
|
|
|
|
|
|
|
// Test bytecode integrity
|
|
|
|
// String parsing and bytecode parsing must yield the same result
|
|
|
|
fVal[0] = p1->Eval(); // result from stringparsing
|
|
|
|
fVal[1] = p1->Eval(); // result from bytecode
|
|
|
|
if (fVal[0]!=fVal[1])
|
2014-04-25 17:01:23 +02:00
|
|
|
throw QmuParser::exception_type( "Bytecode / string parsing mismatch." );
|
2014-04-25 14:58:14 +02:00
|
|
|
|
|
|
|
// Test copy and assignement operators
|
|
|
|
try
|
|
|
|
{
|
|
|
|
// Test copy constructor
|
|
|
|
std::vector<qmu::QmuParser> vParser;
|
|
|
|
vParser.push_back(*(p1.get()));
|
|
|
|
qmu::QmuParser p2 = vParser[0]; // take parser from vector
|
|
|
|
|
|
|
|
// destroy the originals from p2
|
|
|
|
vParser.clear(); // delete the vector
|
|
|
|
p1.reset(0);
|
|
|
|
|
|
|
|
fVal[2] = p2.Eval();
|
|
|
|
|
|
|
|
// Test assignement operator
|
|
|
|
// additionally disable Optimizer this time
|
|
|
|
qmu::QmuParser p3;
|
|
|
|
p3 = p2;
|
|
|
|
p3.EnableOptimizer(false);
|
|
|
|
fVal[3] = p3.Eval();
|
|
|
|
|
|
|
|
// Test Eval function for multiple return values
|
|
|
|
// use p2 since it has the optimizer enabled!
|
|
|
|
int nNum;
|
2014-04-25 17:01:23 +02:00
|
|
|
qreal *v = p2.Eval(nNum);
|
2014-04-25 14:58:14 +02:00
|
|
|
fVal[4] = v[nNum-1];
|
|
|
|
}
|
|
|
|
catch(std::exception &e)
|
|
|
|
{
|
2014-04-26 09:23:50 +02:00
|
|
|
qmu::console() << "\n " << e.what() << "\n";
|
2014-04-25 14:58:14 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// limited floating point accuracy requires the following test
|
|
|
|
bool bCloseEnough(true);
|
2014-04-25 17:01:23 +02:00
|
|
|
for (unsigned i=0; i<sizeof(fVal)/sizeof(qreal); ++i)
|
2014-04-25 14:58:14 +02:00
|
|
|
{
|
|
|
|
bCloseEnough &= (fabs(a_fRes-fVal[i]) <= fabs(fVal[i]*0.00001));
|
|
|
|
|
|
|
|
// The tests equations never result in infinity, if they do thats a bug.
|
|
|
|
// reference:
|
|
|
|
// http://sourceforge.net/projects/muparser/forums/forum/462843/topic/5037825
|
2014-04-25 17:01:23 +02:00
|
|
|
if (numeric_limits<qreal>::has_infinity)
|
|
|
|
bCloseEnough &= (fabs(fVal[i]) != numeric_limits<qreal>::infinity());
|
2014-04-25 14:58:14 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
iRet = ((bCloseEnough && a_fPass) || (!bCloseEnough && !a_fPass)) ? 0 : 1;
|
|
|
|
|
|
|
|
|
|
|
|
if (iRet==1)
|
|
|
|
{
|
2014-04-26 09:23:50 +02:00
|
|
|
qmu::console() << "\n fail: " << a_str.c_str()
|
2014-04-25 17:01:23 +02:00
|
|
|
<< " (incorrect result; expected: " << a_fRes
|
|
|
|
<< " ;calculated: " << fVal[0] << ","
|
|
|
|
<< fVal[1] << ","
|
|
|
|
<< fVal[2] << ","
|
|
|
|
<< fVal[3] << ","
|
|
|
|
<< fVal[4] << ").";
|
2014-04-25 14:58:14 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
catch(QmuParser::exception_type &e)
|
|
|
|
{
|
|
|
|
if (a_fPass)
|
|
|
|
{
|
|
|
|
if (fVal[0]!=fVal[2] && fVal[0]!=-999 && fVal[1]!=-998)
|
2014-04-26 09:23:50 +02:00
|
|
|
qmu::console() << "\n fail: " << a_str.c_str() << " (copy construction)";
|
2014-04-25 14:58:14 +02:00
|
|
|
else
|
2014-04-26 09:23:50 +02:00
|
|
|
qmu::console() << "\n fail: " << a_str.c_str() << " (" << e.GetMsg() << ")";
|
2014-04-25 14:58:14 +02:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
catch(std::exception &e)
|
|
|
|
{
|
2014-04-26 09:23:50 +02:00
|
|
|
qmu::console() << "\n fail: " << a_str.c_str() << " (" << e.what() << ")";
|
2014-04-25 14:58:14 +02:00
|
|
|
return 1; // always return a failure since this exception is not expected
|
|
|
|
}
|
|
|
|
catch(...)
|
|
|
|
{
|
2014-04-26 09:23:50 +02:00
|
|
|
qmu::console() << "\n fail: " << a_str.c_str() << " (unexpected exception)";
|
2014-04-25 14:58:14 +02:00
|
|
|
return 1; // exceptions other than ParserException are not allowed
|
|
|
|
}
|
|
|
|
|
|
|
|
return iRet;
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
/** \brief Internal error in test class Test is going to be aborted. */
|
|
|
|
void QmuParserTester::Abort() const
|
|
|
|
{
|
2014-04-26 09:23:50 +02:00
|
|
|
qmu::console() << "Test failed (internal error in test class)" << endl;
|
2014-04-25 14:58:14 +02:00
|
|
|
while (!getchar());
|
|
|
|
exit(-1);
|
|
|
|
}
|
|
|
|
} // namespace test
|
|
|
|
} // namespace qmu
|