muParser API -  1.35
muParserTest.cpp
Go to the documentation of this file.
1 /*
2 
3  _____ __ _____________ _______ ______ ___________
4  / \| | \____ \__ \\_ __ \/ ___// __ \_ __ \
5  | Y Y \ | / |_> > __ \| | \/\___ \\ ___/| | \/
6  |__|_| /____/| __(____ /__| /____ >\___ >__|
7  \/ |__| \/ \/ \/
8  Copyright (C) 2004 - 2020 Ingo Berg
9 
10  Redistribution and use in source and binary forms, with or without modification, are permitted
11  provided that the following conditions are met:
12 
13  * Redistributions of source code must retain the above copyright notice, this list of
14  conditions and the following disclaimer.
15  * Redistributions in binary form must reproduce the above copyright notice, this list of
16  conditions and the following disclaimer in the documentation and/or other materials provided
17  with the distribution.
18 
19  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
20  IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
21  FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
22  CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23  DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
25  IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
26  OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28 
29 #include "muParserTest.h"
30 
31 #include <cstdio>
32 #include <cmath>
33 #include <iostream>
34 #include <limits>
35 
36 using namespace std;
37 
38 /** \file
39  \brief This file contains the implementation of parser test cases.
40 */
41 
42 namespace mu
43 {
44  namespace Test
45  {
46  int ParserTester::c_iCount = 0;
47 
48  //---------------------------------------------------------------------------------------------
49  ParserTester::ParserTester()
50  :m_vTestFun()
51  {
52  AddTest(&ParserTester::TestNames);
53  AddTest(&ParserTester::TestSyntax);
54  AddTest(&ParserTester::TestPostFix);
55  AddTest(&ParserTester::TestInfixOprt);
56  AddTest(&ParserTester::TestVarConst);
57  AddTest(&ParserTester::TestMultiArg);
58  AddTest(&ParserTester::TestExpression);
59  AddTest(&ParserTester::TestIfThenElse);
60  AddTest(&ParserTester::TestInterface);
61  AddTest(&ParserTester::TestBinOprt);
62  AddTest(&ParserTester::TestException);
63  AddTest(&ParserTester::TestStrArg);
64  AddTest(&ParserTester::TestBulkMode);
65 
66  ParserTester::c_iCount = 0;
67  }
68 
69  //---------------------------------------------------------------------------------------------
70  int ParserTester::IsHexVal(const char_type* a_szExpr, int* a_iPos, value_type* a_fVal)
71  {
72  if (a_szExpr[1] == 0 || (a_szExpr[0] != '0' || a_szExpr[1] != 'x'))
73  return 0;
74 
75  unsigned iVal(0);
76 
77  // New code based on streams for UNICODE compliance:
78  stringstream_type::pos_type nPos(0);
79  stringstream_type ss(a_szExpr + 2);
80  ss >> std::hex >> iVal;
81  nPos = ss.tellg();
82 
83  if (nPos == (stringstream_type::pos_type)0)
84  return 1;
85 
86  *a_iPos += (int)(2 + nPos);
87  *a_fVal = (value_type)iVal;
88  return 1;
89  }
90 
91  //---------------------------------------------------------------------------------------------
92  int ParserTester::TestInterface()
93  {
94  int iStat = 0;
95  mu::console() << _T("testing member functions...");
96 
97  // Test RemoveVar
98  value_type afVal[3] = { 1,2,3 };
99  Parser p;
100 
101  try
102  {
103  p.DefineVar(_T("a"), &afVal[0]);
104  p.DefineVar(_T("b"), &afVal[1]);
105  p.DefineVar(_T("c"), &afVal[2]);
106  p.SetExpr(_T("a+b+c"));
107  p.Eval();
108  }
109  catch (...)
110  {
111  iStat += 1; // this is not supposed to happen
112  }
113 
114  try
115  {
116  p.RemoveVar(_T("c"));
117  p.Eval();
118  iStat += 1; // not supposed to reach this, nonexisting variable "c" deleted...
119  }
120  catch (...)
121  {
122  // failure is expected...
123  }
124 
125  if (iStat == 0)
126  mu::console() << _T("passed") << endl;
127  else
128  mu::console() << _T("\n failed with ") << iStat << _T(" errors") << endl;
129 
130  return iStat;
131  }
132 
133  //---------------------------------------------------------------------------------------------
134  int ParserTester::TestStrArg()
135  {
136  int iStat = 0;
137  mu::console() << _T("testing string arguments...");
138 
139 
140  // from oss-fuzz: https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=23410
141  iStat += ThrowTest(_T(R"(6 - 6 ? 4 : "", ? 4 : "", ? 4 : "")"), ecUNEXPECTED_STR, true);
142  // variations:
143  iStat += ThrowTest(_T(R"(1 ? 4 : "")"), ecUNEXPECTED_STR, true);
144  iStat += ThrowTest(_T(R"(1 ? "" : 4)"), ecUNEXPECTED_STR, true);
145  iStat += ThrowTest(_T(R"(1 ? "" : "")"), ecUNEXPECTED_STR, true);
146 
147  // from oss-fuzz: https://oss-fuzz.com/testcase-detail/5106868061208576
148  iStat += ThrowTest(_T(R"("","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","",8)"), ecSTR_RESULT);
149  // variations:
150  iStat += ThrowTest(_T(R"("","",9)"), ecSTR_RESULT);
151 
152  iStat += EqnTest(_T("valueof(\"\")"), 123, true); // empty string arguments caused a crash
153  iStat += EqnTest(_T("valueof(\"aaa\")+valueof(\"bbb\") "), 246, true);
154  iStat += EqnTest(_T("2*(valueof(\"aaa\")-23)+valueof(\"bbb\")"), 323, true);
155 
156  // use in expressions with variables
157  iStat += EqnTest(_T("a*(atof(\"10\")-b)"), 8, true);
158  iStat += EqnTest(_T("a-(atof(\"10\")*b)"), -19, true);
159 
160  // string + numeric arguments
161  iStat += EqnTest(_T("strfun1(\"100\")"), 100, true);
162  iStat += EqnTest(_T("strfun2(\"100\",1)"), 101, true);
163  iStat += EqnTest(_T("strfun3(\"99\",1,2)"), 102, true);
164  iStat += EqnTest(_T("strfun4(\"99\",1,2,3)"), 105, true);
165  iStat += EqnTest(_T("strfun5(\"99\",1,2,3,4)"), 109, true);
166 
167  // string constants
168  iStat += EqnTest(_T("atof(str1)+atof(str2)"), 3.33, true);
169 
170  if (iStat == 0)
171  mu::console() << _T("passed") << endl;
172  else
173  mu::console() << _T("\n failed with ") << iStat << _T(" errors") << endl;
174 
175  return iStat;
176  }
177 
178  //---------------------------------------------------------------------------------------------
179  int ParserTester::TestBulkMode()
180  {
181  int iStat = 0;
182  mu::console() << _T("testing bulkmode...");
183 
184 #define EQN_TEST_BULK(EXPR, R1, R2, R3, R4, PASS) \
185  { \
186  double res[] = { R1, R2, R3, R4 }; \
187  iStat += EqnTestBulk(_T(EXPR), res, (PASS)); \
188  }
189 
190  // Bulk Variables for the test:
191  // a: 1,2,3,4
192  // b: 2,2,2,2
193  // c: 3,3,3,3
194  // d: 5,4,3,2
195  EQN_TEST_BULK("a", 1, 1, 1, 1, false)
196  EQN_TEST_BULK("a", 1, 2, 3, 4, true)
197  EQN_TEST_BULK("b=a", 1, 2, 3, 4, true)
198  EQN_TEST_BULK("b=a, b*10", 10, 20, 30, 40, true)
199  EQN_TEST_BULK("b=a, b*10, a", 1, 2, 3, 4, true)
200  EQN_TEST_BULK("a+b", 3, 4, 5, 6, true)
201  EQN_TEST_BULK("c*(a+b)", 9, 12, 15, 18, true)
202 #undef EQN_TEST_BULK
203 
204  if (iStat == 0)
205  mu::console() << _T("passed") << endl;
206  else
207  mu::console() << _T("\n failed with ") << iStat << _T(" errors") << endl;
208 
209  return iStat;
210  }
211 
212  //---------------------------------------------------------------------------------------------
213  int ParserTester::TestBinOprt()
214  {
215  int iStat = 0;
216  mu::console() << _T("testing binary operators...");
217 
218  // built in operators
219  // xor operator
220 
221  iStat += EqnTest(_T("a++b"), 3, true);
222  iStat += EqnTest(_T("a ++ b"), 3, true);
223  iStat += EqnTest(_T("1++2"), 3, true);
224  iStat += EqnTest(_T("1 ++ 2"), 3, true);
225  iStat += EqnTest(_T("a add b"), 3, true);
226  iStat += EqnTest(_T("1 add 2"), 3, true);
227  iStat += EqnTest(_T("a<b"), 1, true);
228  iStat += EqnTest(_T("b>a"), 1, true);
229  iStat += EqnTest(_T("a>a"), 0, true);
230  iStat += EqnTest(_T("a<a"), 0, true);
231  iStat += EqnTest(_T("a>a"), 0, true);
232  iStat += EqnTest(_T("a<=a"), 1, true);
233  iStat += EqnTest(_T("a<=b"), 1, true);
234  iStat += EqnTest(_T("b<=a"), 0, true);
235  iStat += EqnTest(_T("a>=a"), 1, true);
236  iStat += EqnTest(_T("b>=a"), 1, true);
237  iStat += EqnTest(_T("a>=b"), 0, true);
238 
239  // Test logical operators, especially if user defined "&" and the internal "&&" collide
240  iStat += EqnTest(_T("1 && 1"), 1, true);
241  iStat += EqnTest(_T("1 && 0"), 0, true);
242  iStat += EqnTest(_T("(a<b) && (b>a)"), 1, true);
243  iStat += EqnTest(_T("(a<b) && (a>b)"), 0, true);
244  //iStat += EqnTest(_T("12 and 255"), 12, true);
245  //iStat += EqnTest(_T("12 and 0"), 0, true);
246  iStat += EqnTest(_T("12 & 255"), 12, true);
247  iStat += EqnTest(_T("12 & 0"), 0, true);
248  iStat += EqnTest(_T("12&255"), 12, true);
249  iStat += EqnTest(_T("12&0"), 0, true);
250 
251  // Assignment operator
252  iStat += EqnTest(_T("a = b"), 2, true);
253  iStat += EqnTest(_T("a = sin(b)"), 0.909297, true);
254  iStat += EqnTest(_T("a = 1+sin(b)"), 1.909297, true);
255  iStat += EqnTest(_T("(a=b)*2"), 4, true);
256  iStat += EqnTest(_T("2*(a=b)"), 4, true);
257  iStat += EqnTest(_T("2*(a=b+1)"), 6, true);
258  iStat += EqnTest(_T("(a=b+1)*2"), 6, true);
259  iStat += EqnTest(_T("a=c, a*10"), 30, true);
260 
261  iStat += EqnTest(_T("2^2^3"), 256, true);
262  iStat += EqnTest(_T("1/2/3"), 1.0 / 6.0, true);
263 
264  // reference: http://www.wolframalpha.com/input/?i=3%2B4*2%2F%281-5%29^2^3
265  iStat += EqnTest(_T("3+4*2/(1-5)^2^3"), 3.0001220703125, true);
266 
267  // Test user defined binary operators
268  iStat += EqnTestInt(_T("1 | 2"), 3, true);
269  iStat += EqnTestInt(_T("1 || 2"), 1, true);
270  iStat += EqnTestInt(_T("123 & 456"), 72, true);
271  iStat += EqnTestInt(_T("(123 & 456) % 10"), 2, true);
272  iStat += EqnTestInt(_T("1 && 0"), 0, true);
273  iStat += EqnTestInt(_T("123 && 456"), 1, true);
274  iStat += EqnTestInt(_T("1 << 3"), 8, true);
275  iStat += EqnTestInt(_T("8 >> 3"), 1, true);
276  iStat += EqnTestInt(_T("9 / 4"), 2, true);
277  iStat += EqnTestInt(_T("9 % 4"), 1, true);
278  iStat += EqnTestInt(_T("if(5%2,1,0)"), 1, true);
279  iStat += EqnTestInt(_T("if(4%2,1,0)"), 0, true);
280  iStat += EqnTestInt(_T("-10+1"), -9, true);
281  iStat += EqnTestInt(_T("1+2*3"), 7, true);
282  iStat += EqnTestInt(_T("const1 != const2"), 1, true);
283  iStat += EqnTestInt(_T("const1 != const2"), 0, false);
284  iStat += EqnTestInt(_T("const1 == const2"), 0, true);
285  iStat += EqnTestInt(_T("const1 == 1"), 1, true);
286  iStat += EqnTestInt(_T("10*(const1 == 1)"), 10, true);
287  iStat += EqnTestInt(_T("2*(const1 | const2)"), 6, true);
288  iStat += EqnTestInt(_T("2*(const1 | const2)"), 7, false);
289  iStat += EqnTestInt(_T("const1 < const2"), 1, true);
290  iStat += EqnTestInt(_T("const2 > const1"), 1, true);
291  iStat += EqnTestInt(_T("const1 <= 1"), 1, true);
292  iStat += EqnTestInt(_T("const2 >= 2"), 1, true);
293  iStat += EqnTestInt(_T("2*(const1 + const2)"), 6, true);
294  iStat += EqnTestInt(_T("2*(const1 - const2)"), -2, true);
295  iStat += EqnTestInt(_T("a != b"), 1, true);
296  iStat += EqnTestInt(_T("a != b"), 0, false);
297  iStat += EqnTestInt(_T("a == b"), 0, true);
298  iStat += EqnTestInt(_T("a == 1"), 1, true);
299  iStat += EqnTestInt(_T("10*(a == 1)"), 10, true);
300  iStat += EqnTestInt(_T("2*(a | b)"), 6, true);
301  iStat += EqnTestInt(_T("2*(a | b)"), 7, false);
302  iStat += EqnTestInt(_T("a < b"), 1, true);
303  iStat += EqnTestInt(_T("b > a"), 1, true);
304  iStat += EqnTestInt(_T("a <= 1"), 1, true);
305  iStat += EqnTestInt(_T("b >= 2"), 1, true);
306  iStat += EqnTestInt(_T("2*(a + b)"), 6, true);
307  iStat += EqnTestInt(_T("2*(a - b)"), -2, true);
308  iStat += EqnTestInt(_T("a + (a << b)"), 5, true);
309  iStat += EqnTestInt(_T("-2^2"), -4, true);
310  iStat += EqnTestInt(_T("3--a"), 4, true);
311  iStat += EqnTestInt(_T("3+-3^2"), -6, true);
312 
313  // Test reading of hex values:
314  iStat += EqnTestInt(_T("0xff"), 255, true);
315  iStat += EqnTestInt(_T("10+0xff"), 265, true);
316  iStat += EqnTestInt(_T("0xff+10"), 265, true);
317  iStat += EqnTestInt(_T("10*0xff"), 2550, true);
318  iStat += EqnTestInt(_T("0xff*10"), 2550, true);
319  iStat += EqnTestInt(_T("10+0xff+1"), 266, true);
320  iStat += EqnTestInt(_T("1+0xff+10"), 266, true);
321 
322  // incorrect: '^' is yor here, not power
323  // iStat += EqnTestInt("-(1+2)^2", -9, true);
324  // iStat += EqnTestInt("-1^3", -1, true);
325 
326  // Test precedence
327  // a=1, b=2, c=3
328  iStat += EqnTestInt(_T("a + b * c"), 7, true);
329  iStat += EqnTestInt(_T("a * b + c"), 5, true);
330  iStat += EqnTestInt(_T("a<b && b>10"), 0, true);
331  iStat += EqnTestInt(_T("a<b && b<10"), 1, true);
332 
333  iStat += EqnTestInt(_T("a + b << c"), 17, true);
334  iStat += EqnTestInt(_T("a << b + c"), 7, true);
335  iStat += EqnTestInt(_T("c * b < a"), 0, true);
336  iStat += EqnTestInt(_T("c * b == 6 * a"), 1, true);
337  iStat += EqnTestInt(_T("2^2^3"), 256, true);
338 
339 
340  if (iStat == 0)
341  mu::console() << _T("passed") << endl;
342  else
343  mu::console() << _T("\n failed with ") << iStat << _T(" errors") << endl;
344 
345  return iStat;
346  }
347 
348  //---------------------------------------------------------------------------------------------
349  /** \brief Check muParser name restriction enforcement. */
350  int ParserTester::TestNames()
351  {
352  int iStat = 0,
353  iErr = 0;
354 
355  mu::console() << "testing name restriction enforcement...";
356 
357  Parser p;
358 
359 #define PARSER_THROWCHECK(DOMAIN, FAIL, EXPR, ARG) \
360  iErr = 0; \
361  ParserTester::c_iCount++; \
362  try \
363  { \
364  p.Define##DOMAIN(EXPR, ARG); \
365  iErr = (FAIL) ? 0 : 1; \
366  } \
367  catch(...) \
368  { \
369  iErr = (!FAIL) ? 0 : 1; \
370  } \
371  iStat += iErr;
372 
373  // constant names
374  PARSER_THROWCHECK(Const, false, _T("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"), 1)
375  PARSER_THROWCHECK(Const, false, _T("0a"), 1)
376  PARSER_THROWCHECK(Const, false, _T("9a"), 1)
377  PARSER_THROWCHECK(Const, false, _T("+a"), 1)
378  PARSER_THROWCHECK(Const, false, _T("-a"), 1)
379  PARSER_THROWCHECK(Const, false, _T("a-"), 1)
380  PARSER_THROWCHECK(Const, false, _T("a*"), 1)
381  PARSER_THROWCHECK(Const, false, _T("a?"), 1)
382  PARSER_THROWCHECK(Const, true, _T("a"), 1)
383  PARSER_THROWCHECK(Const, true, _T("a_min"), 1)
384  PARSER_THROWCHECK(Const, true, _T("a_min0"), 1)
385  PARSER_THROWCHECK(Const, true, _T("a_min9"), 1)
386 
387  // variable names
388  value_type a;
389  p.ClearConst();
390  PARSER_THROWCHECK(Var, false, _T("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"), &a);
391  PARSER_THROWCHECK(Var, false, _T("123abc"), &a)
392  PARSER_THROWCHECK(Var, false, _T("9a"), &a)
393  PARSER_THROWCHECK(Var, false, _T("0a"), &a)
394  PARSER_THROWCHECK(Var, false, _T("+a"), &a)
395  PARSER_THROWCHECK(Var, false, _T("-a"), &a)
396  PARSER_THROWCHECK(Var, false, _T("?a"), &a)
397  PARSER_THROWCHECK(Var, false, _T("!a"), &a)
398  PARSER_THROWCHECK(Var, false, _T("a+"), &a)
399  PARSER_THROWCHECK(Var, false, _T("a-"), &a)
400  PARSER_THROWCHECK(Var, false, _T("a*"), &a)
401  PARSER_THROWCHECK(Var, false, _T("a?"), &a)
402  PARSER_THROWCHECK(Var, true, _T("a"), &a)
403  PARSER_THROWCHECK(Var, true, _T("a_min"), &a)
404  PARSER_THROWCHECK(Var, true, _T("a_min0"), &a)
405  PARSER_THROWCHECK(Var, true, _T("a_min9"), &a)
406  PARSER_THROWCHECK(Var, false, _T("a_min9"), 0)
407 
408  // Postfix operators
409  // fail
410  PARSER_THROWCHECK(PostfixOprt, false, _T("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"), f1of1);
411  PARSER_THROWCHECK(PostfixOprt, false, _T("(k"), f1of1)
412  PARSER_THROWCHECK(PostfixOprt, false, _T("9+"), f1of1)
413  PARSER_THROWCHECK(PostfixOprt, false, _T("+"), 0)
414  // pass
415  PARSER_THROWCHECK(PostfixOprt, true, _T("-a"), f1of1)
416  PARSER_THROWCHECK(PostfixOprt, true, _T("?a"), f1of1)
417  PARSER_THROWCHECK(PostfixOprt, true, _T("_"), f1of1)
418  PARSER_THROWCHECK(PostfixOprt, true, _T("#"), f1of1)
419  PARSER_THROWCHECK(PostfixOprt, true, _T("&&"), f1of1)
420  PARSER_THROWCHECK(PostfixOprt, true, _T("||"), f1of1)
421  PARSER_THROWCHECK(PostfixOprt, true, _T("&"), f1of1)
422  PARSER_THROWCHECK(PostfixOprt, true, _T("|"), f1of1)
423  PARSER_THROWCHECK(PostfixOprt, true, _T("++"), f1of1)
424  PARSER_THROWCHECK(PostfixOprt, true, _T("--"), f1of1)
425  PARSER_THROWCHECK(PostfixOprt, true, _T("?>"), f1of1)
426  PARSER_THROWCHECK(PostfixOprt, true, _T("?<"), f1of1)
427  PARSER_THROWCHECK(PostfixOprt, true, _T("**"), f1of1)
428  PARSER_THROWCHECK(PostfixOprt, true, _T("xor"), f1of1)
429  PARSER_THROWCHECK(PostfixOprt, true, _T("and"), f1of1)
430  PARSER_THROWCHECK(PostfixOprt, true, _T("or"), f1of1)
431  PARSER_THROWCHECK(PostfixOprt, true, _T("not"), f1of1)
432  PARSER_THROWCHECK(PostfixOprt, true, _T("!"), f1of1)
433 
434  // Binary operator
435  // The following must fail with builtin operators activated
436  // p.EnableBuiltInOp(true); -> this is the default
437  p.ClearPostfixOprt();
438  PARSER_THROWCHECK(Oprt, false, _T("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"), f1of2);
439  PARSER_THROWCHECK(Oprt, false, _T("+"), f1of2)
440  PARSER_THROWCHECK(Oprt, false, _T("-"), f1of2)
441  PARSER_THROWCHECK(Oprt, false, _T("*"), f1of2)
442  PARSER_THROWCHECK(Oprt, false, _T("/"), f1of2)
443  PARSER_THROWCHECK(Oprt, false, _T("^"), f1of2)
444  PARSER_THROWCHECK(Oprt, false, _T("&&"), f1of2)
445  PARSER_THROWCHECK(Oprt, false, _T("||"), f1of2)
446 
447  // without activated built in operators it should work
448  p.EnableBuiltInOprt(false);
449  PARSER_THROWCHECK(Oprt, true, _T("+"), f1of2)
450  PARSER_THROWCHECK(Oprt, true, _T("-"), f1of2)
451  PARSER_THROWCHECK(Oprt, true, _T("*"), f1of2)
452  PARSER_THROWCHECK(Oprt, true, _T("/"), f1of2)
453  PARSER_THROWCHECK(Oprt, true, _T("^"), f1of2)
454  PARSER_THROWCHECK(Oprt, true, _T("&&"), f1of2)
455  PARSER_THROWCHECK(Oprt, true, _T("||"), f1of2)
456 #undef PARSER_THROWCHECK
457 
458  if (iStat == 0)
459  mu::console() << _T("passed") << endl;
460  else
461  mu::console() << _T("\n failed with ") << iStat << _T(" errors") << endl;
462 
463  return iStat;
464  }
465 
466  //---------------------------------------------------------------------------
467  int ParserTester::TestSyntax()
468  {
469  int iStat = 0;
470  mu::console() << _T("testing syntax engine...");
471 
472  iStat += ThrowTest(_T("1,"), ecUNEXPECTED_EOF); // incomplete hex definition
473  iStat += ThrowTest(_T("a,"), ecUNEXPECTED_EOF); // incomplete hex definition
474  iStat += ThrowTest(_T("sin(8),"), ecUNEXPECTED_EOF); // incomplete hex definition
475  iStat += ThrowTest(_T("(sin(8)),"), ecUNEXPECTED_EOF); // incomplete hex definition
476  iStat += ThrowTest(_T("a{m},"), ecUNEXPECTED_EOF); // incomplete hex definition
477 
478  iStat += EqnTest(_T("(1+ 2*a)"), 3, true); // Spaces within formula
479  iStat += EqnTest(_T("sqrt((4))"), 2, true); // Multiple brackets
480  iStat += EqnTest(_T("sqrt((2)+2)"), 2, true);// Multiple brackets
481  iStat += EqnTest(_T("sqrt(2+(2))"), 2, true);// Multiple brackets
482  iStat += EqnTest(_T("sqrt(a+(3))"), 2, true);// Multiple brackets
483  iStat += EqnTest(_T("sqrt((3)+a)"), 2, true);// Multiple brackets
484  iStat += EqnTest(_T("order(1,2)"), 1, true); // May not cause name collision with operator "or"
485  iStat += EqnTest(_T("(2+"), 0, false); // missing closing bracket
486  iStat += EqnTest(_T("2++4"), 0, false); // unexpected operator
487  iStat += EqnTest(_T("2+-4"), 0, false); // unexpected operator
488  iStat += EqnTest(_T("(2+)"), 0, false); // unexpected closing bracket
489  iStat += EqnTest(_T("--2"), 0, false); // double sign
490  iStat += EqnTest(_T("ksdfj"), 0, false); // unknown token
491  iStat += EqnTest(_T("()"), 0, false); // empty bracket without a function
492  iStat += EqnTest(_T("5+()"), 0, false); // empty bracket without a function
493  iStat += EqnTest(_T("sin(cos)"), 0, false); // unexpected function
494  iStat += EqnTest(_T("5t6"), 0, false); // unknown token
495  iStat += EqnTest(_T("5 t 6"), 0, false); // unknown token
496  iStat += EqnTest(_T("8*"), 0, false); // unexpected end of formula
497  iStat += EqnTest(_T(",3"), 0, false); // unexpected comma
498  iStat += EqnTest(_T("3,5"), 0, false); // unexpected comma
499  iStat += EqnTest(_T("sin(8,8)"), 0, false); // too many function args
500  iStat += EqnTest(_T("(7,8)"), 0, false); // too many function args
501  iStat += EqnTest(_T("sin)"), 0, false); // unexpected closing bracket
502  iStat += EqnTest(_T("a)"), 0, false); // unexpected closing bracket
503  iStat += EqnTest(_T("pi)"), 0, false); // unexpected closing bracket
504  iStat += EqnTest(_T("sin(())"), 0, false); // unexpected closing bracket
505  iStat += EqnTest(_T("sin()"), 0, false); // unexpected closing bracket
506 
507  if (iStat == 0)
508  mu::console() << _T("passed") << endl;
509  else
510  mu::console() << _T("\n failed with ") << iStat << _T(" errors") << endl;
511 
512  return iStat;
513  }
514 
515  //---------------------------------------------------------------------------
516  int ParserTester::TestVarConst()
517  {
518  int iStat = 0;
519  mu::console() << _T("testing variable/constant detection...");
520 
521  // Test if the result changes when a variable changes
522  iStat += EqnTestWithVarChange(_T("a"), 1, 1, 2, 2);
523  iStat += EqnTestWithVarChange(_T("2*a"), 2, 4, 3, 6);
524 
525  // distinguish constants with same basename
526  iStat += EqnTest(_T("const"), 1, true);
527  iStat += EqnTest(_T("const1"), 2, true);
528  iStat += EqnTest(_T("const2"), 3, true);
529  iStat += EqnTest(_T("2*const"), 2, true);
530  iStat += EqnTest(_T("2*const1"), 4, true);
531  iStat += EqnTest(_T("2*const2"), 6, true);
532  iStat += EqnTest(_T("2*const+1"), 3, true);
533  iStat += EqnTest(_T("2*const1+1"), 5, true);
534  iStat += EqnTest(_T("2*const2+1"), 7, true);
535  iStat += EqnTest(_T("const"), 0, false);
536  iStat += EqnTest(_T("const1"), 0, false);
537  iStat += EqnTest(_T("const2"), 0, false);
538 
539  // distinguish variables with same basename
540  iStat += EqnTest(_T("a"), 1, true);
541  iStat += EqnTest(_T("aa"), 2, true);
542  iStat += EqnTest(_T("2*a"), 2, true);
543  iStat += EqnTest(_T("2*aa"), 4, true);
544  iStat += EqnTest(_T("2*a-1"), 1, true);
545  iStat += EqnTest(_T("2*aa-1"), 3, true);
546 
547  // custom value recognition
548  iStat += EqnTest(_T("0xff"), 255, true);
549  iStat += EqnTest(_T("0x97 + 0xff"), 406, true);
550 
551  // Finally test querying of used variables
552  try
553  {
554  int idx;
555  mu::Parser p;
556  mu::value_type vVarVal[] = { 1, 2, 3, 4, 5 };
557  p.DefineVar(_T("a"), &vVarVal[0]);
558  p.DefineVar(_T("b"), &vVarVal[1]);
559  p.DefineVar(_T("c"), &vVarVal[2]);
560  p.DefineVar(_T("d"), &vVarVal[3]);
561  p.DefineVar(_T("e"), &vVarVal[4]);
562 
563  // Test lookup of defined variables
564  // 4 used variables
565  p.SetExpr(_T("a+b+c+d"));
566  mu::varmap_type UsedVar = p.GetUsedVar();
567  int iCount = (int)UsedVar.size();
568  if (iCount != 4)
569  throw false;
570 
571  // the next check will fail if the parser
572  // erroneously creates new variables internally
573  if (p.GetVar().size() != 5)
574  throw false;
575 
576  mu::varmap_type::const_iterator item = UsedVar.begin();
577  for (idx = 0; item != UsedVar.end(); ++item)
578  {
579  if (&vVarVal[idx++] != item->second)
580  throw false;
581  }
582 
583  // Test lookup of undefined variables
584  p.SetExpr(_T("undef1+undef2+undef3"));
585  UsedVar = p.GetUsedVar();
586  iCount = (int)UsedVar.size();
587  if (iCount != 3)
588  throw false;
589 
590  // the next check will fail if the parser
591  // erroneously creates new variables internally
592  if (p.GetVar().size() != 5)
593  throw false;
594 
595  for (item = UsedVar.begin(); item != UsedVar.end(); ++item)
596  {
597  if (item->second != 0)
598  throw false; // all pointers to undefined variables must be null
599  }
600 
601  // 1 used variables
602  p.SetExpr(_T("a+b"));
603  UsedVar = p.GetUsedVar();
604  iCount = (int)UsedVar.size();
605  if (iCount != 2) throw false;
606  item = UsedVar.begin();
607  for (idx = 0; item != UsedVar.end(); ++item)
608  if (&vVarVal[idx++] != item->second) throw false;
609 
610  }
611  catch (...)
612  {
613  iStat += 1;
614  }
615 
616  if (iStat == 0)
617  mu::console() << _T("passed") << endl;
618  else
619  mu::console() << _T("\n failed with ") << iStat << _T(" errors") << endl;
620 
621  return iStat;
622  }
623 
624  //---------------------------------------------------------------------------
625  int ParserTester::TestMultiArg()
626  {
627  int iStat = 0;
628  mu::console() << _T("testing multiarg functions...");
629 
630  // from oss-fzz.com: UNKNOWN READ; https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=23330#c1
631  iStat += ThrowTest(_T("6, +, +, +, +, +, +, +, +, +, +, +, +, +, +, 1, +, +, +, +, +, +, +, +, +, +, +, +, +, +, +, +, +, +, +, +, +, +, +, +, +, +, +, +, +, +, +, +"), ecUNEXPECTED_ARG_SEP, true);
632 
633  // Compound expressions
634  iStat += EqnTest(_T("1,2,3"), 3, true);
635  iStat += EqnTest(_T("a,b,c"), 3, true);
636  iStat += EqnTest(_T("a=10,b=20,c=a*b"), 200, true);
637  iStat += EqnTest(_T("1,\n2,\n3"), 3, true);
638  iStat += EqnTest(_T("a,\nb,\nc"), 3, true);
639  iStat += EqnTest(_T("a=10,\nb=20,\nc=a*b"), 200, true);
640  iStat += EqnTest(_T("1,\r\n2,\r\n3"), 3, true);
641  iStat += EqnTest(_T("a,\r\nb,\r\nc"), 3, true);
642  iStat += EqnTest(_T("a=10,\r\nb=20,\r\nc=a*b"), 200, true);
643 
644  // picking the right argument
645  iStat += EqnTest(_T("f1of1(1)"), 1, true);
646  iStat += EqnTest(_T("f1of2(1, 2)"), 1, true);
647  iStat += EqnTest(_T("f2of2(1, 2)"), 2, true);
648  iStat += EqnTest(_T("f1of3(1, 2, 3)"), 1, true);
649  iStat += EqnTest(_T("f2of3(1, 2, 3)"), 2, true);
650  iStat += EqnTest(_T("f3of3(1, 2, 3)"), 3, true);
651  iStat += EqnTest(_T("f1of4(1, 2, 3, 4)"), 1, true);
652  iStat += EqnTest(_T("f2of4(1, 2, 3, 4)"), 2, true);
653  iStat += EqnTest(_T("f3of4(1, 2, 3, 4)"), 3, true);
654  iStat += EqnTest(_T("f4of4(1, 2, 3, 4)"), 4, true);
655  iStat += EqnTest(_T("f1of5(1, 2, 3, 4, 5)"), 1, true);
656  iStat += EqnTest(_T("f2of5(1, 2, 3, 4, 5)"), 2, true);
657  iStat += EqnTest(_T("f3of5(1, 2, 3, 4, 5)"), 3, true);
658  iStat += EqnTest(_T("f4of5(1, 2, 3, 4, 5)"), 4, true);
659  iStat += EqnTest(_T("f5of5(1, 2, 3, 4, 5)"), 5, true);
660  // Too few arguments / Too many arguments
661  iStat += EqnTest(_T("1+ping()"), 11, true);
662  iStat += EqnTest(_T("ping()+1"), 11, true);
663  iStat += EqnTest(_T("2*ping()"), 20, true);
664  iStat += EqnTest(_T("ping()*2"), 20, true);
665  iStat += EqnTest(_T("ping(1,2)"), 0, false);
666  iStat += EqnTest(_T("1+ping(1,2)"), 0, false);
667  iStat += EqnTest(_T("f1of1(1,2)"), 0, false);
668  iStat += EqnTest(_T("f1of1()"), 0, false);
669  iStat += EqnTest(_T("f1of2(1, 2, 3)"), 0, false);
670  iStat += EqnTest(_T("f1of2(1)"), 0, false);
671  iStat += EqnTest(_T("f1of3(1, 2, 3, 4)"), 0, false);
672  iStat += EqnTest(_T("f1of3(1)"), 0, false);
673  iStat += EqnTest(_T("f1of4(1, 2, 3, 4, 5)"), 0, false);
674  iStat += EqnTest(_T("f1of4(1)"), 0, false);
675  iStat += EqnTest(_T("(1,2,3)"), 0, false);
676  iStat += EqnTest(_T("1,2,3"), 0, false);
677  iStat += EqnTest(_T("(1*a,2,3)"), 0, false);
678  iStat += EqnTest(_T("1,2*a,3"), 0, false);
679 
680  // correct calculation of arguments
681  iStat += EqnTest(_T("min(a, 1)"), 1, true);
682  iStat += EqnTest(_T("min(3*2, 1)"), 1, true);
683  iStat += EqnTest(_T("min(3*2, 1)"), 6, false);
684  iStat += EqnTest(_T("firstArg(2,3,4)"), 2, true);
685  iStat += EqnTest(_T("lastArg(2,3,4)"), 4, true);
686  iStat += EqnTest(_T("min(3*a+1, 1)"), 1, true);
687  iStat += EqnTest(_T("max(3*a+1, 1)"), 4, true);
688  iStat += EqnTest(_T("max(3*a+1, 1)*2"), 8, true);
689  iStat += EqnTest(_T("2*max(3*a+1, 1)+2"), 10, true);
690 
691  // functions with Variable argument count
692  iStat += EqnTest(_T("sum(a)"), 1, true);
693  iStat += EqnTest(_T("sum(1,2,3)"), 6, true);
694  iStat += EqnTest(_T("sum(a,b,c)"), 6, true);
695  iStat += EqnTest(_T("sum(1,-max(1,2),3)*2"), 4, true);
696  iStat += EqnTest(_T("2*sum(1,2,3)"), 12, true);
697  iStat += EqnTest(_T("2*sum(1,2,3)+2"), 14, true);
698  iStat += EqnTest(_T("2*sum(-1,2,3)+2"), 10, true);
699  iStat += EqnTest(_T("2*sum(-1,2,-(-a))+2"), 6, true);
700  iStat += EqnTest(_T("2*sum(-1,10,-a)+2"), 18, true);
701  iStat += EqnTest(_T("2*sum(1,2,3)*2"), 24, true);
702  iStat += EqnTest(_T("sum(1,-max(1,2),3)*2"), 4, true);
703  iStat += EqnTest(_T("sum(1*3, 4, a+2)"), 10, true);
704  iStat += EqnTest(_T("sum(1*3, 2*sum(1,2,2), a+2)"), 16, true);
705  iStat += EqnTest(_T("sum(1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2)"), 24, true);
706 
707  // some failures
708  iStat += EqnTest(_T("sum()"), 0, false);
709  iStat += EqnTest(_T("sum(,)"), 0, false);
710  iStat += EqnTest(_T("sum(1,2,)"), 0, false);
711  iStat += EqnTest(_T("sum(,1,2)"), 0, false);
712 
713  if (iStat == 0)
714  mu::console() << _T("passed") << endl;
715  else
716  mu::console() << _T("\n failed with ") << iStat << _T(" errors") << endl;
717 
718  return iStat;
719  }
720 
721 
722  //---------------------------------------------------------------------------
723  int ParserTester::TestInfixOprt()
724  {
725  int iStat(0);
726  mu::console() << "testing infix operators...";
727 
728  iStat += EqnTest(_T("+1"), +1, true);
729  iStat += EqnTest(_T("-(+1)"), -1, true);
730  iStat += EqnTest(_T("-(+1)*2"), -2, true);
731  iStat += EqnTest(_T("-(+2)*sqrt(4)"), -4, true);
732  iStat += EqnTest(_T("3-+a"), 2, true);
733  iStat += EqnTest(_T("+1*3"), 3, true);
734 
735  iStat += EqnTest(_T("-1"), -1, true);
736  iStat += EqnTest(_T("-(-1)"), 1, true);
737  iStat += EqnTest(_T("-(-1)*2"), 2, true);
738  iStat += EqnTest(_T("-(-2)*sqrt(4)"), 4, true);
739  iStat += EqnTest(_T("-_pi"), -MathImpl<double>::CONST_PI, true);
740  iStat += EqnTest(_T("-a"), -1, true);
741  iStat += EqnTest(_T("-(a)"), -1, true);
742  iStat += EqnTest(_T("-(-a)"), 1, true);
743  iStat += EqnTest(_T("-(-a)*2"), 2, true);
744  iStat += EqnTest(_T("-(8)"), -8, true);
745  iStat += EqnTest(_T("-8"), -8, true);
746  iStat += EqnTest(_T("-(2+1)"), -3, true);
747  iStat += EqnTest(_T("-(f1of1(1+2*3)+1*2)"), -9, true);
748  iStat += EqnTest(_T("-(-f1of1(1+2*3)+1*2)"), 5, true);
749  iStat += EqnTest(_T("-sin(8)"), -0.989358, true);
750  iStat += EqnTest(_T("3-(-a)"), 4, true);
751  iStat += EqnTest(_T("3--a"), 4, true);
752  iStat += EqnTest(_T("-1*3"), -3, true);
753 
754  // Postfix / infix priorities
755  iStat += EqnTest(_T("~2#"), 8, true);
756  iStat += EqnTest(_T("~f1of1(2)#"), 8, true);
757  iStat += EqnTest(_T("~(b)#"), 8, true);
758  iStat += EqnTest(_T("(~b)#"), 12, true);
759  iStat += EqnTest(_T("~(2#)"), 8, true);
760  iStat += EqnTest(_T("~(f1of1(2)#)"), 8, true);
761  //
762  iStat += EqnTest(_T("-2^2"), -4, true);
763  iStat += EqnTest(_T("-(a+b)^2"), -9, true);
764  iStat += EqnTest(_T("(-3)^2"), 9, true);
765  iStat += EqnTest(_T("-(-2^2)"), 4, true);
766  iStat += EqnTest(_T("3+-3^2"), -6, true);
767  // The following assumes use of sqr as postfix operator together
768  // with a sign operator of low priority:
769  iStat += EqnTest(_T("-2'"), -4, true);
770  iStat += EqnTest(_T("-(1+1)'"), -4, true);
771  iStat += EqnTest(_T("2+-(1+1)'"), -2, true);
772  iStat += EqnTest(_T("2+-2'"), -2, true);
773  // This is the classic behaviour of the infix sign operator (here: "$") which is
774  // now deprecated:
775  iStat += EqnTest(_T("$2^2"), 4, true);
776  iStat += EqnTest(_T("$(a+b)^2"), 9, true);
777  iStat += EqnTest(_T("($3)^2"), 9, true);
778  iStat += EqnTest(_T("$($2^2)"), -4, true);
779  iStat += EqnTest(_T("3+$3^2"), 12, true);
780 
781  // infix operators sharing the first few characters
782  iStat += EqnTest(_T("~ 123"), (value_type)123.0 + 2, true);
783  iStat += EqnTest(_T("~~ 123"), (value_type)123.0 + 2, true);
784 
785  if (iStat == 0)
786  mu::console() << _T("passed") << endl;
787  else
788  mu::console() << _T("\n failed with ") << iStat << _T(" errors") << endl;
789 
790  return iStat;
791  }
792 
793 
794  //---------------------------------------------------------------------------
795  int ParserTester::TestPostFix()
796  {
797  int iStat = 0;
798  mu::console() << _T("testing postfix operators...");
799 
800  // application
801  iStat += EqnTest(_T("3{m}+5"), 5.003, true);
802  iStat += EqnTest(_T("1000{m}"), 1, true);
803  iStat += EqnTest(_T("1000 {m}"), 1, true);
804  iStat += EqnTest(_T("(a){m}"), 1e-3, true);
805  iStat += EqnTest(_T("a{m}"), 1e-3, true);
806  iStat += EqnTest(_T("a {m}"), 1e-3, true);
807  iStat += EqnTest(_T("-(a){m}"), -1e-3, true);
808  iStat += EqnTest(_T("-2{m}"), -2e-3, true);
809  iStat += EqnTest(_T("-2 {m}"), -2e-3, true);
810  iStat += EqnTest(_T("f1of1(1000){m}"), 1, true);
811  iStat += EqnTest(_T("-f1of1(1000){m}"), -1, true);
812  iStat += EqnTest(_T("-f1of1(-1000){m}"), 1, true);
813  iStat += EqnTest(_T("f4of4(0,0,0,1000){m}"), 1, true);
814  iStat += EqnTest(_T("2+(a*1000){m}"), 3, true);
815 
816  // can postfix operators "m" und "meg" be told apart properly?
817  iStat += EqnTest(_T("2*3000meg+2"), 2 * 3e9 + 2, true);
818 
819  // some incorrect results
820  iStat += EqnTest(_T("1000{m}"), 0.1, false);
821  iStat += EqnTest(_T("(a){m}"), 2, false);
822  // failure due to syntax checking
823  iStat += ThrowTest(_T("0x"), ecUNASSIGNABLE_TOKEN); // incomplete hex definition
824  iStat += ThrowTest(_T("3+"), ecUNEXPECTED_EOF);
825  iStat += ThrowTest(_T("4 + {m}"), ecUNASSIGNABLE_TOKEN);
826  iStat += ThrowTest(_T("{m}4"), ecUNASSIGNABLE_TOKEN);
827  iStat += ThrowTest(_T("sin({m})"), ecUNASSIGNABLE_TOKEN);
828  iStat += ThrowTest(_T("{m} {m}"), ecUNASSIGNABLE_TOKEN);
829  iStat += ThrowTest(_T("{m}(8)"), ecUNASSIGNABLE_TOKEN);
830  iStat += ThrowTest(_T("4,{m}"), ecUNASSIGNABLE_TOKEN);
831  iStat += ThrowTest(_T("-{m}"), ecUNASSIGNABLE_TOKEN);
832  iStat += ThrowTest(_T("2(-{m})"), ecUNEXPECTED_PARENS);
833  iStat += ThrowTest(_T("2({m})"), ecUNEXPECTED_PARENS);
834 
835  iStat += ThrowTest(_T("multi*1.0"), ecUNASSIGNABLE_TOKEN);
836 
837  if (iStat == 0)
838  mu::console() << _T("passed") << endl;
839  else
840  mu::console() << _T("\n failed with ") << iStat << _T(" errors") << endl;
841 
842  return iStat;
843  }
844 
845  //---------------------------------------------------------------------------
846  int ParserTester::TestExpression()
847  {
848  int iStat = 0;
849  mu::console() << _T("testing expression samples...");
850 
851  value_type b = 2;
852 
853  iStat += EqnTest(_T("f0()"), 42, true);
854  iStat += EqnTest(_T("b^2"), 4, true);
855  iStat += EqnTest(_T("b^1"), 2, true);
856  iStat += EqnTest(_T("b^0"), 1, true);
857  iStat += EqnTest(_T("b^-1"), 0.5, true);
858 
859  // Optimization
860  iStat += EqnTest(_T("2*b*5"), 20, true);
861  iStat += EqnTest(_T("2*b*5 + 4*b"), 28, true);
862  iStat += EqnTest(_T("2*a/3"), 2.0 / 3.0, true);
863 
864  // Addition auf cmVARMUL
865  iStat += EqnTest(_T("3+b"), b + 3, true);
866  iStat += EqnTest(_T("b+3"), b + 3, true);
867  iStat += EqnTest(_T("b*3+2"), b * 3 + 2, true);
868  iStat += EqnTest(_T("3*b+2"), b * 3 + 2, true);
869  iStat += EqnTest(_T("2+b*3"), b * 3 + 2, true);
870  iStat += EqnTest(_T("2+3*b"), b * 3 + 2, true);
871  iStat += EqnTest(_T("b+3*b"), b + 3 * b, true);
872  iStat += EqnTest(_T("3*b+b"), b + 3 * b, true);
873 
874  iStat += EqnTest(_T("2+b*3+b"), 2 + b * 3 + b, true);
875  iStat += EqnTest(_T("b+2+b*3"), b + 2 + b * 3, true);
876 
877  iStat += EqnTest(_T("(2*b+1)*4"), (2 * b + 1) * 4, true);
878  iStat += EqnTest(_T("4*(2*b+1)"), (2 * b + 1) * 4, true);
879 
880  // operator precedences
881  iStat += EqnTest(_T("1+2-3*4/5^6"), 2.99923, true);
882  iStat += EqnTest(_T("1^2/3*4-5+6"), 2.33333333, true);
883  iStat += EqnTest(_T("1+2*3"), 7, true);
884  iStat += EqnTest(_T("1+2*3"), 7, true);
885  iStat += EqnTest(_T("(1+2)*3"), 9, true);
886  iStat += EqnTest(_T("(1+2)*(-3)"), -9, true);
887  iStat += EqnTest(_T("2/4"), 0.5, true);
888 
889  iStat += EqnTest(_T("exp(ln(7))"), 7, true);
890  iStat += EqnTest(_T("e^ln(7)"), 7, true);
891  iStat += EqnTest(_T("e^(ln(7))"), 7, true);
892  iStat += EqnTest(_T("(e^(ln(7)))"), 7, true);
893  iStat += EqnTest(_T("1-(e^(ln(7)))"), -6, true);
894  iStat += EqnTest(_T("2*(e^(ln(7)))"), 14, true);
895  iStat += EqnTest(_T("10^log(5)"), pow(10.0, log(5.0)), true);
896  iStat += EqnTest(_T("10^log10(5)"), 5, true);
897  iStat += EqnTest(_T("2^log2(4)"), 4, true);
898  iStat += EqnTest(_T("-(sin(0)+1)"), -1, true);
899  iStat += EqnTest(_T("-(2^1.1)"), -2.14354692, true);
900 
901  iStat += EqnTest(_T("(cos(2.41)/b)"), -0.372056, true);
902  iStat += EqnTest(_T("(1*(2*(3*(4*(5*(6*(a+b)))))))"), 2160, true);
903  iStat += EqnTest(_T("(1*(2*(3*(4*(5*(6*(7*(a+b))))))))"), 15120, true);
904  iStat += EqnTest(_T("(a/((((b+(((e*(((((pi*((((3.45*((pi+a)+pi))+b)+b)*a))+0.68)+e)+a)/a))+a)+b))+b)*a)-pi))"), 0.00377999, true);
905 
906  // long formula (Reference: Matlab)
907  iStat += EqnTest(
908  _T("(((-9))-e/(((((((pi-(((-7)+(-3)/4/e))))/(((-5))-2)-((pi+(-0))*(sqrt((e+e))*(-8))*(((-pi)+(-pi)-(-9)*(6*5))")
909  _T("/(-e)-e))/2)/((((sqrt(2/(-e)+6)-(4-2))+((5/(-2))/(1*(-pi)+3))/8)*pi*((pi/((-2)/(-6)*1*(-1))*(-6)+(-e)))))/")
910  _T("((e+(-2)+(-e)*((((-3)*9+(-e)))+(-9)))))))-((((e-7+(((5/pi-(3/1+pi)))))/e)/(-5))/(sqrt((((((1+(-7))))+((((-")
911  _T("e)*(-e)))-8))*(-5)/((-e)))*(-6)-((((((-2)-(-9)-(-e)-1)/3))))/(sqrt((8+(e-((-6))+(9*(-9))))*(((3+2-8))*(7+6")
912  _T("+(-5))+((0/(-e)*(-pi))+7)))+(((((-e)/e/e)+((-6)*5)*e+(3+(-5)/pi))))+pi))/sqrt((((9))+((((pi))-8+2))+pi))/e")
913  _T("*4)*((-5)/(((-pi))*(sqrt(e)))))-(((((((-e)*(e)-pi))/4+(pi)*(-9)))))))+(-pi)"), -12.23016549, true);
914 
915  // long formula (Reference: Matlab)
916  iStat += EqnTest(
917  _T("(atan(sin((((((((((((((((pi/cos((a/((((0.53-b)-pi)*e)/b))))+2.51)+a)-0.54)/0.98)+b)*b)+e)/a)+b)+a)+b)+pi)/e")
918  _T(")+a)))*2.77)"), -2.16995656, true);
919 
920  // long formula (Reference: Matlab)
921  iStat += EqnTest(_T("1+2-3*4/5^6*(2*(1-5+(3*7^9)*(4+6*7-3)))+12"), -7995810.09926, true);
922 
923  if (iStat == 0)
924  mu::console() << _T("passed") << endl;
925  else
926  mu::console() << _T("\n failed with ") << iStat << _T(" errors") << endl;
927 
928  return iStat;
929  }
930 
931 
932 
933  //---------------------------------------------------------------------------
934  int ParserTester::TestIfThenElse()
935  {
936  int iStat = 0;
937  mu::console() << _T("testing if-then-else operator...");
938 
939  // from oss-fuzz.com: https://oss-fuzz.com/testcase-detail/4777121158529024
940  iStat += ThrowTest(_T("3!=min(0?2>2,2>5,1:6)"), ecUNEXPECTED_ARG_SEP);
941 
942  // Test error detection
943  iStat += ThrowTest(_T(":3"), ecUNEXPECTED_CONDITIONAL);
944  iStat += ThrowTest(_T("? 1 : 2"), ecUNEXPECTED_CONDITIONAL);
945  iStat += ThrowTest(_T("(a<b) ? (b<c) ? 1 : 2"), ecMISSING_ELSE_CLAUSE);
946  iStat += ThrowTest(_T("(a<b) ? 1"), ecMISSING_ELSE_CLAUSE);
947  iStat += ThrowTest(_T("(a<b) ? a"), ecMISSING_ELSE_CLAUSE);
948  iStat += ThrowTest(_T("(a<b) ? a+b"), ecMISSING_ELSE_CLAUSE);
949  iStat += ThrowTest(_T("a : b"), ecMISPLACED_COLON);
950  iStat += ThrowTest(_T("1 : 2"), ecMISPLACED_COLON);
951  iStat += ThrowTest(_T("(1) ? 1 : 2 : 3"), ecMISPLACED_COLON);
952  iStat += ThrowTest(_T("(true) ? 1 : 2 : 3"), ecUNASSIGNABLE_TOKEN);
953 
954  // from oss-fzz.com: UNKNOWN READ; https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=22922#c1
955  iStat += ThrowTest(_T("1?2:0?(7:1)"), ecMISPLACED_COLON);
956 
957  // from oss-fuzz.com: https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=22938
958  iStat += ThrowTest(_T("sum(0?1,0,0:3)"), ecUNEXPECTED_ARG_SEP);
959  iStat += ThrowTest(_T("sum(0?(1,0,0):3)"), ecUNEXPECTED_ARG);
960  iStat += ThrowTest(_T("sum(2>3?2,4,2:4)"), ecUNEXPECTED_ARG_SEP);
961  iStat += ThrowTest(_T("sum(2>3?2,4,sin(2):4)"), ecUNEXPECTED_ARG_SEP);
962  iStat += ThrowTest(_T("sum(2>3?sin(2),4,2:4)"), ecUNEXPECTED_ARG_SEP);
963  iStat += ThrowTest(_T("sum(2>3?sin(a),4,2:4)"), ecUNEXPECTED_ARG_SEP);
964  iStat += ThrowTest(_T("sum(2>3?sin(2),4,2:4)"), ecUNEXPECTED_ARG_SEP);
965 
966  iStat += EqnTest(_T("1 ? 128 : 255"), 128, true);
967  iStat += EqnTest(_T("1<2 ? 128 : 255"), 128, true);
968  iStat += EqnTest(_T("a<b ? 128 : 255"), 128, true);
969  iStat += EqnTest(_T("(a<b) ? 128 : 255"), 128, true);
970  iStat += EqnTest(_T("(1) ? 10 : 11"), 10, true);
971  iStat += EqnTest(_T("(0) ? 10 : 11"), 11, true);
972  iStat += EqnTest(_T("(1) ? a+b : c+d"), 3, true);
973  iStat += EqnTest(_T("(0) ? a+b : c+d"), 1, true);
974  iStat += EqnTest(_T("(1) ? 0 : 1"), 0, true);
975  iStat += EqnTest(_T("(0) ? 0 : 1"), 1, true);
976  iStat += EqnTest(_T("(a<b) ? 10 : 11"), 10, true);
977  iStat += EqnTest(_T("(a>b) ? 10 : 11"), 11, true);
978  iStat += EqnTest(_T("(a<b) ? c : d"), 3, true);
979  iStat += EqnTest(_T("(a>b) ? c : d"), -2, true);
980 
981  iStat += EqnTest(_T("(a>b) ? 1 : 0"), 0, true);
982  iStat += EqnTest(_T("((a>b) ? 1 : 0) ? 1 : 2"), 2, true);
983  iStat += EqnTest(_T("((a>b) ? 1 : 0) ? 1 : sum((a>b) ? 1 : 2)"), 2, true);
984  iStat += EqnTest(_T("((a>b) ? 0 : 1) ? 1 : sum((a>b) ? 1 : 2)"), 1, true);
985 
986  iStat += EqnTest(_T("sum((a>b) ? 1 : 2)"), 2, true);
987  iStat += EqnTest(_T("sum((1) ? 1 : 2)"), 1, true);
988  iStat += EqnTest(_T("sum((a>b) ? 1 : 2, 100)"), 102, true);
989  iStat += EqnTest(_T("sum((1) ? 1 : 2, 100)"), 101, true);
990  iStat += EqnTest(_T("sum(3, (a>b) ? 3 : 10)"), 13, true);
991  iStat += EqnTest(_T("sum(3, (a<b) ? 3 : 10)"), 6, true);
992  iStat += EqnTest(_T("10*sum(3, (a>b) ? 3 : 10)"), 130, true);
993  iStat += EqnTest(_T("10*sum(3, (a<b) ? 3 : 10)"), 60, true);
994  iStat += EqnTest(_T("sum(3, (a>b) ? 3 : 10)*10"), 130, true);
995  iStat += EqnTest(_T("sum(3, (a<b) ? 3 : 10)*10"), 60, true);
996  iStat += EqnTest(_T("(a<b) ? sum(3, (a<b) ? 3 : 10)*10 : 99"), 60, true);
997  iStat += EqnTest(_T("(a>b) ? sum(3, (a<b) ? 3 : 10)*10 : 99"), 99, true);
998  iStat += EqnTest(_T("(a<b) ? sum(3, (a<b) ? 3 : 10,10,20)*10 : 99"), 360, true);
999  iStat += EqnTest(_T("(a>b) ? sum(3, (a<b) ? 3 : 10,10,20)*10 : 99"), 99, true);
1000  iStat += EqnTest(_T("(a>b) ? sum(3, (a<b) ? 3 : 10,10,20)*10 : sum(3, (a<b) ? 3 : 10)*10"), 60, true);
1001 
1002  // todo: also add for muParserX!
1003  iStat += EqnTest(_T("(a<b)&&(a<b) ? 128 : 255"), 128, true);
1004  iStat += EqnTest(_T("(a>b)&&(a<b) ? 128 : 255"), 255, true);
1005  iStat += EqnTest(_T("(1<2)&&(1<2) ? 128 : 255"), 128, true);
1006  iStat += EqnTest(_T("(1>2)&&(1<2) ? 128 : 255"), 255, true);
1007  iStat += EqnTest(_T("((1<2)&&(1<2)) ? 128 : 255"), 128, true);
1008  iStat += EqnTest(_T("((1>2)&&(1<2)) ? 128 : 255"), 255, true);
1009  iStat += EqnTest(_T("((a<b)&&(a<b)) ? 128 : 255"), 128, true);
1010  iStat += EqnTest(_T("((a>b)&&(a<b)) ? 128 : 255"), 255, true);
1011 
1012  iStat += EqnTest(_T("1>0 ? 1>2 ? 128 : 255 : 1>0 ? 32 : 64"), 255, true);
1013  iStat += EqnTest(_T("1>0 ? 1>2 ? 128 : 255 :(1>0 ? 32 : 64)"), 255, true);
1014  iStat += EqnTest(_T("1>0 ? 1>0 ? 128 : 255 : 1>2 ? 32 : 64"), 128, true);
1015  iStat += EqnTest(_T("1>0 ? 1>0 ? 128 : 255 :(1>2 ? 32 : 64)"), 128, true);
1016  iStat += EqnTest(_T("1>2 ? 1>2 ? 128 : 255 : 1>0 ? 32 : 64"), 32, true);
1017  iStat += EqnTest(_T("1>2 ? 1>0 ? 128 : 255 : 1>2 ? 32 : 64"), 64, true);
1018  iStat += EqnTest(_T("1>0 ? 50 : 1>0 ? 128 : 255"), 50, true);
1019  iStat += EqnTest(_T("1>0 ? 50 : (1>0 ? 128 : 255)"), 50, true);
1020  iStat += EqnTest(_T("1>0 ? 1>0 ? 128 : 255 : 50"), 128, true);
1021  iStat += EqnTest(_T("1>2 ? 1>2 ? 128 : 255 : 1>0 ? 32 : 1>2 ? 64 : 16"), 32, true);
1022  iStat += EqnTest(_T("1>2 ? 1>2 ? 128 : 255 : 1>0 ? 32 :(1>2 ? 64 : 16)"), 32, true);
1023  iStat += EqnTest(_T("1>0 ? 1>2 ? 128 : 255 : 1>0 ? 32 :1>2 ? 64 : 16"), 255, true);
1024  iStat += EqnTest(_T("1>0 ? 1>2 ? 128 : 255 : (1>0 ? 32 :1>2 ? 64 : 16)"), 255, true);
1025  iStat += EqnTest(_T("1 ? 0 ? 128 : 255 : 1 ? 32 : 64"), 255, true);
1026 
1027  // assignment operators
1028  iStat += EqnTest(_T("a= 0 ? 128 : 255, a"), 255, true);
1029  iStat += EqnTest(_T("a=((a>b)&&(a<b)) ? 128 : 255, a"), 255, true);
1030  iStat += EqnTest(_T("c=(a<b)&&(a<b) ? 128 : 255, c"), 128, true);
1031  iStat += EqnTest(_T("0 ? a=a+1 : 666, a"), 1, true);
1032  iStat += EqnTest(_T("1?a=10:a=20, a"), 10, true);
1033  iStat += EqnTest(_T("0?a=10:a=20, a"), 20, true);
1034  iStat += EqnTest(_T("0?a=sum(3,4):10, a"), 1, true); // a should not change its value due to lazy calculation
1035 
1036  iStat += EqnTest(_T("a=1?b=1?3:4:5, a"), 3, true);
1037  iStat += EqnTest(_T("a=1?b=1?3:4:5, b"), 3, true);
1038  iStat += EqnTest(_T("a=0?b=1?3:4:5, a"), 5, true);
1039  iStat += EqnTest(_T("a=0?b=1?3:4:5, b"), 2, true);
1040 
1041  iStat += EqnTest(_T("a=1?5:b=1?3:4, a"), 5, true);
1042  iStat += EqnTest(_T("a=1?5:b=1?3:4, b"), 2, true);
1043  iStat += EqnTest(_T("a=0?5:b=1?3:4, a"), 3, true);
1044  iStat += EqnTest(_T("a=0?5:b=1?3:4, b"), 3, true);
1045 
1046  if (iStat == 0)
1047  mu::console() << _T("passed") << endl;
1048  else
1049  mu::console() << _T("\n failed with ") << iStat << _T(" errors") << endl;
1050 
1051  return iStat;
1052  }
1053 
1054  //---------------------------------------------------------------------------
1055  int ParserTester::TestException()
1056  {
1057  int iStat = 0;
1058  mu::console() << _T("testing error codes...");
1059 
1060  iStat += ThrowTest(_T("3+"), ecUNEXPECTED_EOF);
1061  iStat += ThrowTest(_T("3+)"), ecUNEXPECTED_PARENS);
1062  iStat += ThrowTest(_T("()"), ecUNEXPECTED_PARENS);
1063  iStat += ThrowTest(_T("3+()"), ecUNEXPECTED_PARENS);
1064  iStat += ThrowTest(_T("sin(3,4)"), ecTOO_MANY_PARAMS);
1065  iStat += ThrowTest(_T("sin()"), ecTOO_FEW_PARAMS);
1066  iStat += ThrowTest(_T("(1+2"), ecMISSING_PARENS);
1067  iStat += ThrowTest(_T("sin(3)3"), ecUNEXPECTED_VAL);
1068  iStat += ThrowTest(_T("sin(3)xyz"), ecUNASSIGNABLE_TOKEN);
1069  iStat += ThrowTest(_T("sin(3)cos(3)"), ecUNEXPECTED_FUN);
1070  iStat += ThrowTest(_T("a+b+c=10"), ecUNEXPECTED_OPERATOR);
1071  iStat += ThrowTest(_T("a=b=3"), ecUNEXPECTED_OPERATOR);
1072 
1073  // functions without parameter
1074  iStat += ThrowTest(_T("3+ping(2)"), ecTOO_MANY_PARAMS);
1075  iStat += ThrowTest(_T("3+ping(a+2)"), ecTOO_MANY_PARAMS);
1076  iStat += ThrowTest(_T("3+ping(sin(a)+2)"), ecTOO_MANY_PARAMS);
1077  iStat += ThrowTest(_T("3+ping(1+sin(a))"), ecTOO_MANY_PARAMS);
1078 
1079  // String function related
1080  iStat += ThrowTest(_T("valueof(\"xxx\")"), 999, false);
1081  iStat += ThrowTest(_T("valueof()"), ecUNEXPECTED_PARENS);
1082  iStat += ThrowTest(_T("1+valueof(\"abc\""), ecMISSING_PARENS);
1083  iStat += ThrowTest(_T("valueof(\"abc\""), ecMISSING_PARENS);
1084  iStat += ThrowTest(_T("valueof(\"abc"), ecUNTERMINATED_STRING);
1085  iStat += ThrowTest(_T("valueof(\"abc\",3)"), ecTOO_MANY_PARAMS);
1086  iStat += ThrowTest(_T("valueof(3)"), ecSTRING_EXPECTED);
1087  iStat += ThrowTest(_T("sin(\"abc\")"), ecVAL_EXPECTED);
1088  iStat += ThrowTest(_T("valueof(\"\\\"abc\\\"\")"), 999, false);
1089  iStat += ThrowTest(_T("\"hello world\""), ecSTR_RESULT);
1090  iStat += ThrowTest(_T("(\"hello world\")"), ecSTR_RESULT);
1091  iStat += ThrowTest(_T("\"abcd\"+100"), ecSTR_RESULT);
1092  iStat += ThrowTest(_T("\"a\"+\"b\""), ecSTR_RESULT);
1093  iStat += ThrowTest(_T("strfun1(\"100\",3)"), ecTOO_MANY_PARAMS);
1094  iStat += ThrowTest(_T("strfun2(\"100\",3,5)"), ecTOO_MANY_PARAMS);
1095  iStat += ThrowTest(_T("strfun3(\"100\",3,5,6)"), ecTOO_MANY_PARAMS);
1096  iStat += ThrowTest(_T("strfun2(\"100\")"), ecTOO_FEW_PARAMS);
1097  iStat += ThrowTest(_T("strfun3(\"100\",6)"), ecTOO_FEW_PARAMS);
1098  iStat += ThrowTest(_T("strfun2(1,1)"), ecSTRING_EXPECTED);
1099  iStat += ThrowTest(_T("strfun2(a,1)"), ecSTRING_EXPECTED);
1100  iStat += ThrowTest(_T("strfun2(1,1,1)"), ecTOO_MANY_PARAMS);
1101  iStat += ThrowTest(_T("strfun2(a,1,1)"), ecTOO_MANY_PARAMS);
1102  iStat += ThrowTest(_T("strfun3(1,2,3)"), ecSTRING_EXPECTED);
1103  iStat += ThrowTest(_T("strfun3(1, \"100\",3)"), ecSTRING_EXPECTED);
1104  iStat += ThrowTest(_T("strfun3(\"1\", \"100\",3)"), ecVAL_EXPECTED);
1105  iStat += ThrowTest(_T("strfun3(\"1\", 3, \"100\")"), ecVAL_EXPECTED);
1106  iStat += ThrowTest(_T("strfun3(\"1\", \"100\", \"100\", \"100\")"), ecTOO_MANY_PARAMS);
1107 
1108  // assignment operator
1109  iStat += ThrowTest(_T("3=4"), ecUNEXPECTED_OPERATOR);
1110  iStat += ThrowTest(_T("sin(8)=4"), ecUNEXPECTED_OPERATOR);
1111  iStat += ThrowTest(_T("\"test\"=a"), ecSTR_RESULT);
1112 
1113  // <ibg 20090529>
1114  // this is now legal, for reference see:
1115  // https://sourceforge.net/forum/message.php?msg_id=7411373
1116  // iStat += ThrowTest( _T("sin=9"), ecUNEXPECTED_OPERATOR);
1117  // </ibg>
1118 
1119  iStat += ThrowTest(_T("(8)=5"), ecUNEXPECTED_OPERATOR);
1120  iStat += ThrowTest(_T("(a)=5"), ecUNEXPECTED_OPERATOR);
1121  iStat += ThrowTest(_T("a=\"tttt\""), ecOPRT_TYPE_CONFLICT);
1122 
1123  if (iStat == 0)
1124  mu::console() << _T("passed") << endl;
1125  else
1126  mu::console() << _T("\n failed with ") << iStat << _T(" errors") << endl;
1127 
1128  return iStat;
1129  }
1130 
1131 
1132  //---------------------------------------------------------------------------
1133  void ParserTester::AddTest(testfun_type a_pFun)
1134  {
1135  m_vTestFun.push_back(a_pFun);
1136  }
1137 
1138  //---------------------------------------------------------------------------
1139  int ParserTester::Run()
1140  {
1141  int iStat = 0;
1142  try
1143  {
1144  for (int i = 0; i < (int)m_vTestFun.size(); ++i)
1145  iStat += (this->*m_vTestFun[i])();
1146  }
1147  catch (Parser::exception_type& e)
1148  {
1149  mu::console() << "\n" << e.GetMsg() << endl;
1150  mu::console() << e.GetToken() << endl;
1151  Abort();
1152  }
1153  catch (std::exception& e)
1154  {
1155  mu::console() << e.what() << endl;
1156  Abort();
1157  }
1158  catch (...)
1159  {
1160  mu::console() << "Internal error";
1161  Abort();
1162  }
1163 
1164  if (iStat == 0)
1165  {
1166  mu::console() << "Test passed (" << ParserTester::c_iCount << " expressions)" << endl;
1167  }
1168  else
1169  {
1170  mu::console() << "Test failed with " << iStat
1171  << " errors (" << ParserTester::c_iCount
1172  << " expressions)" << endl;
1173  }
1174  ParserTester::c_iCount = 0;
1175  return iStat;
1176  }
1177 
1178 
1179  //---------------------------------------------------------------------------
1180  int ParserTester::ThrowTest(const string_type& a_str, int a_iErrc, bool a_expectedToFail)
1181  {
1182  ParserTester::c_iCount++;
1183 
1184  try
1185  {
1186  value_type fVal[] = { 1,1,1 };
1187  Parser p;
1188 
1189  p.DefineVar(_T("a"), &fVal[0]);
1190  p.DefineVar(_T("b"), &fVal[1]);
1191  p.DefineVar(_T("c"), &fVal[2]);
1192  p.DefinePostfixOprt(_T("{m}"), Milli);
1193  p.DefinePostfixOprt(_T("m"), Milli);
1194  p.DefineFun(_T("ping"), Ping);
1195  p.DefineFun(_T("valueof"), ValueOf);
1196  p.DefineFun(_T("strfun1"), StrFun1);
1197  p.DefineFun(_T("strfun2"), StrFun2);
1198  p.DefineFun(_T("strfun3"), StrFun3);
1199  p.DefineFun(_T("strfun4"), StrFun4);
1200  p.DefineFun(_T("strfun5"), StrFun5);
1201  p.SetExpr(a_str);
1202 // p.EnableDebugDump(1, 0);
1203  p.Eval();
1204  }
1205  catch (ParserError& e)
1206  {
1207  // output the formula in case of an failed test
1208  if (a_expectedToFail == false || (a_expectedToFail == true && a_iErrc != e.GetCode()))
1209  {
1210  mu::console() << _T("\n ")
1211  << _T("Expression: ") << a_str
1212  << _T(" Code:") << e.GetCode() << _T("(") << e.GetMsg() << _T(")")
1213  << _T(" Expected:") << a_iErrc;
1214  }
1215 
1216  return (a_iErrc == e.GetCode()) ? 0 : 1;
1217  }
1218 
1219  // if a_expectedToFail == false no exception is expected
1220  bool bRet((a_expectedToFail == false) ? 0 : 1);
1221  if (bRet == 1)
1222  {
1223  mu::console() << _T("\n ")
1224  << _T("Expression: ") << a_str
1225  << _T(" did evaluate; Expected error:") << a_iErrc;
1226  }
1227 
1228  return bRet;
1229  }
1230 
1231  //---------------------------------------------------------------------------
1232  /** \brief Evaluate a tet expression.
1233 
1234  \return 1 in case of a failure, 0 otherwise.
1235  */
1236  int ParserTester::EqnTestWithVarChange(const string_type& a_str,
1237  double a_fVar1,
1238  double a_fRes1,
1239  double a_fVar2,
1240  double a_fRes2)
1241  {
1242  ParserTester::c_iCount++;
1243 
1244  try
1245  {
1246  value_type fVal[2] = { -999, -999 }; // should be equal
1247 
1248  Parser p;
1249  value_type var = 0;
1250 
1251  // variable
1252  p.DefineVar(_T("a"), &var);
1253  p.SetExpr(a_str);
1254 
1255  var = a_fVar1;
1256  fVal[0] = p.Eval();
1257 
1258  var = a_fVar2;
1259  fVal[1] = p.Eval();
1260 
1261  if (fabs(a_fRes1 - fVal[0]) > 0.0000000001)
1262  throw std::runtime_error("incorrect result (first pass)");
1263 
1264  if (fabs(a_fRes2 - fVal[1]) > 0.0000000001)
1265  throw std::runtime_error("incorrect result (second pass)");
1266  }
1267  catch (Parser::exception_type& e)
1268  {
1269  mu::console() << _T("\n fail: ") << a_str.c_str() << _T(" (") << e.GetMsg() << _T(")");
1270  return 1;
1271  }
1272  catch (std::exception& e)
1273  {
1274  mu::console() << _T("\n fail: ") << a_str.c_str() << _T(" (") << e.what() << _T(")");
1275  return 1; // always return a failure since this exception is not expected
1276  }
1277  catch (...)
1278  {
1279  mu::console() << _T("\n fail: ") << a_str.c_str() << _T(" (unexpected exception)");
1280  return 1; // exceptions other than ParserException are not allowed
1281  }
1282 
1283  return 0;
1284  }
1285 
1286  //---------------------------------------------------------------------------
1287  /** \brief Evaluate a tet expression.
1288 
1289  \return 1 in case of a failure, 0 otherwise.
1290  */
1291  int ParserTester::EqnTest(const string_type& a_str, double a_fRes, bool a_fPass)
1292  {
1293  ParserTester::c_iCount++;
1294  int iRet(0);
1295  value_type fVal[5] = { -999, -998, -997, -996, -995 }; // initially should be different
1296 
1297  try
1298  {
1299  std::unique_ptr<Parser> p1;
1300  Parser p2, p3; // three parser objects
1301  // they will be used for testing copy and assignment operators
1302  // p1 is a pointer since i'm going to delete it in order to test if
1303  // parsers after copy construction still refer to members of it.
1304  // !! If this is the case this function will crash !!
1305 
1306  p1.reset(new mu::Parser());
1307  // Add constants
1308  p1->DefineConst(_T("pi"), MathImpl<value_type>::CONST_PI);
1309  p1->DefineConst(_T("e"), MathImpl<value_type>::CONST_E);
1310  p1->DefineConst(_T("const"), 1);
1311  p1->DefineConst(_T("const1"), 2);
1312  p1->DefineConst(_T("const2"), 3);
1313  // string constants
1314  p1->DefineStrConst(_T("str1"), _T("1.11"));
1315  p1->DefineStrConst(_T("str2"), _T("2.22"));
1316  // variables
1317  value_type vVarVal[] = { 1, 2, 3, -2 };
1318  p1->DefineVar(_T("a"), &vVarVal[0]);
1319  p1->DefineVar(_T("aa"), &vVarVal[1]);
1320  p1->DefineVar(_T("b"), &vVarVal[1]);
1321  p1->DefineVar(_T("c"), &vVarVal[2]);
1322  p1->DefineVar(_T("d"), &vVarVal[3]);
1323 
1324  // custom value ident functions
1325  p1->AddValIdent(&ParserTester::IsHexVal);
1326 
1327  // functions
1328  p1->DefineFun(_T("ping"), Ping);
1329  p1->DefineFun(_T("f0"), f0); // no parameter
1330  p1->DefineFun(_T("f1of1"), f1of1); // one parameter
1331  p1->DefineFun(_T("f1of2"), f1of2); // two parameter
1332  p1->DefineFun(_T("f2of2"), f2of2);
1333  p1->DefineFun(_T("f1of3"), f1of3); // three parameter
1334  p1->DefineFun(_T("f2of3"), f2of3);
1335  p1->DefineFun(_T("f3of3"), f3of3);
1336  p1->DefineFun(_T("f1of4"), f1of4); // four parameter
1337  p1->DefineFun(_T("f2of4"), f2of4);
1338  p1->DefineFun(_T("f3of4"), f3of4);
1339  p1->DefineFun(_T("f4of4"), f4of4);
1340  p1->DefineFun(_T("f1of5"), f1of5); // five parameter
1341  p1->DefineFun(_T("f2of5"), f2of5);
1342  p1->DefineFun(_T("f3of5"), f3of5);
1343  p1->DefineFun(_T("f4of5"), f4of5);
1344  p1->DefineFun(_T("f5of5"), f5of5);
1345 
1346  // binary operators
1347  p1->DefineOprt(_T("add"), add, 0);
1348  p1->DefineOprt(_T("++"), add, 0);
1349  p1->DefineOprt(_T("&"), land, prLAND);
1350 
1351  // sample functions
1352  p1->DefineFun(_T("min"), Min);
1353  p1->DefineFun(_T("max"), Max);
1354  p1->DefineFun(_T("sum"), Sum);
1355  p1->DefineFun(_T("valueof"), ValueOf);
1356  p1->DefineFun(_T("atof"), StrToFloat);
1357  p1->DefineFun(_T("strfun1"), StrFun1);
1358  p1->DefineFun(_T("strfun2"), StrFun2);
1359  p1->DefineFun(_T("strfun3"), StrFun3);
1360  p1->DefineFun(_T("strfun4"), StrFun4);
1361  p1->DefineFun(_T("strfun5"), StrFun5);
1362  p1->DefineFun(_T("lastArg"), LastArg);
1363  p1->DefineFun(_T("firstArg"), FirstArg);
1364  p1->DefineFun(_T("order"), FirstArg);
1365 
1366  // infix / postfix operator
1367  // Note: Identifiers used here do not have any meaning
1368  // they are mere placeholders to test certain features.
1369  p1->DefineInfixOprt(_T("$"), sign, prPOW + 1); // sign with high priority
1370  p1->DefineInfixOprt(_T("~"), plus2); // high priority
1371  p1->DefineInfixOprt(_T("~~"), plus2);
1372  p1->DefinePostfixOprt(_T("{m}"), Milli);
1373  p1->DefinePostfixOprt(_T("{M}"), Mega);
1374  p1->DefinePostfixOprt(_T("m"), Milli);
1375  p1->DefinePostfixOprt(_T("meg"), Mega);
1376  p1->DefinePostfixOprt(_T("#"), times3);
1377  p1->DefinePostfixOprt(_T("'"), sqr);
1378  p1->SetExpr(a_str);
1379 
1380  // Test bytecode integrity
1381  // String parsing and bytecode parsing must yield the same result
1382  fVal[0] = p1->Eval(); // result from stringparsing
1383  fVal[1] = p1->Eval(); // result from bytecode
1384  if (fVal[0] != fVal[1])
1385  throw Parser::exception_type(_T("Bytecode / string parsing mismatch."));
1386 
1387  // Test copy and assignment operators
1388  try
1389  {
1390  // Test copy constructor
1391  std::vector<mu::Parser> vParser;
1392  vParser.push_back(*(p1.get()));
1393  mu::Parser p4 = vParser[0]; // take parser from vector
1394 
1395  // destroy the originals from p2
1396  vParser.clear(); // delete the vector
1397  p1.reset(0);
1398 
1399  fVal[2] = p4.Eval();
1400 
1401  // Test assignment operator
1402  // additionally disable Optimizer this time
1403  mu::Parser p5;
1404  p5 = p4;
1405  p5.EnableOptimizer(false);
1406  fVal[3] = p5.Eval();
1407 
1408  // Test Eval function for multiple return values
1409  // use p2 since it has the optimizer enabled!
1410  int nNum;
1411  value_type* v = p4.Eval(nNum);
1412  fVal[4] = v[nNum - 1];
1413  }
1414  catch (std::exception& e)
1415  {
1416  mu::console() << _T("\n ") << e.what() << _T("\n");
1417  }
1418 
1419  // limited floating point accuracy requires the following test
1420  bool bCloseEnough(true);
1421  for (unsigned i = 0; i < sizeof(fVal) / sizeof(value_type); ++i)
1422  {
1423  bCloseEnough &= (fabs(a_fRes - fVal[i]) <= fabs(fVal[i] * 0.00001));
1424 
1425  // The tests equations never result in infinity, if they do thats a bug.
1426  // reference:
1427  // http://sourceforge.net/projects/muparser/forums/forum/462843/topic/5037825
1428 #ifdef _MSC_VER
1429 #pragma warning(push)
1430 #pragma warning(disable:4127)
1431 #endif
1432  if (std::numeric_limits<value_type>::has_infinity)
1433 #ifdef _MSC_VER
1434 #pragma warning(pop)
1435 #endif
1436  {
1437  bCloseEnough &= (fabs(fVal[i]) != numeric_limits<value_type>::infinity());
1438  }
1439  }
1440 
1441  iRet = ((bCloseEnough && a_fPass) || (!bCloseEnough && !a_fPass)) ? 0 : 1;
1442 
1443 
1444  if (iRet == 1)
1445  {
1446  mu::console() << _T("\n fail: ") << a_str.c_str()
1447  << _T(" (incorrect result; expected: ") << a_fRes
1448  << _T(" ;calculated: ") << fVal[0] << _T(",")
1449  << fVal[1] << _T(",")
1450  << fVal[2] << _T(",")
1451  << fVal[3] << _T(",")
1452  << fVal[4] << _T(").");
1453  }
1454  }
1455  catch (Parser::exception_type& e)
1456  {
1457  if (a_fPass)
1458  {
1459  if (fVal[0] != fVal[2] && fVal[0] != -999 && fVal[1] != -998)
1460  mu::console() << _T("\n fail: ") << a_str.c_str() << _T(" (copy construction)");
1461  else
1462  mu::console() << _T("\n fail: ") << a_str.c_str() << _T(" (") << e.GetMsg() << _T(")");
1463  return 1;
1464  }
1465  }
1466  catch (std::exception& e)
1467  {
1468  mu::console() << _T("\n fail: ") << a_str.c_str() << _T(" (") << e.what() << _T(")");
1469  return 1; // always return a failure since this exception is not expected
1470  }
1471  catch (...)
1472  {
1473  mu::console() << _T("\n fail: ") << a_str.c_str() << _T(" (unexpected exception)");
1474  return 1; // exceptions other than ParserException are not allowed
1475  }
1476 
1477  return iRet;
1478  }
1479 
1480  //---------------------------------------------------------------------------
1481  int ParserTester::EqnTestInt(const string_type& a_str, double a_fRes, bool a_fPass)
1482  {
1483  ParserTester::c_iCount++;
1484 
1485  value_type vVarVal[] = { 1, 2, 3 }; // variable values
1486  int iRet(0);
1487 
1488  try
1489  {
1490  value_type fVal[2] = { -99, -999 }; // results: initially should be different
1491  ParserInt p;
1492  p.DefineConst(_T("const1"), 1);
1493  p.DefineConst(_T("const2"), 2);
1494  p.DefineVar(_T("a"), &vVarVal[0]);
1495  p.DefineVar(_T("b"), &vVarVal[1]);
1496  p.DefineVar(_T("c"), &vVarVal[2]);
1497 
1498  p.SetExpr(a_str);
1499  fVal[0] = p.Eval(); // result from stringparsing
1500  fVal[1] = p.Eval(); // result from bytecode
1501 
1502  if (fVal[0] != fVal[1])
1503  throw Parser::exception_type(_T("Bytecode corrupt."));
1504 
1505  iRet = ((a_fRes == fVal[0] && a_fPass) ||
1506  (a_fRes != fVal[0] && !a_fPass)) ? 0 : 1;
1507  if (iRet == 1)
1508  {
1509  mu::console() << _T("\n fail: ") << a_str.c_str()
1510  << _T(" (incorrect result; expected: ") << a_fRes
1511  << _T(" ;calculated: ") << fVal[0] << _T(").");
1512  }
1513  }
1514  catch (Parser::exception_type& e)
1515  {
1516  if (a_fPass)
1517  {
1518  mu::console() << _T("\n fail: ") << e.GetExpr() << _T(" : ") << e.GetMsg();
1519  iRet = 1;
1520  }
1521  }
1522  catch (...)
1523  {
1524  mu::console() << _T("\n fail: ") << a_str.c_str() << _T(" (unexpected exception)");
1525  iRet = 1; // exceptions other than ParserException are not allowed
1526  }
1527 
1528  return iRet;
1529  }
1530 
1531  //---------------------------------------------------------------------------
1532  /** \brief Test an expression in Bulk Mode. */
1533  int ParserTester::EqnTestBulk(const string_type& a_str, double a_fRes[4], bool a_fPass)
1534  {
1535  ParserTester::c_iCount++;
1536 
1537  // Define Bulk Variables
1538  int nBulkSize = 4;
1539  value_type vVariableA[] = { 1, 2, 3, 4 }; // variable values
1540  value_type vVariableB[] = { 2, 2, 2, 2 }; // variable values
1541  value_type vVariableC[] = { 3, 3, 3, 3 }; // variable values
1542  value_type vResults[] = { 0, 0, 0, 0 }; // variable values
1543  int iRet(0);
1544 
1545  try
1546  {
1547  Parser p;
1548  p.DefineConst(_T("const1"), 1);
1549  p.DefineConst(_T("const2"), 2);
1550  p.DefineVar(_T("a"), vVariableA);
1551  p.DefineVar(_T("b"), vVariableB);
1552  p.DefineVar(_T("c"), vVariableC);
1553 
1554  p.SetExpr(a_str);
1555  p.Eval(vResults, nBulkSize);
1556 
1557  bool bCloseEnough(true);
1558  for (int i = 0; i < nBulkSize; ++i)
1559  {
1560  bCloseEnough &= (fabs(a_fRes[i] - vResults[i]) <= fabs(a_fRes[i] * 0.00001));
1561  }
1562 
1563  iRet = ((bCloseEnough && a_fPass) || (!bCloseEnough && !a_fPass)) ? 0 : 1;
1564  if (iRet == 1)
1565  {
1566  mu::console() << _T("\n fail: ") << a_str.c_str()
1567  << _T(" (incorrect result; expected: {") << a_fRes[0] << _T(",") << a_fRes[1] << _T(",") << a_fRes[2] << _T(",") << a_fRes[3] << _T("}")
1568  << _T(" ;calculated: ") << vResults[0] << _T(",") << vResults[1] << _T(",") << vResults[2] << _T(",") << vResults[3] << _T("}");
1569  }
1570  }
1571  catch (Parser::exception_type& e)
1572  {
1573  if (a_fPass)
1574  {
1575  mu::console() << _T("\n fail: ") << e.GetExpr() << _T(" : ") << e.GetMsg();
1576  iRet = 1;
1577  }
1578  }
1579  catch (...)
1580  {
1581  mu::console() << _T("\n fail: ") << a_str.c_str() << _T(" (unexpected exception)");
1582  iRet = 1; // exceptions other than ParserException are not allowed
1583  }
1584 
1585  return iRet;
1586  }
1587 
1588  //---------------------------------------------------------------------------
1589  /** \brief Internal error in test class Test is going to be aborted. */
1590  void ParserTester::Abort() const
1591  {
1592  mu::console() << _T("Test failed (internal error in test class)") << endl;
1593  while (!getchar());
1594  exit(-1);
1595  }
1596  } // namespace test
1597 } // namespace mu
void DefinePostfixOprt(const string_type &a_strFun, fun_type1 a_pOprt, bool a_bAllowOpt=true)
Add a user defined operator.
#define _T(x)
Activate this option in order to compile with OpenMP support.
Definition: muParserDef.h:69
const string_type & GetExpr() const
gets the expression related tp this error.
binary operators may only be applied to value items of the same type
Definition: muParserDef.h:245
An unexpected comma has been found. (Example: "1,23")
Definition: muParserDef.h:232
void DefineVar(const string_type &a_sName, value_type *a_fVar)
Add a user defined variable.
std::ostream & console()
Encapsulate cout.
Definition: muParserDef.h:115
Token can't be identified.
Definition: muParserDef.h:230
result is a string
Definition: muParserDef.h:246
An unexpected argument has been found.
Definition: muParserDef.h:233
std::map< string_type, value_type * > varmap_type
Type used for storing variables.
Definition: muParserDef.h:314
unterminated string constant. (Example: "3*valueof("hello)")
Definition: muParserDef.h:242
void ClearConst()
Clear all user defined constants.
STL namespace.
Unexpected function found. (Example: "sin(8)cos(9)")
Definition: muParserDef.h:241
power operator priority (highest)
Definition: muParserDef.h:217
value_type Eval() const
Calculate the result.
std::basic_stringstream< char_type, std::char_traits< char_type >, std::allocator< char_type > > stringstream_type
Typedef for easily using stringstream that respect the parser stringtype.
Definition: muParserDef.h:309
void EnableOptimizer(bool a_bIsOn=true)
Enable or disable the formula optimization feature.
An unexpected value token has been found.
Definition: muParserDef.h:234
void ClearPostfixOprt()
Clear all user defined postfix operators.
void DefineFun(const string_type &a_strName, T a_pFun, bool a_bAllowOpt=true)
Define a parser function without arguments.
Definition: muParserBase.h:134
Error class of the parser.
Definition: muParserError.h:68
const varmap_type & GetVar() const
Return a map containing the used variables only.
MUP_BASETYPE value_type
The numeric datatype used by the parser.
Definition: muParserDef.h:294
void SetExpr(const string_type &a_sExpr)
Set the formula.
Mathematical expressions parser.
Definition: muParserInt.h:49
Too many function parameters.
Definition: muParserDef.h:243
A numerical function has been called with a non value type of argument.
Definition: muParserDef.h:239
A template class for providing wrappers for essential math functions.
Namespace for mathematical applications.
Definition: muParser.cpp:46
Unexpected end of formula. (Example: "2+sin(")
Definition: muParserDef.h:231
Too few function parameters. (Example: "ite(1<2,2)")
Definition: muParserDef.h:244
A string function has been called with a different type of argument.
Definition: muParserDef.h:238
EErrorCodes GetCode() const
Return the error code.
void RemoveVar(const string_type &a_strVarName)
Remove a variable from internal storage.
string_type::value_type char_type
The character type used by the parser.
Definition: muParserDef.h:306
Mathematical expressions parser.
Definition: muParser.h:50
void DefineConst(const string_type &a_sName, value_type a_fVal)
Add a user defined constant.
void EnableBuiltInOprt(bool a_bIsOn=true)
Enable or disable the built in binary operators.
This file contains the parser test class.
Unexpected binary operator found.
Definition: muParserDef.h:229
MUP_STRING_TYPE string_type
The stringtype used by the parser.
Definition: muParserDef.h:300
Unexpected Parenthesis, opening or closing.
Definition: muParserDef.h:236
const string_type & GetToken() const
Return string related with this token (if available).
Missing parens. (Example: "3*sin(3")
Definition: muParserDef.h:240
const string_type & GetMsg() const
Returns the message string for this error.
A string has been found at an inapropriate position.
Definition: muParserDef.h:237
const varmap_type & GetUsedVar() const
Return a map containing the used variables only.