Giter VIP home page Giter VIP logo

tinyexpr's People

Contributors

bavay avatar blake-madden avatar chendotjs avatar codeplea avatar evilpudding avatar giacomo-b avatar mortie avatar nicolasdanet avatar timgates42 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

tinyexpr's Issues

Compilation under Visual Studio 2010

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:

  1. The macros 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
  1. The malloc() call at tinyexpr.c, line 88 should be prefixed with (te_expr *) to cast void* to te_expr *.

  2. The function fac() needs the declarations of the following at the beginning of the function:

    unsigned int ua;
    unsigned long int result, i;
  1. The function ncr() needs the declarations of the following at the beginning of the function:
    unsigned long int un, ur, i;
    unsigned long int result;
  1. The function next_token() needs the declarations of the following at the beginning of the function:
    const te_variable *var = NULL;
  1. 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.

  2. 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.

  3. 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;
  1. Visual Studio 2010 do not support ... syntax in a define statement like the one at tinyexpr.c, line 82.
    This one is more complicated to fix and the fix is less elegant than actual code but please consider to use these corrections. I managed to fix these errors by declaring a macro for each variant of the number of argument:
#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.

"initializer is not a constant" error in Visual Studio.

Hi, I tried to build tinyexpr using Visual Studio on Windows 10 but an error occurs saying "initializer is not a constant".
screen shot 2018-01-21 at 4 04 27 am
If I remove all elements in functions[] array, the error no longer occurred.
Could someone please teach me how to fix this error?
Thank you so much in advance!

complex number calculation

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!

Undefined reference to `te_interp`

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

Undefined reference to 'fabs' on arduino ide 1.8

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.

Addition to benchmark suite

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:

  1. https://github.com/ArashPartow/math-parser-benchmark-project/blob/master/tinyexpr/tinyexpr.c#L232
  2. https://github.com/ArashPartow/math-parser-benchmark-project/blob/master/tinyexpr/tinyexpr.h#L38
  3. https://github.com/ArashPartow/math-parser-benchmark-project/blob/master/tinyexpr/tinyexpr.h#L56

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

Adding the random() function?

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!

Is there a chance to compile it with C++?

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.

reason to limit variable names?

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.

Are upppercase letters in variables and functions supported?

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.

Program

#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;
}

Cannot use with trim function

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').

Support for symbolic differentiation

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.

Out of memory check and static memory implementation

Hey guys,

I've used the library for a while in an embedded environment and noticed two things:

  • You don't support the case of failed memory allocation, which can happen in resource-constrained devices.
    In new_expr(...):
    te_expr *ret = malloc(size);
    memset(ret, 0, size);
  • It would be nice to have the option of static memory allocation for embedded applications. That is to drop 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

Question about logic branch

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!

Compiler warning - fix

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.

Nested expressions / Passing sub-expressions as variables

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:

  • is it possible to solve these expressions with today's version (could not figure out from source "how", if it is possible) ?
  • if not, is it something you would consider as an addition to your library? Would you be able to guide me with implementing such patch? What would be the API to pass such things ?

Question regarding te_free()

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.

Comparisons and logical operators

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.

Invalid conversion compiler warnings

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;
}

Bytecode interpreter?

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.

Compiling error : initializer is not a constant

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.

Implicit multiplication?

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

get undeclared variables names

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

no variable assigning?

why isnt there the ability to do something like "a=24" "b=a+4" "c=ab" or "a = 8 + 5; b=a+4"

power

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:

  1. Change all functions to one dynamic array.
    • Pros: Only needs one binary search.
    • Cons: There would need be an init call to fill the builtin functions,
      or the first call to any function would have to check if the functions
      were initialized.
  2. Keep the constant builtin array, and one dynamic array for custom
    functions.
    • Pros: No need for initialization; Possibility to override builtin
      functions by searching the custom functions before searching the builtin
      ones.
    • Cons: Two binary searches would have to be performed.

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.

nested ncr() calls can be very slow (potential denial of service)

Issue description

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.

Debug information:

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

Disclosure information

I have privately reported this issue to @codeplea. He asked me to document it via a public Github issue.

Benchmarking

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.

Add support for factorials

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.

Using uint64_t instead of double

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?

New function

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];}

How to use with inequalities ?

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 ?

Compiler warnings.

There are warnings related to different const qualifiers.
Built under Microsoft (R) C/C++ Optimizing Compiler Version 19.16.27027.1 for x86

Is it possible to use array as variable?

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.

Potential Security Issue

👋 Hello, we've received a report for a potential medium severity security issue in your repository.

Next Steps

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.


Confused or need more help?

  • 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.

Result is wayyyy off.

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

Problem when having more than 2 variables and some not used

Hi, i'm having some issues with tinyexpr.

The problem is in this configuration :

variables :

  • rt = 6000
  • temp = 0
  • rh = 1000
  • hum = 0

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.

Parse expression, get variable list

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.

(-1)^0 returns -1 instead of 1

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.

Software version and tagged release?

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?

Missing CMake build system support

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

Add support for combinations and permutations

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.

Compiling with GCC < 4.6

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.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.