mirror of
				https://github.com/eledio-devices/thirdparty-tinyexpr.git
				synced 2025-10-31 00:32:38 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			396 lines
		
	
	
		
			8.5 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			396 lines
		
	
	
		
			8.5 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*
 | |
|  * TINYEXPR - Tiny recursive descent parser and evaluation engine in C
 | |
|  *
 | |
|  * Copyright (c) 2015, 2016 Lewis Van Winkle
 | |
|  *
 | |
|  * http://CodePlea.com
 | |
|  *
 | |
|  * This software is provided 'as-is', without any express or implied
 | |
|  * warranty. In no event will the authors be held liable for any damages
 | |
|  * arising from the use of this software.
 | |
|  *
 | |
|  * Permission is granted to anyone to use this software for any purpose,
 | |
|  * including commercial applications, and to alter it and redistribute it
 | |
|  * freely, subject to the following restrictions:
 | |
|  *
 | |
|  * 1. The origin of this software must not be misrepresented; you must not
 | |
|  * claim that you wrote the original software. If you use this software
 | |
|  * in a product, an acknowledgement in the product documentation would be
 | |
|  * appreciated but is not required.
 | |
|  * 2. Altered source versions must be plainly marked as such, and must not be
 | |
|  * misrepresented as being the original software.
 | |
|  * 3. This notice may not be removed or altered from any source distribution.
 | |
|  */
 | |
| 
 | |
| #include "tinyexpr.h"
 | |
| #include <stdio.h>
 | |
| #include "minctest.h"
 | |
| 
 | |
| 
 | |
| typedef struct {
 | |
|     const char *expr;
 | |
|     double answer;
 | |
| } test_case;
 | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
