📄 muparsertest.cpp
字号:
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);
// long formula (Reference: Matlab)
iStat += EqnTest(
"(((-9))-e/(((((((pi-(((-7)+(-3)/4/e))))/(((-5))-2)-((pi+(-0))*(sqrt((e+e))*(-8))*(((-pi)+(-pi)-(-9)*(6*5))"
"/(-e)-e))/2)/((((sqrt(2/(-e)+6)-(4-2))+((5/(-2))/(1*(-pi)+3))/8)*pi*((pi/((-2)/(-6)*1*(-1))*(-6)+(-e)))))/"
"((e+(-2)+(-e)*((((-3)*9+(-e)))+(-9)))))))-((((e-7+(((5/pi-(3/1+pi)))))/e)/(-5))/(sqrt((((((1+(-7))))+((((-"
"e)*(-e)))-8))*(-5)/((-e)))*(-6)-((((((-2)-(-9)-(-e)-1)/3))))/(sqrt((8+(e-((-6))+(9*(-9))))*(((3+2-8))*(7+6"
"+(-5))+((0/(-e)*(-pi))+7)))+(((((-e)/e/e)+((-6)*5)*e+(3+(-5)/pi))))+pi))/sqrt((((9))+((((pi))-8+2))+pi))/e"
"*4)*((-5)/(((-pi))*(sqrt(e)))))-(((((((-e)*(e)-pi))/4+(pi)*(-9)))))))+(-pi)", -12.23016549, true);
// long formula (Reference: Matlab)
iStat += EqnTest("1+2-3*4/5^6*(2*(1-5+(3*7^9)*(4+6*7-3)))+12", -7995810.09926, true);
// long formula (Reference: Matlab)
iStat += EqnTest(
"(atan(sin((((((((((((((((pi/cos((a/((((0.53-b)-pi)*e)/b))))+2.51)+a)-0.54)/0.98)+b)*b)+e)/a)+b)+a)+b)+pi)/e"
")+a)))*2.77)", -2.16995656, true);
if (iStat==0)
*m_stream << "passed" << endl;
else
*m_stream << "\n failed with " << iStat << " errors" << endl;
return iStat;
}
//---------------------------------------------------------------------------
int ParserTester::TestException()
{
int iStat = 0;
*m_stream << "testing error codes...";
iStat += ThrowTest("3+", ecUNEXPECTED_EOF);
iStat += ThrowTest("3+)", ecUNEXPECTED_PARENS);
iStat += ThrowTest("sin(3,4)", ecTOO_MANY_PARAMS);
iStat += ThrowTest("3,4", ecUNEXPECTED_COMMA);
iStat += ThrowTest("if(3)", 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);
// String function related
iStat += ThrowTest("valueof(\"xxx\")", 999, false);
iStat += ThrowTest("valueof()", ecUNEXPECTED_PARENS);
iStat += ThrowTest("valueof(\"abc\"", ecMISSING_PARENS);
iStat += ThrowTest("valueof(\"abc", ecUNTERMINATED_STRING);
iStat += ThrowTest("valueof(\"abc\",3)", ecUNEXPECTED_COMMA);
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);
// assignement operator
iStat += ThrowTest("3=4", ecUNEXPECTED_OPERATOR);
iStat += ThrowTest("sin(8)=4", ecUNEXPECTED_OPERATOR);
iStat += ThrowTest("\"test\"=a", ecUNEXPECTED_OPERATOR);
iStat += ThrowTest("sin=9", ecUNEXPECTED_OPERATOR);
iStat += ThrowTest("(8)=5", ecUNEXPECTED_OPERATOR);
iStat += ThrowTest("(a)=5", ecUNEXPECTED_OPERATOR);
iStat += ThrowTest("a=\"tttt\"", ecOPRT_TYPE_CONFLICT);
if (iStat==0)
*m_stream << "passed" << endl;
else
*m_stream << "\n failed with " << iStat << " errors" << endl;
return iStat;
}
//---------------------------------------------------------------------------
void ParserTester::AddTest(testfun_type a_pFun)
{
m_vTestFun.push_back(a_pFun);
}
//---------------------------------------------------------------------------
/** \brief Set the stream that takes the output of the test session. */
void ParserTester::SetStream(std::ostream *a_stream)
{
assert(a_stream);
m_stream = a_stream;
}
//---------------------------------------------------------------------------
void ParserTester::Run()
{
int iStat = 0;
try
{
for (int i=0; i<(int)m_vTestFun.size(); ++i)
iStat += (this->*m_vTestFun[i])();
}
catch(Parser::exception_type &e)
{
*m_stream << e.GetMsg() << endl;
*m_stream << e.GetToken() << endl;
Abort();
}
catch(std::exception &e)
{
*m_stream << e.what() << endl;
Abort();
}
catch(...)
{
*m_stream << "Internal error";
Abort();
}
if (iStat==0)
{
*m_stream << "Test passed (" << ParserTester::c_iCount << " expressions)" << endl;
}
else
{
*m_stream << "Test failed with " << iStat
<< " errors (" << ParserTester::c_iCount
<< " expressions)" << endl;
}
ParserTester::c_iCount = 0;
}
//---------------------------------------------------------------------------
int ParserTester::ThrowTest(const std::string &a_str, int a_iErrc, bool a_bFail)
{
double fRes(0);
ParserTester::c_iCount++;
try
{
double fVal=0;
Parser p;
p.DefineVar("a", &fVal);
p.DefineFun("valueof", ValueOf);
p.SetExpr(a_str);
fRes = p.Eval();
}
catch(Parser::exception_type &e)
{
// output the formula in case of an failed test
if (a_bFail==true && a_iErrc!=e.GetCode() )
{
cout << "\n "
<< "Expression: " << a_str
<< " Code:" << e.GetCode()
<< " Expected:" << a_iErrc;
}
return (a_iErrc==e.GetCode()) ? 0 : 1;
}
// if a_bFail==false no exception is expected
return (a_bFail==false) ? 0 : 1;
}
//---------------------------------------------------------------------------
/** \brief Evaluate a tet expression.
\return 1 in case of a failure, 0 otherwise.
*/
int ParserTester::EqnTest(const std::string &a_str, double a_fRes, bool a_fPass)
{
ParserTester::c_iCount++;
try
{
Parser *p1, p2, p3; // three parser objects
// they will be used for testing copy and assihnment operators
// p1 is a pointer since i'm going to delete it in order to test if
// parsers after copy construction still refer to members of it.
// !! If this is the case this function will crash !!
p1 = new mu::Parser();
// Add constants
p1->DefineConst("pi", (value_type)PARSER_CONST_PI);
p1->DefineConst("e", (value_type)PARSER_CONST_E);
p1->DefineConst("const", 1);
p1->DefineConst("const1", 2);
p1->DefineConst("const2", 3);
// variables
value_type 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]);
// functions
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);
// sample functions
p1->DefineFun("min", Min);
p1->DefineFun("max", Max);
p1->DefineFun("sum", Sum);
p1->DefineFun("valueof", ValueOf);
p1->DefineFun("atof", StrToFloat);
// infix / postfix operator
p1->DefineInfixOprt("~", plus2);
p1->DefinePostfixOprt("m", Milli);
p1->DefinePostfixOprt("#", times3);
p1->SetExpr(a_str);
// Test bytecode integrity
// String parsing and bytecode parsing must yield the same result
value_type fVal[4] = {-999, -998, -997, -996}; // initially should be different
fVal[0] = p1->Eval(); // result from stringparsing
fVal[1] = p1->Eval(); // result from bytecode
if (fVal[0]!=fVal[1])
throw Parser::exception_type("Bytecode corrupt.");
// Test copy and assignement operators
try
{
// Test copy constructor
std::vector<mu::Parser> vParser;
vParser.push_back(*p1);
mu::Parser p2 = vParser[0]; // take parser from vector
// destroy the originals from p2
vParser.clear(); // delete the vector
delete p1; // delete the original
p1 = 0;
fVal[2] = p2.Eval();
// Test assignement operator
// additionally disable Optimizer this time
mu::Parser p3;
p3 = p2;
p3.EnableOptimizer(false);
fVal[3] = p3.Eval();
}
catch(exception &e)
{
*m_stream << "\n " << e.what() << "\n";
}
// limited floating point accuracy requires the following test
bool bCloseEnough(true);
for (int i=0; i<4; ++i)
{
bCloseEnough &= (fabs(a_fRes-fVal[i]) <= fabs(fVal[i]*0.0001));
}
return ((bCloseEnough && a_fPass) || (!bCloseEnough && !a_fPass)) ? 0 : 1;
}
catch(Parser::exception_type &e)
{
if (a_fPass)
*m_stream << "\n " << e.GetExpr() << " : " << e.GetMsg();
}
catch(std::exception &e)
{
*m_stream << "\n " << a_str << " : " << e.what() << "\n";
// always return a failure since this exception is not expected
return 1;
}
catch(...)
{
// exceptions other than ParserException are not allowed
*m_stream << "\n \"" << a_str << "\" : " << "Unexpected Eception";
return 1;
}
return (a_fPass==false) ? 0 : 1;
}
//---------------------------------------------------------------------------
int ParserTester::EqnTestInt(const std::string &a_str, double a_fRes, bool a_fPass)
{
ParserTester::c_iCount++;
value_type vVarVal[] = {1, 2, 3}; // variable values
value_type fVal[2] = {-99, -999}; // results: initially should be different
try
{
ParserInt p;
p.DefineConst("const1", 1);
p.DefineConst("const2", 2);
p.DefineVar("a", &vVarVal[0]);
p.DefineVar("b", &vVarVal[1]);
p.DefineVar("c", &vVarVal[2]);
p.SetExpr(a_str);
fVal[0] = p.Eval(); // result from stringparsing
fVal[1] = p.Eval(); // result from bytecode
if (fVal[0]!=fVal[1])
throw Parser::exception_type("Bytecode corrupt.");
return ( (a_fRes==fVal[0] && a_fPass) ||
(a_fRes!=fVal[0] && !a_fPass) ) ? 0 : 1;
}
catch(Parser::exception_type &e)
{
if (a_fPass)
*m_stream << "\n " << e.GetExpr() << " : " << e.GetMsg();
}
catch(...)
{
return 1;
}
return (a_fPass==false) ? 0 : 1;
}
//---------------------------------------------------------------------------
/** \brief Internal error in test class Test is going to be aborted. */
void ParserTester::Abort() const
{
*m_stream << "Test failed (internal error in test class)" << endl;
while (!getchar());
exit(-1);
}
} // namespace test
} // namespace mu
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -