codeplea / tinyexpr Goto Github PK
View Code? Open in Web Editor NEWtiny recursive descent expression parser, compiler, and evaluation engine for math expressions
Home Page: https://codeplea.com/tinyexpr
License: zlib License
tiny recursive descent expression parser, compiler, and evaluation engine for math expressions
Home Page: https://codeplea.com/tinyexpr
License: zlib License
Hi.
I apologize if my English is not always clear. English is not my native language and I sometime lack using the proper word.
I plan to integrate an expression parser in one of my projects. I have taken a look at tinyexpr and think that it is a really nice library. I found multiple compilation errors when compiling with Visual Studio 2010. I do realize vs2010 is old but this is my development environment for my project. I would be really happy if you would consider supporting vs2010.
When compiling revision ffb0d41, the following errors were identified by Visual Studio:
1>f:\projets\programmation\cpp\tinyexpr\master\tinyexpr.c(124): error C2124: divide or mod by zero
1>f:\projets\programmation\cpp\tinyexpr\master\tinyexpr.c(127): error C2143: syntax error : missing ';' before 'type'
1>f:\projets\programmation\cpp\tinyexpr\master\tinyexpr.c(129): error C2065: 'i' : undeclared identifier
1>f:\projets\programmation\cpp\tinyexpr\master\tinyexpr.c(256): error C2223: left of '->type' must point to struct/union
1>f:\projets\programmation\cpp\tinyexpr\master\tinyexpr.c(403): error C2275: 'te_expr' : illegal use of this type as an expression
1> f:\projets\programmation\cpp\tinyexpr\master\tinyexpr.h(39) : see declaration of 'te_expr'
1>f:\projets\programmation\cpp\tinyexpr\master\tinyexpr.c(408): error C2059: syntax error : '{'
1>f:\projets\programmation\cpp\tinyexpr\master\tinyexpr.c(487): error C2371: 'expr' : redefinition; different basic types
1> f:\projets\programmation\cpp\tinyexpr\master\tinyexpr.c(297) : see declaration of 'expr'
1>f:\projets\programmation\cpp\tinyexpr\master\tinyexpr.c(596): error C2275: 'te_expr' : illegal use of this type as an expression
1> f:\projets\programmation\cpp\tinyexpr\master\tinyexpr.h(39) : see declaration of 'te_expr'
(I only added one example of each error)
The full error log can be found here: Compilation of ffb0d41b13e5f8d318db95feb071c220c134fe70 under VS2010.txt
Most of these errors are not critical and could be easily resolved by adding the declarations of variables at the beginning of the functions instead of after if
statements. I hope you are open to suggestion and that you would like to support this compiler. I also wish that you would not count this change "as a new feature" and that it does not break your contributing rules and keep tinyexpr as simple as possible.
I have already take a look at most of the compilation issues. Here are the corrections to solve all issues:
NAN
and INFINITE
are undeclared identifier. On VS2010, these macros are not defined. The solution would be something like specified at https://gitlab.com/embeddable-common-lisp/ecl/-/issues/282:#include <stdint.h>
#ifndef INFINITY
#if _MSC_VER == 1600
union {
uint8_t bytes [ sizeof ( float ) ];
float inf;
} __ecl_inf = {
{ 0, 0, 0xf0, 0x7f }
};
#define INFINITY (__ecl_inf.inf)
#else
# define INFINITY (1.0/0.0)
#endif
#endif
#ifndef NAN
#if _MSC_VER == 1600
union {
uint8_t bytes [ sizeof ( float ) ];
float nan;
} __ecl_nan = {
{ 0, 0, 0xc0, 0x7f }
};
#define NAN (__ecl_nan.nan)
#else
# define NAN (0.0/0.0)
#endif
#endif
The malloc()
call at tinyexpr.c, line 88 should be prefixed with (te_expr *)
to cast void*
to te_expr *
.
The function fac()
needs the declarations of the following at the beginning of the function:
unsigned int ua;
unsigned long int result, i;
ncr()
needs the declarations of the following at the beginning of the function: unsigned long int un, ur, i;
unsigned long int result;
next_token()
needs the declarations of the following at the beginning of the function: const te_variable *var = NULL;
The declaration of the ret
variable in function in function power()
at tinyexpr.c, line 403 needs to be moved at the beginning of the function.
The declaration of the root
variable in function in function te_compile()
at tinyexpr.c, line 596 needs to be moved at the beginning of the function.
Expression such as te_fun2 t = s->function;
need to be type casted to compile properly. This change affects lines 431, 460, 477 and 492. The result would be the following:
te_fun2 t = (te_fun2)s->function;
...
syntax in a define statement like the one at tinyexpr.c, line 82.#define NEW_EXPR1(type, expr1) new_expr1((type), (expr1))
#define NEW_EXPR2(type, expr1, expr2) new_expr2((type), (expr1), (expr2))
static te_expr *new_expr1(const int type, te_expr *param1) {
const te_expr *parameters[] = {param1};
return new_expr(type, parameters);
}
static te_expr *new_expr2(const int type, te_expr *param1, te_expr *param2) {
const te_expr *parameters[] = {param1, param2};
return new_expr(type, parameters);
}
and replacing usage of the NEW_EXPR()
macro by NEW_EXPR1()
or NEW_EXPR2()
. For example, replace line 494 by the following:
ret = NEW_EXPR2(TE_FUNCTION2 | TE_FLAG_PURE, ret, power(s));
Since the number of calls to the NEW_EXPR
macro is limited to 5 lines of code with 2 or 3 argument variants, I think this does not hurt the code too much.
I have never contributed to a github project but if you are open to these corrections, I can modify the code and create a pull request if you wish.
Looking forward for your feedback.
Now I want to calculate complex number, for example, (2+3i)/(4+5i)=0.560 976 + 0.048 780 5i, I hope to add this feature, thanks!
Hello, and thank you for your good parser.
I am trying to use this parser in my project, And I'm receiving this error:
/tmp/ccqoOUPZ.o: In function 'cexpGen': /home/amirali/MPSC/projects/c-exp/main.c:56: undefined reference to 'te_interp' collect2: error: ld returned 1 exit status The terminal process terminated with exit code: 1
I've added tinyexpr.h and tinyexpr.c to my project folder, And added :#include "tinyexpr.h"
to my main.c.
Here is my code:
https://github.com/amiralisalimi/c-exp
Hi.
Thanks for the wonderful library.
I am trying to include your library of one of my projects. When compiling, it returns an error.
#include "tinyexpr.h"
void setup() {
// put your setup code here, to run once:
te_interp("5*5", 0);
}
void loop() {
// put your main code here, to run repeatedly:
}
/tmp/cce6imJi.ltrans0.ltrans.o:(.rodata+0x2): undefined reference to `fabs'
collect2: error: ld returned 1 exit status
exit status 1
Error compiling for board Arduino/Genuino Uno.
I tried to compile simple arduino program using only the math library and it works.
#include <math.h>
void setup() {
// put your setup code here, to run once:
fabs(-3);
}
void loop() {
// put your main code here, to run repeatedly:
}
Sketch uses 444 bytes (1%) of program storage space. Maximum is 32256 bytes.
Global variables use 9 bytes (0%) of dynamic memory, leaving 2039 bytes for local variables. Maximum is 2048 bytes.
I love to use your library on embedded systems. I hope you can fix the issue. Thanks.
I've added added TinyExpr to the following mathematical expression benchmark suite:
https://github.com/ArashPartow/math-parser-benchmark-project
The specific code can be found here:
https://github.com/ArashPartow/math-parser-benchmark-project/blob/master/src/BenchTinyExpr.cpp
If possible could you review the above and make sure, the library is being used correctly and that there's no unintended latencies being added.
Here is an example run:
Expression 92 of 205: "abs(sin(sqrt(a^2+b^2)))"; Progress: ############
[01] ExprTk ( 64.072 ns, 0.630283156548410717, 630283.156537625589407980)
[02] muparserSSE ( 68.056 ns, 0.630283117294311523, 630283.117294311523437500)
[03] ExprTkFloat ( 68.758 ns, 0.630283117294311523, 630283.117294311523437500)
[04] atmsp 1.0.4 ( 74.571 ns, 0.630283156548410717, 630283.156537625589407980)
[05] FParser 4.5 ( 74.707 ns, 0.630283156548410717, 630283.156537625589407980)
[06] muparser 2.2.4 ( 81.762 ns, 0.630283156548410717, 630283.156537625589407980)
[07] muparser 2.2.4 (omp) ( 83.188 ns, 0.630283156548410717, 630283.156537625589407980)
[08] MTParser (123.360 ns, 0.630283156548410717, 630283.156537625589407980)
[09] MathExpr (251.873 ns, 0.630283156548410717, 630283.156537625589407980)
[10] TinyExpr (295.522 ns, 0.630283156548410717, 630283.156537625589407980)
[11] Lepton (408.594 ns, 0.630283156548410717, 630283.156537625589407980)
[12] muparserx (533.038 ns, 0.630283156548410717, 630283.156537625589407980)
During integration of TinyExpr issues relating to compiler diagnostics during compilation of library and bugs relating to expression parsing were found, here are some of the changes:
When you get a chance, can you review the above changes and make sure they're correct, and don't add any unintended latencies or evaluation errors.
There also seems to be general precedence and associativity issues with regards to the generated AST/evaluation. The following is one example of such problems:
Expression 61 of 96: "-a^-b"; Progress: ############
[01] muparserSSE ( 68.395 ns, -0.810841679573059082, -6154.623031616210937500)
[02] ExprTk ( 71.411 ns, -0.810841732005176952, -6154.623390711222782556)
[03] atmsp 1.0.4 ( 72.180 ns, -0.810841732005176952, -6154.623390711222782556)
[04] ExprTkFloat ( 79.174 ns, -0.810841679573059082, -6154.623031616210937500)
[05] MathExpr ( 90.595 ns, -0.810841732005176952, -6154.623390711222782556)
[06] FParser 4.5 ( 96.818 ns, -0.810841732005177063, -6154.623390711222782556)
[07] muparser 2.2.4 (108.624 ns, -0.810841732005176952, -6154.623390711222782556)
[08] muparser 2.2.4 (omp) (109.714 ns, -0.810841732005176952, -6154.623390711222782556)
[09] Lepton (362.571 ns, -0.810841732005176952, -6154.623390711222782556)
[10] muparserx (397.474 ns, -0.810841732005176952, -6154.623390711222782556)
DNQ List
[01] TinyExpr ( 85.975 ns, 0.000000000000000000, 0.000000000000000000)
In the following the call to log seems to have a different meaning to the one commonly used in programming languages (log is generally known as the natural logarithm base e (aka ln), where as TinyExpr seems to implement it as log base 10):
Expression 170 of 205: "10^log(3+b)"; Progress: ############
[01] ExprTk ( 86.773 ns,44.530608078220737411, 35146523.457089543342590332)
[02] muparserSSE ( 97.832 ns,44.530609130859375000, 35146521.568298339843750000)
[03] ExprTkFloat ( 99.054 ns,44.530609130859375000, 35146521.568298339843750000)
[04] MTParser (102.149 ns,44.530608078220737411, 35146523.457089543342590332)
[05] muparser 2.2.4 (104.093 ns,44.530608078220737411, 35146523.457089543342590332)
[06] atmsp 1.0.4 (104.925 ns,44.530608078220737411, 35146523.457089543342590332)
[07] muparser 2.2.4 (105.696 ns,44.530608078220737411, 35146523.457089543342590332)
[08] FParser 4.5 (118.495 ns,44.530608078220751622, 35146523.457089543342590332)
[09] MathExpr (127.893 ns,44.530608078220737411, 35146523.457089543342590332)
[10] Lepton (296.503 ns,44.530608078220737411, 35146523.457089543342590332)
[11] muparserx (418.354 ns,44.530608078220737411, 35146523.457089543342590332)
DNQ List
[01] TinyExpr (117.324 ns, 5.200000000000001066, 4650000.000055111944675446)
Here's a list of only some of the problematic expressions, though running all the benchmarks results in a great deal more:
(0.1*a+1)*a+1.1-sin(a)-log(a)/a*3/4 - incorrect result
-(-b^2^3)+b^6 - incorrect result
-a^(-b) - incorrect result
-a^+b - incorrect result
-a^-b - incorrect result
-a^-b+1.1 - incorrect result
-a^-b/1.1 - incorrect result
-a^2^3-a^8 - incorrect result
-b^2^3-b^6 - incorrect result
+a^+2^+3-a^+8 - incorrect result
10^log(3+b) - incorrect result
a-(e^(log(7+b))) - incorrect result
a^-2^-3-1/a^1/8 - incorrect result
a^-2^3-1/a^8 - incorrect result
a^2.2^3.3 - incorrect result
a^2.2^3.3-a^13.48946876053338489126547 - incorrect result
a^2.2^3.3^1.1 - incorrect result
a^2^3 - incorrect result
a^2^3-a^8 - incorrect result
e^log(7*a) - incorrect result
Hi, I'm trying to add arandom()
function that returns a random value within the specified range.
But if I use constants to get the random value (e.g. random(1,4)
) then it seems to store the first returned value and not actually call random()
function again which has to be called each time when I call te_eval()
.
It seems to work only when I use a variable inside random (e.g. random(0, max)) which makes it called each time when te_eval()
is called I think.
Is there any easy way to make the specific function be called each time when te_eval()
is called? (without having to use any variable as function arguments)
Thank you in advance!
I rename these files:
tinyexpr.c -> tinyexpr.cpp
example.c -> example.cpp
and trying to compile it with:
g++ tinyexpr.cpp example.cpp
As for output I get a bunch of errors:
$ g++ tinyexpr.cpp example.cpp
tinyexpr.cpp:82:14: error: cannot initialize a variable of type 'te_expr *' with an rvalue of type 'void *'
te_expr *ret = malloc(size);
^ ~~~~~~~~~~~~
tinyexpr.cpp:96:46: error: no matching function for call to 'te_free'
case TE_FUNCTION7: case TE_CLOSURE7: te_free(n->parameters[6]);
^~~~~~~
./tinyexpr.h:79:6: note: candidate function not viable: cannot convert argument of incomplete type 'void *' to 'te_expr *'
void te_free(te_expr *n);
^
tinyexpr.cpp:97:46: error: no matching function for call to 'te_free'
case TE_FUNCTION6: case TE_CLOSURE6: te_free(n->parameters[5]);
^~~~~~~
./tinyexpr.h:79:6: note: candidate function not viable: cannot convert argument of incomplete type 'void *' to 'te_expr *'
void te_free(te_expr *n);
^
tinyexpr.cpp:98:46: error: no matching function for call to 'te_free'
case TE_FUNCTION5: case TE_CLOSURE5: te_free(n->parameters[4]);
^~~~~~~
./tinyexpr.h:79:6: note: candidate function not viable: cannot convert argument of incomplete type 'void *' to 'te_expr *'
void te_free(te_expr *n);
^
tinyexpr.cpp:99:46: error: no matching function for call to 'te_free'
case TE_FUNCTION4: case TE_CLOSURE4: te_free(n->parameters[3]);
^~~~~~~
./tinyexpr.h:79:6: note: candidate function not viable: cannot convert argument of incomplete type 'void *' to 'te_expr *'
void te_free(te_expr *n);
^
tinyexpr.cpp:100:46: error: no matching function for call to 'te_free'
case TE_FUNCTION3: case TE_CLOSURE3: te_free(n->parameters[2]);
^~~~~~~
./tinyexpr.h:79:6: note: candidate function not viable: cannot convert argument of incomplete type 'void *' to 'te_expr *'
void te_free(te_expr *n);
^
tinyexpr.cpp:101:46: error: no matching function for call to 'te_free'
case TE_FUNCTION2: case TE_CLOSURE2: te_free(n->parameters[1]);
^~~~~~~
./tinyexpr.h:79:6: note: candidate function not viable: cannot convert argument of incomplete type 'void *' to 'te_expr *'
void te_free(te_expr *n);
^
tinyexpr.cpp:102:46: error: no matching function for call to 'te_free'
case TE_FUNCTION1: case TE_CLOSURE1: te_free(n->parameters[0]);
^~~~~~~
./tinyexpr.h:79:6: note: candidate function not viable: cannot convert argument of incomplete type 'void *' to 'te_expr *'
void te_free(te_expr *n);
^
tinyexpr.cpp:119:13: error: cannot initialize a member subobject of type 'const void *' with an lvalue of type 'double (double)'
{"abs", fabs, TE_FUNCTION1 | TE_FLAG_PURE, 0},
^~~~
tinyexpr.cpp:120:14: error: cannot initialize a member subobject of type 'const void *' with an lvalue of type 'double (double)'
{"acos", acos, TE_FUNCTION1 | TE_FLAG_PURE, 0},
^~~~
tinyexpr.cpp:121:14: error: cannot initialize a member subobject of type 'const void *' with an lvalue of type 'double (double)'
{"asin", asin, TE_FUNCTION1 | TE_FLAG_PURE, 0},
^~~~
tinyexpr.cpp:122:14: error: cannot initialize a member subobject of type 'const void *' with an lvalue of type 'double (double)'
{"atan", atan, TE_FUNCTION1 | TE_FLAG_PURE, 0},
^~~~
tinyexpr.cpp:123:15: error: cannot initialize a member subobject of type 'const void *' with an lvalue of type 'double (double, double)'
{"atan2", atan2, TE_FUNCTION2 | TE_FLAG_PURE, 0},
^~~~~
tinyexpr.cpp:124:14: error: cannot initialize a member subobject of type 'const void *' with an lvalue of type 'double (double)'
{"ceil", ceil, TE_FUNCTION1 | TE_FLAG_PURE, 0},
^~~~
tinyexpr.cpp:125:13: error: cannot initialize a member subobject of type 'const void *' with an lvalue of type 'double (double)'
{"cos", cos, TE_FUNCTION1 | TE_FLAG_PURE, 0},
^~~
tinyexpr.cpp:126:14: error: cannot initialize a member subobject of type 'const void *' with an lvalue of type 'double (double)'
{"cosh", cosh, TE_FUNCTION1 | TE_FLAG_PURE, 0},
^~~~
tinyexpr.cpp:127:11: error: cannot initialize a member subobject of type 'const void *' with an lvalue of type 'double ()'
{"e", e, TE_FUNCTION0 | TE_FLAG_PURE, 0},
^
tinyexpr.cpp:128:13: error: cannot initialize a member subobject of type 'const void *' with an lvalue of type 'double (double)'
{"exp", exp, TE_FUNCTION1 | TE_FLAG_PURE, 0},
^~~
tinyexpr.cpp:129:15: error: cannot initialize a member subobject of type 'const void *' with an lvalue of type 'double (double)'
{"floor", floor, TE_FUNCTION1 | TE_FLAG_PURE, 0},
^~~~~
fatal error: too many errors emitted, stopping now [-ferror-limit=]
20 errors generated.
How to properly use TinyExpr with C++?
OS X: 10.12.3 / CLang / The latest build of TinyExpr.
I want to use it in C++ programs. Please develop a program such that it can be used in C++ as well.
For the c program which do not run in c++, refer to this link and upload a new file soon please...
https://www.geeksforgeeks.org/write-c-program-wont-compiler-c/
Is there a compelling reason that variable names are limited to starting with a-z?
I needed $ to be a variable to process some data but that opens the question of what others may need.
trivial change, 2 lines in next_token I can do a pull request if need be I just did not know if there was a good reason to limit it.
I am trying to parse the following expression. H
is function and rest are variables.
p + g + L + len + dia + H(1-L)
The program is failing with the following error:
Evaluating:
p + g + L + len + dia + H(1-L)
^
Error near here
My guess is that uppercase letters are not supported. If it is true, it is bit lacking.
#include "tinyexpr.h"
#include <stdio.h>
#include <math.h>
double H( double a )
{
return 2.0 + pow(a, 3.0);
}
int main(int argc, char *argv[])
{
const char* expr = "p + g + L + len + dia + H(1-L)";
printf("Evaluating:\n\t%s\n", expr);
/* This shows an example where the variables
* x and y are bound at eval-time. */
double p, g, L, len, dia;
te_variable vars[] = {{"p", &p}, {"g", &g}, {"L", &L}, {"len", &len}, {"dia", &dia}
, { "H", H, TE_FUNCTION1}
};
/* This will compile the expression and check for errors. */
int err;
te_expr *n = te_compile(expr, vars, 6, &err);
if (n) {
/* The variables can be changed here, and eval can be called as many
* times as you like. This is fairly efficient because the parsing has
* already been done. */
p = 1; g = 0.1; L = 10; len = 2; dia = 0.2;
const double r = te_eval(n); printf("Result:\n\t%f\n", r);
te_free(n);
} else {
/* Show the user where the error is at. */
printf("\t%*s^\nError near here", err-1, "");
}
return 0;
}
I am trying to use tinyexpr in my project. Here is my code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "lib/tinyexpr.h"
#define TRUE 1
void cexpGen();
char *trim(char input[]);
int main()
{
cexpGen();
getchar();
return 0;
}
void cexpGen()
{
char c_exp[18] = "1 2 3 4 5 6 7 8 9";
char expressions[4] = "+- ";
int index[8];
char c[9];
int j = 1;
for (int i = 0; i < 8; i++)
{
c[i] = '+';
index[i] = 0;
}
while (TRUE)
{
if (te_interp(trim(c_exp), 0) == 100)
{
printf("%s = 100", c_exp);
}
int k = sizeof(index) / sizeof(index[0]) - 1;
for (; k >= 0; k--)
{
index[k] += 1;
if (index[k] < 3)
{
c[k] = expressions[index[k]];
break;
}
index[k] = 0;
c[k] = expressions[index[k]];
}
for (int i = 0; i < 8; i++)
{
if (j >= 17)
break;
c_exp[j] = c[i];
j += 2;
}
j = 1;
if (k < 0)
break;
}
}
char *trim(char input[])
{
char output[18];
for (int i = 0; i < (strlen(input) - 1); i++)
{
output[i] = input[i];
}
for (int i = 0; i < 18; i++)
{
if (isspace(output[i]))
{
for (int j = i; j < strlen(output); j++)
{
if (j == (strlen(output) - 1))
{
memmove(&output[strlen(output) - 1], &output[strlen(output)], strlen(output) - (strlen(output) - 1));
}
output[j] = output[j + 1];
}
}
}
return output;
}
On line 36 (cexpGen(), while (TRUE), First if statement) when I want to interpret my code with te_interp()
, It gives me segmentation fault when I use my trim()
function, But it disappears when I remove trim()
. What have I done wrong? (memmove(&output[strlen(output) - 1], &output[strlen(output)], strlen(output) - (strlen(output) - 1));
removes wanted characters of an array by changing them to '\0').
There is a problem with this code "0.0/0.0". Correct form is this define
Hello, very nice project.
I've been looking for a relatively simple C library for expression parsing that also supported symbolic differentiation and happened to find your project. Your code seems pretty straightforward and easy to use. However, it lacks the differentiation feature.
So I have a question: do you know of anybody who has extended this code to support that? If so, please point me there. If not, do you know of any other similar lightweight library that supports it?
Also, if I were to try and implement it, do you have any suggestions (general guidelines) on how to tackle that? To be clear, I'm not asking about the theory, like the power or the chain rule, but about how you would go about implementing it.
Thanks.
Hey guys,
I've used the library for a while in an embedded environment and noticed two things:
new_expr(...)
: te_expr *ret = malloc(size);
memset(ret, 0, size);
malloc()
whatsoever as a preprocessor configuration option, and preallocate the memory in a configurable size given by the user. That would limit of course the amount of parseable nested expressions to the given configured size.Are you interested in implementing that or me dropping in some PRs?
Cheers
Dear,
Do you have any plans to merge it to master or should I use the logic branch if I need logic/equality operators?
Thank you!
A trivial fix and I do not know if this violates some style guide you adhere to or not (it certainly doesnt look as neat as the original).
In tinyexpr.c comma does not use 'a'. This generates a warning if -Wunused-variable is enabled. The easy fix is just to cast it as a void as follows (pragma instructions are too compiler dependent).
static double comma(double a, double b) {(void)a; return b;}
Since its just 1 line I did not attach a diff or do a pull request.
I get this LINK error when including tinyexpr.h and tinyexpr.c files in a dynamic library C++ project.
The project can't compile.
Is it a configuration mismatch or does the "#ifdef __cplusplus extern "C" { #endif" has a problem ?
I'm using Visual studio 2015.
I want to start with thanking you for your effort to create this amazing library. I am a C novice, but it was rather straightforward so far to use this library.
The problem that I am facing is solving nested expressions.
Basically I have formulas that look like this: quantity * combined_price
, where combined_price
is an expression itself calculated from two variables: materials_price + service_fee
.
Questions:
Hi, This may be a stupid question but I wonder if I have to call te_free()
even when te_compile()
returns 0.
I mean do I need to free it only after te_eval()
is called? or do I still need to free it even when te_compile()
returns 0 so I don't call te_eval()
?
Thanks in advance.
I've been using TinyExpr in my project and recently needed standard comparison operators (<, <=, >, >=) and boolean logic (!, &&, ||). I've just implemented this in my fork with this commit cschreib@c1b01d8 and this one cschreib@4cdeb91 (adding == and !=).
Booleans are simply 0.0
for false
and 1.0
(or any non-zero value) for true
.
I've written a test suite to make sure that the precedence with respect to other binary operators is correct, and it all seems to work fine. There is one little thing that I do not understand though, is that I was expecting !!1
to produce 1
, since --1
produces 1
in TinyExpr. I do not understand what the difference is, but !!1
currently produces 0
regardless of how many negation operators are used. It's no big deal for me, but it would be good if this worked to be consistent with the other unary operators.
Let me know if you're interested in merging this feature, then I'll prepare a pull request.
te_expr ret = malloc(size);
88 31 D:\ ...\Mywork\Cpp\Starter\tinyexpr.c [Error] invalid conversion from 'void' to 'te_expr*' [-fpermissive]
case TE_FUNCTION7: case TE_CLOSURE7: te_free(n->parameters[6]); / Falls through. /
102 69 D:\ ...\Mywork\Cpp\Starter\tinyexpr.c [Error] invalid conversion from 'void*' to 'te_expr*' [-fpermissive]
...
183 1 D:\ ...\Mywork\Cpp\Starter\tinyexpr.c [Error] invalid conversion from 'double ()(double)' to 'const void' [-fpermissive]
Call code:
int main()
{
std::cout << "Input arithmetic calculation: \n";
std::string calc;
std::cin >> calc;
double result = te_interp("calc", 0);
std::cout << "Result = ";
std::cout << result << '\n';
return 0;
}
TinyExpr is currently an AST walking interpreter. Interpreting bytecode might be a little faster. If you want competitive speed then JITing is the way to go but then this library will nolonger be tiny, simple or cross-platform. I haven't done any benchmarks with a mock bytecode interpreter but it might be worthwhile. I'd be glad to implement it myself (it sounds like a fun challenge!).
I'll do some benchmarks and see if this makes sense.
There is a problem with these types of expressions. For example 2++-3 returns -1 but I think it is not valid expression.
I used visual studio 2015 to compile tinyexpr, but I got two compile errors(initializer is not a constant) when meet
{"ceil", ceil, TE_FUNCTION1 | TE_FLAG_PURE, 0} and {"floor", floor, TE_FUNCTION1 | TE_FLAG_PURE, 0} line.
It only happens under release, but there is nothing happed under debug mode.
Thanks for your effort in this project.
I have been wondering if there is a way to evaluate terms with implicit multiplication.
2x
2sin(x)
sin(x)cos(x)
2(5)
...
Is that to much weight for TinyExpr?
Thanks in advance
Hello,
im creating a geometry node in blender 3d for create parametric surfaces and im using your famous library.
btw i would like to use any formula, by ex with a formula (with predefined params u and v) :
cos(v) * ( r0 + r1 * cos(v) )
can be usefull if i can get the var names r0 and r1.
like that, i could bind them to some double var and recompile the expr.
is there a way to achieve that ?
for ex, here the current node : https://twitter.com/aiekick/status/1412574701587738626
Thanks
why isnt there the ability to do something like "a=24" "b=a+4" "c=ab" or "a = 8 + 5; b=a+4"
i want to use this in mysql udf
sample code here https://github.com/MariaDB/server/blob/10.3/sql/udf_example.c
i just tried to #include tinyexpr.h
then use
double r = te_interp(input, 0);
return r;
it shows error
mcalc.cc:82:31: error: 'te_interp' was not declared in this scope
I have a project where I solve mathematical expressions using OpenCL, but
as as a fallback to devices that do not support GPU acceleration, I'm using
this library, but there is one problem, the difference in syntax of the
power function.
In OpenCL, which is a subset of C99, there is no '^', so every time I use
'a ^ b' in the expression, I would have to parse it and substitute for
'pow(a, b)', and that could be a complicated expression to parse. And
because there is no 'pow' in this lib, I would have to do the reverse if I
decided to stick to only using 'pow'.
This made me want to either add pow to the builtin functions, or something
even better; I would like to suggest adding the capability to define custom
functions to the library. The only problem with this would be that right
now, builtin functions are constant, so there would be two options:
The syntax for it would probably look something like this:
te_func("pow", my_power_function);
Then, when using it in the expression, it would count the number of
arguments being used, and assume that the function receives that number of
arguments, and cast it to '(double)(*func)(double, double)'
The only problem with that approach would be that if the user uses a wrong
number of arguments by mistake, the result would be unexpected, and depend
on the C implementation.
An alternative would be to explicitly state the number of arguments:
te_func("pow", my_power_function, 2);
Making it so it's cast to the right type, and add the possibility to return
an error if the user calls it with a wrong number of arguments.
I've recently discovered via fuzzing with libFuzzer that nested usage of the ncr()
expression can result in unexpectedly long expression evaluation times.
The smallest representation of the problematic expression that I could find so far, as generated by libFuzzer:
ncr(ncr(2,7),1)
I measured around ~60s runtime at -O3 (with fuzzing instrumentation overhead) on a modern AMD Ryzen CPU.
This may be a low-severity security issue for projects that evaluate externally provided expression input.
The undefined behavior sanitizer also complains about the 'unsigned int' range, here are some variants:
tinyexpr.c:127:23: runtime error: nan is outside the range of representable values of type 'unsigned int'
tinyexpr.c:139:28: runtime error: -nan is outside the range of representable values of type 'unsigned int'
tinyexpr.c:139:28: runtime error: nan is outside the range of representable values of type 'unsigned int'
tinyexpr.c:139:52: runtime error: -nan is outside the range of representable values of type 'unsigned int'
tinyexpr.c:139:52: runtime error: nan is outside the range of representable values of type 'unsigned int'
Some gdb debug info for the above mentioned ncr(ncr(2,7),1)
case where this happens:
Thread 1 "tinyexpr_fuzzer" hit Breakpoint 1, ncr
(n=nan(0x8000000000000), r=1) at tinyexpr.c:139
139 unsigned long int un = (unsigned int)(n), ur = (unsigned int)(r), i;
(gdb) bt
#0 ncr (n=nan(0x8000000000000), r=1) at tinyexpr.c:139
#1 0x000000000055d8fe in te_eval (n=0x603000000100) at tinyexpr.c:532
#2 0x0000000000569940 in optimize (n=0x603000000100) at tinyexpr.c:580
#3 0x00000000005681b9 in te_compile (expression=0x7fffffffd780 "ncr(ncr(2,7),1)", variables=0x0, var_count=0, error=0x0) at tinyexpr.c:606
#4 0x0000000000569d6b in te_interp (expression=0x7fffffffd780 "ncr(ncr(2,7),1)", error=0x0) at tinyexpr.c:614
#5 0x000000000055366b in LLVMFuzzerTestOneInput (data=0x6020000000b0 "ncr(ncr(2,7),1)", size=15) at tinyexpr_fuzzer.c:29
(gdb) print r
$1 = 1
(gdb) print n
$2 = nan(0x8000000000000)
(gdb) print i
$3 = 105690555220240
I have privately reported this issue to @codeplea. He asked me to document it via a public Github issue.
What would you think of me adding the possibility to compile the
benchmarking tool with support for GNU's libmatheval, and other languages
commonly used to solve expressions in C like perl and python?
It wouldn't bring the libraries with the program, so there is no problem
about size. It would just add the possibility to test them in case the user
has them installed.
Ideally, 5!
would be equivalent to 5*4*3*2
, which could be implemented using a recursive function:
double factorial(double n) {
if (n < 1) {
return 1;
}
return n * factorial(n - 1);
}
Of course, some error checking would be required, as calculating the factorial of negatives or fractional numbers is invalid.
Hi,
I have been thinking about using your library with uint64_t instead of doubles and only use it for integer expressions (so things like sin(x)
will not work, this is fine)
I wonder if you see any issues with that or that it should work fine to replace all floats with uint64_t?
Nice job! What can be done in order to have evaluated expression as function f(x)?
Such as:
double f (double x)
{return [parsed expression, p.e. x+3];}
I use code from example2
to compile expressions such as:
x > 2
x >= 3
x != 3
x == 3
All I get is these types of errors:
$ ./example2.exe "(x > 1)"
Evaluating:
(x > 1)
How can I use tinyexpr to return 0 or 1 for the inequalities ?
There are warnings related to different const qualifiers.
Built under Microsoft (R) C/C++ Optimizing Compiler Version 19.16.27027.1 for x86
Hi, I'm using TinyExpr for my project and I wonder If it would be possible to implement a special function that uses square brackets[] used to access array variables.
For example, If I have arr[1+2] in an expression, it will calculate 1+2 first, and then find *(arr + 3) to get the value.
Would it be too much complicated work to implement this feature?
If not, any advise or guidance would be greatly appreciated.
i have variable with underscore, e.g. my_var. There is parsing erorr
👋 Hello, we've received a report for a potential medium severity security issue in your repository.
1️⃣ Visit https://huntr.dev/bounties/1-other-codeplea/tinyexpr for more advisory information.
2️⃣ Sign-up to validate or speak to the researcher for more assistance.
3️⃣ Propose a patch or outsource it to our community.
Join us on our Discord and a member of our team will be happy to help! 🤗
Speak to a member of our team: @JamieSlome
This issue was automatically generated by huntr.dev - a bug bounty board for securing open source code.
I'm getting the output -1048576.459961 for this snippet of code, which should equal 7. What's up with that?
{
float a, b, c, d, e, f, g;
const char formula[50] = "5 * b - a * c";
int err;
te_variable vr[] = { {"a",&a}, {"b",&b}, {"c",&c}, { "d",&d }, { "e",&e }, { "f",&f }, { "g",&g } };
const te_expr* expr = te_compile(formula, vr, 7, &err);
a = 1.0f;
b = 2.0f;
c = 3.0f;
d = 4.0f;
e = 5.0f;
f = 6.0f;
g = 7.0f;
printf("%lf\n", te_eval(expr));
}
Similarly, a+b+c = 32800.015663
I knows it's 2021, but come on:P
Hi, i'm having some issues with tinyexpr.
The problem is in this configuration :
variables :
Expressions :
"log10(rt)" => Ok with good result
"log10(temp)" => Nan : OK
"log10(rh)" => error near ")"
"log10(hum)" => error near ")"
if i remove rt from my variables:
"log10(temp)" => Nan : OK
"log10(rh)" = Ok with good result
"log10(hum)" => error near ")"
if i remove rt and temp from my variables:
"log10(rh)" = Ok with good result
"log10(hum)" => Nan : OK
So it seems that if i have more than 2 variables and the variable used in the expression is after the second one the tinyexpr can't compile the expression...
Thansk for your help.
This is a feature request, which I can't seem to be able to implement
without making way too many changes, I need a function which only receives
the const char *
expression and returns the array of variables used in such
expression.
te_variable *te_get_variables(const char *expression, int *var_count);
The purpose of this, is that I want the user to be able to define the
variables just by using them, and I don't think it would be practical to be
using a separate parser, when this library already has it implemented.
Having this array, then I could easily allocate the double
memory for the
values, then assign and feed the array to te_compile.
When I evaluate (-1)^0
I get -1, which is wrong. Yet when I evaluate pow(-1, 0)
I get 1, which is correct. It seems to be an expression compilation error since te_eval only runs case TE_CONSTANT: return n->value;
. (-x)^0
also always yields -1 regardless of the value of x, while x^0
and (0-x)^0
correctly yield 1. I'm using the logic branch in case that matters, with TE_POW_FROM_RIGHT
defined.
Btw for the ease of debugging I suggest that you name your tokens enum, something like enum token
and use enum token type;
instead of int type;
in the state
struct, this way debuggers will show the name of the enum.
I haven't found a software version number, and there don't appear to be any git tags with releases. Having a version number included in the header file would make it easier to tell if the library is up to date.
In the case that tinyexpr.h
is installed instead of copied into a project, a version number inside a pre-processor define would allow conditionally working around bugs that may be present in older versions.
Would the maintainer be willing to add a version number?
Hello,
I would like to ask if this project could be made available and allowed to be libre, GNU,... for Unix community?
ZLIB is compatible with GNU GPL, but the licence GNU GPL would be a nicer licensing.
Would it be possible?
Thank you
Best regards
More info: https://www.gnu.org/licenses/license-list.en.html
Hi.
I think it would be nice to have the option to build with CMake.
A lot of new projects (including mine) make use of it.
Please, see my PR, thanks: #26
nCr
and nPr
, while achievable with factorials (see #14), would be nice to have as "helpers".
double npr(double n, double r) {
return factorial(n) / factorial(n - r);
}
double ncr(double n, double r) {
return factorial(n) / (factorial(r) * factorial(n - r));
}
As with factorials, some checks are required to ensure that people don't get unexpected results from negative or fractional numbers.
I have just had a user reporting they could not build my project because of tinyexpr.c
refusing to compile. The reason why was that they were using GCC 4.4.7. Indeed, exploring this with godbolt.org, I found that GCC < 4.6 will not compile TinyExpr when the -ansi
option is set. This would seem like correct behavior, because you use an anonymous union in the te_expr
structure, which is a feature only available with the C11 standard, and it should not compile in ANSI C. Intriguingly, GCC only started supporting C11 in versions >= 4.6, so GCC 4.4 should not be able to compile this anyway. I suppose it does only because anonymous unions also happen to be a GNU extension, which gets enabled if -std=gnu99
(indeed, the default in GCC 4.4).
Why GCC >= 4.6 accepts this as valid ANSI code, I have no idea. Sounds like a bug to me.
I ended up fixing the issue by removing the -ansi
option from the compiler options (in the CMake scripts I have at https://github.com/cschreib/tinyexpr). Given that TinyExpr uses some features from C11, it seems that specifying -ansi
was indeed incorrect, but given the mess above I'm happy to hear your opinion on this. I suppose a proper implementation would explicitly ask for -std=c11
for GCC >= 4.6 and -std=gnu99
for GCC < 4.6. But it seems that dropping -ansi
effectively does that, so...
Anyway, I thought you might want to consider making this modification for the Makefile as well, hence the issue.
I love single file libraries, but it should be clear the performance of the lib with respect to the other common choices like muparser ( http://muparser.beltoforion.de/ ).
Could you add a very simple comparison?
Thanks!
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.