| void test_results() {
 | |
| 
 | |
|     test_case cases[] = {
 | |
|         {"1", 1},
 | |
|         {"(1)", 1},
 | |
| 
 | |
|         {"pi", 3.14159},
 | |
|         {"atan(1)*4 - pi", 0},
 | |
|         {"e", 2.71828},
 | |
| 
 | |
|         {"2+1", 2+1},
 | |
|         {"(((2+(1))))", 2+1},
 | |
|         {"3+2", 3+2},
 | |
| 
 | |
|         {"3+2+4", 3+2+4},
 | |
|         {"(3+2)+4", 3+2+4},
 | |
|         {"3+(2+4)", 3+2+4},
 | |
|         {"(3+2+4)", 3+2+4},
 | |
| 
 | |
|         {"3*2*4", 3*2*4},
 | |
|         {"(3*2)*4", 3*2*4},
 | |
|         {"3*(2*4)", 3*2*4},
 | |
|         {"(3*2*4)", 3*2*4},
 | |
| 
 | |
|         {"3-2-4", 3-2-4},
 | |
|         {"(3-2)-4", (3-2)-4},
 | |
|         {"3-(2-4)", 3-(2-4)},
 | |
|         {"(3-2-4)", 3-2-4},
 | |
| 
 | |
|         {"3/2/4", 3.0/2.0/4.0},
 | |
|         {"(3/2)/4", (3.0/2.0)/4.0},
 | |
|         {"3/(2/4)", 3.0/(2.0/4.0)},
 | |
|         {"(3/2/4)", 3.0/2.0/4.0},
 | |
| 
 | |
|         {"(3*2/4)", 3.0*2.0/4.0},
 | |
|         {"(3/2*4)", 3.0/2.0*4.0},
 | |
|         {"3*(2/4)", 3.0*(2.0/4.0)},
 | |
| 
 | |
|         {"asin sin .5", 0.5},
 | |
|         {"sin asin .5", 0.5},
 | |
|         {"ln exp .5", 0.5},
 | |
|         {"exp ln .5", 0.5},
 | |
| 
 | |
|         {"asin sin-.5", -0.5},
 | |
|         {"asin sin-0.5", -0.5},
 | |
|         {"asin sin -0.5", -0.5},
 | |
|         {"asin (sin -0.5)", -0.5},
 | |
|         {"asin (sin (-0.5))", -0.5},
 | |
|         {"asin sin (-0.5)", -0.5},
 | |
|         {"(asin sin (-0.5))", -0.5},
 | |
| 
 | |
|         {"log 1000", 3},
 | |
|         {"log 1e3", 3},
 | |
|         {"log 1000", 3},
 | |
|         {"log 1e3", 3},
 | |
|         {"log(1000)", 3},
 | |
|         {"log(1e3)", 3},
 | |
|         {"log 1.0e3", 3},
 | |
|         {"10^5*5e-5", 5},
 | |
| 
 | |
|         {"100^.5+1", 11},
 | |
|         {"100 ^.5+1", 11},
 | |
|         {"100^+.5+1", 11},
 | |
|         {"100^--.5+1", 11},
 | |
|         {"100^---+-++---++-+-+-.5+1", 11},
 | |
| 
 | |
|         {"100^-.5+1", 1.1},
 | |
|         {"100^---.5+1", 1.1},
 | |
|         {"100^+---.5+1", 1.1},
 | |
|         {"1e2^+---.5e0+1e0", 1.1},
 | |
|         {"--(1e2^(+(-(-(-.5e0))))+1e0)", 1.1},
 | |
| 
 | |
|         {"sqrt 100 + 7", 17},
 | |
|         {"sqrt 100 * 7", 70},
 | |
|         {"sqrt (100 * 100)", 100},
 | |
| 
 | |
|         {"1,2", 2},
 | |
|         {"1,2,3", 3},
 | |
|         {"(1,2),3", 3},
 | |
|         {"1,(2,3)", 3},
 | |
| 
 | |
|         {"2^2", 4},
 | |
|         {"pow(2,2)", 4},
 | |
| 
 | |
|         {"atan2(1,1)", 0.7854},
 | |
|         {"atan2(1,2)", 0.4636},
 | |
|         {"atan2(2,1)", 1.1071},
 | |
|         {"atan2(3,4)", 0.6435},
 | |
|         {"atan2(3+3,4*2)", 0.6435},
 | |
|         {"atan2(3+3,(4*2))", 0.6435},
 | |
|         {"atan2((3+3),4*2)", 0.6435},
 | |
|         {"atan2((3+3),(4*2))", 0.6435},
 | |
| 
 | |
|     };
 | |
| 
 | |
| 
 | |
|     int i;
 | |
|     for (i = 0; i < sizeof(cases) / sizeof(test_case); ++i) {
 | |
|         const char *expr = cases[i].expr;
 | |
|         const double answer = cases[i].answer;
 | |
| 
 | |
|         int err;
 | |
|         const double ev = te_interp(expr, &err);
 | |
|         lok(!err);
 | |
|         lfequal(ev, answer);
 | |
| 
 | |
|         if (err) {
 | |
|             printf("FAILED: %s (%d)\n", expr, err);
 | |
|         }
 | |
|     }
 | |
| }
 | |
| 
 | |
| 
 | |
| void test_syntax() {
 | |
| 
 | |
| 
 | |
|     test_case errors[] = {
 | |
|         {"", 1},
 | |
|         {"1+", 2},
 | |
|         {"1)", 2},
 | |
|         {"(1", 2},
 | |
|         {"1**1", 3},
 | |
|         {"1*2(+4", 4},
 | |
|         {"1*2(1+4", 4},
 | |
|         {"a+5", 1},
 | |
|         {"A+5", 1},
 | |
|         {"Aa+5", 1},
 | |
|         {"1^^5", 3},
 | |
|         {"1**5", 3},
 | |
|         {"sin(cos5", 8},
 | |
|     };
 | |
| 
 | |
| 
 | |
|     int i;
 | |
|     for (i = 0; i < sizeof(errors) / sizeof(test_case); ++i) {
 | |
|         const char *expr = errors[i].expr;
 | |
|         const int e = errors[i].answer;
 | |
| 
 | |
|         int err;
 | |
|         const double r = te_interp(expr, &err);
 | |
|         lequal(err, e);
 | |
|         lok(r != r);
 | |
| 
 | |
|         te_expr *n = te_compile(expr, 0, 0, &err);
 | |
|         lequal(err, e);
 | |
|         lok(!n);
 | |
| 
 | |
|         if (err != e) {
 | |
|             printf("FAILED: %s\n", expr);
 | |
|         }
 | |
| 
 | |
|         const double k = te_interp(expr, 0);
 | |
|         lok(k != k);
 | |
|     }
 | |
| }
 | |
