1 //---------------------------------------------------------------------------
2 //
3 // __________
4 // _____ __ __\______ \_____ _______ ______ ____ _______
5 // / \ | | \| ___/\__ \ \_ __ \/ ___/_/ __ \\_ __ \
6 // | Y Y \| | /| | / __ \_| | \/\___ \ \ ___/ | | \/
7 // |__|_| /|____/ |____| (____ /|__| /____ > \___ >|__|
8 // \/ \/ \/ \/
9 // (C) 2015 Ingo Berg
10 //
11 // example1.cpp - using the parser as a static library
12 //
13 //---------------------------------------------------------------------------
14
15 #include "muParserTest.h"
16
17 #if defined(_WIN32) && defined(_DEBUG)
18 #define _CRTDBG_MAP_ALLOC
19 #include <stdlib.h>
20 #include <crtdbg.h>
21 #define CREATE_LEAKAGE_REPORT
22 #endif
23
24 #if defined( USINGDLL ) && defined( _WIN32 )
25 #error This sample can be used only with STATIC builds of muParser (on win32)
26 #endif
27
28 /** \brief This macro will enable mathematical constants like M_PI. */
29 #define _USE_MATH_DEFINES
30
31 #include <cstdlib>
32 #include <cstring>
33 #include <cmath>
34 #include <string>
35 #include <iostream>
36 #include <locale>
37 #include <limits>
38 #include <ios>
39 #include <iomanip>
40 #include <numeric>
41
42 #include "muParser.h"
43
44 using namespace std;
45 using namespace mu;
46
47
48 #if defined(CREATE_LEAKAGE_REPORT)
49
50 // Dumping memory leaks in the destructor of the static guard
51 // guarantees i won't get false positives from the ParserErrorMsg
52 // class wich is a singleton with a static instance.
53 struct DumpLeaks
54 {
~DumpLeaksDumpLeaks55 ~DumpLeaks()
56 {
57 _CrtDumpMemoryLeaks();
58 }
59 } static LeakDumper;
60
61 #endif
62
63 // Forward declarations
64 void CalcBulk();
65
66 // Operator callback functions
Mega(value_type a_fVal)67 value_type Mega(value_type a_fVal) { return a_fVal * 1e6; }
Milli(value_type a_fVal)68 value_type Milli(value_type a_fVal) { return a_fVal / (value_type)1e3; }
Rnd(value_type v)69 value_type Rnd(value_type v) { return v*std::rand()/(value_type)(RAND_MAX+1.0); }
Not(value_type v)70 value_type Not(value_type v) { return v==0; }
Add(value_type v1,value_type v2)71 value_type Add(value_type v1, value_type v2) { return v1+v2; }
Mul(value_type v1,value_type v2)72 value_type Mul(value_type v1, value_type v2) { return v1*v2; }
73
74 //---------------------------------------------------------------------------
ThrowAnException(value_type)75 value_type ThrowAnException(value_type)
76 {
77 throw std::runtime_error("This function does throw an exception.");
78 }
79
80 //---------------------------------------------------------------------------
BulkFun1(int nBulkIdx,int nThreadIdx,value_type v1)81 value_type BulkFun1(int nBulkIdx, int nThreadIdx, value_type v1)
82 {
83 // Note: I'm just doing something with all three parameters to shut
84 // compiler warnings up!
85 return nBulkIdx + nThreadIdx + v1;
86 }
87
88 //---------------------------------------------------------------------------
Ping()89 value_type Ping()
90 {
91 mu::console() << "ping\n";
92 return 0;
93 }
94
95 //---------------------------------------------------------------------------
StrFun0(const char_type * szMsg)96 value_type StrFun0(const char_type *szMsg)
97 {
98 if (szMsg)
99 mu::console() << szMsg << std::endl;
100
101 return 999;
102 }
103
104 //---------------------------------------------------------------------------
StrFun2(const char_type * v1,value_type v2,value_type v3)105 value_type StrFun2(const char_type *v1, value_type v2,value_type v3)
106 {
107 mu::console() << v1 << std::endl;
108 return v2+v3;
109 }
110
111 //---------------------------------------------------------------------------
Debug(mu::value_type v1,mu::value_type v2)112 value_type Debug(mu::value_type v1, mu::value_type v2)
113 {
114 ParserBase::EnableDebugDump(v1!=0, v2!=0);
115 mu::console() << _T("Bytecode dumping ") << ((v1!=0) ? _T("active") : _T("inactive")) << _T("\n");
116 return 1;
117 }
118
119 //---------------------------------------------------------------------------
120 // Factory function for creating new parser variables
121 // This could as well be a function performing database queries.
AddVariable(const char_type * a_szName,void * a_pUserData)122 value_type* AddVariable(const char_type *a_szName, void *a_pUserData)
123 {
124 // I don't want dynamic allocation here, so i used this static buffer
125 // If you want dynamic allocation you must allocate all variables dynamically
126 // in order to delete them later on. Or you find other ways to keep track of
127 // variables that have been created implicitely.
128 static value_type afValBuf[100];
129 static int iVal = -1;
130
131 ++iVal;
132
133 mu::console() << _T("Generating new variable \"")
134 << a_szName << std::dec << _T("\" (slots left: ")
135 << 99-iVal << _T(")")
136 << _T(" User data pointer is:")
137 << std::hex << a_pUserData <<endl;
138 afValBuf[iVal] = 0;
139
140 if (iVal>=99)
141 throw mu::ParserError( _T("Variable buffer overflow.") );
142 else
143 return &afValBuf[iVal];
144 }
145
IsHexValue(const char_type * a_szExpr,int * a_iPos,value_type * a_fVal)146 int IsHexValue(const char_type *a_szExpr, int *a_iPos, value_type *a_fVal)
147 {
148 if (a_szExpr[1]==0 || (a_szExpr[0]!='0' || a_szExpr[1]!='x') )
149 return 0;
150
151 unsigned iVal(0);
152
153 // New code based on streams for UNICODE compliance:
154 stringstream_type::pos_type nPos(0);
155 stringstream_type ss(a_szExpr + 2);
156 ss >> std::hex >> iVal;
157 nPos = ss.tellg();
158
159 if (nPos==(stringstream_type::pos_type)0)
160 return 1;
161
162 *a_iPos += (int)(2 + nPos);
163 *a_fVal = (value_type)iVal;
164
165 return 1;
166 }
167
168 //---------------------------------------------------------------------------
Splash()169 void Splash()
170 {
171 mu::console() << _T(" __________ \n");
172 mu::console() << _T(" _____ __ __\\______ \\_____ _______ ______ ____ _______\n");
173 mu::console() << _T(" / \\ | | \\| ___/\\__ \\ \\_ __ \\/ ___/_/ __ \\\\_ __ \\ \n");
174 mu::console() << _T(" | Y Y \\| | /| | / __ \\_| | \\/\\___ \\ \\ ___/ | | \\/ \n");
175 mu::console() << _T(" |__|_| /|____/ |____| (____ /|__| /____ > \\___ >|__| \n");
176 mu::console() << _T(" \\/ \\/ \\/ \\/ \n");
177 mu::console() << _T(" Version ") << Parser().GetVersion(pviFULL) << _T("\n");
178 mu::console() << _T(" (C) 2015 Ingo Berg\n");
179 }
180
181 //---------------------------------------------------------------------------
SelfTest()182 value_type SelfTest()
183 {
184 mu::console() << _T( "-----------------------------------------------------------\n");
185 mu::console() << _T( "Running test suite:\n\n");
186
187 // Skip the self test if the value type is set to an integer type.
188 if (mu::TypeInfo<mu::value_type>::IsInteger())
189 {
190 mu::console() << _T( " Test skipped: integer data type are not compatible with the unit test!\n\n");
191 }
192 else
193 {
194 mu::Test::ParserTester pt;
195 pt.Run();
196 }
197
198 return 0;
199 }
200
201 //---------------------------------------------------------------------------
Help()202 value_type Help()
203 {
204 mu::console() << _T( "-----------------------------------------------------------\n");
205 mu::console() << _T( "Commands:\n\n");
206 mu::console() << _T( " list var - list parser variables\n");
207 mu::console() << _T( " list exprvar - list expression variables\n");
208 mu::console() << _T( " list const - list all numeric parser constants\n");
209 mu::console() << _T( " opt on - enable optimizer (default)\n");
210 mu::console() << _T( " opt off - disable optimizer\n");
211 mu::console() << _T( " locale de - switch to german locale\n");
212 mu::console() << _T( " locale en - switch to english locale\n");
213 mu::console() << _T( " locale reset - reset locale\n");
214 mu::console() << _T( " test bulk - test bulk mode\n");
215 mu::console() << _T( " quit - exits the parser\n");
216 mu::console() << _T( "\nConstants:\n\n");
217 mu::console() << _T( " \"_e\" 2.718281828459045235360287\n");
218 mu::console() << _T( " \"_pi\" 3.141592653589793238462643\n");
219 mu::console() << _T( "-----------------------------------------------------------\n");
220 return 0;
221 }
222
223 //---------------------------------------------------------------------------
224 /*
225 void CheckLocale()
226 {
227 // Local names:
228 // "C" - the classic C locale
229 // "de_DE" - not for Windows?
230 // "en_US" - not for Windows?
231 // "German_germany" - For MSVC8
232 try
233 {
234 std::locale loc("German_germany");
235 console() << _T("Locale settings:\n");
236 console() << _T(" Decimal point: '") << std::use_facet<numpunct<char_type> >(loc).decimal_point() << _T("'\n");
237 console() << _T(" Thousands sep: '") << std::use_facet<numpunct<char_type> >(loc).thousands_sep() << _T("'\n");
238 console() << _T(" Grouping: '") << std::use_facet<numpunct<char_type> >(loc).grouping() << _T("'\n");
239 console() << _T(" True is named: '") << std::use_facet<numpunct<char_type> >(loc).truename() << _T("'\n");
240 console() << _T(" False is named: '") << std::use_facet<numpunct<char_type> >(loc).falsename() << _T("'\n");
241 console() << _T("-----------------------------------------------------------\n");
242 }
243 catch(...)
244 {
245 console() << _T("Locale settings:\n");
246 console() << _T(" invalid locale name\n");
247 console() << _T("-----------------------------------------------------------\n");
248 }
249 }
250
251 //---------------------------------------------------------------------------
252 void CheckDiff()
253 {
254 mu::Parser parser;
255 value_type x = 1,
256 v1,
257 v2,
258 v3,
259 eps(pow(std::numeric_limits<value_type>::epsilon(), 0.2));
260 parser.DefineVar(_T("x"), &x);
261 parser.SetExpr(_T("_e^-x*sin(x)"));
262
263 v1 = parser.Diff(&x, 1),
264 v2 = parser.Diff(&x, 1, eps);
265 v3 = cos((value_type)1.0)/exp((value_type)1) - sin((value_type)1.0)/exp((value_type)1); //-0.110793765307;
266 mu::console() << parser.GetExpr() << _T("\n");
267 mu::console() << _T("v1 = ") << v1 << _T("; v1-v3 = ") << v1-v3 << _T("\n");
268 mu::console() << _T("v2 = ") << v2 << _T("; v2-v3 = ") << v2-v3 << _T("\n");
269 }
270 */
271
272 //---------------------------------------------------------------------------
ListVar(const mu::ParserBase & parser)273 void ListVar(const mu::ParserBase &parser)
274 {
275 // Query the used variables (must be done after calc)
276 mu::varmap_type variables = parser.GetVar();
277 if (!variables.size())
278 return;
279
280 cout << "\nParser variables:\n";
281 cout << "-----------------\n";
282 cout << "Number: " << (int)variables.size() << "\n";
283 varmap_type::const_iterator item = variables.begin();
284 for (; item!=variables.end(); ++item)
285 mu::console() << _T("Name: ") << item->first << _T(" Address: [0x") << item->second << _T("]\n");
286 }
287
288 //---------------------------------------------------------------------------
ListConst(const mu::ParserBase & parser)289 void ListConst(const mu::ParserBase &parser)
290 {
291 mu::console() << _T("\nParser constants:\n");
292 mu::console() << _T("-----------------\n");
293
294 mu::valmap_type cmap = parser.GetConst();
295 if (!cmap.size())
296 {
297 mu::console() << _T("Expression does not contain constants\n");
298 }
299 else
300 {
301 valmap_type::const_iterator item = cmap.begin();
302 for (; item!=cmap.end(); ++item)
303 mu::console() << _T(" ") << item->first << _T(" = ") << item->second << _T("\n");
304 }
305 }
306
307 //---------------------------------------------------------------------------
ListExprVar(const mu::ParserBase & parser)308 void ListExprVar(const mu::ParserBase &parser)
309 {
310 string_type sExpr = parser.GetExpr();
311 if (sExpr.length()==0)
312 {
313 cout << _T("Expression string is empty\n");
314 return;
315 }
316
317 // Query the used variables (must be done after calc)
318 mu::console() << _T("\nExpression variables:\n");
319 mu::console() << _T("---------------------\n");
320 mu::console() << _T("Expression: ") << parser.GetExpr() << _T("\n");
321
322 varmap_type variables = parser.GetUsedVar();
323 if (!variables.size())
324 {
325 mu::console() << _T("Expression does not contain variables\n");
326 }
327 else
328 {
329 mu::console() << _T("Number: ") << (int)variables.size() << _T("\n");
330 mu::varmap_type::const_iterator item = variables.begin();
331 for (; item!=variables.end(); ++item)
332 mu::console() << _T("Name: ") << item->first << _T(" Address: [0x") << item->second << _T("]\n");
333 }
334 }
335
336 //---------------------------------------------------------------------------
337 /** \brief Check for external keywords.
338 */
CheckKeywords(const mu::char_type * a_szLine,mu::Parser & a_Parser)339 int CheckKeywords(const mu::char_type *a_szLine, mu::Parser &a_Parser)
340 {
341 string_type sLine(a_szLine);
342
343 if ( sLine == _T("quit") )
344 {
345 return -1;
346 }
347 else if ( sLine == _T("list var") )
348 {
349 ListVar(a_Parser);
350 return 1;
351 }
352 else if ( sLine == _T("opt on") )
353 {
354 a_Parser.EnableOptimizer(true);
355 mu::console() << _T("Optimizer enabled\n");
356 return 1;
357 }
358 else if ( sLine == _T("opt off") )
359 {
360 a_Parser.EnableOptimizer(false);
361 mu::console() << _T("Optimizer disabled\n");
362 return 1;
363 }
364 else if ( sLine == _T("list const") )
365 {
366 ListConst(a_Parser);
367 return 1;
368 }
369 else if ( sLine == _T("list exprvar") )
370 {
371 ListExprVar(a_Parser);
372 return 1;
373 }
374 else if ( sLine == _T("locale de") )
375 {
376 mu::console() << _T("Setting german locale: ArgSep=';' DecSep=',' ThousandsSep='.'\n");
377 a_Parser.SetArgSep(';');
378 a_Parser.SetDecSep(',');
379 a_Parser.SetThousandsSep('.');
380 return 1;
381 }
382 else if ( sLine == _T("locale en") )
383 {
384 mu::console() << _T("Setting english locale: ArgSep=',' DecSep='.' ThousandsSep=''\n");
385 a_Parser.SetArgSep(',');
386 a_Parser.SetDecSep('.');
387 a_Parser.SetThousandsSep();
388 return 1;
389 }
390 else if ( sLine == _T("locale reset") )
391 {
392 mu::console() << _T("Resetting locale\n");
393 a_Parser.ResetLocale();
394 return 1;
395 }
396 else if ( sLine == _T("test bulk") )
397 {
398 mu::console() << _T("Testing bulk mode\n");
399 CalcBulk();
400 return 1;
401 }
402
403 return 0;
404 }
405
406 //---------------------------------------------------------------------------
CalcBulk()407 void CalcBulk()
408 {
409 const int nBulkSize = 200;
410 value_type *x = new value_type[nBulkSize];
411 value_type *y = new value_type[nBulkSize];
412 value_type *result = new value_type[nBulkSize];
413
414 try
415 {
416 for (int i=0; i<nBulkSize; ++i)
417 {
418 x[i] = i;
419 y[i] = (value_type)i/10;
420 }
421 mu::Parser parser;
422 parser.DefineVar(_T("x"), x);
423 parser.DefineVar(_T("y"), y);
424 parser.DefineFun(_T("fun1"), BulkFun1);
425 parser.SetExpr(_T("fun1(0)+x+y"));
426 parser.Eval(result, nBulkSize);
427
428 for (int i=0; i<nBulkSize; ++i)
429 {
430 mu::console() << _T("Eqn. ") << i << _T(": x=") << x[i] << _T("; y=") << y[i] << _T("; result=") << result[i] << _T("\n");
431 }
432 }
433 catch(...)
434 {
435 delete [] x;
436 delete [] y;
437 delete [] result;
438 throw;
439 }
440
441 delete [] x;
442 delete [] y;
443 delete [] result;
444 }
445
446 //---------------------------------------------------------------------------
Calc()447 void Calc()
448 {
449 mu::Parser parser;
450
451 // Change locale settings if necessary
452 // function argument separator: sum(2;3;4) vs. sum(2,3,4)
453 // decimal separator: 3,14 vs. 3.14
454 // thousands separator: 1000000 vs 1.000.000
455 //#define USE_GERMAN_LOCALE
456 #ifdef USE_GERMAN_LOCALE
457 parser.SetArgSep(';');
458 parser.SetDecSep(',');
459 parser.SetThousandsSep('.');
460 #else
461 // this is the default, so i it's commented:
462 //parser.SetArgSep(',');
463 //parser.SetDecSep('.');
464 //parser.SetThousandsSep('');
465 #endif
466
467 // Add some variables
468 value_type vVarVal[] = { 1, 2 }; // Values of the parser variables
469 parser.DefineVar(_T("a"), &vVarVal[0]); // Assign Variable names and bind them to the C++ variables
470 parser.DefineVar(_T("b"), &vVarVal[1]);
471 parser.DefineVar(_T("ft"), &vVarVal[1]);
472 parser.DefineStrConst(_T("sVar1"), _T("Sample string 1") );
473 parser.DefineStrConst(_T("sVar2"), _T("Sample string 2") );
474 parser.AddValIdent(IsHexValue);
475
476 // Add user defined unary operators
477 parser.DefinePostfixOprt(_T("M"), Mega);
478 parser.DefinePostfixOprt(_T("m"), Milli);
479 parser.DefineInfixOprt(_T("!"), Not);
480 parser.DefineFun(_T("strfun0"), StrFun0);
481 parser.DefineFun(_T("strfun2"), StrFun2);
482 parser.DefineFun(_T("ping"), Ping);
483 parser.DefineFun(_T("rnd"), Rnd); // Add an unoptimizeable function
484 parser.DefineFun(_T("throw"), ThrowAnException);
485
486
487 parser.DefineOprt(_T("add"), Add, 0);
488 parser.DefineOprt(_T("mul"), Mul, 1);
489
490 // These are service and debug functions
491 parser.DefineFun(_T("debug"), Debug);
492 parser.DefineFun(_T("selftest"), SelfTest);
493 parser.DefineFun(_T("help"), Help);
494
495 parser.DefinePostfixOprt(_T("{ft}"), Milli);
496 parser.DefinePostfixOprt(_T("ft"), Milli);
497 #ifdef _DEBUG
498 // parser.EnableDebugDump(1, 0);
499 #endif
500
501 // Define the variable factory
502 parser.SetVarFactory(AddVariable, &parser);
503
504 for(;;)
505 {
506 try
507 {
508 string_type sLine;
509 std::getline(mu::console_in(), sLine);
510
511 switch (CheckKeywords(sLine.c_str(), parser))
512 {
513 case 0: break;
514 case 1: continue;
515 case -1: return;
516 }
517
518 if (!sLine.length())
519 continue;
520
521 parser.SetExpr(sLine);
522 mu::console() << std::setprecision(12);
523
524 // There are multiple ways to retrieve the result...
525 // 1.) If you know there is only a single return value or in case you only need the last
526 // result of an expression consisting of comma separated subexpressions you can
527 // simply use:
528 mu::console() << _T("ans=") << parser.Eval() << _T("\n");
529
530 // 2.) As an alternative you can also retrieve multiple return values using this API:
531 int nNum = parser.GetNumResults();
532 if (nNum>1)
533 {
534 mu::console() << _T("Multiple return values detected! Complete list:\n");
535
536 // this is the hard way if you need to retrieve multiple subexpression
537 // results
538 value_type *v = parser.Eval(nNum);
539 mu::console() << std::setprecision(12);
540 for (int i=0; i<nNum; ++i)
541 {
542 mu::console() << v[i] << _T("\n");
543 }
544 }
545 }
546 catch(mu::Parser::exception_type &e)
547 {
548 mu::console() << _T("\nError:\n");
549 mu::console() << _T("------\n");
550 mu::console() << _T("Message: ") << e.GetMsg() << _T("\n");
551 mu::console() << _T("Expression: \"") << e.GetExpr() << _T("\"\n");
552 mu::console() << _T("Token: \"") << e.GetToken() << _T("\"\n");
553 mu::console() << _T("Position: ") << (int)e.GetPos() << _T("\n");
554 mu::console() << _T("Errc: ") << std::dec << e.GetCode() << _T("\n");
555 }
556 } // while running
557 }
558
559 //---------------------------------------------------------------------------
main(int,char **)560 int main(int, char**)
561 {
562 Splash();
563 SelfTest();
564 Help();
565
566 // CheckLocale();
567 // CheckDiff();
568
569 mu::console() << _T("Enter an expression or a command:\n");
570
571 try
572 {
573 Calc();
574 }
575 catch(Parser::exception_type &e)
576 {
577 // Only erros raised during the initialization will end up here
578 // formula related errors are treated in Calc()
579 console() << _T("Initialization error: ") << e.GetMsg() << endl;
580 console() << _T("aborting...") << endl;
581 string_type sBuf;
582 console_in() >> sBuf;
583 }
584 catch(std::exception & /*exc*/)
585 {
586 // there is no unicode compliant way to query exc.what()
587 // so i'll leave it for this example.
588 console() << _T("aborting...\n");
589 }
590
591 return 0;
592 }
593