1 Star 0 Fork 1

sgnah/tinyexpr

forked from SunBeau/tinyexpr 
加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
克隆/下载
smoke.c 16.28 KB
一键复制 编辑 原始数据 按行查看 历史
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709
/*
* TINYEXPR - Tiny recursive descent parser and evaluation engine in C
*
* Copyright (c) 2015-2020 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;
typedef struct {
const char *expr1;
const char *expr2;
} test_equ;
void test_results() {
test_case cases[] = {
{"1", 1},
{"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},
{"log10 1000", 3},
{"log10 1e3", 3},
{"log10 1000", 3},
{"log10 1e3", 3},
{"log10(1000)", 3},
{"log10(1e3)", 3},
{"log10 1.0e3", 3},
{"10^5*5e-5", 5},
#ifdef TE_NAT_LOG
{"log 1000", 6.9078},
{"log e", 1},
{"log (e^10)", 10},
#else
{"log 1000", 3},
#endif
{"ln (e^10)", 10},
{"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+1", 3},
{"1+1,2+2,2+1", 3},
{"1,2,3", 3},
{"(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},
{"!+5", 1},
{"_a+5", 1},
{"#a+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",
"fac(-1)",
"ncr(2, 4)",
"ncr(-2, 4)",
"ncr(2, -4)",
"npr(2, 4)",
"npr(-2, 4)",
"npr(2, -4)",
};
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_infs() {
const char *infs[] = {
"1/0",
"log(0)",
"pow(2,10000000)",
"fac(300)",
"ncr(300,100)",
"ncr(300000,100)",
"ncr(300000,100)*8",
"npr(3,2)*ncr(300000,100)",
"npr(100,90)",
"npr(30,25)",
};
int i;
for (i = 0; i < sizeof(infs) / sizeof(const char *); ++i) {
const char *expr = infs[i];
int err;
const double r = te_interp(expr, &err);
lequal(err, 0);
lok(r == r + 1);
te_expr *n = te_compile(expr, 0, 0, &err);
lok(n);
lequal(err, 0);
const double c = te_eval(n);
lok(c == c + 1);
te_free(n);
}
}
void test_variables() {
double x, y, test;
te_variable lookup[] = {{"x", &x}, {"y", &y}, {"te_st", &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("te_st+5", lookup, 3, &err);
lok(expr4);
lok(!err);
for (y = 2; y < 3; ++y) {
for (x = 0; x < 5; ++x) {
double ev;
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+5);
}
}
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 {\
if ((b)!=(b)) break;\
expr = te_compile((a), lookup, 2, &err);\
lfequal(te_eval(expr), (b));\
lok(!err);\
te_free(expr);\
}while(0)
void test_functions() {
double x, y;
te_variable lookup[] = {{"x", &x}, {"y", &y}};
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("log10 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));
for (y = -2; y < 2; y += .2) {
if (fabs(x) < 0.01) break;
cross_check("atan2(x,y)", atan2(x, y));
cross_check("pow(x,y)", pow(x, y));
}
}
}
double sum0() {
return 6;
}
double sum1(double a) {
return a * 2;
}
double sum2(double a, double b) {
return a + b;
}
double sum3(double a, double b, double c) {
return a + b + c;
}
double sum4(double a, double b, double c, double d) {
return a + b + c + d;
}
double sum5(double a, double b, double c, double d, double e) {
return a + b + c + d + e;
}
double sum6(double a, double b, double c, double d, double e, double f) {
return a + b + c + d + e + f;
}
double sum7(double a, double b, double c, double d, double e, double f, double g) {
return a + b + c + d + e + f + g;
}
void test_dynamic() {
double x, f;
te_variable lookup[] = {
{"x", &x},
{"f", &f},
{"sum0", sum0, TE_FUNCTION0},
{"sum1", sum1, TE_FUNCTION1},
{"sum2", sum2, TE_FUNCTION2},
{"sum3", sum3, TE_FUNCTION3},
{"sum4", sum4, TE_FUNCTION4},
{"sum5", sum5, TE_FUNCTION5},
{"sum6", sum6, TE_FUNCTION6},
{"sum7", sum7, TE_FUNCTION7},
};
test_case cases[] = {
{"x", 2},
{"f+x", 7},
{"x+x", 4},
{"x+f", 7},
{"f+f", 10},
{"f+sum0", 11},
{"sum0+sum0", 12},
{"sum0()+sum0", 12},
{"sum0+sum0()", 12},
{"sum0()+(0)+sum0()", 12},
{"sum1 sum0", 12},
{"sum1(sum0)", 12},
{"sum1 f", 10},
{"sum1 x", 4},
{"sum2 (sum0, x)", 8},
{"sum3 (sum0, x, 2)", 10},
{"sum2(2,3)", 5},
{"sum3(2,3,4)", 9},
{"sum4(2,3,4,5)", 14},
{"sum5(2,3,4,5,6)", 20},
{"sum6(2,3,4,5,6,7)", 27},
{"sum7(2,3,4,5,6,7,8)", 35},
};
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)/sizeof(te_variable), &err);
lok(ex);
lfequal(te_eval(ex), answer);
te_free(ex);
}
}
double clo0(void *context) {
if (context) return *((double*)context) + 6;
return 6;
}
double clo1(void *context, double a) {
if (context) return *((double*)context) + a * 2;
return a * 2;
}
double clo2(void *context, double a, double b) {
if (context) return *((double*)context) + a + b;
return a + b;
}
double cell(void *context, double a) {
double *c = context;
return c[(int)a];
}
void test_closure() {
double extra;
double c[] = {5,6,7,8,9};
te_variable lookup[] = {
{"c0", clo0, TE_CLOSURE0, &extra},
{"c1", clo1, TE_CLOSURE1, &extra},
{"c2", clo2, TE_CLOSURE2, &extra},
{"cell", cell, TE_CLOSURE1, c},
};
test_case cases[] = {
{"c0", 6},
{"c1 4", 8},
{"c2 (10, 20)", 30},
};
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)/sizeof(te_variable), &err);
lok(ex);
extra = 0;
lfequal(te_eval(ex), answer + extra);
extra = 10;
lfequal(te_eval(ex), answer + extra);
te_free(ex);
}
test_case cases2[] = {
{"cell 0", 5},
{"cell 1", 6},
{"cell 0 + cell 1", 11},
{"cell 1 * cell 3 + cell 4", 57},
};
for (i = 0; i < sizeof(cases2) / sizeof(test_case); ++i) {
const char *expr = cases2[i].expr;
const double answer = cases2[i].answer;
int err;
te_expr *ex = te_compile(expr, lookup, sizeof(lookup)/sizeof(te_variable), &err);
lok(ex);
lfequal(te_eval(ex), answer);
te_free(ex);
}
}
void test_optimize() {
test_case cases[] = {
{"5+5", 10},
{"pow(2,2)", 4},
{"sqrt 100", 10},
{"pi * 2", 6.2832},
};
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, 0, 0, &err);
lok(ex);
/* The answer should be know without
* even running eval. */
lfequal(ex->value, answer);
lfequal(te_eval(ex), answer);
te_free(ex);
}
}
void test_pow() {
#ifdef TE_POW_FROM_RIGHT
test_equ cases[] = {
{"2^3^4", "2^(3^4)"},
{"-2^2", "-(2^2)"},
{"--2^2", "(2^2)"},
{"---2^2", "-(2^2)"},
{"-(2*1)^2", "-(2^2)"},
{"-2^2", "-4"},
{"2^1.1^1.2^1.3", "2^(1.1^(1.2^1.3))"},
{"-a^b", "-(a^b)"},
{"-a^-b", "-(a^-b)"},
{"1^0", "1"},
{"(1)^0", "1"},
{"-(2)^2", "-(2^2)"}
/* TODO POW FROM RIGHT IS STILL BUGGY
{"(-2)^2", "4"},
{"(-1)^0", "1"},
{"(-5)^0", "1"},
{"-2^-3^-4", "-(2^(-(3^-4)))"}*/
};
#else
test_equ cases[] = {
{"2^3^4", "(2^3)^4"},
{"-2^2", "(-2)^2"},
{"(-2)^2", "4"},
{"--2^2", "2^2"},
{"---2^2", "(-2)^2"},
{"-2^2", "4"},
{"2^1.1^1.2^1.3", "((2^1.1)^1.2)^1.3"},
{"-a^b", "(-a)^b"},
{"-a^-b", "(-a)^(-b)"},
{"1^0", "1"},
{"(1)^0", "1"},
{"(-1)^0", "1"},
{"(-5)^0", "1"},
{"-2^-3^-4", "((-2)^(-3))^(-4)"}
};
#endif
double a = 2, b = 3;
te_variable lookup[] = {
{"a", &a},
{"b", &b}
};
int i;
for (i = 0; i < sizeof(cases) / sizeof(test_equ); ++i) {
const char *expr1 = cases[i].expr1;
const char *expr2 = cases[i].expr2;
te_expr *ex1 = te_compile(expr1, lookup, sizeof(lookup)/sizeof(te_variable), 0);
te_expr *ex2 = te_compile(expr2, lookup, sizeof(lookup)/sizeof(te_variable), 0);
lok(ex1);
lok(ex2);
double r1 = te_eval(ex1);
double r2 = te_eval(ex2);
fflush(stdout);
const int olfail = lfails;
lfequal(r1, r2);
if (olfail != lfails) {
printf("Failed expression: %s <> %s\n", expr1, expr2);
}
te_free(ex1);
te_free(ex2);
}
}
void test_combinatorics() {
test_case cases[] = {
{"fac(0)", 1},
{"fac(0.2)", 1},
{"fac(1)", 1},
{"fac(2)", 2},
{"fac(3)", 6},
{"fac(4.8)", 24},
{"fac(10)", 3628800},
{"ncr(0,0)", 1},
{"ncr(10,1)", 10},
{"ncr(10,0)", 1},
{"ncr(10,10)", 1},
{"ncr(16,7)", 11440},
{"ncr(16,9)", 11440},
{"ncr(100,95)", 75287520},
{"npr(0,0)", 1},
{"npr(10,1)", 10},
{"npr(10,0)", 1},
{"npr(10,10)", 3628800},
{"npr(20,5)", 1860480},
{"npr(100,4)", 94109400},
};
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);
}
}
}
int main(int argc, char *argv[])
{
lrun("Results", test_results);
lrun("Syntax", test_syntax);
lrun("NaNs", test_nans);
lrun("INFs", test_infs);
lrun("Variables", test_variables);
lrun("Functions", test_functions);
lrun("Dynamic", test_dynamic);
lrun("Closure", test_closure);
lrun("Optimize", test_optimize);
lrun("Pow", test_pow);
lrun("Combinatorics", test_combinatorics);
lresults();
return lfails != 0;
}
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
C
1
https://gitee.com/sgnah/tinyexpr.git
git@gitee.com:sgnah/tinyexpr.git
sgnah
tinyexpr
tinyexpr
master

搜索帮助