1839 lines
71 KiB
C++
1839 lines
71 KiB
C++
![]() |
/** \file
|
||
|
\brief Implementation of the unit test for muParserX.
|
||
|
|
||
|
<pre>
|
||
|
__________ ____ ___
|
||
|
_____ __ _\______ \_____ _______ ______ __________\ \/ /
|
||
|
/ \| | \ ___/\__ \\_ __ \/ ___// __ \_ __ \ /
|
||
|
| Y Y \ | / | / __ \| | \/\___ \\ ___/| | \/ \
|
||
|
|__|_| /____/|____| (____ /__| /____ >\___ >__| /___/\ \
|
||
|
\/ \/ \/ \/ \_/
|
||
|
Copyright (C) 2016 Ingo Berg
|
||
|
All rights reserved.
|
||
|
|
||
|
muParserX - A C++ math parser library with array and string support
|
||
|
Copyright (c) 2016, Ingo Berg
|
||
|
All rights reserved.
|
||
|
|
||
|
Redistribution and use in source and binary forms, with or without
|
||
|
modification, are permitted provided that the following conditions are met:
|
||
|
|
||
|
* Redistributions of source code must retain the above copyright notice,
|
||
|
this list of conditions and the following disclaimer.
|
||
|
* Redistributions in binary form must reproduce the above copyright notice,
|
||
|
this list of conditions and the following disclaimer in the documentation
|
||
|
and/or other materials provided with the distribution.
|
||
|
|
||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||
|
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||
|
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||
|
IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
|
||
|
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||
|
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||
|
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||
|
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||
|
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||
|
POSSIBILITY OF SUCH DAMAGE.
|
||
|
</pre>
|
||
|
*/
|
||
|
#include "mpTest.h"
|
||
|
#include "mpValue.h"
|
||
|
|
||
|
#include <cstdio>
|
||
|
#include <cstdlib>
|
||
|
#include <cmath>
|
||
|
#include <iostream>
|
||
|
#include <complex>
|
||
|
#include <limits>
|
||
|
|
||
|
#define MUP_CONST_PI 3.141592653589793238462643
|
||
|
#define MUP_CONST_E 2.718281828459045235360287
|
||
|
|
||
|
using namespace std;
|
||
|
|
||
|
MUP_NAMESPACE_START
|
||
|
|
||
|
//-----------------------------------------------------------------------------------------------
|
||
|
//
|
||
|
// class OprtStrAdd
|
||
|
//
|
||
|
//-----------------------------------------------------------------------------------------------
|
||
|
|
||
|
class DbgSillyAdd : public IOprtBin
|
||
|
{
|
||
|
public:
|
||
|
|
||
|
DbgSillyAdd()
|
||
|
:IOprtBin(_T("++"), (int)prADD_SUB, oaLEFT)
|
||
|
{}
|
||
|
|
||
|
//-----------------------------------------------------------------------------------------------
|
||
|
void Eval(ptr_val_type& ret, const ptr_val_type *arg, int argc)
|
||
|
{
|
||
|
assert(argc == 2);
|
||
|
float_type a = arg[0]->GetFloat();
|
||
|
float_type b = arg[1]->GetFloat();
|
||
|
*ret = a + b;
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------------------------
|
||
|
const char_type* GetDesc() const
|
||
|
{
|
||
|
return _T("internally used operator without special meaning for unit testing");
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------------------------
|
||
|
IToken* Clone() const
|
||
|
{
|
||
|
return new DbgSillyAdd(*this);
|
||
|
}
|
||
|
};
|
||
|
|
||
|
//------------------------------------------------------------------------------
|
||
|
class FunTest0 : public ICallback
|
||
|
{
|
||
|
public:
|
||
|
FunTest0() : ICallback(cmFUNC, _T("test0"), 0)
|
||
|
{}
|
||
|
|
||
|
virtual void Eval(ptr_val_type &ret, const ptr_val_type * /*a_pArg*/, int /*a_iArgc*/)
|
||
|
{
|
||
|
*ret = 0.0;
|
||
|
}
|
||
|
|
||
|
virtual const char_type* GetDesc() const
|
||
|
{
|
||
|
return _T("");
|
||
|
}
|
||
|
|
||
|
virtual IToken* Clone() const
|
||
|
{
|
||
|
return new FunTest0(*this);
|
||
|
}
|
||
|
}; // class FunTest0
|
||
|
|
||
|
//---------------------------------------------------------------------------
|
||
|
int ParserTester::c_iCount = 0;
|
||
|
|
||
|
//---------------------------------------------------------------------------
|
||
|
ParserTester::ParserTester()
|
||
|
:m_vTestFun()
|
||
|
, m_stream(&console())
|
||
|
{
|
||
|
AddTest(&ParserTester::TestParserValue);
|
||
|
AddTest(&ParserTester::TestUndefVar);
|
||
|
AddTest(&ParserTester::TestErrorCodes);
|
||
|
AddTest(&ParserTester::TestEqn);
|
||
|
AddTest(&ParserTester::TestIfElse);
|
||
|
AddTest(&ParserTester::TestStringFun);
|
||
|
AddTest(&ParserTester::TestMatrix);
|
||
|
AddTest(&ParserTester::TestComplex);
|
||
|
AddTest(&ParserTester::TestVector);
|
||
|
AddTest(&ParserTester::TestBinOp);
|
||
|
AddTest(&ParserTester::TestPostfix);
|
||
|
AddTest(&ParserTester::TestInfix);
|
||
|
AddTest(&ParserTester::TestMultiArg);
|
||
|
AddTest(&ParserTester::TestScript);
|
||
|
AddTest(&ParserTester::TestValReader);
|
||
|
AddTest(&ParserTester::TestIssueReports);
|
||
|
|
||
|
ParserTester::c_iCount = 0;
|
||
|
}
|
||
|
|
||
|
//---------------------------------------------------------------------------
|
||
|
int ParserTester::TestIssueReports()
|
||
|
{
|
||
|
int iNumErr = 0;
|
||
|
*m_stream << _T("testing github issue reports...");
|
||
|
|
||
|
// Github: Issue 55
|
||
|
iNumErr += ThrowTest(_T("{0,{0}}*{0,{0}}*{,{0}}*{0,{0}0,{0}}*{0,{0}}*{,{0}}*{0}*{000}"), ecUNEXPECTED_COMMA);
|
||
|
|
||
|
// Github: Issue 56
|
||
|
iNumErr += ThrowTest(_T("0M[,1][0/1M[0M]M]"), ecUNEXPECTED_COMMA);
|
||
|
|
||
|
// Github Issue 57:
|
||
|
iNumErr += ThrowTest(_T("{?{{{{:44"), ecUNEXPECTED_CONDITIONAL);
|
||
|
|
||
|
// Github Issue 60
|
||
|
iNumErr += ThrowTest(_T("0<01?1=:1"), ecMISPLACED_COLON);
|
||
|
iNumErr += ThrowTest(_T("0<01?1<:1"), ecMISPLACED_COLON);
|
||
|
iNumErr += ThrowTest(_T("0<01?1>:1"), ecMISPLACED_COLON);
|
||
|
iNumErr += ThrowTest(_T("0<01?1-:1"), ecMISPLACED_COLON);
|
||
|
iNumErr += ThrowTest(_T("0<01?1-:1"), ecMISPLACED_COLON);
|
||
|
iNumErr += ThrowTest(_T("0<01?1-:1"), ecMISPLACED_COLON);
|
||
|
iNumErr += ThrowTest(_T("0<01?1-:1"), ecMISPLACED_COLON);
|
||
|
iNumErr += ThrowTest(_T("0<01?1-:1"), ecMISPLACED_COLON);
|
||
|
iNumErr += ThrowTest(_T("0<01?1+:1"), ecMISPLACED_COLON);
|
||
|
iNumErr += ThrowTest(_T("0<01?1*:1"), ecMISPLACED_COLON);
|
||
|
iNumErr += ThrowTest(_T("0<01?1/:1"), ecMISPLACED_COLON);
|
||
|
iNumErr += ThrowTest(_T("0<01?1&:1"), ecMISPLACED_COLON);
|
||
|
iNumErr += ThrowTest(_T("0<01?1<<:1"), ecMISPLACED_COLON);
|
||
|
iNumErr += ThrowTest(_T("0<01?1>>:1"), ecMISPLACED_COLON);
|
||
|
iNumErr += ThrowTest(_T("{ ? 0 : 7m}-{7, -00007m}-{7M}"), ecUNEXPECTED_CONDITIONAL);
|
||
|
iNumErr += ThrowTest(_T("{ { { ? 2 }, 7:2 }*7m }"), ecUNEXPECTED_CONDITIONAL);
|
||
|
|
||
|
// Not too happy about the undefined code, but better than a crash of an assertion at runtime
|
||
|
iNumErr += ThrowTest(_T("{0<0?0,0:0<0}"), ecUNEXPECTED_COMMA);
|
||
|
|
||
|
// Github Issue 63
|
||
|
iNumErr += ThrowTest(_T("0<0-0--eye()"), ecINVALID_NUMBER_OF_PARAMETERS);
|
||
|
|
||
|
Assessment(iNumErr);
|
||
|
return iNumErr;
|
||
|
}
|
||
|
|
||
|
//---------------------------------------------------------------------------
|
||
|
int ParserTester::TestUndefVar()
|
||
|
{
|
||
|
int iNumErr = 0;
|
||
|
*m_stream << _T("testing implicit definition of undefined variables...");
|
||
|
|
||
|
// Test 1: No variables defined, test detection of undefined variables
|
||
|
{
|
||
|
ParserX p;
|
||
|
p.SetExpr(_T("a+b+c+d"));
|
||
|
const mup::var_maptype &expr_var = p.GetExprVar();
|
||
|
const mup::var_maptype &var = p.GetVar();
|
||
|
|
||
|
// The expression contains 4 undefined variables
|
||
|
if (expr_var.size() != 4)
|
||
|
iNumErr++;
|
||
|
|
||
|
// There are no variables defined
|
||
|
if (var.size() != 0)
|
||
|
iNumErr++;
|
||
|
}
|
||
|
|
||
|
// Test 2: Variables were defined explicitely, test detection of variables
|
||
|
{
|
||
|
ParserX p;
|
||
|
|
||
|
// Now define the variables and perform the check again
|
||
|
Value vVarVal[] = { 1.0, 2.0, 3.0, 4.0 };
|
||
|
p.DefineVar(_T("a"), Variable(&vVarVal[0]));
|
||
|
p.DefineVar(_T("b"), Variable(&vVarVal[1]));
|
||
|
p.DefineVar(_T("c"), Variable(&vVarVal[2]));
|
||
|
p.DefineVar(_T("d"), Variable(&vVarVal[3]));
|
||
|
|
||
|
p.SetExpr(_T("a+b+c+d"));
|
||
|
const mup::var_maptype &expr_var = p.GetExprVar();
|
||
|
const mup::var_maptype &var = p.GetVar();
|
||
|
|
||
|
// The expression contains 4 undefined variables
|
||
|
if (expr_var.size() != 4)
|
||
|
iNumErr++;
|
||
|
|
||
|
// There are no variables defined
|
||
|
if (var.size() != 4)
|
||
|
iNumErr++;
|
||
|
}
|
||
|
|
||
|
// Test 3: Variables were defined implicitels, test detection of undefined variables
|
||
|
{
|
||
|
ParserX p;
|
||
|
|
||
|
// Now define the variables and perform the check again
|
||
|
p.EnableAutoCreateVar(true);
|
||
|
|
||
|
p.SetExpr(_T("a+b+c+d"));
|
||
|
const mup::var_maptype &expr_var = p.GetExprVar();
|
||
|
const mup::var_maptype &var = p.GetVar();
|
||
|
|
||
|
// The expression contains 4 undefined variables
|
||
|
if (expr_var.size() != 4)
|
||
|
iNumErr++;
|
||
|
|
||
|
// There are no variables defined
|
||
|
if (var.size() != 4)
|
||
|
iNumErr++;
|
||
|
}
|
||
|
|
||
|
Assessment(iNumErr);
|
||
|
return iNumErr;
|
||
|
}
|
||
|
|
||
|
//---------------------------------------------------------------------------
|
||
|
int ParserTester::TestMatrix()
|
||
|
{
|
||
|
int iNumErr = 0;
|
||
|
*m_stream << _T("testing matrix calculations...");
|
||
|
|
||
|
Value unity(3, 3, 0);
|
||
|
unity.At(0, 0) = 1.0;
|
||
|
unity.At(1, 1) = 1.0;
|
||
|
unity.At(2, 2) = 1.0;
|
||
|
|
||
|
Value va(3, 0);
|
||
|
va.At(0) = 1.0;
|
||
|
va.At(1) = 2.0;
|
||
|
va.At(2) = 3.0;
|
||
|
|
||
|
//Value m2(3, 3, 0);
|
||
|
//m2.At(0, 0) = 1; m2.At(0, 1) = 2; m2.At(0, 2) = 3;
|
||
|
//m2.At(1, 0) = 4; m2.At(1, 1) = 5; m2.At(1, 2) = 6;
|
||
|
//m2.At(2, 0) = 7; m2.At(2, 1) = 8; m2.At(2, 2) = 9;
|
||
|
|
||
|
Value m1_plus_m2(3, 3, 0);
|
||
|
m1_plus_m2.At(0, 0) = 2.0; m1_plus_m2.At(0, 1) = 2.0; m1_plus_m2.At(0, 2) = 3.0;
|
||
|
m1_plus_m2.At(1, 0) = 4.0; m1_plus_m2.At(1, 1) = 6.0; m1_plus_m2.At(1, 2) = 6.0;
|
||
|
m1_plus_m2.At(2, 0) = 7.0; m1_plus_m2.At(2, 1) = 8.0; m1_plus_m2.At(2, 2) = 10.0;
|
||
|
|
||
|
Value m2_minus_m1(3, 3, 0);
|
||
|
m2_minus_m1.At(0, 0) = 0.0; m2_minus_m1.At(0, 1) = 2.0; m2_minus_m1.At(0, 2) = 3.0;
|
||
|
m2_minus_m1.At(1, 0) = 4.0; m2_minus_m1.At(1, 1) = 4.0; m2_minus_m1.At(1, 2) = 6.0;
|
||
|
m2_minus_m1.At(2, 0) = 7.0; m2_minus_m1.At(2, 1) = 8.0; m2_minus_m1.At(2, 2) = 8.0;
|
||
|
|
||
|
Value m2_times_10(3, 3, 0);
|
||
|
m2_times_10.At(0, 0) = 10.0; m2_times_10.At(0, 1) = 20.0; m2_times_10.At(0, 2) = 30.0;
|
||
|
m2_times_10.At(1, 0) = 40.0; m2_times_10.At(1, 1) = 50.0; m2_times_10.At(1, 2) = 60.0;
|
||
|
m2_times_10.At(2, 0) = 70.0; m2_times_10.At(2, 1) = 80.0; m2_times_10.At(2, 2) = 90.0;
|
||
|
|
||
|
Value va_times_vb_transp(3, 3, 0);
|
||
|
va_times_vb_transp.At(0, 0) = 4.0; va_times_vb_transp.At(0, 1) = 3.0; va_times_vb_transp.At(0, 2) = 2.0;
|
||
|
va_times_vb_transp.At(1, 0) = 8.0; va_times_vb_transp.At(1, 1) = 6.0; va_times_vb_transp.At(1, 2) = 4.0;
|
||
|
va_times_vb_transp.At(2, 0) = 12.0; va_times_vb_transp.At(2, 1) = 9.0; va_times_vb_transp.At(2, 2) = 6.0;
|
||
|
|
||
|
Value size_3x6(1, 2, 0);
|
||
|
size_3x6.At(0, 0) = 3.0;
|
||
|
size_3x6.At(0, 1) = 6.0;
|
||
|
|
||
|
Value size_3x3(1, 2, 0);
|
||
|
size_3x3.At(0, 0) = 3.0;
|
||
|
size_3x3.At(0, 1) = 3.0;
|
||
|
|
||
|
Value size_3x1(1, 2, 0);
|
||
|
size_3x1.At(0, 0) = 3.0;
|
||
|
size_3x1.At(0, 1) = 1.0;
|
||
|
|
||
|
Value size_1x3(1, 2, 0);
|
||
|
size_1x3.At(0, 0) = 1.0;
|
||
|
size_1x3.At(0, 1) = 3.0;
|
||
|
|
||
|
// Check matrix dimension mismatch error
|
||
|
iNumErr += ThrowTest(_T("\"hallo\"+m1"), ecEVAL);
|
||
|
iNumErr += ThrowTest(_T("m1+\"hallo\""), ecEVAL);
|
||
|
iNumErr += ThrowTest(_T("va+m1"), ecMATRIX_DIMENSION_MISMATCH);
|
||
|
iNumErr += ThrowTest(_T("m1+va"), ecMATRIX_DIMENSION_MISMATCH);
|
||
|
iNumErr += ThrowTest(_T("va-m1"), ecMATRIX_DIMENSION_MISMATCH);
|
||
|
iNumErr += ThrowTest(_T("m1-va"), ecMATRIX_DIMENSION_MISMATCH);
|
||
|
iNumErr += ThrowTest(_T("va*m1"), ecMATRIX_DIMENSION_MISMATCH);
|
||
|
iNumErr += ThrowTest(_T("va+eye(2)"), ecMATRIX_DIMENSION_MISMATCH);
|
||
|
|
||
|
// Issue 63:
|
||
|
iNumErr += ThrowTest(_T("0-0-eye()"), ecINVALID_NUMBER_OF_PARAMETERS);
|
||
|
|
||
|
iNumErr += ThrowTest(_T("m1[1]"), ecINDEX_DIMENSION);
|
||
|
iNumErr += ThrowTest(_T("m1[1,2,3]"), ecINDEX_DIMENSION);
|
||
|
iNumErr += ThrowTest(_T("va[1,2]"), ecINDEX_OUT_OF_BOUNDS); // va has 1 column, 3 rows -> the coulumn index is referencing the third column
|
||
|
iNumErr += ThrowTest(_T("a+m1"), ecEVAL);
|
||
|
iNumErr += ThrowTest(_T("m1+a"), ecEVAL);
|
||
|
iNumErr += ThrowTest(_T("a-m1"), ecEVAL);
|
||
|
iNumErr += ThrowTest(_T("m1-a"), ecEVAL);
|
||
|
iNumErr += ThrowTest(_T("va[,1]"), ecUNEXPECTED_COMMA);
|
||
|
iNumErr += ThrowTest(_T("va[{1]"), ecMISSING_CURLY_BRACKET);
|
||
|
iNumErr += ThrowTest(_T("{,1}"), ecUNEXPECTED_COMMA);
|
||
|
|
||
|
// sample expressions
|
||
|
iNumErr += EqnTest(_T("m1"), unity, true);
|
||
|
iNumErr += EqnTest(_T("m1*m1"), unity, true);
|
||
|
iNumErr += EqnTest(_T("m1+m2"), m1_plus_m2, true);
|
||
|
iNumErr += EqnTest(_T("m2-m1"), m2_minus_m1, true);
|
||
|
iNumErr += EqnTest(_T("10*m2"), m2_times_10, true);
|
||
|
iNumErr += EqnTest(_T("m2*10"), m2_times_10, true);
|
||
|
iNumErr += EqnTest(_T("5*m2*b"), m2_times_10, true);
|
||
|
iNumErr += EqnTest(_T("b*m2*5"), m2_times_10, true);
|
||
|
iNumErr += EqnTest(_T("m1*va"), va, true);
|
||
|
|
||
|
// ones
|
||
|
Value ones_3(3, 1.0);
|
||
|
Value ones_3x3(3, 3, 1.0);
|
||
|
iNumErr += ThrowTest(_T("ones(1,2,3)"), ecINVALID_NUMBER_OF_PARAMETERS);
|
||
|
iNumErr += ThrowTest(_T("ones()"), ecINVALID_NUMBER_OF_PARAMETERS);
|
||
|
iNumErr += EqnTest(_T("ones(1,1)"), 1.0, true);
|
||
|
iNumErr += EqnTest(_T("ones(1)"), 1.0, true);
|
||
|
iNumErr += EqnTest(_T("ones(3,3)"), ones_3x3, true);
|
||
|
iNumErr += EqnTest(_T("ones(3,1)"), ones_3, true);
|
||
|
iNumErr += EqnTest(_T("ones(3)"), ones_3, true);
|
||
|
|
||
|
iNumErr += EqnTest(_T("size(ones(3,3))"), size_3x3, true); // check return value dimension
|
||
|
iNumErr += EqnTest(_T("size(ones(1,3))"), size_1x3, true); // check return value dimension
|
||
|
iNumErr += EqnTest(_T("size(ones(3,1))"), size_3x1, true); // check return value dimension
|
||
|
iNumErr += EqnTest(_T("size(ones(3))"), size_3x3, true); // check return value dimension
|
||
|
|
||
|
// zeros
|
||
|
iNumErr += ThrowTest(_T("zeros()"), ecINVALID_NUMBER_OF_PARAMETERS);
|
||
|
iNumErr += EqnTest(_T("size(zeros(3,3))"), size_3x3, true); // check return value dimension
|
||
|
iNumErr += EqnTest(_T("size(zeros(1,3))"), size_1x3, true); // check return value dimension
|
||
|
iNumErr += EqnTest(_T("size(zeros(3,1))"), size_3x1, true); // check return value dimension
|
||
|
iNumErr += EqnTest(_T("size(zeros(3))"), size_3x3, true); // check return value dimension
|
||
|
|
||
|
// eye
|
||
|
iNumErr += ThrowTest(_T("eye()"), ecINVALID_NUMBER_OF_PARAMETERS);
|
||
|
iNumErr += EqnTest(_T("size(eye(3,3))"), size_3x3, true); // check return value dimension
|
||
|
iNumErr += EqnTest(_T("size(eye(1,3))"), size_1x3, true); // check return value dimension
|
||
|
iNumErr += EqnTest(_T("size(eye(3,1))"), size_3x1, true); // check return value dimension
|
||
|
iNumErr += EqnTest(_T("size(eye(3))"), size_3x3, true); // check return value dimension
|
||
|
|
||
|
iNumErr += EqnTest(_T("size(eye(3,6))"), size_3x6, true); // check return value dimension
|
||
|
|
||
|
// transposition
|
||
|
iNumErr += EqnTest(_T("va'*vb"), 16.0, true);
|
||
|
iNumErr += EqnTest(_T("2*va'*vb"), 32.0, true);
|
||
|
iNumErr += EqnTest(_T("va*vb'"), va_times_vb_transp, true);
|
||
|
|
||
|
// index operator
|
||
|
// erster index: Zeilenindex, zweiter index: Spaltenindex
|
||
|
iNumErr += EqnTest(_T("va[0]"), 1.0, true);
|
||
|
iNumErr += EqnTest(_T("va[1]"), 2.0, true);
|
||
|
iNumErr += EqnTest(_T("va[2]"), 3.0, true);
|
||
|
// Use two dimensional index operator on a vector
|
||
|
iNumErr += EqnTest(_T("va[0,0]"), 1.0, true);
|
||
|
iNumErr += EqnTest(_T("va[1,0]"), 2.0, true);
|
||
|
iNumErr += EqnTest(_T("va[2,0]"), 3.0, true);
|
||
|
|
||
|
// Now test the same with a transposed vector:
|
||
|
iNumErr += EqnTest(_T("va'[0]"), 1.0, true);
|
||
|
iNumErr += EqnTest(_T("va'[1]"), 2.0, true);
|
||
|
iNumErr += EqnTest(_T("va'[2]"), 3.0, true);
|
||
|
// Use two dimensional index operator on a vector
|
||
|
iNumErr += EqnTest(_T("va'[0,0]"), 1.0, true);
|
||
|
iNumErr += EqnTest(_T("va'[0,1]"), 2.0, true);
|
||
|
iNumErr += EqnTest(_T("va'[0,2]"), 3.0, true);
|
||
|
iNumErr += EqnTest(_T("(va')[0,2]"), 3.0, true); // <- Index operator after closing bracket is ok
|
||
|
|
||
|
// vector creation
|
||
|
iNumErr += EqnTest(_T("{1,2,3}'"), va, true);
|
||
|
iNumErr += EqnTest(_T("{a,2,3}'"), va, true); // that was an actual bug: variable a was overwritten
|
||
|
|
||
|
// assignment to element:
|
||
|
iNumErr += ThrowTest(_T("va'[0]=123"), ecASSIGNEMENT_TO_VALUE);
|
||
|
|
||
|
Assessment(iNumErr);
|
||
|
return iNumErr;
|
||
|
}
|
||
|
|
||
|
//---------------------------------------------------------------------------
|
||
|
int ParserTester::TestComplex()
|
||
|
{
|
||
|
int iNumErr = 0;
|
||
|
*m_stream << _T("testing complex calculations...");
|
||
|
|
||
|
// complex numbers
|
||
|
// ca=1+i, cb=2+3i, cc=3+4i
|
||
|
iNumErr += EqnTest(_T("ca==1+i"), true, true);
|
||
|
iNumErr += EqnTest(_T("ca==ca"), true, true);
|
||
|
iNumErr += EqnTest(_T("ca!=1+i"), false, true);
|
||
|
iNumErr += EqnTest(_T("ca!=ca"), false, true);
|
||
|
iNumErr += EqnTest(_T("ca!=cb"), true, true);
|
||
|
iNumErr += EqnTest(_T("ca!=va"), true, true);
|
||
|
iNumErr += EqnTest(_T("ca==va"), false, true);
|
||
|
|
||
|
// When comparing complex number Matlab/Octave compare only the real part
|
||
|
// I'll do the same...
|
||
|
iNumErr += EqnTest(_T("ca<10+i"), true, true);
|
||
|
iNumErr += EqnTest(_T("ca>10+i"), false, true);
|
||
|
iNumErr += EqnTest(_T("ca<=10+i"), true, true);
|
||
|
iNumErr += EqnTest(_T("ca>=10+i"), false, true);
|
||
|
iNumErr += EqnTest(_T("ca<=1"), true, true);
|
||
|
iNumErr += EqnTest(_T("ca>=1"), true, true);
|
||
|
|
||
|
// complex numbers
|
||
|
iNumErr += EqnTest(_T("i*i"), -1.0, true, 0);
|
||
|
iNumErr += EqnTest(_T("1i"), cmplx_type(0, 1), true, 0);
|
||
|
iNumErr += EqnTest(_T("norm(3+4i)"), 25.0, true, 0);
|
||
|
iNumErr += EqnTest(_T("norm(4i+3)"), 25.0, true, 0);
|
||
|
iNumErr += EqnTest(_T("norm(3i+4)"), 25.0, true, 0);
|
||
|
iNumErr += EqnTest(_T("real(4.1i+3.1)"), (float_type)3.1, true, 0);
|
||
|
iNumErr += EqnTest(_T("imag(3.1i+4.1)"), (float_type)3.1, true, 0);
|
||
|
iNumErr += EqnTest(_T("real(3.1)"), (float_type)3.1, true, 0);
|
||
|
iNumErr += EqnTest(_T("imag(2.1i)"), (float_type)2.1, true, 0);
|
||
|
iNumErr += EqnTest(_T("-(4i+5)"), cmplx_type(-5, -4), true, 0);
|
||
|
iNumErr += EqnTest(_T("sqrt(-1)"), cmplx_type(0, 1), true, 0);
|
||
|
iNumErr += EqnTest(_T("(-1)^0.5"), cmplx_type(0, 1), true, 0);
|
||
|
iNumErr += EqnTest(_T("(-3)^(4/3)"), std::pow(cmplx_type(-3, 0),
|
||
|
cmplx_type(4.0 / 3, 0)), true, 0);
|
||
|
|
||
|
// Issue 41: Complex pow of small numbers zeros out the imaginary part
|
||
|
// https://code.google.com/p/muparserx/issues/detail?id=41
|
||
|
iNumErr += EqnTest(_T("(1e-15 + 1e-15*i) ^ 2"), std::pow(cmplx_type(1e-15, 1e-15), 2), true, 0);
|
||
|
|
||
|
iNumErr += EqnTest(_T("sqrt(i*i)"), cmplx_type(0, 1), true, 0);
|
||
|
iNumErr += EqnTest(_T("sqrt(f)"), cmplx_type(0, 1), true, 1);
|
||
|
iNumErr += EqnTest(_T("sqrt(2-3)"), cmplx_type(0, 1), true, 0);
|
||
|
iNumErr += EqnTest(_T("sqrt(a-b)"), cmplx_type(0, 1), true, 2);
|
||
|
iNumErr += EqnTest(_T("sqrt((2-3))"), cmplx_type(0, 1), true, 0);
|
||
|
iNumErr += EqnTest(_T("sqrt((a-b))"), cmplx_type(0, 1), true, 2);
|
||
|
iNumErr += EqnTest(_T("sqrt(-(1))"), cmplx_type(0, 1), true, 0);
|
||
|
iNumErr += EqnTest(_T("sqrt((-1))"), cmplx_type(0, 1), true, 0);
|
||
|
iNumErr += EqnTest(_T("sqrt(-(-1))"), cmplx_type(1, 0), true, 0);
|
||
|
iNumErr += EqnTest(_T("sqrt(1)"), cmplx_type(1, 0), true, 0);
|
||
|
iNumErr += EqnTest(_T("a=1+2i"), cmplx_type(1, 2), true, 1);
|
||
|
iNumErr += EqnTest(_T("-(1+2i)"), cmplx_type(-1, -2), true, 0);
|
||
|
iNumErr += EqnTest(_T("-(-1-2i)"), cmplx_type(1, 2), true, 0);
|
||
|
iNumErr += EqnTest(_T("a*i"), cmplx_type(0, 1), true, 1);
|
||
|
iNumErr += EqnTest(_T("-(a+b*i)"), cmplx_type(-1, -2), true, 2);
|
||
|
iNumErr += EqnTest(_T("-(-a-b*i)"), cmplx_type(1, 2), true, 2);
|
||
|
iNumErr += EqnTest(_T("(2+4i)*(8-6i)"), cmplx_type(40, 20), true, 0);
|
||
|
|
||
|
// Issue 17: Wrong result on complex power.
|
||
|
iNumErr += EqnTest(_T("(-0.27 + 0.66*i)^2"), cmplx_type(-0.3627, -0.3564), true, 0);
|
||
|
iNumErr += EqnTest(_T("(-1+5i)^2"), cmplx_type(-24, -10), true, 0);
|
||
|
|
||
|
iNumErr += EqnTest(_T("c=(a=1+2i)"), cmplx_type(1, 2), true, 2);
|
||
|
|
||
|
Assessment(iNumErr);
|
||
|
return iNumErr;
|
||
|
}
|
||
|
|
||
|
//---------------------------------------------------------------------------
|
||
|
int ParserTester::TestParserValue()
|
||
|
{
|
||
|
int iNumErr = 0;
|
||
|
*m_stream << _T("testing parser value types...");
|
||
|
|
||
|
// Define values and variables for each type
|
||
|
Value bVal = true;
|
||
|
Value fVal = (float_type)3.14;
|
||
|
Value sVal = string_type(_T("hello world"));
|
||
|
Value sVal1 = _T("hello world"); // Test assignment from const char* to string
|
||
|
Value cVal = cmplx_type(1, 1);
|
||
|
Value aVal(2, 0);
|
||
|
aVal.At(0) = (float_type)2.0;
|
||
|
aVal.At(1) = (float_type)3.0;
|
||
|
|
||
|
// Create a 3x3 matrix
|
||
|
Value matrix(3, 0);
|
||
|
matrix.At(0) = Value(3, 0);
|
||
|
matrix.At(1) = Value(3, 0);
|
||
|
matrix.At(2) = Value(3, 0);
|
||
|
|
||
|
Variable bVar(&bVal),
|
||
|
fVar(&fVal),
|
||
|
sVar(&sVal),
|
||
|
sVar1(&sVal1),
|
||
|
cVar(&cVal),
|
||
|
aVar(&aVal);
|
||
|
|
||
|
// Check the value types
|
||
|
try
|
||
|
{
|
||
|
// Test if matrix values do work
|
||
|
if (!matrix.IsMatrix() || matrix.GetRows() != 3)
|
||
|
iNumErr++;
|
||
|
|
||
|
std::size_t sz = matrix.GetRows();
|
||
|
for (std::size_t i = 0; i < sz; ++i)
|
||
|
{
|
||
|
std::size_t dim_row = matrix.At(static_cast<int>(i)).GetRows();
|
||
|
if (dim_row != 3)
|
||
|
{
|
||
|
iNumErr++;
|
||
|
console() << _T("\n Array dimension mismatch in matrix row ") << i
|
||
|
<< _T(" (expected=3; dim=") << dim_row << _T(")");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// test type checking of values
|
||
|
if (!fVal.IsScalar() || fVal.IsMatrix() || fVal.GetType() != 'f') iNumErr++;
|
||
|
if (!cVal.IsScalar() || cVal.IsMatrix() || cVal.GetType() != 'c') iNumErr++;
|
||
|
if (aVal.IsScalar() || !aVal.IsMatrix() || aVal.GetType() != 'm') iNumErr++;
|
||
|
if (sVal.IsScalar() || sVal.IsMatrix() || sVal.GetType() != 's') iNumErr++;
|
||
|
if (sVal1.IsScalar() || sVal1.IsMatrix() || sVal1.GetType() != 's') iNumErr++;
|
||
|
if (bVal.IsScalar() || bVal.IsMatrix() || bVal.GetType() != 'b') iNumErr++;
|
||
|
|
||
|
// test type checking of variables
|
||
|
if (!fVar.IsScalar() || fVar.IsMatrix() || fVar.GetType() != 'f') iNumErr++;
|
||
|
if (!cVar.IsScalar() || cVar.IsMatrix() || cVar.GetType() != 'c') iNumErr++;
|
||
|
if (aVar.IsScalar() || !aVar.IsMatrix() || aVar.GetType() != 'm') iNumErr++;
|
||
|
if (sVar.IsScalar() || sVar.IsMatrix() || sVar.GetType() != 's') iNumErr++;
|
||
|
if (sVar1.IsScalar() || sVar1.IsMatrix() || sVar1.GetType() != 's') iNumErr++;
|
||
|
if (bVar.IsScalar() || bVar.IsMatrix() || bVar.GetType() != 'b') iNumErr++;
|
||
|
|
||
|
// Issue 33: https://code.google.com/p/muparserx/issues/detail?id=33
|
||
|
// Remark: Type information was not properly set when invoking +=, -= operators
|
||
|
{
|
||
|
Value x = 1.0;
|
||
|
Value y = cmplx_type(0, 1);
|
||
|
x += y;
|
||
|
if (x.GetImag() != 1 || x.GetFloat() != 1 || x.GetType() != 'c')
|
||
|
{
|
||
|
*m_stream << _T("\nValue::operator+=(...) failed.");
|
||
|
iNumErr++;
|
||
|
}
|
||
|
|
||
|
x = 1.0;
|
||
|
y = cmplx_type(0, 1);
|
||
|
x -= y;
|
||
|
if (x.GetImag() != -1 || x.GetFloat() != 1 || x.GetType() != 'c')
|
||
|
{
|
||
|
*m_stream << _T("\nValue::operator-=(...) failed.");
|
||
|
iNumErr++;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
catch (...)
|
||
|
{
|
||
|
iNumErr++;
|
||
|
}
|
||
|
|
||
|
bool bError;
|
||
|
|
||
|
#define VALUE_THROWCHECK(VAR, FAIL, MEMBER) \
|
||
|
bError = (FAIL); \
|
||
|
try \
|
||
|
{ \
|
||
|
VAR.MEMBER(); \
|
||
|
} \
|
||
|
catch (...) \
|
||
|
{ \
|
||
|
bError ^= true; \
|
||
|
} \
|
||
|
iNumErr += (bError) ? 1 : 0; \
|
||
|
c_iCount++;
|
||
|
|
||
|
// Check if the getter functions really throw an exception
|
||
|
// when used with an incorrect value type
|
||
|
// Case 1: test float values
|
||
|
VALUE_THROWCHECK(fVal, false, GetFloat)
|
||
|
VALUE_THROWCHECK(fVal, false, GetImag)
|
||
|
VALUE_THROWCHECK(fVal, true, GetBool)
|
||
|
VALUE_THROWCHECK(fVal, true, GetString)
|
||
|
VALUE_THROWCHECK(fVal, true, GetArray)
|
||
|
// for variables
|
||
|
VALUE_THROWCHECK(fVar, false, GetFloat)
|
||
|
VALUE_THROWCHECK(fVar, false, GetImag)
|
||
|
VALUE_THROWCHECK(fVar, true, GetBool)
|
||
|
VALUE_THROWCHECK(fVar, true, GetString)
|
||
|
VALUE_THROWCHECK(fVar, true, GetArray)
|
||
|
|
||
|
// Case 2: test bool values
|
||
|
VALUE_THROWCHECK(bVal, false, GetFloat)
|
||
|
VALUE_THROWCHECK(bVal, true, GetImag)
|
||
|
VALUE_THROWCHECK(bVal, false, GetBool)
|
||
|
VALUE_THROWCHECK(bVal, true, GetString)
|
||
|
VALUE_THROWCHECK(bVal, true, GetArray)
|
||
|
// for variables
|
||
|
VALUE_THROWCHECK(bVar, false, GetFloat)
|
||
|
VALUE_THROWCHECK(bVar, true, GetImag)
|
||
|
VALUE_THROWCHECK(bVar, false, GetBool)
|
||
|
VALUE_THROWCHECK(bVar, true, GetString)
|
||
|
VALUE_THROWCHECK(bVar, true, GetArray)
|
||
|
|
||
|
// Case 3: test string values
|
||
|
// VALUE_THROWCHECK(sVal, true, GetFloat)
|
||
|
VALUE_THROWCHECK(sVal, true, GetImag)
|
||
|
VALUE_THROWCHECK(sVal, true, GetBool)
|
||
|
VALUE_THROWCHECK(sVal, false, GetString)
|
||
|
VALUE_THROWCHECK(sVal, true, GetArray)
|
||
|
// for variables
|
||
|
// VALUE_THROWCHECK(sVar, true, GetFloat)
|
||
|
VALUE_THROWCHECK(sVar, true, GetImag)
|
||
|
VALUE_THROWCHECK(sVar, true, GetBool)
|
||
|
VALUE_THROWCHECK(sVar, false, GetString)
|
||
|
VALUE_THROWCHECK(sVar, true, GetArray)
|
||
|
|
||
|
// Case 4: test array values
|
||
|
// VALUE_THROWCHECK(aVal, true, GetFloat)
|
||
|
VALUE_THROWCHECK(aVal, true, GetImag)
|
||
|
VALUE_THROWCHECK(aVal, true, GetBool)
|
||
|
VALUE_THROWCHECK(aVal, true, GetString)
|
||
|
VALUE_THROWCHECK(aVal, false, GetArray)
|
||
|
// for variables
|
||
|
// VALUE_THROWCHECK(aVar, true, GetFloat)
|
||
|
VALUE_THROWCHECK(aVar, true, GetImag)
|
||
|
VALUE_THROWCHECK(aVar, true, GetBool)
|
||
|
VALUE_THROWCHECK(aVar, true, GetString)
|
||
|
VALUE_THROWCHECK(aVar, false, GetArray)
|
||
|
|
||
|
// Case 5: test complex values
|
||
|
// VALUE_THROWCHECK(cVal, false, GetFloat)
|
||
|
VALUE_THROWCHECK(cVal, false, GetImag)
|
||
|
VALUE_THROWCHECK(cVal, true, GetBool)
|
||
|
VALUE_THROWCHECK(cVal, true, GetString)
|
||
|
VALUE_THROWCHECK(cVal, true, GetArray)
|
||
|
// for variables
|
||
|
// VALUE_THROWCHECK(cVar, false, GetFloat)
|
||
|
VALUE_THROWCHECK(cVar, false, GetImag)
|
||
|
VALUE_THROWCHECK(cVar, true, GetBool)
|
||
|
VALUE_THROWCHECK(cVar, true, GetString)
|
||
|
VALUE_THROWCHECK(cVar, true, GetArray)
|
||
|
#undef VALUE_THROWCHECK
|
||
|
|
||
|
Assessment(iNumErr);
|
||
|
return iNumErr;
|
||
|
}
|
||
|
|
||
|
//---------------------------------------------------------------------------
|
||
|
int ParserTester::TestErrorCodes()
|
||
|
{
|
||
|
int iNumErr = 0;
|
||
|
*m_stream << _T("testing error codes...");
|
||
|
|
||
|
iNumErr += ThrowTest(_T("a,b"), ecUNEXPECTED_COMMA);
|
||
|
iNumErr += ThrowTest(_T("(a,b)"), ecUNEXPECTED_COMMA);
|
||
|
iNumErr += ThrowTest(_T("((a,b))"), ecUNEXPECTED_COMMA);
|
||
|
iNumErr += ThrowTest(_T("2*1,2"), ecUNEXPECTED_COMMA);
|
||
|
iNumErr += ThrowTest(_T("sin(1,2)"), ecTOO_MANY_PARAMS);
|
||
|
|
||
|
// Invalid expression
|
||
|
// Issue 20: http://code.google.com/p/muparserx/issues/detail?id=20
|
||
|
iNumErr += ThrowTest(_T(" "), ecUNEXPECTED_EOF);
|
||
|
|
||
|
iNumErr += ThrowTest(_T("sin(nonexistent_var)"), ecUNASSIGNABLE_TOKEN, 4, _T("nonexistent_var"));
|
||
|
|
||
|
// Invalid function argument types
|
||
|
iNumErr += ThrowTest(_T("sin(\"test\")"), ecEVAL, 0);
|
||
|
iNumErr += ThrowTest(_T("max(1, \"test\")"), ecEVAL, 0);
|
||
|
iNumErr += ThrowTest(_T("max(1,sin(8), \"t\")"), ecEVAL, 0);
|
||
|
iNumErr += ThrowTest(_T("str2dbl(sin(3.14))"), ecEVAL, 0);
|
||
|
|
||
|
// Invalid unary operator argument types
|
||
|
iNumErr += ThrowTest(_T("\"test\"n"), ecEVAL, 6); // (nano can only be applied to floats)
|
||
|
iNumErr += ThrowTest(_T("(1+3i)/(8*9i)+\"hallo\""), ecEVAL);
|
||
|
iNumErr += ThrowTest(_T("(1+3i)/(8*9i)-\"hallo\""), ecEVAL);
|
||
|
iNumErr += ThrowTest(_T("(1+3i)/(8*9i)*\"hallo\""), ecEVAL);
|
||
|
iNumErr += ThrowTest(_T("(1+3i)/(8*9i)/\"hallo\""), ecEVAL);
|
||
|
iNumErr += ThrowTest(_T("10+va"), ecEVAL, 2);
|
||
|
|
||
|
// Type conflicts in binary operators
|
||
|
iNumErr += ThrowTest(_T("\"test\" // 8"), ecEVAL, 7);
|
||
|
iNumErr += ThrowTest(_T("8//\"test\""), ecEVAL, 1);
|
||
|
iNumErr += ThrowTest(_T("5//8"), ecEVAL, 1);
|
||
|
iNumErr += ThrowTest(_T("\"t\"//sin(8)"), ecEVAL, 3);
|
||
|
iNumErr += ThrowTest(_T("sin(8)//\"t\""), ecEVAL, 6);
|
||
|
|
||
|
// Unexpected end of expression
|
||
|
iNumErr += ThrowTest(_T("3+"), ecUNEXPECTED_EOF);
|
||
|
iNumErr += ThrowTest(_T("8*"), ecUNEXPECTED_EOF);
|
||
|
iNumErr += ThrowTest(_T("3+("), ecUNEXPECTED_EOF);
|
||
|
iNumErr += ThrowTest(_T("3+sin"), ecUNEXPECTED_EOF);
|
||
|
iNumErr += ThrowTest(_T("(2+"), ecUNEXPECTED_EOF);
|
||
|
|
||
|
iNumErr += ThrowTest(_T("3+)"), ecUNEXPECTED_PARENS);
|
||
|
iNumErr += ThrowTest(_T("3)"), ecUNEXPECTED_PARENS);
|
||
|
iNumErr += ThrowTest(_T("(3))"), ecUNEXPECTED_PARENS);
|
||
|
iNumErr += ThrowTest(_T("()"), ecUNEXPECTED_PARENS);
|
||
|
iNumErr += ThrowTest(_T("(2+)"), ecUNEXPECTED_PARENS);
|
||
|
iNumErr += ThrowTest(_T("sin(cos)"), ecUNEXPECTED_PARENS);
|
||
|
iNumErr += ThrowTest(_T("sin(())"), ecUNEXPECTED_PARENS);
|
||
|
iNumErr += ThrowTest(_T("sin()"), ecTOO_FEW_PARAMS);
|
||
|
iNumErr += ThrowTest(_T("sin)"), ecUNEXPECTED_PARENS);
|
||
|
iNumErr += ThrowTest(_T("pi)"), ecUNEXPECTED_PARENS);
|
||
|
iNumErr += ThrowTest(_T("a)"), ecUNEXPECTED_PARENS);
|
||
|
iNumErr += ThrowTest(_T("2(-m)"), ecUNEXPECTED_PARENS);
|
||
|
iNumErr += ThrowTest(_T("2(m)"), ecUNEXPECTED_PARENS);
|
||
|
|
||
|
iNumErr += ThrowTest(_T("(1+2"), ecMISSING_PARENS);
|
||
|
iNumErr += ThrowTest(_T("((3)"), ecMISSING_PARENS);
|
||
|
|
||
|
// The behaviour in the next function depends on whether
|
||
|
// implicit variable creation is allowed or not. (momentarily its active)
|
||
|
iNumErr += ThrowTest(_T("5z)"), ecUNASSIGNABLE_TOKEN);
|
||
|
iNumErr += ThrowTest(_T("sin(3)xyz"), ecUNASSIGNABLE_TOKEN);
|
||
|
iNumErr += ThrowTest(_T("5t6"), ecUNASSIGNABLE_TOKEN);
|
||
|
iNumErr += ThrowTest(_T("5 t 6"), ecUNASSIGNABLE_TOKEN);
|
||
|
iNumErr += ThrowTest(_T("ksdfj"), ecUNASSIGNABLE_TOKEN);
|
||
|
iNumErr += ThrowTest(_T("-m"), ecUNASSIGNABLE_TOKEN);
|
||
|
iNumErr += ThrowTest(_T("m4"), ecUNASSIGNABLE_TOKEN);
|
||
|
iNumErr += ThrowTest(_T("sin(m)"), ecUNASSIGNABLE_TOKEN);
|
||
|
iNumErr += ThrowTest(_T("m m"), ecUNASSIGNABLE_TOKEN);
|
||
|
iNumErr += ThrowTest(_T("m(8)"), ecUNASSIGNABLE_TOKEN);
|
||
|
iNumErr += ThrowTest(_T("4 + m"), ecUNASSIGNABLE_TOKEN);
|
||
|
|
||
|
// unexpected operator
|
||
|
iNumErr += ThrowTest(_T("5+*3)"), ecUNEXPECTED_OPERATOR);
|
||
|
|
||
|
// unexpected comma (used without a function)
|
||
|
iNumErr += ThrowTest(_T(",3"), ecUNEXPECTED_COMMA);
|
||
|
iNumErr += ThrowTest(_T("sin(,sin(8))"), ecUNEXPECTED_COMMA);
|
||
|
|
||
|
// unexpected variable
|
||
|
iNumErr += ThrowTest(_T("a _xxx_ b"), ecUNASSIGNABLE_TOKEN, 2); // if a variable factory is installed ecUNEXPECTED_VAR
|
||
|
|
||
|
iNumErr += ThrowTest(_T("sin(3)cos(3)"), ecUNEXPECTED_FUN);
|
||
|
iNumErr += ThrowTest(_T("sin(3)3"), ecUNEXPECTED_VAL);
|
||
|
iNumErr += ThrowTest(_T("sin(3)+"), ecUNEXPECTED_EOF);
|
||
|
|
||
|
// value recognition
|
||
|
iNumErr += ThrowTest(_T("0x"), ecUNASSIGNABLE_TOKEN); // incomplete hex value
|
||
|
iNumErr += ThrowTest(_T("1+0x"), ecUNASSIGNABLE_TOKEN); // incomplete hex value
|
||
|
iNumErr += ThrowTest(_T("a+0x"), ecUNASSIGNABLE_TOKEN); // incomplete hex value
|
||
|
|
||
|
// index operator
|
||
|
iNumErr += ThrowTest(_T("3n[1]"), ecINDEX_OUT_OF_BOUNDS); // Indexing a scalar is ok, but this index is out of bounds (0 would be ok)
|
||
|
iNumErr += ThrowTest(_T("min(3,]"), ecUNEXPECTED_SQR_BRACKET);
|
||
|
iNumErr += ThrowTest(_T("sin(]"), ecUNEXPECTED_SQR_BRACKET);
|
||
|
iNumErr += ThrowTest(_T("va[]"), ecUNEXPECTED_SQR_BRACKET);
|
||
|
iNumErr += ThrowTest(_T("3+]"), ecUNEXPECTED_SQR_BRACKET);
|
||
|
iNumErr += ThrowTest(_T("sin[a)"), ecUNEXPECTED_SQR_BRACKET);
|
||
|
iNumErr += ThrowTest(_T("1+[8]"), ecUNEXPECTED_SQR_BRACKET);
|
||
|
iNumErr += ThrowTest(_T("1[8]"), ecUNEXPECTED_SQR_BRACKET);
|
||
|
iNumErr += ThrowTest(_T("[1]"), ecUNEXPECTED_SQR_BRACKET);
|
||
|
iNumErr += ThrowTest(_T("]1"), ecUNEXPECTED_SQR_BRACKET);
|
||
|
iNumErr += ThrowTest(_T("va[[3]]"), ecUNEXPECTED_SQR_BRACKET);
|
||
|
|
||
|
Assessment(iNumErr);
|
||
|
return iNumErr;
|
||
|
}
|
||
|
|
||
|
//---------------------------------------------------------------------------
|
||
|
int ParserTester::TestStringFun()
|
||
|
{
|
||
|
int iNumErr = 0;
|
||
|
*m_stream << _T("testing string functions...");
|
||
|
|
||
|
// escape sequences
|
||
|
iNumErr += EqnTest(_T("\"\\\"quoted_string\\\"\""), _T("\"quoted_string\""), true); // "\"quoted_string\"" -> "quoted_string"
|
||
|
iNumErr += EqnTest(_T("\"\\\"\\\"\""), _T("\"\""), true); // "\"\"" -> ""
|
||
|
iNumErr += EqnTest(_T("\"\\\\\""), _T("\\"), true); // "\\" -> \ (single backslash)
|
||
|
|
||
|
// String functions
|
||
|
iNumErr += EqnTest(_T("strlen(\"12345\")"), 5.0, true);
|
||
|
iNumErr += EqnTest(_T("strlen(toupper(\"abcde\"))"), 5.0, true);
|
||
|
iNumErr += EqnTest(_T("sin(0)+(float)strlen(\"12345\")"), (float_type)5.0, true);
|
||
|
iNumErr += EqnTest(_T("10*(float)strlen(toupper(\"12345\"))"), (float_type)50.0, true);
|
||
|
iNumErr += EqnTest(_T("\"hello \"//\"world\""), string_type(_T("hello world")), true);
|
||
|
iNumErr += EqnTest(_T("toupper(\"hello \")//\"world\""), string_type(_T("HELLO world")), true);
|
||
|
iNumErr += EqnTest(_T("\"hello \"//toupper(\"world\")//\" !!!\""), string_type(_T("hello WORLD !!!")), true);
|
||
|
|
||
|
Assessment(iNumErr);
|
||
|
|
||
|
return iNumErr;
|
||
|
}
|
||
|
|
||
|
//---------------------------------------------------------------------------
|
||
|
int ParserTester::TestPostfix()
|
||
|
{
|
||
|
int iNumErr = 0;
|
||
|
*m_stream << _T("testing postfix operators...");
|
||
|
|
||
|
// application
|
||
|
iNumErr += EqnTest(_T("1n"), (float_type)1e-9, true);
|
||
|
iNumErr += EqnTest(_T("8n"), (float_type)8e-9, true);
|
||
|
iNumErr += EqnTest(_T("8n"), (float_type)123.0, false);
|
||
|
iNumErr += EqnTest(_T("3m+5"), (float_type)5.003, true);
|
||
|
iNumErr += EqnTest(_T("1000m"), (float_type)1.0, true);
|
||
|
iNumErr += EqnTest(_T("1000 m"), (float_type)1.0, true);
|
||
|
iNumErr += EqnTest(_T("(a)m"), (float_type)1e-3, true);
|
||
|
iNumErr += EqnTest(_T("-(a)m"), (float_type)-1e-3, true);
|
||
|
iNumErr += EqnTest(_T("-2m"), (float_type)-2e-3, true);
|
||
|
iNumErr += EqnTest(_T("a++b"), 3.0, true);
|
||
|
iNumErr += EqnTest(_T("a ++ b"), 3.0, true);
|
||
|
iNumErr += EqnTest(_T("1++2"), 3.0, true);
|
||
|
iNumErr += EqnTest(_T("1 ++ 2"), 3.0, true);
|
||
|
iNumErr += EqnTest(_T("2+(a*1000)m"), (float_type)3.0, true);
|
||
|
// some incorrect results
|
||
|
iNumErr += EqnTest(_T("1000m"), (float_type)0.1, false);
|
||
|
iNumErr += EqnTest(_T("(a)m"), (float_type)2.0, false);
|
||
|
// factorial operator
|
||
|
iNumErr += EqnTest(_T("5!"), 120.0, true);
|
||
|
iNumErr += EqnTest(_T("-5!"), -120.0, true);
|
||
|
iNumErr += ThrowTest(_T("(-5)!"), ecDOMAIN_ERROR);
|
||
|
|
||
|
// Special tests for systems not supporting IEEE 754
|
||
|
if (!std::numeric_limits<float_type>::is_iec559)
|
||
|
{
|
||
|
iNumErr += ThrowTest(_T("123456!"), ecOVERFLOW);
|
||
|
}
|
||
|
|
||
|
Assessment(iNumErr);
|
||
|
return iNumErr;
|
||
|
}
|
||
|
|
||
|
//---------------------------------------------------------------------------
|
||
|
int ParserTester::TestInfix()
|
||
|
{
|
||
|
int iNumErr = 0;
|
||
|
*m_stream << _T("testing infix operators...");
|
||
|
|
||
|
float_type a = 1;
|
||
|
float_type b = 2;
|
||
|
|
||
|
iNumErr += EqnTest(_T("-1"), (float_type)-1.0, true);
|
||
|
iNumErr += EqnTest(_T("-(-1)"), (float_type)1.0, true);
|
||
|
iNumErr += EqnTest(_T("-(-1)*2"), (float_type)2.0, true);
|
||
|
iNumErr += EqnTest(_T("-(-2)*sqrt(4)"), (float_type)4.0, true);
|
||
|
iNumErr += EqnTest(_T("-a"), -a, true);
|
||
|
iNumErr += EqnTest(_T("-(a)"), -(a), true);
|
||
|
iNumErr += EqnTest(_T("-(-a)"), -(-a), true);
|
||
|
iNumErr += EqnTest(_T("-(-a)*2"), -(-a) * 2, true);
|
||
|
iNumErr += EqnTest(_T("-(8)"), (float_type)-8.0, true);
|
||
|
iNumErr += EqnTest(_T("-8"), (float_type)-8.0, true);
|
||
|
iNumErr += EqnTest(_T("-(2+1)"), (float_type)-3.0, true);
|
||
|
//iNumErr += EqnTest("-(f1of1(1+2*3)+1*2)", -9.0, true);
|
||
|
//iNumErr += EqnTest("-(-f1of1(1+2*3)+1*2)", 5.0, true);
|
||
|
iNumErr += EqnTest(_T("-sin(8)"), (float_type)-0.989358, true);
|
||
|
iNumErr += EqnTest(_T("-sin(8)"), (float_type)0.989358, false);
|
||
|
iNumErr += EqnTest(_T("3-(-a)"), (float_type)4.0, true);
|
||
|
iNumErr += EqnTest(_T("3--a"), (float_type)4.0, true);
|
||
|
iNumErr += EqnTest(_T("2++4"), (float_type)6.0, true);
|
||
|
iNumErr += EqnTest(_T("--1"), (float_type)1.0, true);
|
||
|
|
||
|
// sign precedence
|
||
|
// Issue 14: https://code.google.com/p/muparserx/issues/detail?id=14
|
||
|
iNumErr += EqnTest(_T("-3^2"), -9.0, true);
|
||
|
iNumErr += EqnTest(_T("-b^2^3-b^8"), -std::pow(b, std::pow(2.0, 3.0)) - std::pow(b, 8), true);
|
||
|
|
||
|
Assessment(iNumErr);
|
||
|
return iNumErr;
|
||
|
}
|
||
|
|
||
|
//---------------------------------------------------------------------------
|
||
|
int ParserTester::TestMultiArg()
|
||
|
{
|
||
|
int iNumErr = 0;
|
||
|
*m_stream << _T("testing multiarg functions...");
|
||
|
|
||
|
// Multiarg functions being called without any parameters
|
||
|
iNumErr += ThrowTest(_T("min()"), ecTOO_FEW_PARAMS);
|
||
|
iNumErr += ThrowTest(_T("max()"), ecTOO_FEW_PARAMS);
|
||
|
iNumErr += ThrowTest(_T("sum()"), ecTOO_FEW_PARAMS);
|
||
|
|
||
|
// application
|
||
|
iNumErr += EqnTest(_T("max(1,8,9,(int)6)"), (float_type)9.0, true);
|
||
|
iNumErr += EqnTest(_T("max((int)6, 1+2, 4, -9)"), (float_type)6.0, true);
|
||
|
iNumErr += EqnTest(_T("min((int)6, 1+2, 4, -9)"), (float_type)-9.0, true);
|
||
|
|
||
|
//
|
||
|
iNumErr += EqnTest(_T("a=test0()"), (float_type)0, true);
|
||
|
iNumErr += EqnTest(_T("b=a+test0()"), (float_type)1, true);
|
||
|
|
||
|
// added as response to this bugreport:
|
||
|
// http://code.google.com/p/muparserx/issues/detail?id=1
|
||
|
// cause of the error: Function tokens were not cloned in the tokenreader when beeing found.
|
||
|
// a pointer to the one and only function onject was returned instead
|
||
|
// consequently the argument counter was overwritten by the second function call
|
||
|
// causing an assertion later on.
|
||
|
iNumErr += EqnTest(_T("sum(1,2)/sum(3,4)"), (float_type)0.428571, true);
|
||
|
iNumErr += EqnTest(_T("3/sum(3,4,5)"), (float_type)0.25, true);
|
||
|
iNumErr += EqnTest(_T("sum(3)/sum(3,4,5)"), (float_type)0.25, true);
|
||
|
iNumErr += EqnTest(_T("sum(3)+sum(3,4,5)"), (float_type)15, true);
|
||
|
iNumErr += EqnTest(_T("sum(1,2)/sum(3,4,5)"), (float_type)0.25, true);
|
||
|
|
||
|
Assessment(iNumErr);
|
||
|
return iNumErr;
|
||
|
}
|
||
|
|
||
|
//---------------------------------------------------------------------------
|
||
|
int ParserTester::TestVector()
|
||
|
{
|
||
|
int iNumErr = 0;
|
||
|
*m_stream << _T("testing vector operations...");
|
||
|
|
||
|
// Vector operations
|
||
|
iNumErr += ThrowTest(_T("10+2*va"), ecEVAL); // fail: number + vector
|
||
|
iNumErr += ThrowTest(_T("10+va*2"), ecEVAL); // fail: number + vector
|
||
|
iNumErr += ThrowTest(_T("va+vc"), ecMATRIX_DIMENSION_MISMATCH); // fail: vectors of different size
|
||
|
iNumErr += ThrowTest(_T("va-vc"), ecMATRIX_DIMENSION_MISMATCH); // fail: vectors of different size
|
||
|
iNumErr += ThrowTest(_T("va*vc"), ecMATRIX_DIMENSION_MISMATCH); // fail: vectors of different size
|
||
|
iNumErr += ThrowTest(_T("va*vb"), ecMATRIX_DIMENSION_MISMATCH); // fail: matrix dimension mismatch
|
||
|
iNumErr += ThrowTest(_T("va*va"), ecMATRIX_DIMENSION_MISMATCH); // fail: matrix dimension mismatch
|
||
|
iNumErr += ThrowTest(_T("(va*vb)*b"), ecMATRIX_DIMENSION_MISMATCH); // fail: matrix dimension mismatch
|
||
|
iNumErr += ThrowTest(_T("va[1.23]"), ecTYPE_CONFLICT_IDX, 7); // fail: float value used as index
|
||
|
iNumErr += ThrowTest(_T("va[sin(8)]"), ecTYPE_CONFLICT_IDX, 9); // fail: float value used as index
|
||
|
iNumErr += ThrowTest(_T("va[-1]"), ecINDEX_OUT_OF_BOUNDS); // fail: negative value used as an index
|
||
|
iNumErr += ThrowTest(_T("va[c]"), ecINDEX_OUT_OF_BOUNDS);
|
||
|
iNumErr += ThrowTest(_T("va[(3)]"), ecINDEX_OUT_OF_BOUNDS);
|
||
|
iNumErr += ThrowTest(_T("a[1]"), ecINDEX_OUT_OF_BOUNDS); // indexing a scalar is ok, but this index is out of bounds (0 would be ok...)
|
||
|
iNumErr += ThrowTest(_T("va[1"), ecMISSING_SQR_BRACKET);
|
||
|
iNumErr += ThrowTest(_T("va[1]]"), ecUNEXPECTED_SQR_BRACKET);
|
||
|
|
||
|
//iNumErr += ThrowTest(_T("va==9"), ecEVAL);
|
||
|
//iNumErr += ThrowTest(_T("va==a"), ecEVAL);
|
||
|
//iNumErr += ThrowTest(_T("a==va"), ecEVAL);
|
||
|
//iNumErr += ThrowTest(_T("9==va"), ecEVAL);
|
||
|
|
||
|
Value v(3, 0);
|
||
|
v.At(0) = (float_type)5.0;
|
||
|
v.At(1) = (float_type)5.0;
|
||
|
v.At(2) = (float_type)5.0;
|
||
|
iNumErr += EqnTest(_T("va+vb"), v, true);
|
||
|
|
||
|
v.At(0) = (float_type)5.0;
|
||
|
v.At(1) = (float_type)5.0;
|
||
|
v.At(2) = (float_type)6.0;
|
||
|
iNumErr += EqnTest(_T("va+vb"), v, false);
|
||
|
|
||
|
v.At(0) = (float_type)-1.0;
|
||
|
v.At(1) = (float_type)-2.0;
|
||
|
v.At(2) = (float_type)-3.0;
|
||
|
iNumErr += EqnTest(_T("-va"), v, true);
|
||
|
|
||
|
iNumErr += EqnTest(_T("sizeof(va+vb)"), 3.0, true);
|
||
|
iNumErr += EqnTest(_T("sizeof(va-vb)"), 3.0, true);
|
||
|
|
||
|
iNumErr += EqnTest(_T("va==vb"), false, true);
|
||
|
iNumErr += EqnTest(_T("va!=vb"), true, true);
|
||
|
//iNumErr += EqnTest(_T("va<vb"), false, true);
|
||
|
//iNumErr += EqnTest(_T("va>vb"), true, true);
|
||
|
//iNumErr += EqnTest(_T("va<=vb"), false, true);
|
||
|
//iNumErr += EqnTest(_T("va>=vb"), true, true);
|
||
|
|
||
|
iNumErr += EqnTest(_T("vb[va[0]]"), 3.0, true);
|
||
|
iNumErr += EqnTest(_T("m1[0,0]+m1[1,1]+m1[2,2]"), 3.0, true);
|
||
|
iNumErr += EqnTest(_T("vb[m1[0,0]]"), 3.0, true);
|
||
|
|
||
|
iNumErr += EqnTest(_T("m1[0,0]=2"), 2.0, true);
|
||
|
iNumErr += EqnTest(_T("m1[1,1]=2"), 2.0, true);
|
||
|
iNumErr += EqnTest(_T("m1[2,2]=2"), 2.0, true);
|
||
|
iNumErr += EqnTest(_T("va[0]=12.3"), (float_type)12.3, true);
|
||
|
iNumErr += EqnTest(_T("va[1]=12.3"), (float_type)12.3, true);
|
||
|
iNumErr += EqnTest(_T("va[2]=12.3"), (float_type)12.3, true);
|
||
|
|
||
|
iNumErr += EqnTest(_T("va[0]"), 1.0, true);
|
||
|
iNumErr += EqnTest(_T("va[1]"), 2.0, true);
|
||
|
iNumErr += EqnTest(_T("va[2]"), 3.0, true);
|
||
|
iNumErr += EqnTest(_T("(va[2])"), 3.0, true);
|
||
|
iNumErr += EqnTest(_T("va[a]"), 2.0, true);
|
||
|
iNumErr += EqnTest(_T("(va[a])"), 2.0, true);
|
||
|
iNumErr += EqnTest(_T("va[b]"), 3.0, true);
|
||
|
iNumErr += EqnTest(_T("va[(2)]"), 3.0, true);
|
||
|
iNumErr += EqnTest(_T("va[-(-2)]"), 3.0, true);
|
||
|
iNumErr += EqnTest(_T("(va[(2)])"), 3.0, true);
|
||
|
iNumErr += EqnTest(_T("(va[-(-2)])"), 3.0, true);
|
||
|
iNumErr += EqnTest(_T("va[1+1]"), 3.0, true);
|
||
|
iNumErr += EqnTest(_T("va[(int)sin(8)+1]"), 2.0, true);
|
||
|
|
||
|
iNumErr += EqnTest(_T("va[2]+4"), 7.0, true);
|
||
|
iNumErr += EqnTest(_T("4+va[2]"), 7.0, true);
|
||
|
iNumErr += EqnTest(_T("va[2]*4"), 12.0, true);
|
||
|
iNumErr += EqnTest(_T("4*va[2]"), 12.0, true);
|
||
|
iNumErr += EqnTest(_T("va[2]+a"), 4.0, true);
|
||
|
iNumErr += EqnTest(_T("a+va[2]"), 4.0, true);
|
||
|
iNumErr += EqnTest(_T("va[2]*b"), 6.0, true);
|
||
|
iNumErr += EqnTest(_T("b*va[2]"), 6.0, true);
|
||
|
|
||
|
// Issue 68 (and related issues):
|
||
|
iNumErr += EqnTest(_T("(abs(-3)+2)>=min(6,5)"), true, true);
|
||
|
iNumErr += EqnTest(_T("(abs(-3))>abs(2)"), true, true);
|
||
|
iNumErr += EqnTest(_T("min(1,2,-3)>-4"), true, true);
|
||
|
iNumErr += EqnTest(_T("(abs(-3))>-2"), true, true);
|
||
|
iNumErr += EqnTest(_T("abs(-3)>abs(2)"), true, true);
|
||
|
|
||
|
// Issue 42:
|
||
|
// https://code.google.com/p/muparserx/issues/detail?id=42
|
||
|
v.At(0) = (float_type)1.0;
|
||
|
v.At(1) = (float_type)0.0;
|
||
|
v.At(2) = (float_type)0.0;
|
||
|
iNumErr += EqnTest(_T("{1,0,0}'"), v, true);
|
||
|
iNumErr += EqnTest(_T("{(1),0,0}'"), v, true);
|
||
|
|
||
|
Assessment(iNumErr);
|
||
|
return iNumErr;
|
||
|
}
|
||
|
|
||
|
//---------------------------------------------------------------------------
|
||
|
int ParserTester::TestBinOp()
|
||
|
{
|
||
|
int iNumErr = 0;
|
||
|
*m_stream << _T("testing binary operators...");
|
||
|
float_type a = 1;
|
||
|
|
||
|
// standard aperators
|
||
|
iNumErr += EqnTest(_T("1+7"), (float_type)8.0, true);
|
||
|
iNumErr += EqnTest(_T("10-1"), (float_type)9.0, true);
|
||
|
iNumErr += EqnTest(_T("3*4"), (float_type)12.0, true);
|
||
|
iNumErr += EqnTest(_T("10/2"), (float_type)5.0, true);
|
||
|
// operator associativity
|
||
|
iNumErr += EqnTest(_T("2^2^3"), (float_type)256.0, true);
|
||
|
iNumErr += EqnTest(_T("3+4*2/(1-5)^2^3"), (float_type)3.0001220703125, true);
|
||
|
iNumErr += EqnTest(_T("1/2/3"), (float_type)1.0 / (float_type)6.0, true);
|
||
|
|
||
|
// operator precedencs
|
||
|
iNumErr += EqnTest(_T("1+2-3*4/5^6"), (float_type)2.99923, true);
|
||
|
iNumErr += EqnTest(_T("a+b-c*4/5^6"), (float_type)2.99923, true);
|
||
|
iNumErr += EqnTest(_T("1^2/3*4-5+6"), (float_type)2.3333, true);
|
||
|
iNumErr += EqnTest(_T("a^b/c*4-5+6"), (float_type)2.3333, true);
|
||
|
iNumErr += EqnTest(_T("1+2*3"), (float_type)7.0, true);
|
||
|
iNumErr += EqnTest(_T("a+b*c"), (float_type)7.0, true);
|
||
|
iNumErr += EqnTest(_T("(1+2)*3"), (float_type)9.0, true);
|
||
|
iNumErr += EqnTest(_T("(a+b)*c"), (float_type)9.0, true);
|
||
|
iNumErr += EqnTest(_T("(1+2)*(-3)"), (float_type)-9.0, true);
|
||
|
iNumErr += EqnTest(_T("(a+b)*(-c)"), (float_type)-9.0, true);
|
||
|
iNumErr += EqnTest(_T("2/4"), (float_type)0.5, true);
|
||
|
iNumErr += EqnTest(_T("4&4"), 4.0, true);
|
||
|
iNumErr += EqnTest(_T("2+2&(a+b+c)"), 4.0, true);
|
||
|
iNumErr += EqnTest(_T("3&3"), 3.0, true);
|
||
|
iNumErr += EqnTest(_T("c&3"), 3.0, true);
|
||
|
iNumErr += EqnTest(_T("(c)&3"), 3.0, true);
|
||
|
iNumErr += EqnTest(_T("(a+b)&3"), 3.0, true);
|
||
|
iNumErr += EqnTest(_T("(a+b+c)&6"), 6.0, true);
|
||
|
iNumErr += EqnTest(_T("(1+2+3)&6"), 6.0, true);
|
||
|
iNumErr += EqnTest(_T("3&c"), 3.0, true);
|
||
|
iNumErr += EqnTest(_T("(a<<1)+2"), 4.0, true);
|
||
|
iNumErr += EqnTest(_T("(a<<2)+2"), 6.0, true);
|
||
|
iNumErr += EqnTest(_T("(a<<3)+2"), 10.0, true);
|
||
|
iNumErr += EqnTest(_T("(a<<4)+2"), 18.0, true);
|
||
|
iNumErr += EqnTest(_T("(a<<5)+2"), 34.0, true);
|
||
|
iNumErr += EqnTest(_T("1<<31"), (float_type)2147483648, true);
|
||
|
iNumErr += EqnTest(_T("-1<<31"), (float_type)-2147483648.0, true);
|
||
|
iNumErr += EqnTest(_T("1<<45"), (float_type)35184372088832.0, true);
|
||
|
iNumErr += EqnTest(_T("-1<<45"), (float_type)-35184372088832.0, true);
|
||
|
iNumErr += EqnTest(_T("8<<-2"), 2.0, true);
|
||
|
iNumErr += EqnTest(_T("8<<-4"), 0.0, true);
|
||
|
// Issue 25: http://code.google.com/p/muparserx/issues/detail?id=25
|
||
|
iNumErr += ThrowTest(_T("55<<2222222"), ecOVERFLOW);
|
||
|
// Issue 16: http://code.google.com/p/muparserx/issues/detail?id=16
|
||
|
iNumErr += EqnTest(_T("true == true && false"), true == true && false, true);
|
||
|
iNumErr += EqnTest(_T("false == true && false"), false == true && false, true);
|
||
|
iNumErr += EqnTest(_T("a==1.0 && a==1.0"), a == 1.0 && a == 1.0, true);
|
||
|
|
||
|
// bool operators for comparing values
|
||
|
iNumErr += EqnTest(_T("a<b"), true, true);
|
||
|
iNumErr += EqnTest(_T("b>a"), true, true);
|
||
|
iNumErr += EqnTest(_T("a>a"), false, true);
|
||
|
iNumErr += EqnTest(_T("a<a"), false, true);
|
||
|
iNumErr += EqnTest(_T("a>a"), false, true);
|
||
|
iNumErr += EqnTest(_T("a<=a"), true, true);
|
||
|
iNumErr += EqnTest(_T("a<=b"), true, true);
|
||
|
iNumErr += EqnTest(_T("b<=a"), false, true);
|
||
|
iNumErr += EqnTest(_T("a>=a"), true, true);
|
||
|
iNumErr += EqnTest(_T("b>=a"), true, true);
|
||
|
iNumErr += EqnTest(_T("a>=b"), false, true);
|
||
|
// The following equations were raising type conflict errors once
|
||
|
// since the result of sqrt(1) is 1 which is an integer as fas as muParserX
|
||
|
// is concerned:
|
||
|
iNumErr += EqnTest(_T("sqrt(a)<sin(8)"), false, true);
|
||
|
iNumErr += EqnTest(_T("sqrt(a)<=sin(8)"), false, true);
|
||
|
iNumErr += EqnTest(_T("sqrt(a)>sin(8)"), true, true);
|
||
|
iNumErr += EqnTest(_T("sqrt(a)>=sin(8)"), true, true);
|
||
|
iNumErr += EqnTest(_T("sqrt(a)==sin(8)"), false, true);
|
||
|
iNumErr += EqnTest(_T("sqrt(a)!=sin(8)"), true, true);
|
||
|
iNumErr += EqnTest(_T("sqrt(a)+1.01"), (float_type)2.01, true);
|
||
|
iNumErr += EqnTest(_T("sqrt(a)-1.01"), (float_type)-0.01, true);
|
||
|
|
||
|
// interaction with sign operator
|
||
|
iNumErr += EqnTest(_T("3-(-a)"), 4.0, true);
|
||
|
iNumErr += EqnTest(_T("3--a"), 4.0, true);
|
||
|
|
||
|
// Problems with small bogus real/imag values introduced due to limited floating point accuracy
|
||
|
iNumErr += EqnTest(_T("(-2)^3"), -8.0, true); // may introduce incorrect imaginary value (When computed with the log/exp formula: -8 + 2.93e-15i)
|
||
|
iNumErr += EqnTest(_T("imag((-2)^3)==0"), true, true); // may introduce incorrect imaginary value (When computed with the log/exp formula: -8 + 2.93e-15i)
|
||
|
|
||
|
Assessment(iNumErr);
|
||
|
return iNumErr;
|
||
|
}
|
||
|
|
||
|
//---------------------------------------------------------------------------
|
||
|
int ParserTester::TestIfElse()
|
||
|
{
|
||
|
int iNumErr = 0;
|
||
|
*m_stream << _T("testing if-else conditional...");
|
||
|
|
||
|
float_type a = 1;
|
||
|
|
||
|
// test case copied from muparser (https://oss-fuzz.com/testcase-detail/4777121158529024)
|
||
|
iNumErr += ThrowTest(_T("3!=min(false?2>2,2>5,1:6)"), ecUNEXPECTED_COMMA);
|
||
|
|
||
|
// test case copied from muparser (https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=22922#c1)
|
||
|
iNumErr += ThrowTest(_T("1?2:0?(7:1)"), ecMISPLACED_COLON);
|
||
|
|
||
|
// test case copied from muparser (https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=23410)
|
||
|
// with variations
|
||
|
iNumErr += ThrowTest(_T(R"(false ? 4 : "", ? 4 : "", ? 4 : "")"), ecUNEXPECTED_COMMA);
|
||
|
iNumErr += EqnTest(_T(R"(false ? "four" : 4)"), 4, true);
|
||
|
iNumErr += EqnTest(_T(R"(true ? "four" : 4)"), "four", true);
|
||
|
iNumErr += EqnTest(_T(R"(true ? "foo" : "bar")"), "foo", true);
|
||
|
|
||
|
// test case and variations copied from muparser https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=22938
|
||
|
iNumErr += ThrowTest(_T("sum(false?1,0,0:3)"), ecUNEXPECTED_COMMA);
|
||
|
iNumErr += ThrowTest(_T("sum(false?(1,0,0):3)"), ecUNEXPECTED_COMMA);
|
||
|
iNumErr += ThrowTest(_T("sum(2>3?2,4,2:4)"), ecUNEXPECTED_COMMA);
|
||
|
iNumErr += ThrowTest(_T("sum(2>3?2,4,sin(2):4)"), ecUNEXPECTED_COMMA);
|
||
|
iNumErr += ThrowTest(_T("sum(2>3?sin(2),4,2:4)"), ecUNEXPECTED_COMMA);
|
||
|
iNumErr += ThrowTest(_T("sum(2>3?sin(a),4,2:4)"), ecUNEXPECTED_COMMA);
|
||
|
iNumErr += ThrowTest(_T("sum(2>3?sin(2),4,2:4)"), ecUNEXPECTED_COMMA);
|
||
|
|
||
|
// Test error detection
|
||
|
iNumErr += ThrowTest(_T(": 2"), ecMISPLACED_COLON);
|
||
|
iNumErr += ThrowTest(_T("? 1 : 2"), ecUNEXPECTED_CONDITIONAL);
|
||
|
iNumErr += ThrowTest(_T("(a<b) ? (b<c) ? 1 : 2"), ecMISSING_ELSE_CLAUSE);
|
||
|
iNumErr += ThrowTest(_T("(a<b) ? 1"), ecMISSING_ELSE_CLAUSE);
|
||
|
iNumErr += ThrowTest(_T("(a<b) ? a"), ecMISSING_ELSE_CLAUSE);
|
||
|
iNumErr += ThrowTest(_T("(a<b) ? a+b"), ecMISSING_ELSE_CLAUSE);
|
||
|
iNumErr += ThrowTest(_T("a : b"), ecMISPLACED_COLON, 2);
|
||
|
iNumErr += ThrowTest(_T("1 : 2"), ecMISPLACED_COLON, 2);
|
||
|
iNumErr += ThrowTest(_T("(true) ? 1 : 2 : 3"), ecMISPLACED_COLON);
|
||
|
|
||
|
iNumErr += ThrowTest(_T("1==?"), ecUNEXPECTED_CONDITIONAL);
|
||
|
iNumErr += ThrowTest(_T("1+?"), ecUNEXPECTED_CONDITIONAL); // bin oprt + ?
|
||
|
iNumErr += ThrowTest(_T("1m?"), ecUNEXPECTED_CONDITIONAL); // postfix + ?
|
||
|
iNumErr += ThrowTest(_T("-?"), ecUNEXPECTED_CONDITIONAL); // infix + ?
|
||
|
|
||
|
iNumErr += EqnTest(_T("(true) ? 128 : 255"), 128.0, true);
|
||
|
iNumErr += EqnTest(_T("(1<2) ? 128 : 255"), 128.0, true);
|
||
|
iNumErr += EqnTest(_T("(a<b) ? 128 : 255"), 128.0, true);
|
||
|
iNumErr += EqnTest(_T("((a>b) ? true : false) ? 1 : 2"), 2.0, true);
|
||
|
iNumErr += EqnTest(_T("((a>b) ? true : false) ? 1 : sum((a>b) ? 1 : 2)"), 2.0, true);
|
||
|
iNumErr += EqnTest(_T("((a>b) ? false : true) ? 1 : sum((a>b) ? 1 : 2)"), 1.0, true);
|
||
|
iNumErr += EqnTest(_T("(true) ? 10 : 11"), 10.0, true);
|
||
|
iNumErr += EqnTest(_T("(true) ? a+b : c+d"), 3.0, true);
|
||
|
iNumErr += EqnTest(_T("(true) ? false : true"), false, true);
|
||
|
iNumErr += EqnTest(_T("(false) ? 10 : 11"), 11.0, true);
|
||
|
iNumErr += EqnTest(_T("(false) ? a+b : c+d"), 1.0, true);
|
||
|
iNumErr += EqnTest(_T("(false) ? false : true"), true, true);
|
||
|
iNumErr += EqnTest(_T("(a<b) ? 10 : 11"), 10.0, true);
|
||
|
iNumErr += EqnTest(_T("(a>b) ? 10 : 11"), 11.0, true);
|
||
|
iNumErr += EqnTest(_T("(a<b) ? c : d"), 3.0, true);
|
||
|
iNumErr += EqnTest(_T("(a>b) ? c : d"), -2.0, true);
|
||
|
iNumErr += EqnTest(_T("(a>b) ? true : false"), false, true);
|
||
|
|
||
|
// With multiarg functions
|
||
|
iNumErr += EqnTest(_T("sum((a>b) ? 1 : 2)"), 2.0, true);
|
||
|
iNumErr += EqnTest(_T("sum((a>b) ? 1 : 2, 100)"), 102.0, true);
|
||
|
iNumErr += EqnTest(_T("sum((true) ? 1 : 2)"), 1.0, true);
|
||
|
iNumErr += EqnTest(_T("sum((true) ? 1 : 2, 100)"), 101.0, true);
|
||
|
iNumErr += EqnTest(_T("sum(3, (a>b) ? 3 : 10)"), 13.0, true);
|
||
|
iNumErr += EqnTest(_T("sum(3, (a<b) ? 3 : 10)"), 6., true);
|
||
|
iNumErr += EqnTest(_T("sum(3, (a>b) ? 3 : 10)*10"), 130.0, true);
|
||
|
iNumErr += EqnTest(_T("sum(3, (a<b) ? 3 : 10)*10"), 60.0, true);
|
||
|
iNumErr += EqnTest(_T("10*sum(3, (a>b) ? 3 : 10)"), 130.0, true);
|
||
|
iNumErr += EqnTest(_T("10*sum(3, (a<b) ? 3 : 10)"), 60.0, true);
|
||
|
iNumErr += EqnTest(_T("(a<b) ? sum(3, (a<b) ? 3 : 10)*10 : 99"), 60.0, true);
|
||
|
iNumErr += EqnTest(_T("(a>b) ? sum(3, (a<b) ? 3 : 10)*10 : 99"), 99.0, true);
|
||
|
iNumErr += EqnTest(_T("(a<b) ? sum(3, (a<b) ? 3 : 10,10,20)*10 : 99"), 360.0, true);
|
||
|
iNumErr += EqnTest(_T("(a>b) ? sum(3, (a<b) ? 3 : 10,10,20)*10 : 99"), 99.0, true);
|
||
|
iNumErr += EqnTest(_T("(a>b) ? sum(3, (a<b) ? 3 : 10,10,20)*10 : sum(3, (a<b) ? 3 : 10)*10"), 60.0, true);
|
||
|
|
||
|
iNumErr += EqnTest(_T("(a<b)&&(a<b) ? 128 : 255"), 128.0, true);
|
||
|
iNumErr += EqnTest(_T("(a>b)&&(a<b) ? 128 : 255"), 255.0, true);
|
||
|
iNumErr += EqnTest(_T("(1<2)&&(1<2) ? 128 : 255"), 128.0, true);
|
||
|
iNumErr += EqnTest(_T("(1>2)&&(1<2) ? 128 : 255"), 255.0, true);
|
||
|
iNumErr += EqnTest(_T("((1<2)&&(1<2)) ? 128 : 255"), 128.0, true);
|
||
|
iNumErr += EqnTest(_T("((1>2)&&(1<2)) ? 128 : 255"), 255.0, true);
|
||
|
iNumErr += EqnTest(_T("((a<b)&&(a<b)) ? 128 : 255"), 128.0, true);
|
||
|
iNumErr += EqnTest(_T("((a>b)&&(a<b)) ? 128 : 255"), 255.0, true);
|
||
|
|
||
|
// nested conditionals with brackets
|
||
|
iNumErr += EqnTest(_T("(a<b) ? ((b<c) ? 2*(a+b) : 2) : 3"), 6., true);
|
||
|
iNumErr += EqnTest(_T("(a<b) ? 3 : ((b<c) ? 2*(a+b) : 2)"), 3., true);
|
||
|
iNumErr += EqnTest(_T("(a<b) ? ((b>c) ? 1 : 2) : 3"), 2., true);
|
||
|
iNumErr += EqnTest(_T("(a>b) ? ((b<c) ? 1 : 2) : 3"), 3., true);
|
||
|
iNumErr += EqnTest(_T("(a>b) ? ((b>c) ? 1 : 2) : 3"), 3., true);
|
||
|
|
||
|
// nested conditionals without brackets
|
||
|
iNumErr += EqnTest(_T("(a<b) ? (b<c) ? 1 : 2 : 3"), 1., true);
|
||
|
iNumErr += EqnTest(_T("(a<b) ? (b>c) ? 1 : 2 : 3"), 2., true);
|
||
|
iNumErr += EqnTest(_T("(a>b) ? (b<c) ? 1 : 2 : 3"), 3., true);
|
||
|
iNumErr += EqnTest(_T("(a>b) ? (b>c) ? 1 : 2 : 3"), 3., true);
|
||
|
|
||
|
// Neue Tests
|
||
|
iNumErr += EqnTest(_T("(a<b)&&(a<b) ? 128 : 255"), 128., true);
|
||
|
iNumErr += EqnTest(_T("(a>b)&&(a<b) ? 128 : 255"), 255., true);
|
||
|
iNumErr += EqnTest(_T("(1<2)&&(1<2) ? 128 : 255"), 128., true);
|
||
|
iNumErr += EqnTest(_T("(1>2)&&(1<2) ? 128 : 255"), 255., true);
|
||
|
iNumErr += EqnTest(_T("((1<2)&&(1<2)) ? 128 : 255"), 128., true);
|
||
|
iNumErr += EqnTest(_T("((1>2)&&(1<2)) ? 128 : 255"), 255., true);
|
||
|
iNumErr += EqnTest(_T("((a<b)&&(a<b)) ? 128 : 255"), 128., true);
|
||
|
iNumErr += EqnTest(_T("((a>b)&&(a<b)) ? 128 : 255"), 255., true);
|
||
|
|
||
|
iNumErr += EqnTest(_T("1>0 ? 1>2 ? 128 : 255 : 1>0 ? 32 : 64"), 255., true);
|
||
|
iNumErr += EqnTest(_T("1>0 ? 1>2 ? 128 : 255 :(1>0 ? 32 : 64)"), 255., true);
|
||
|
iNumErr += EqnTest(_T("1>0 ? 1>0 ? 128 : 255 : 1>2 ? 32 : 64"), 128., true);
|
||
|
iNumErr += EqnTest(_T("1>0 ? 1>0 ? 128 : 255 :(1>2 ? 32 : 64)"), 128., true);
|
||
|
iNumErr += EqnTest(_T("1>2 ? 1>2 ? 128 : 255 : 1>0 ? 32 : 64"), 32., true);
|
||
|
iNumErr += EqnTest(_T("1>2 ? 1>0 ? 128 : 255 : 1>2 ? 32 : 64"), 64., true);
|
||
|
iNumErr += EqnTest(_T("1>0 ? 50 : 1>0 ? 128 : 255"), 50., true);
|
||
|
iNumErr += EqnTest(_T("1>0 ? 50 : (1>0 ? 128 : 255)"), 50., true);
|
||
|
iNumErr += EqnTest(_T("1>0 ? 1>0 ? 128 : 255 : 50"), 128., true);
|
||
|
iNumErr += EqnTest(_T("1>2 ? 1>2 ? 128 : 255 : 1>0 ? 32 : 1>2 ? 64 : 16"), 32., true);
|
||
|
iNumErr += EqnTest(_T("1>2 ? 1>2 ? 128 : 255 : 1>0 ? 32 :(1>2 ? 64 : 16)"), 32., true);
|
||
|
iNumErr += EqnTest(_T("1>0 ? 1>2 ? 128 : 255 : 1>0 ? 32 :1>2 ? 64 : 16"), 255., true);
|
||
|
iNumErr += EqnTest(_T("1>0 ? 1>2 ? 128 : 255 : (1>0 ? 32 :1>2 ? 64 : 16)"), 255., true);
|
||
|
iNumErr += EqnTest(_T("true ? false ? 128 : 255 : true ? 32 : 64"), 255., true);
|
||
|
|
||
|
// assignment operators
|
||
|
iNumErr += EqnTest(_T("a= false ? 128 : 255"), 255., true);
|
||
|
iNumErr += EqnTest(_T("a=((a>b)&&(a<b)) ? 128 : 255"), 255., true);
|
||
|
iNumErr += EqnTest(_T("c=(a<b)&&(a<b) ? 128 : 255"), 128., true);
|
||
|
|
||
|
iNumErr += EqnTest(_T("a=true?b=true?3:4:5"), 3., true);
|
||
|
iNumErr += EqnTest(_T("a=false?b=true?3:4:5"), 5., true);
|
||
|
|
||
|
iNumErr += EqnTest(_T("a=true?5:b=true?3:4"), 5., true);
|
||
|
iNumErr += EqnTest(_T("a=false?5:b=true?3:4"), 3., true);
|
||
|
|
||
|
// Issue 42: ?: operator must be last in expression
|
||
|
// https://code.google.com/p/muparserx/issues/detail?id=42
|
||
|
iNumErr += EqnTest(_T("abs(0.1) < 0.25 ? (-1) : (1) + 1"), fabs(0.1) < 0.25 ? (-1.) : (1.) + 1, true);
|
||
|
iNumErr += EqnTest(_T("abs(a) < 0.25 ? (-1) : (1) + 1"), fabs(a) < 0.25 ? (-1.) : (1.) + 1, true);
|
||
|
iNumErr += EqnTest(_T("(abs(a) < 0.25 ? -1 : 1)"), (abs(a) < 0.25 ? -1. : 1.), true);
|
||
|
|
||
|
Assessment(iNumErr);
|
||
|
return iNumErr;
|
||
|
}
|
||
|
|
||
|
//---------------------------------------------------------------------------
|
||
|
int ParserTester::TestEqn()
|
||
|
{
|
||
|
int iNumErr = 0;
|
||
|
*m_stream << _T("testing sample equations...");
|
||
|
|
||
|
// test case copied from muparser: https://oss-fuzz.com/testcase-detail/5106868061208576
|
||
|
iNumErr += ThrowTest(_T(R"("","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",8)"), ecUNEXPECTED_COMMA);
|
||
|
iNumErr += ThrowTest(_T(R"("","",9)"), ecUNEXPECTED_COMMA);
|
||
|
|
||
|
// test case copied from muparser: https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=23330#c1
|
||
|
iNumErr += ThrowTest(_T("6, +, +, +, +, +, +, +, +, +, +, +, +, +, +, 1, +, +, +, +, +, +, +, +, +, +, +, +, +, +, +, +, +, +, +, +, +, +, +, +, +, +, +, +, +, +, +, +"), ecUNEXPECTED_COMMA);
|
||
|
|
||
|
iNumErr += ThrowTest(_T("1e1234"), ecUNASSIGNABLE_TOKEN);
|
||
|
iNumErr += ThrowTest(_T("-1e1234"), ecUNASSIGNABLE_TOKEN);
|
||
|
|
||
|
iNumErr += EqnTest(_T("-2--8"), (float_type)6.0, true);
|
||
|
iNumErr += EqnTest(_T("2*(a=9)*3"), 54., true);
|
||
|
|
||
|
// Functions
|
||
|
iNumErr += EqnTest(_T("10*strlen(toupper(\"12345\"))"), 50., true);
|
||
|
|
||
|
// hex value recognition
|
||
|
iNumErr += EqnTest(_T("0xff"), 255., true);
|
||
|
iNumErr += EqnTest(_T("10+0xff"), 265., true);
|
||
|
iNumErr += EqnTest(_T("0xff+10"), 265., true);
|
||
|
iNumErr += EqnTest(_T("10*0xff"), 2550., true);
|
||
|
iNumErr += EqnTest(_T("0xff*10"), 2550., true);
|
||
|
iNumErr += EqnTest(_T("10+0xff+1"), 266., true);
|
||
|
iNumErr += EqnTest(_T("1+0xff+10"), 266., true);
|
||
|
|
||
|
// ...
|
||
|
iNumErr += EqnTest(_T("exp(ln(7))"), 7., true);
|
||
|
iNumErr += EqnTest(_T("e^ln(7)"), 7., true);
|
||
|
iNumErr += EqnTest(_T("e^(ln(7))"), 7., true);
|
||
|
iNumErr += EqnTest(_T("(e^(ln(7)))"), 7., true);
|
||
|
iNumErr += EqnTest(_T("1-(e^(ln(7)))"), -6., true);
|
||
|
iNumErr += EqnTest(_T("2*(e^(ln(7)))"), 14., true);
|
||
|
iNumErr += EqnTest(_T("10^log10(5)"), 5., true);
|
||
|
iNumErr += EqnTest(_T("10^log10(5)"), 5., true);
|
||
|
iNumErr += EqnTest(_T("2^log2(4)"), (float_type)4.0, true);
|
||
|
iNumErr += EqnTest(_T("-(sin(0)+1)"), (float_type)-1.0, true);
|
||
|
iNumErr += EqnTest(_T("-(2^1.1)"), (float_type)-2.14354692, true);
|
||
|
|
||
|
// infix + postfix operator in arguments for binary operators (Reference: Matlab)
|
||
|
iNumErr += EqnTest(_T("-sin(8)m*6"), (float_type)-0.00593615, true);
|
||
|
iNumErr += EqnTest(_T("-sin(8)m/6"), (float_type)-1.6489e-4, true);
|
||
|
iNumErr += EqnTest(_T("-sin(8)m+6"), (float_type)5.99901, true);
|
||
|
iNumErr += EqnTest(_T("-sin(8)m-6"), (float_type)-6.000989, true);
|
||
|
|
||
|
iNumErr += EqnTest(_T("(cos(2.41)/b)"), (float_type)-0.372056, true);
|
||
|
|
||
|
// long formula (Reference: Matlab)
|
||
|
iNumErr += EqnTest(
|
||
|
_T("(((-9))-e/(((((((pi-(((-7)+(-3)/4/e))))/(((-5))-2)-((pi+(-0))*(sqrt((e+e))*(-8))*(((-pi)+(-pi)-(-9)*(6*5))")
|
||
|
_T("/(-e)-e))/2)/((((sqrt(2/(-e)+6)-(4-2))+((5/(-2))/(1*(-pi)+3))/8)*pi*((pi/((-2)/(-6)*1*(-1))*(-6)+(-e)))))/")
|
||
|
_T("((e+(-2)+(-e)*((((-3)*9+(-e)))+(-9)))))))-((((e-7+(((5/pi-(3/1+pi)))))/e)/(-5))/(sqrt((((((1+(-7))))+((((-")
|
||
|
_T("e)*(-e)))-8))*(-5)/((-e)))*(-6)-((((((-2)-(-9)-(-e)-1)/3))))/(sqrt((8+(e-((-6))+(9*(-9))))*(((3+2-8))*(7+6")
|
||
|
_T("+(-5))+((0/(-e)*(-pi))+7)))+(((((-e)/e/e)+((-6)*5)*e+(3+(-5)/pi))))+pi))/sqrt((((9))+((((pi))-8+2))+pi))/e")
|
||
|
_T("*4)*((-5)/(((-pi))*(sqrt(e)))))-(((((((-e)*(e)-pi))/4+(pi)*(-9)))))))+(-pi)"), (float_type)-12.23016549, true);
|
||
|
|
||
|
// long formula (Reference: Matlab)
|
||
|
iNumErr += EqnTest(_T("1+2-3*4/5^6*(2*(1-5+(3*7^9)*(4+6*7-3)))+12"), (float_type)-7995810.09926, true);
|
||
|
|
||
|
/* <ibg 20100321 atan currently unsupported/>
|
||
|
|
||
|
// long formula (Reference: Matlab)
|
||
|
iNumErr += 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);
|
||
|
*/
|
||
|
|
||
|
Assessment(iNumErr);
|
||
|
return iNumErr;
|
||
|
}
|
||
|
|
||
|
//---------------------------------------------------------------------------
|
||
|
int ParserTester::TestScript()
|
||
|
{
|
||
|
int iNumErr = 0;
|
||
|
*m_stream << _T("testing script features...");
|
||
|
|
||
|
// Test error detection
|
||
|
iNumErr += ThrowTest(_T("sin(\n"), ecUNEXPECTED_NEWLINE);
|
||
|
iNumErr += ThrowTest(_T("1+\n"), ecUNEXPECTED_NEWLINE);
|
||
|
iNumErr += ThrowTest(_T("a*\n"), ecUNEXPECTED_NEWLINE);
|
||
|
iNumErr += ThrowTest(_T("va[\n"), ecUNEXPECTED_NEWLINE);
|
||
|
iNumErr += ThrowTest(_T("(true) ? \n"), ecUNEXPECTED_NEWLINE);
|
||
|
iNumErr += ThrowTest(_T("(true) ? 10:\n"), ecUNEXPECTED_NEWLINE);
|
||
|
|
||
|
// Expressions spanning multiple lines
|
||
|
iNumErr += EqnTest(_T("a=1\n")
|
||
|
_T("b=2\n")
|
||
|
_T("c=3\n")
|
||
|
_T("a+b+c"), 6., true);
|
||
|
|
||
|
// Ending an expression with a newline
|
||
|
iNumErr += EqnTest(_T("3\n"), 3., true);
|
||
|
iNumErr += EqnTest(_T("1+2\n"), 3., true);
|
||
|
iNumErr += EqnTest(_T("\n1+2\n"), 3., true);
|
||
|
iNumErr += EqnTest(_T("\n1+2\n\na+b"), 3., true);
|
||
|
|
||
|
// Testing comments
|
||
|
/* 20130107 Not yet...
|
||
|
iNumErr += EqnTest(_T("a=10 % this is a comment\n")
|
||
|
_T("b=23 % this is another comment\n")
|
||
|
_T("a+b"), 33, true);
|
||
|
*/
|
||
|
Assessment(iNumErr);
|
||
|
return iNumErr;
|
||
|
}
|
||
|
|
||
|
//---------------------------------------------------------------------------
|
||
|
int ParserTester::TestValReader()
|
||
|
{
|
||
|
int iNumErr = 0;
|
||
|
*m_stream << _T("testing value reader...");
|
||
|
|
||
|
// Hex value reader
|
||
|
iNumErr += EqnTest(_T("0x1"), 1., true);
|
||
|
iNumErr += EqnTest(_T("0x1+0x2"), 3., true);
|
||
|
iNumErr += EqnTest(_T("0xff"), 255., true);
|
||
|
|
||
|
// Reading of binary values
|
||
|
iNumErr += EqnTest(_T("0b1"), 1., true);
|
||
|
iNumErr += EqnTest(_T("0b01"), 1., true);
|
||
|
iNumErr += EqnTest(_T("0b11"), 3., true);
|
||
|
iNumErr += EqnTest(_T("0b011"), 3., true);
|
||
|
iNumErr += EqnTest(_T("0b11111111"), 255., true);
|
||
|
iNumErr += EqnTest(_T("b*0b011"), 6.0, true);
|
||
|
iNumErr += EqnTest(_T("0b1111111111111111111111111111111"), 2147483647., true);
|
||
|
iNumErr += EqnTest(_T("0b10000000000000000000000000000000"), -2147483647. - 1, true);
|
||
|
iNumErr += EqnTest(_T("0b11111111111111111111111111111111"), -1., true);
|
||
|
iNumErr += ThrowTest(_T("0b100000000000000000000000000000000"), ecUNDEFINED);
|
||
|
|
||
|
// string value reader
|
||
|
iNumErr += EqnTest(_T("\"hallo\""), _T("hallo"), true);
|
||
|
|
||
|
// boolean value reader
|
||
|
iNumErr += EqnTest(_T("true"), true, true);
|
||
|
iNumErr += EqnTest(_T("false"), false, true);
|
||
|
|
||
|
// boolean value reader
|
||
|
iNumErr += EqnTest(_T("true"), true, true);
|
||
|
iNumErr += EqnTest(_T("false"), false, true);
|
||
|
|
||
|
// mixed
|
||
|
iNumErr += EqnTest(_T("0b011+0xef"), 242., true);
|
||
|
|
||
|
Assessment(iNumErr);
|
||
|
return iNumErr;
|
||
|
}
|
||
|
|
||
|
//---------------------------------------------------------------------------
|
||
|
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 (ParserError &e)
|
||
|
{
|
||
|
*m_stream << e.GetMsg() << endl;
|
||
|
*m_stream << e.GetToken() << endl;
|
||
|
Abort();
|
||
|
}
|
||
|
catch (std::exception &e)
|
||
|
{
|
||
|
*m_stream << e.what() << endl;
|
||
|
Abort();
|
||
|
}
|
||
|
catch (...)
|
||
|
{
|
||
|
*m_stream << _T("Internal error");
|
||
|
Abort();
|
||
|
}
|
||
|
|
||
|
if (iStat == 0)
|
||
|
{
|
||
|
*m_stream << _T("Test passed (") << ParserTester::c_iCount << _T(" expressions)") << endl;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
*m_stream << _T("Test failed with ") << iStat
|
||
|
<< _T(" errors (") << ParserTester::c_iCount
|
||
|
<< _T(" expressions)") << endl;
|
||
|
}
|
||
|
ParserTester::c_iCount = 0;
|
||
|
|
||
|
#ifdef MUP_LEAKAGE_REPORT
|
||
|
// All tokens must have been destroyed by now, verify this
|
||
|
IToken::LeakageReport();
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
//---------------------------------------------------------------------------
|
||
|
int ParserTester::ThrowTest(const string_type &a_sExpr, int a_nErrc, int a_nPos, string_type a_sIdent)
|
||
|
{
|
||
|
ParserTester::c_iCount++;
|
||
|
|
||
|
try
|
||
|
{
|
||
|
ParserX p;
|
||
|
|
||
|
// Add variables
|
||
|
Value vVarVal[] = { 1., 2., 3., -2. };
|
||
|
p.DefineVar(_T("a"), Variable(&vVarVal[0]));
|
||
|
p.DefineVar(_T("b"), Variable(&vVarVal[1]));
|
||
|
p.DefineVar(_T("c"), Variable(&vVarVal[2]));
|
||
|
p.DefineVar(_T("d"), Variable(&vVarVal[3]));
|
||
|
|
||
|
// array variables
|
||
|
Value aVal1(3, 0);
|
||
|
aVal1.At(0) = (float_type)1.0;
|
||
|
aVal1.At(1) = (float_type)2.0;
|
||
|
aVal1.At(2) = (float_type)3.0;
|
||
|
|
||
|
Value aVal2(3, 0);
|
||
|
aVal2.At(0) = (float_type)4.0;
|
||
|
aVal2.At(1) = (float_type)3.0;
|
||
|
aVal2.At(2) = (float_type)2.0;
|
||
|
|
||
|
Value aVal3(4, 0);
|
||
|
aVal3.At(0) = (float_type)4.0;
|
||
|
aVal3.At(1) = (float_type)3.0;
|
||
|
aVal3.At(2) = (float_type)2.0;
|
||
|
aVal3.At(3) = (float_type)5.0;
|
||
|
|
||
|
Value aVal4(4, 0);
|
||
|
aVal4.At(0) = (float_type)4.0;
|
||
|
aVal4.At(1) = false;
|
||
|
aVal4.At(2) = _T("hallo");
|
||
|
|
||
|
// Matrix variables
|
||
|
Value m1(3, 3, 0);
|
||
|
m1.At(0, 0) = 1.;
|
||
|
m1.At(1, 1) = 1.;
|
||
|
m1.At(2, 2) = 1.;
|
||
|
|
||
|
Value m2(3, 3, 0);
|
||
|
m2.At(0, 0) = 1.; m2.At(0, 1) = 2.; m2.At(0, 2) = 3.;
|
||
|
m2.At(1, 0) = 4.; m2.At(1, 1) = 5.; m2.At(1, 2) = 6.;
|
||
|
m2.At(2, 0) = 7.; m2.At(2, 1) = 8.; m2.At(2, 2) = 9.;
|
||
|
|
||
|
p.DefineVar(_T("m1"), Variable(&m1));
|
||
|
p.DefineVar(_T("m2"), Variable(&m2));
|
||
|
p.DefineVar(_T("va"), Variable(&aVal1));
|
||
|
p.DefineVar(_T("vb"), Variable(&aVal2));
|
||
|
p.DefineVar(_T("vc"), Variable(&aVal3));
|
||
|
p.DefineVar(_T("vd"), Variable(&aVal4));
|
||
|
|
||
|
p.SetExpr(a_sExpr);
|
||
|
Value fRes = p.Eval();
|
||
|
}
|
||
|
catch (ParserError &e)
|
||
|
{
|
||
|
// output the formula in case of an failed test
|
||
|
if (a_nErrc != e.GetCode())
|
||
|
{
|
||
|
*m_stream << _T("\n ")
|
||
|
<< _T("Expression: \"") << a_sExpr
|
||
|
<< _T("\" Code:") << e.GetCode()
|
||
|
<< _T(" Expected:") << a_nErrc;
|
||
|
}
|
||
|
|
||
|
// Check whether the error is reported at the correct expression position
|
||
|
if (a_nPos != -1 && a_nPos != e.GetPos())
|
||
|
{
|
||
|
*m_stream << _T("\n ")
|
||
|
<< _T("Invalid error position: \"") << a_sExpr
|
||
|
<< _T("\" Pos:") << e.GetPos()
|
||
|
<< _T(" Expected:") << a_nPos;
|
||
|
}
|
||
|
|
||
|
if (a_sIdent.length() && a_sIdent != e.GetContext().Ident)
|
||
|
{
|
||
|
*m_stream << _T("\n ")
|
||
|
<< _T("Invalid identifier: \"") << a_sExpr
|
||
|
<< _T("\" Ident:") << e.GetContext().Ident
|
||
|
<< _T(" Expected:") << a_sIdent;
|
||
|
}
|
||
|
|
||
|
return (a_nErrc == e.GetCode() && (a_nPos == -1 || a_nPos == e.GetPos())) ? 0 : 1;
|
||
|
}
|
||
|
|
||
|
*m_stream << _T("\n ")
|
||
|
<< _T("Expression failed: \"")
|
||
|
<< a_sExpr
|
||
|
<< _T("\" (no exception raised).");
|
||
|
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
//---------------------------------------------------------------------------
|
||
|
int ParserTester::EqnTest(const string_type &a_str, Value a_val, bool a_fPass, int nExprVar)
|
||
|
{
|
||
|
ParserTester::c_iCount++;
|
||
|
int iRet(1);
|
||
|
Value fVal[5];
|
||
|
|
||
|
try
|
||
|
{
|
||
|
// 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 the deleted object.
|
||
|
// !! If this is the case this function will crash !!
|
||
|
std::unique_ptr<ParserX> p1(new ParserX());
|
||
|
|
||
|
// Add variables
|
||
|
Value vVarVal[] = { 1., 2., 3., -2., -1. };
|
||
|
|
||
|
// m1 ist die Einheitsmatrix
|
||
|
Value m1(3, 3, 0);
|
||
|
m1.At(0, 0) = 1.;
|
||
|
m1.At(1, 1) = 1.;
|
||
|
m1.At(2, 2) = 1.;
|
||
|
|
||
|
// m2 ist die Einheitsmatrix
|
||
|
Value m2(3, 3, 0);
|
||
|
m2.At(0, 0) = 1.; m2.At(0, 1) = 2.; m2.At(0, 2) = 3.;
|
||
|
m2.At(1, 0) = 4.; m2.At(1, 1) = 5.; m2.At(1, 2) = 6.;
|
||
|
m2.At(2, 0) = 7.; m2.At(2, 1) = 8.; m2.At(2, 2) = 9.;
|
||
|
|
||
|
p1->DefineOprt(new DbgSillyAdd);
|
||
|
p1->DefineFun(new FunTest0);
|
||
|
|
||
|
p1->DefineVar(_T("a"), Variable(&vVarVal[0]));
|
||
|
p1->DefineVar(_T("b"), Variable(&vVarVal[1]));
|
||
|
p1->DefineVar(_T("c"), Variable(&vVarVal[2]));
|
||
|
p1->DefineVar(_T("d"), Variable(&vVarVal[3]));
|
||
|
p1->DefineVar(_T("f"), Variable(&vVarVal[4]));
|
||
|
p1->DefineVar(_T("m1"), Variable(&m1));
|
||
|
p1->DefineVar(_T("m2"), Variable(&m2));
|
||
|
|
||
|
// Add constants
|
||
|
p1->DefineConst(_T("const"), 1.);
|
||
|
p1->DefineConst(_T("const1"), 2.);
|
||
|
p1->DefineConst(_T("const2"), 3.);
|
||
|
|
||
|
// some vector variables
|
||
|
Value aVal1(3, 0);
|
||
|
aVal1.At(0) = (float_type)1.0;
|
||
|
aVal1.At(1) = (float_type)2.0;
|
||
|
aVal1.At(2) = (float_type)3.0;
|
||
|
|
||
|
Value aVal2(3, 0);
|
||
|
aVal2.At(0) = (float_type)4.0;
|
||
|
aVal2.At(1) = (float_type)3.0;
|
||
|
aVal2.At(2) = (float_type)2.0;
|
||
|
p1->DefineVar(_T("va"), Variable(&aVal1));
|
||
|
p1->DefineVar(_T("vb"), Variable(&aVal2));
|
||
|
|
||
|
// complex variables
|
||
|
Value cVal[3];
|
||
|
cVal[0] = mup::cmplx_type(1, 1);
|
||
|
cVal[1] = mup::cmplx_type(2, 3);
|
||
|
cVal[2] = mup::cmplx_type(3, 4);
|
||
|
p1->DefineVar(_T("ca"), Variable(&cVal[0]));
|
||
|
p1->DefineVar(_T("cb"), Variable(&cVal[1]));
|
||
|
p1->DefineVar(_T("cc"), Variable(&cVal[2]));
|
||
|
|
||
|
p1->SetExpr(a_str);
|
||
|
|
||
|
fVal[0] = p1->Eval();
|
||
|
|
||
|
// Test copy and assignement operators
|
||
|
std::vector<ParserX> vParser;
|
||
|
vParser.push_back(*p1); // Push p1 into the vector
|
||
|
ParserX p2 = vParser[0]; // take parser from vector
|
||
|
|
||
|
// destroy the originals from p2
|
||
|
vParser.clear(); // delete the vector
|
||
|
p1.reset(0); // delete the original
|
||
|
|
||
|
fVal[1] = p2.Eval(); // If copy constructions does not work
|
||
|
// we may see a crash here
|
||
|
|
||
|
// Test assignement operator
|
||
|
// additionally disable Optimizer this time
|
||
|
ParserX p3;
|
||
|
p3 = p2;
|
||
|
fVal[2] = p3.Eval(); // If assignment does not work
|
||
|
// we may see a crash here
|
||
|
|
||
|
// Calculating a second time will parse from rpn rather than from
|
||
|
// string. The result must be the same...
|
||
|
fVal[3] = p3.Eval();
|
||
|
|
||
|
// Calculate yet another time. There is the possibility of
|
||
|
// changing variables as a side effect of expression
|
||
|
// evaluation. So there are really bugs that could make this fail...
|
||
|
fVal[4] = p3.Eval();
|
||
|
|
||
|
// Check i number of used variables is correct
|
||
|
if (nExprVar != -1)
|
||
|
{
|
||
|
std::size_t n2 = p2.GetExprVar().size();
|
||
|
std::size_t n3 = p3.GetExprVar().size();
|
||
|
|
||
|
if (n2 + n3 != 2 * n2 || int(n2) != nExprVar)
|
||
|
{
|
||
|
*m_stream << _T(" Number of expression variables is incorrect. (expected: ")
|
||
|
<< nExprVar << _T("; detected: ") << n2 << _T(")");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Check the three results
|
||
|
// 1.) computed results must have identic type
|
||
|
char_type cType = fVal[0].GetType();
|
||
|
bool bStat = cType == fVal[1].GetType() &&
|
||
|
cType == fVal[2].GetType() &&
|
||
|
cType == fVal[3].GetType() &&
|
||
|
cType == fVal[4].GetType();
|
||
|
if (!bStat)
|
||
|
{
|
||
|
*m_stream << _T("\n ") << a_str << _T(" : inconsistent result type (")
|
||
|
<< fVal[0].GetType() << _T(", ")
|
||
|
<< fVal[1].GetType() << _T(", ")
|
||
|
<< fVal[2].GetType() << _T(", ")
|
||
|
<< fVal[3].GetType() << _T(", ")
|
||
|
<< fVal[4].GetType() << _T(")");
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
if ((cType == 'c' || a_val.GetType() == 'c') && cType != a_val.GetType())
|
||
|
{
|
||
|
*m_stream << _T("\n ") << a_str << _T(" : Complex value sliced!");
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
// Compare the results
|
||
|
switch (cType)
|
||
|
{
|
||
|
case 'i':
|
||
|
case 'b':
|
||
|
case 's': bStat = (a_val == fVal[0] &&
|
||
|
a_val == fVal[1] &&
|
||
|
a_val == fVal[2] &&
|
||
|
a_val == fVal[3] &&
|
||
|
a_val == fVal[4]);
|
||
|
break;
|
||
|
|
||
|
// We need more attention for comaring float values due to floating point
|
||
|
// inaccuracies.
|
||
|
case 'f':
|
||
|
{
|
||
|
bStat = true;
|
||
|
int num = sizeof(fVal) / sizeof(Value);
|
||
|
for (int i = 0; i < num; ++i)
|
||
|
bStat &= (fabs(a_val.GetFloat() - fVal[i].GetFloat()) <= fabs(fVal[i].GetFloat()*0.0001));
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case 'c':
|
||
|
{
|
||
|
bStat = true;
|
||
|
int num = sizeof(fVal) / sizeof(Value);
|
||
|
for (int i = 0; i < num; ++i)
|
||
|
{
|
||
|
bStat &= (fabs(a_val.GetFloat() - fVal[i].GetFloat()) <= std::max((float_type)1e-15, fabs(fVal[i].GetFloat() * (float_type)0.0000001)));
|
||
|
bStat &= (fabs(a_val.GetImag() - fVal[i].GetImag()) <= std::max((float_type)1e-15, fabs(fVal[i].GetImag() * (float_type)0.0000001)));
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case 'm':
|
||
|
{
|
||
|
bStat = true;
|
||
|
int num = sizeof(fVal) / sizeof(Value);
|
||
|
|
||
|
for (int i = 0; i < num; ++i)
|
||
|
{
|
||
|
struct CheckArray
|
||
|
{
|
||
|
CheckArray()
|
||
|
{}
|
||
|
|
||
|
bool Check(IValue &v1, IValue &v2)
|
||
|
{
|
||
|
if (v1.GetType() != v2.GetType())
|
||
|
return false;
|
||
|
|
||
|
if (v1.GetRows() != v2.GetRows())
|
||
|
return false;
|
||
|
|
||
|
if (v1.IsMatrix())
|
||
|
{
|
||
|
for (int i = 0; i < v1.GetRows(); ++i)
|
||
|
{
|
||
|
for (int j = 0; j < v1.GetCols(); ++j)
|
||
|
{
|
||
|
if (!Check(v1.At(i, j), v2.At(i, j)))
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
return (fabs(v1.GetFloat() - v2.GetFloat()) <= std::max((float_type)1e-15, fabs(v1.GetFloat() * (float_type)0.0000001)));
|
||
|
}
|
||
|
}
|
||
|
} checker;
|
||
|
|
||
|
bStat = checker.Check(a_val, fVal[i]);
|
||
|
if (!bStat)
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
throw std::runtime_error("Parser return value has an unexpected typecode.");
|
||
|
}
|
||
|
|
||
|
iRet = (bStat == a_fPass) ? 0 : 1;
|
||
|
}
|
||
|
catch (ParserError &e)
|
||
|
{
|
||
|
*m_stream << _T("\n ") << a_str << _T(" : ") << e.GetMsg();
|
||
|
return 1;
|
||
|
}
|
||
|
catch (std::exception &e)
|
||
|
{
|
||
|
*m_stream << _T("\n ") << a_str << _T(" : ") << e.what() << _T("\n");
|
||
|
return 1;
|
||
|
}
|
||
|
catch (...)
|
||
|
{
|
||
|
*m_stream << _T("\n \"") << a_str << _T("\" : ") << _T("Unexpected Eception");
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
if (iRet)
|
||
|
{
|
||
|
*m_stream << _T("\n ") << a_str << _T(" : ") << _T("(at least one incorrect result ")
|
||
|
<< fVal[0] << _T(", ")
|
||
|
<< fVal[1] << _T(", ")
|
||
|
<< fVal[2] << _T(", ")
|
||
|
<< fVal[3] << _T(", ")
|
||
|
<< fVal[4] << _T("; expected=") << a_val << _T(")");
|
||
|
}
|
||
|
|
||
|
return iRet;
|
||
|
}
|
||
|
|
||
|
//---------------------------------------------------------------------------
|
||
|
/** \brief Internal error in test class Test is going to be aborted. */
|
||
|
void ParserTester::Abort() const
|
||
|
{
|
||
|
*m_stream << _T("\nTest failed (internal error in test class)") << endl;
|
||
|
while (!getchar());
|
||
|
exit(-1);
|
||
|
}
|
||
|
|
||
|
//---------------------------------------------------------------------------
|
||
|
void ParserTester::Assessment(int a_iNumErr) const
|
||
|
{
|
||
|
if (a_iNumErr == 0)
|
||
|
*m_stream << _T("passed") << endl;
|
||
|
else
|
||
|
*m_stream << _T("\n failed with ") << a_iNumErr << _T(" errors") << endl;
|
||
|
}
|
||
|
MUP_NAMESPACE_END
|