1 /*
2 * TINYEXPR - Tiny recursive descent parser and evaluation engine in C
3 *
4 * Copyright (c) 2015, 2016 Lewis Van Winkle
5 *
6 * http://CodePlea.com
7 *
8 * This software is provided 'as-is', without any express or implied
9 * warranty. In no event will the authors be held liable for any damages
10 * arising from the use of this software.
11 *
12 * Permission is granted to anyone to use this software for any purpose,
13 * including commercial applications, and to alter it and redistribute it
14 * freely, subject to the following restrictions:
15 *
16 * 1. The origin of this software must not be misrepresented; you must not
17 * claim that you wrote the original software. If you use this software
18 * in a product, an acknowledgement in the product documentation would be
19 * appreciated but is not required.
20 * 2. Altered source versions must be plainly marked as such, and must not be
21 * misrepresented as being the original software.
22 * 3. This notice may not be removed or altered from any source distribution.
23 */
24
25 #include "tinyexpr.h"
26 #include <stdio.h>
27 #include "minctest.h"
28
29
30 typedef struct {
31 const char *expr;
32 double answer;
33 } test_case;
34
35 typedef struct {
36 const char *expr1;
37 const char *expr2;
38 } test_equ;
39
40
41
test_results()42 void test_results() {
43 test_case cases[] = {
44 {"1", 1},
45 {"1 ", 1},
46 {"(1)", 1},
47
48 {"pi", 3.14159},
49 {"atan(1)*4 - pi", 0},
50 {"e", 2.71828},
51
52 {"2+1", 2+1},
53 {"(((2+(1))))", 2+1},
54 {"3+2", 3+2},
55
56 {"3+2+4", 3+2+4},
57 {"(3+2)+4", 3+2+4},
58 {"3+(2+4)", 3+2+4},
59 {"(3+2+4)", 3+2+4},
60
61 {"3*2*4", 3*2*4},
62 {"(3*2)*4", 3*2*4},
63 {"3*(2*4)", 3*2*4},
64 {"(3*2*4)", 3*2*4},
65
66 {"3-2-4", 3-2-4},
67 {"(3-2)-4", (3-2)-4},
68 {"3-(2-4)", 3-(2-4)},
69 {"(3-2-4)", 3-2-4},
70
71 {"3/2/4", 3.0/2.0/4.0},
72 {"(3/2)/4", (3.0/2.0)/4.0},
73 {"3/(2/4)", 3.0/(2.0/4.0)},
74 {"(3/2/4)", 3.0/2.0/4.0},
75
76 {"(3*2/4)", 3.0*2.0/4.0},
77 {"(3/2*4)", 3.0/2.0*4.0},
78 {"3*(2/4)", 3.0*(2.0/4.0)},
79
80 {"asin sin .5", 0.5},
81 {"sin asin .5", 0.5},
82 {"ln exp .5", 0.5},
83 {"exp ln .5", 0.5},
84
85 {"asin sin-.5", -0.5},
86 {"asin sin-0.5", -0.5},
87 {"asin sin -0.5", -0.5},
88 {"asin (sin -0.5)", -0.5},
89 {"asin (sin (-0.5))", -0.5},
90 {"asin sin (-0.5)", -0.5},
91 {"(asin sin (-0.5))", -0.5},
92
93 {"log10 1000", 3},
94 {"log10 1e3", 3},
95 {"log10 1000", 3},
96 {"log10 1e3", 3},
97 {"log10(1000)", 3},
98 {"log10(1e3)", 3},
99 {"log10 1.0e3", 3},
100 {"10^5*5e-5", 5},
101
102 #ifdef TE_NAT_LOG
103 {"log 1000", 6.9078},
104 {"log e", 1},
105 {"log (e^10)", 10},
106 #else
107 {"log 1000", 3},
108 #endif
109
110 {"ln (e^10)", 10},
111 {"100^.5+1", 11},
112 {"100 ^.5+1", 11},
113 {"100^+.5+1", 11},
114 {"100^--.5+1", 11},
115 {"100^---+-++---++-+-+-.5+1", 11},
116
117 {"100^-.5+1", 1.1},
118 {"100^---.5+1", 1.1},
119 {"100^+---.5+1", 1.1},
120 {"1e2^+---.5e0+1e0", 1.1},
121 {"--(1e2^(+(-(-(-.5e0))))+1e0)", 1.1},
122
123 {"sqrt 100 + 7", 17},
124 {"sqrt 100 * 7", 70},
125 {"sqrt (100 * 100)", 100},
126
127 {"1,2", 2},
128 {"1,2+1", 3},
129 {"1+1,2+2,2+1", 3},
130 {"1,2,3", 3},
131 {"(1,2),3", 3},
132 {"1,(2,3)", 3},
133 {"-(1,(2,3))", -3},
134
135 {"2^2", 4},
136 {"pow(2,2)", 4},
137
138 {"atan2(1,1)", 0.7854},
139 {"atan2(1,2)", 0.4636},
140 {"atan2(2,1)", 1.1071},
141 {"atan2(3,4)", 0.6435},
142 {"atan2(3+3,4*2)", 0.6435},
143 {"atan2(3+3,(4*2))", 0.6435},
144 {"atan2((3+3),4*2)", 0.6435},
145 {"atan2((3+3),(4*2))", 0.6435},
146
147 };
148
149
150 int i;
151 for (i = 0; i < sizeof(cases) / sizeof(test_case); ++i) {
152 const char *expr = cases[i].expr;
153 const double answer = cases[i].answer;
154
155 int err;
156 const double ev = te_interp(expr, &err);
157 lok(!err);
158 lfequal(ev, answer);
159
160 if (err) {
161 printf("FAILED: %s (%d)\n", expr, err);
162 }
163 }
164 }
165
166
test_syntax()167 void test_syntax() {
168 test_case errors[] = {
169 {"", 1},
170 {"1+", 2},
171 {"1)", 2},
172 {"(1", 2},
173 {"1**1", 3},
174 {"1*2(+4", 4},
175 {"1*2(1+4", 4},
176 {"a+5", 1},
177 {"A+5", 1},
178 {"Aa+5", 1},
179 {"1^^5", 3},
180 {"1**5", 3},
181 {"sin(cos5", 8},
182 };
183
184
185 int i;
186 for (i = 0; i < sizeof(errors) / sizeof(test_case); ++i) {
187 const char *expr = errors[i].expr;
188 const int e = errors[i].answer;
189
190 int err;
191 const double r = te_interp(expr, &err);
192 lequal(err, e);
193 lok(r != r);
194
195 te_expr *n = te_compile(expr, 0, 0, &err);
196 lequal(err, e);
197 lok(!n);
198
199 if (err != e) {
200 printf("FAILED: %s\n", expr);
201 }
202
203 const double k = te_interp(expr, 0);
204 lok(k != k);
205 }
206 }
207
208
test_nans()209 void test_nans() {
210
211 const char *nans[] = {
212 "0/0",
213 "1%0",
214 "1%(1%0)",
215 "(1%0)%1",
216 "fac(-1)",
217 "ncr(2, 4)",
218 "ncr(-2, 4)",
219 "ncr(2, -4)",
220 "npr(2, 4)",
221 "npr(-2, 4)",
222 "npr(2, -4)",
223 };
224
225 int i;
226 for (i = 0; i < sizeof(nans) / sizeof(const char *); ++i) {
227 const char *expr = nans[i];
228
229 int err;
230 const double r = te_interp(expr, &err);
231 lequal(err, 0);
232 lok(r != r);
233
234 te_expr *n = te_compile(expr, 0, 0, &err);
235 lok(n);
236 lequal(err, 0);
237 const double c = te_eval(n);
238 lok(c != c);
239 te_free(n);
240 }
241 }
242
243
test_infs()244 void test_infs() {
245
246 const char *infs[] = {
247 "1/0",
248 "log(0)",
249 "pow(2,10000000)",
250 "fac(300)",
251 "ncr(300,100)",
252 "ncr(300000,100)",
253 "ncr(300000,100)*8",
254 "npr(3,2)*ncr(300000,100)",
255 "npr(100,90)",
256 "npr(30,25)",
257 };
258
259 int i;
260 for (i = 0; i < sizeof(infs) / sizeof(const char *); ++i) {
261 const char *expr = infs[i];
262
263 int err;
264 const double r = te_interp(expr, &err);
265 lequal(err, 0);
266 lok(r == r + 1);
267
268 te_expr *n = te_compile(expr, 0, 0, &err);
269 lok(n);
270 lequal(err, 0);
271 const double c = te_eval(n);
272 lok(c == c + 1);
273 te_free(n);
274 }
275 }
276
277
test_variables()278 void test_variables() {
279
280 double x, y, test;
281 te_variable lookup[] = {{"x", &x}, {"y", &y}, {"te_st", &test}};
282
283 int err;
284
285 te_expr *expr1 = te_compile("cos x + sin y", lookup, 2, &err);
286 lok(expr1);
287 lok(!err);
288
289 te_expr *expr2 = te_compile("x+x+x-y", lookup, 2, &err);
290 lok(expr2);
291 lok(!err);
292
293 te_expr *expr3 = te_compile("x*y^3", lookup, 2, &err);
294 lok(expr3);
295 lok(!err);
296
297 te_expr *expr4 = te_compile("te_st+5", lookup, 3, &err);
298 lok(expr4);
299 lok(!err);
300
301 for (y = 2; y < 3; ++y) {
302 for (x = 0; x < 5; ++x) {
303 double ev;
304
305 ev = te_eval(expr1);
306 lfequal(ev, cos(x) + sin(y));
307
308 ev = te_eval(expr2);
309 lfequal(ev, x+x+x-y);
310
311 ev = te_eval(expr3);
312 lfequal(ev, x*y*y*y);
313
314 test = x;
315 ev = te_eval(expr4);
316 lfequal(ev, x+5);
317 }
318 }
319
320 te_free(expr1);
321 te_free(expr2);
322 te_free(expr3);
323 te_free(expr4);
324
325
326
327 te_expr *expr5 = te_compile("xx*y^3", lookup, 2, &err);
328 lok(!expr5);
329 lok(err);
330
331 te_expr *expr6 = te_compile("tes", lookup, 3, &err);
332 lok(!expr6);
333 lok(err);
334
335 te_expr *expr7 = te_compile("sinn x", lookup, 2, &err);
336 lok(!expr7);
337 lok(err);
338
339 te_expr *expr8 = te_compile("si x", lookup, 2, &err);
340 lok(!expr8);
341 lok(err);
342 }
343
344
345
346 #define cross_check(a, b) do {\
347 if ((b)!=(b)) break;\
348 expr = te_compile((a), lookup, 2, &err);\
349 lfequal(te_eval(expr), (b));\
350 lok(!err);\
351 te_free(expr);\
352 }while(0)
353
test_functions()354 void test_functions() {
355
356 double x, y;
357 te_variable lookup[] = {{"x", &x}, {"y", &y}};
358
359 int err;
360 te_expr *expr;
361
362 for (x = -5; x < 5; x += .2) {
363 cross_check("abs x", fabs(x));
364 cross_check("acos x", acos(x));
365 cross_check("asin x", asin(x));
366 cross_check("atan x", atan(x));
367 cross_check("ceil x", ceil(x));
368 cross_check("cos x", cos(x));
369 cross_check("cosh x", cosh(x));
370 cross_check("exp x", exp(x));
371 cross_check("floor x", floor(x));
372 cross_check("ln x", log(x));
373 cross_check("log10 x", log10(x));
374 cross_check("sin x", sin(x));
375 cross_check("sinh x", sinh(x));
376 cross_check("sqrt x", sqrt(x));
377 cross_check("tan x", tan(x));
378 cross_check("tanh x", tanh(x));
379
380 for (y = -2; y < 2; y += .2) {
381 if (fabs(x) < 0.01) break;
382 cross_check("atan2(x,y)", atan2(x, y));
383 cross_check("pow(x,y)", pow(x, y));
384 }
385 }
386 }
387
388
sum0()389 double sum0() {
390 return 6;
391 }
sum1(double a)392 double sum1(double a) {
393 return a * 2;
394 }
sum2(double a,double b)395 double sum2(double a, double b) {
396 return a + b;
397 }
sum3(double a,double b,double c)398 double sum3(double a, double b, double c) {
399 return a + b + c;
400 }
sum4(double a,double b,double c,double d)401 double sum4(double a, double b, double c, double d) {
402 return a + b + c + d;
403 }
sum5(double a,double b,double c,double d,double e)404 double sum5(double a, double b, double c, double d, double e) {
405 return a + b + c + d + e;
406 }
sum6(double a,double b,double c,double d,double e,double f)407 double sum6(double a, double b, double c, double d, double e, double f) {
408 return a + b + c + d + e + f;
409 }
sum7(double a,double b,double c,double d,double e,double f,double g)410 double sum7(double a, double b, double c, double d, double e, double f, double g) {
411 return a + b + c + d + e + f + g;
412 }
413
414
test_dynamic()415 void test_dynamic() {
416
417 double x, f;
418 te_variable lookup[] = {
419 {"x", &x},
420 {"f", &f},
421 {"sum0", sum0, TE_FUNCTION0},
422 {"sum1", sum1, TE_FUNCTION1},
423 {"sum2", sum2, TE_FUNCTION2},
424 {"sum3", sum3, TE_FUNCTION3},
425 {"sum4", sum4, TE_FUNCTION4},
426 {"sum5", sum5, TE_FUNCTION5},
427 {"sum6", sum6, TE_FUNCTION6},
428 {"sum7", sum7, TE_FUNCTION7},
429 };
430
431 test_case cases[] = {
432 {"x", 2},
433 {"f+x", 7},
434 {"x+x", 4},
435 {"x+f", 7},
436 {"f+f", 10},
437 {"f+sum0", 11},
438 {"sum0+sum0", 12},
439 {"sum0()+sum0", 12},
440 {"sum0+sum0()", 12},
441 {"sum0()+(0)+sum0()", 12},
442 {"sum1 sum0", 12},
443 {"sum1(sum0)", 12},
444 {"sum1 f", 10},
445 {"sum1 x", 4},
446 {"sum2 (sum0, x)", 8},
447 {"sum3 (sum0, x, 2)", 10},
448 {"sum2(2,3)", 5},
449 {"sum3(2,3,4)", 9},
450 {"sum4(2,3,4,5)", 14},
451 {"sum5(2,3,4,5,6)", 20},
452 {"sum6(2,3,4,5,6,7)", 27},
453 {"sum7(2,3,4,5,6,7,8)", 35},
454 };
455
456 x = 2;
457 f = 5;
458
459 int i;
460 for (i = 0; i < sizeof(cases) / sizeof(test_case); ++i) {
461 const char *expr = cases[i].expr;
462 const double answer = cases[i].answer;
463
464 int err;
465 te_expr *ex = te_compile(expr, lookup, sizeof(lookup)/sizeof(te_variable), &err);
466 lok(ex);
467 lfequal(te_eval(ex), answer);
468 te_free(ex);
469 }
470 }
471
472
clo0(void * context)473 double clo0(void *context) {
474 if (context) return *((double*)context) + 6;
475 return 6;
476 }
clo1(void * context,double a)477 double clo1(void *context, double a) {
478 if (context) return *((double*)context) + a * 2;
479 return a * 2;
480 }
clo2(void * context,double a,double b)481 double clo2(void *context, double a, double b) {
482 if (context) return *((double*)context) + a + b;
483 return a + b;
484 }
485
cell(void * context,double a)486 double cell(void *context, double a) {
487 double *c = context;
488 return c[(int)a];
489 }
490
test_closure()491 void test_closure() {
492
493 double extra;
494 double c[] = {5,6,7,8,9};
495
496 te_variable lookup[] = {
497 {"c0", clo0, TE_CLOSURE0, &extra},
498 {"c1", clo1, TE_CLOSURE1, &extra},
499 {"c2", clo2, TE_CLOSURE2, &extra},
500 {"cell", cell, TE_CLOSURE1, c},
501 };
502
503 test_case cases[] = {
504 {"c0", 6},
505 {"c1 4", 8},
506 {"c2 (10, 20)", 30},
507 };
508
509 int i;
510 for (i = 0; i < sizeof(cases) / sizeof(test_case); ++i) {
511 const char *expr = cases[i].expr;
512 const double answer = cases[i].answer;
513
514 int err;
515 te_expr *ex = te_compile(expr, lookup, sizeof(lookup)/sizeof(te_variable), &err);
516 lok(ex);
517
518 extra = 0;
519 lfequal(te_eval(ex), answer + extra);
520
521 extra = 10;
522 lfequal(te_eval(ex), answer + extra);
523
524 te_free(ex);
525 }
526
527
528 test_case cases2[] = {
529 {"cell 0", 5},
530 {"cell 1", 6},
531 {"cell 0 + cell 1", 11},
532 {"cell 1 * cell 3 + cell 4", 57},
533 };
534
535 for (i = 0; i < sizeof(cases2) / sizeof(test_case); ++i) {
536 const char *expr = cases2[i].expr;
537 const double answer = cases2[i].answer;
538
539 int err;
540 te_expr *ex = te_compile(expr, lookup, sizeof(lookup)/sizeof(te_variable), &err);
541 lok(ex);
542 lfequal(te_eval(ex), answer);
543 te_free(ex);
544 }
545 }
546
test_optimize()547 void test_optimize() {
548
549 test_case cases[] = {
550 {"5+5", 10},
551 {"pow(2,2)", 4},
552 {"sqrt 100", 10},
553 {"pi * 2", 6.2832},
554 };
555
556 int i;
557 for (i = 0; i < sizeof(cases) / sizeof(test_case); ++i) {
558 const char *expr = cases[i].expr;
559 const double answer = cases[i].answer;
560
561 int err;
562 te_expr *ex = te_compile(expr, 0, 0, &err);
563 lok(ex);
564
565 /* The answer should be know without
566 * even running eval. */
567 lfequal(ex->value, answer);
568 lfequal(te_eval(ex), answer);
569
570 te_free(ex);
571 }
572 }
573
test_pow()574 void test_pow() {
575 #ifdef TE_POW_FROM_RIGHT
576 test_equ cases[] = {
577 {"2^3^4", "2^(3^4)"},
578 {"-2^2", "-(2^2)"},
579 {"--2^2", "(2^2)"},
580 {"---2^2", "-(2^2)"},
581 {"-(2)^2", "-(2^2)"},
582 {"-(2*1)^2", "-(2^2)"},
583 {"-2^2", "-4"},
584 {"2^1.1^1.2^1.3", "2^(1.1^(1.2^1.3))"},
585 {"-a^b", "-(a^b)"},
586 {"-a^-b", "-(a^-b)"}
587 };
588 #else
589 test_equ cases[] = {
590 {"2^3^4", "(2^3)^4"},
591 {"-2^2", "(-2)^2"},
592 {"--2^2", "2^2"},
593 {"---2^2", "(-2)^2"},
594 {"-2^2", "4"},
595 {"2^1.1^1.2^1.3", "((2^1.1)^1.2)^1.3"},
596 {"-a^b", "(-a)^b"},
597 {"-a^-b", "(-a)^(-b)"}
598 };
599 #endif
600
601 double a = 2, b = 3;
602
603 te_variable lookup[] = {
604 {"a", &a},
605 {"b", &b}
606 };
607
608 int i;
609 for (i = 0; i < sizeof(cases) / sizeof(test_equ); ++i) {
610 const char *expr1 = cases[i].expr1;
611 const char *expr2 = cases[i].expr2;
612
613 te_expr *ex1 = te_compile(expr1, lookup, sizeof(lookup)/sizeof(te_variable), 0);
614 te_expr *ex2 = te_compile(expr2, lookup, sizeof(lookup)/sizeof(te_variable), 0);
615
616 lok(ex1);
617 lok(ex2);
618
619 double r1 = te_eval(ex1);
620 double r2 = te_eval(ex2);
621
622 fflush(stdout);
623 lfequal(r1, r2);
624
625 te_free(ex1);
626 te_free(ex2);
627 }
628
629 }
630
test_combinatorics()631 void test_combinatorics() {
632 test_case cases[] = {
633 {"fac(0)", 1},
634 {"fac(0.2)", 1},
635 {"fac(1)", 1},
636 {"fac(2)", 2},
637 {"fac(3)", 6},
638 {"fac(4.8)", 24},
639 {"fac(10)", 3628800},
640
641 {"ncr(0,0)", 1},
642 {"ncr(10,1)", 10},
643 {"ncr(10,0)", 1},
644 {"ncr(10,10)", 1},
645 {"ncr(16,7)", 11440},
646 {"ncr(16,9)", 11440},
647 {"ncr(100,95)", 75287520},
648
649 {"npr(0,0)", 1},
650 {"npr(10,1)", 10},
651 {"npr(10,0)", 1},
652 {"npr(10,10)", 3628800},
653 {"npr(20,5)", 1860480},
654 {"npr(100,4)", 94109400},
655 };
656
657
658 int i;
659 for (i = 0; i < sizeof(cases) / sizeof(test_case); ++i) {
660 const char *expr = cases[i].expr;
661 const double answer = cases[i].answer;
662
663 int err;
664 const double ev = te_interp(expr, &err);
665 lok(!err);
666 lfequal(ev, answer);
667
668 if (err) {
669 printf("FAILED: %s (%d)\n", expr, err);
670 }
671 }
672 }
673
674
main(int argc,char * argv[])675 int main(int argc, char *argv[])
676 {
677 lrun("Results", test_results);
678 lrun("Syntax", test_syntax);
679 lrun("NaNs", test_nans);
680 lrun("INFs", test_infs);
681 lrun("Variables", test_variables);
682 lrun("Functions", test_functions);
683 lrun("Dynamic", test_dynamic);
684 lrun("Closure", test_closure);
685 lrun("Optimize", test_optimize);
686 lrun("Pow", test_pow);
687 lrun("Combinatorics", test_combinatorics);
688 lresults();
689
690 return lfails != 0;
691 }
692