| 
 | |
| 
 | |
| void test_nans() {
 | |
| 
 | |
|     const char *nans[] = {
 | |
|         "0/0",
 | |
|         "1%0",
 | |
|         "1%(1%0)",
 | |
|         "(1%0)%1",
 | |
|     };
 | |
| 
 | |
|     int i;
 | |
|     for (i = 0; i < sizeof(nans) / sizeof(const char *); ++i) {
 | |
|         const char *expr = nans[i];
 | |
| 
 | |
|         int err;
 | |
|         const double r = te_interp(expr, &err);
 | |
|         lequal(err, 0);
 | |
|         lok(r != r);
 | |
| 
 | |
|         te_expr *n = te_compile(expr, 0, 0, &err);
 | |
|         lok(n);
 | |
|         lequal(err, 0);
 | |
|         const double c = te_eval(n);
 | |
|         lok(c != c);
 | |
|         te_free(n);
 | |
|     }
 | |
| }
 | |
| 
 | |
| 
 | |
| void test_variables() {
 | |
| 
 | |
|     double x, y, test;
 | |
|     te_variable lookup[] = {{"x", &x}, {"y", &y}, {"test", &test}};
 | |
| 
 | |
|     int err;
 | |
| 
 | |
|     te_expr *expr1 = te_compile("cos x + sin y", lookup, 2, &err);
 | |
|     lok(expr1);
 | |
|     lok(!err);
 | |
| 
 | |
|     te_expr *expr2 = te_compile("x+x+x-y", lookup, 2, &err);
 | |
|     lok(expr2);
 | |
|     lok(!err);
 | |
| 
 | |
|     te_expr *expr3 = te_compile("x*y^3", lookup, 2, &err);
 | |
|     lok(expr3);
 | |
|     lok(!err);
 | |
| 
 | |
|     te_expr *expr4 = te_compile("test", lookup, 3, &err);
 | |
|     lok(expr4);
 | |
|     lok(!err);
 | |
| 
 | |
|     for (y = 2; y < 3; ++y) {
 | |
|         for (x = 0; x < 5; ++x) {
 | |
|             double ev = te_eval(expr1);
 | |
|             lfequal(ev, cos(x) + sin(y));
 | |
| 
 | |
|             ev = te_eval(expr2);
 | |
|             lfequal(ev, x+x+x-y);
 | |
| 
 | |
|             ev = te_eval(expr3);
 | |
|             lfequal(ev, x*y*y*y);
 | |
| 
 | |
|             test = x;
 | |
|             ev = te_eval(expr4);
 | |
|             lfequal(ev, x);
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     te_free(expr1);
 | |
|     te_free(expr2);
 | |
|     te_free(expr3);
 | |
|     te_free(expr4);
 | |
| 
 | |
| 
 | |
| 
 | |
|     te_expr *expr5 = te_compile("xx*y^3", lookup, 2, &err);
 | |
|     lok(!expr5);
 | |
|     lok(err);
 | |
| 
 | |
|     te_expr *expr6 = te_compile("tes", lookup, 3, &err);
 | |
|     lok(!expr6);
 | |
|     lok(err);
 | |
| 
 | |
|     te_expr *expr7 = te_compile("sinn x", lookup, 2, &err);
 | |
|     lok(!expr7);
 | |
|     lok(err);
 | |
| 
 | |
|     te_expr *expr8 = te_compile("si x", lookup, 2, &err);
 | |
|     lok(!expr8);
 | |
|     lok(err);
 | |
| }
 | |
| 
 | |
| 
 | |
| 
 | |
| #define cross_check(a, b) do {\
 | |
|     expr = te_compile((a), &lookup, 1, &err);\
 | |
|     lfequal(te_eval(expr), (b));\
 | |
|     lok(!err);\
 | |
|     te_free(expr);\
 | |
| }while(0)
 | |
| 
 | |
| void test_functions() {
 | |
| 
 | |
|     double x;
 | |
|     te_variable lookup = {"x", &x};
 | |
| 
 | |
|     int err;
 | |
|     te_expr *expr;
 | |
| 
 | |
|     for (x = -5; x < 5; x += .2) {
 | |
|         cross_check("abs x", fabs(x));
 | |
|         cross_check("acos x", acos(x));
 | |
|         cross_check("asin x", asin(x));
 | |
|         cross_check("atan x", atan(x));
 | |
|         cross_check("ceil x", ceil(x));
 | |
|         cross_check("cos x", cos(x));
 | |
|         cross_check("cosh x", cosh(x));
 | |
|         cross_check("exp x", exp(x));
 | |
|         cross_check("floor x", floor(x));
 | |
|         cross_check("ln x", log(x));
 | |
|         cross_check("log x", log10(x));
 | |
|         cross_check("sin x", sin(x));
 | |
|         cross_check("sinh x", sinh(x));
 | |
|         cross_check("sqrt x", sqrt(x));
 | |
|         cross_check("tan x", tan(x));
 | |
|         cross_check("tanh x", tanh(x));
 | |
|     }
 | |
| }
 | |
| 
 | |
| 
 | |
| double test0() {
 | |
|     return 6;
 | |
| }
 | |
| 
 | |
| double test1(double a) {
 | |
|     return a * 2;
 | |
| }
 | |
| 
 | |
| double test2(double a, double b) {
 | |
|     return a + b;
 | |
| }
 | |
| 
 | |
| 
 | |
| void test_dynamic() {
 | |
| 
 | |
|     double x, f;
 | |
|     te_variable lookup[] = {
 | |
|         {"x", &x},
 | |
|         {"f", &f},
 | |
|         {"test0", test0, TE_FUNCTION0},
 | |
|         {"test1", test1, TE_FUNCTION1},
 | |
|         {"test2", test2, TE_FUNCTION2},
 | |
|     };
 | |
| 
 | |
|     test_case cases[] = {
 | |
|         {"x", 2},
 | |
|         {"f+x", 7},
 | |
|         {"x+x", 4},
 | |
|         {"x+f", 7},
 | |
|         {"f+f", 10},
 | |
|         {"f+test0", 11},
 | |
|         {"test0+test0", 12},
 | |
|         {"test1 test0", 12},
 | |
|         {"test1 f", 10},
 | |
|         {"test1 x", 4},
 | |
|         {"test2 (test0, x)", 8},
 | |
|     };
 | |
| 
 | |
|     x = 2;
 | |
|     f = 5;
 | |
| 
 | |
|     int i;
 | |
|     for (i = 0; i < sizeof(cases) / sizeof(test_case); ++i) {
 | |
|         const char *expr = cases[i].expr;
 | |
|         const double answer = cases[i].answer;
 | |
| 
 | |
|         int err;
 | |
|         te_expr *ex = te_compile(expr, lookup, sizeof(lookup), &err);
 | |
|         lok(ex);
 | |
|         lfequal(te_eval(ex), answer);
 | |
|         te_free(ex);
 | |
|     }
 | |
| }
 | |
| 
 | |
| 
 | |
| int main(int argc, char *argv[])
 | |
| {
 | |
|     lrun("Results", test_results);
 | |
|     lrun("Syntax", test_syntax);
 | |
|     lrun("NaNs", test_nans);
 | |
|     lrun("Variables", test_variables);
 | |
|     lrun("Functions", test_functions);
 | |
|     lrun("Dynamic", test_dynamic);
 | |
|     lresults();
 | |
| 
 | |
|     return lfails != 0;
 | |
| }
 |