diff --git a/Makefile b/Makefile index cc9765c..fc0f391 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -CCFLAGS = -ansi -Wall -Wshadow -O2 +CCFLAGS = -O2 -std=c99 -Wall -Wextra -Wshadow -Wcast-align -Wmissing-declarations -Wmissing-prototypes -Wconversion -Wno-long-long -pedantic LFLAGS = -lm .PHONY = all clean diff --git a/benchmark.c b/benchmark.c index 31d18fa..b56e7e3 100644 --- a/benchmark.c +++ b/benchmark.c @@ -35,13 +35,13 @@ typedef double (*function1)(double); -void bench(const char *expr, function1 func) { +static void bench(const char *expr, function1 func) { int i, j; volatile double d; - double tmp; + static double tmp; clock_t start; - te_variable lk = {"a", &tmp}; + te_variable lk = {"a", {&tmp}, TE_VARIABLE, NULL}; printf("Expression: %s\n", expr); @@ -53,12 +53,12 @@ void bench(const char *expr, function1 func) { tmp = i; d += func(tmp); } - const int nelapsed = (clock() - start) * 1000 / CLOCKS_PER_SEC; + const long int nelapsed = (clock() - start) * 1000 / CLOCKS_PER_SEC; /*Million floats per second input.*/ printf(" %.5g", d); if (nelapsed) - printf("\t%5dms\t%5dmfps\n", nelapsed, loops * loops / nelapsed / 1000); + printf("\t%5ldms\t%5ldmfps\n", nelapsed, loops * loops / nelapsed / 1000); else printf("\tinf\n"); @@ -74,47 +74,47 @@ void bench(const char *expr, function1 func) { tmp = i; d += te_eval(n); } - const int eelapsed = (clock() - start) * 1000 / CLOCKS_PER_SEC; + const long int eelapsed = (clock() - start) * 1000 / CLOCKS_PER_SEC; te_free(n); /*Million floats per second input.*/ printf(" %.5g", d); if (eelapsed) - printf("\t%5dms\t%5dmfps\n", eelapsed, loops * loops / eelapsed / 1000); + printf("\t%5ldms\t%5ldmfps\n", eelapsed, loops * loops / eelapsed / 1000); else printf("\tinf\n"); - printf("%.2f%% longer\n", (((double)eelapsed / nelapsed) - 1.0) * 100.0); + printf("%.2f%% longer\n", (((double)eelapsed / (double)nelapsed) - 1.0) * 100.0); printf("\n"); } -double a5(double a) { +static double a5(double a) { return a+5; } -double a52(double a) { +static double a52(double a) { return (a+5)*2; } -double a10(double a) { +static double a10(double a) { return a+(5*2); } -double as(double a) { +static double as(double a) { return sqrt(pow(a, 1.5) + pow(a, 2.5)); } -double al(double a) { +static double al(double a) { return (1/(a+1)+2/(a+2)+3/(a+3)); } int main(int argc, char *argv[]) { - + (void)argc; (void)argv; bench("sqrt(a^1.5+a^2.5)", as); bench("a+5", a5); bench("a+(5*2)", a10); diff --git a/cmake/CMakeLists.txt b/cmake/CMakeLists.txt new file mode 100755 index 0000000..c48323d --- /dev/null +++ b/cmake/CMakeLists.txt @@ -0,0 +1,95 @@ +# Copyright (C) 2016-2018 |Meso|Star> +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +cmake_minimum_required(VERSION 3.8) +project(tinyexpr C) +enable_testing() + +option(LIB_ONLY "Do not compile the test pograms nor the benchmark" OFF) + +set(TINYEXPR_SOURCE_DIR ${PROJECT_SOURCE_DIR}/..) + +include_directories(${TINYEXPR_SOURCE_DIR}) + +################################################################################ +# Configure and define targets +################################################################################ +set(VERSION_MAJOR 1) +set(VERSION_MINOR 0) +set(VERSION_PATCH 0) +set(VERSION ${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}) + +if(CMAKE_COMPILER_IS_GNUCC) + set(MATH_LIB m) +endif() + +if(MSVC) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /W4") +elseif(CMAKE_COMPILER_IS_GNUCC) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra -Wshadow -Wcast-align -Wmissing-declarations -Wmissing-prototypes -Wconversion -Wno-long-long -pedantic") +endif() + +ADD_LIBRARY(tinyexpr STATIC + ${TINYEXPR_SOURCE_DIR}/tinyexpr.c ${TINYEXPR_SOURCE_DIR}/tinyexpr.h) +target_compile_features(tinyexpr PUBLIC c_std_99) +set_target_properties(tinyexpr PROPERTIES + VERSION ${VERSION} + SOVERSION ${VERSION_MAJOR}) + +################################################################################ +# Define tests +################################################################################ +if(NOT LIB_ONLY) + add_executable(bench ${TINYEXPR_SOURCE_DIR}/benchmark.c) + target_link_libraries(bench tinyexpr ${MATH_LIB}) + + add_executable(example ${TINYEXPR_SOURCE_DIR}/example.c) + target_link_libraries(example tinyexpr ${MATH_LIB}) + add_test(example example) + + add_executable(example2 ${TINYEXPR_SOURCE_DIR}/example2.c) + target_link_libraries(example2 tinyexpr ${MATH_LIB}) + add_test(example2 example2) + + add_executable(example3 ${TINYEXPR_SOURCE_DIR}/example3.c) + target_link_libraries(example3 tinyexpr ${MATH_LIB}) + add_test(example3 example3) + + add_executable(test_pow_left ${TINYEXPR_SOURCE_DIR}/test.c) + target_link_libraries(test_pow_left tinyexpr ${MATH_LIB}) + add_test(test_pow_left test_pow_left) + + file(READ ${TINYEXPR_SOURCE_DIR}/tinyexpr.c tinyexpr_c_file_content) + file(WRITE ${PROJECT_BINARY_DIR}/tinyexpr_pow_right.c +"#define TE_POW_FROM_RIGHT 1 +#define TE_NAT_LOG 1 +${tinyexpr_c_file_content}") + + file(READ ${TINYEXPR_SOURCE_DIR}/test.c test_c_file_content) + file(WRITE ${PROJECT_BINARY_DIR}/test_pow_right.c +"#define TE_POW_FROM_RIGHT 1 +#define TE_NAT_LOG 1 +${test_c_file_content}") + + ADD_LIBRARY(tinyexpr_pow_right STATIC + ${PROJECT_BINARY_DIR}/tinyexpr_pow_right.c ${TINYEXPR_SOURCE_DIR}/tinyexpr.h) + target_compile_features(tinyexpr_pow_right PUBLIC c_std_99) + set_target_properties(tinyexpr_pow_right PROPERTIES VERSION ${VERSION}) + + add_executable(test_pow_right ${PROJECT_BINARY_DIR}/test_pow_right.c) + add_dependencies(test_pow_right tinyexpr_pow_right) + target_link_libraries(test_pow_right tinyexpr_pow_right ${MATH_LIB}) + add_test(test_pow_right test_pow_right) +endif(NOT LIB_ONLY) diff --git a/example.c b/example.c index 040093f..f6c68a0 100644 --- a/example.c +++ b/example.c @@ -3,6 +3,7 @@ int main(int argc, char *argv[]) { + (void)argc; (void)argv; const char *c = "sqrt(5^2+7^2+11^2+(8-2)^2)"; double r = te_interp(c, 0); printf("The expression:\n\t%s\nevaluates to:\n\t%f\n", c, r); diff --git a/example2.c b/example2.c index 70d05d1..7ebe503 100644 --- a/example2.c +++ b/example2.c @@ -3,6 +3,7 @@ int main(int argc, char *argv[]) { + (void)argc; (void)argv; if (argc < 2) { printf("Usage: example2 \"expression\"\n"); return 0; @@ -13,8 +14,8 @@ int main(int argc, char *argv[]) /* This shows an example where the variables * x and y are bound at eval-time. */ - double x, y; - te_variable vars[] = {{"x", &x}, {"y", &y}}; + static double x, y; + te_variable vars[] = {{"x", {&x}, TE_VARIABLE, NULL}, {"y", {&y}, TE_VARIABLE, NULL}}; /* This will compile the expression and check for errors. */ int err; diff --git a/example3.c b/example3.c index 80fe9da..abb1089 100644 --- a/example3.c +++ b/example3.c @@ -3,7 +3,7 @@ /* An example of calling a C function. */ -double my_sum(double a, double b) { +static double my_sum(double a, double b) { printf("Called C function with %f and %f.\n", a, b); return a + b; } @@ -11,8 +11,9 @@ double my_sum(double a, double b) { int main(int argc, char *argv[]) { + (void)argc; (void)argv; te_variable vars[] = { - {"mysum", my_sum, TE_FUNCTION2} + {"mysum", {.f2=my_sum}, TE_FUNCTION2, NULL} }; const char *expression = "mysum(5, 6)"; diff --git a/test.c b/test.c index c772950..dfcaf64 100644 --- a/test.c +++ b/test.c @@ -39,7 +39,7 @@ typedef struct { -void test_results() { +static void test_results() { test_case cases[] = { {"1", 1}, {"1 ", 1}, @@ -147,7 +147,7 @@ void test_results() { }; - int i; + size_t i; for (i = 0; i < sizeof(cases) / sizeof(test_case); ++i) { const char *expr = cases[i].expr; const double answer = cases[i].answer; @@ -164,7 +164,7 @@ void test_results() { } -void test_syntax() { +static void test_syntax() { test_case errors[] = { {"", 1}, {"1+", 2}, @@ -182,10 +182,10 @@ void test_syntax() { }; - int i; + size_t i; for (i = 0; i < sizeof(errors) / sizeof(test_case); ++i) { const char *expr = errors[i].expr; - const int e = errors[i].answer; + const int e = (int)errors[i].answer; int err; const double r = te_interp(expr, &err); @@ -206,7 +206,7 @@ void test_syntax() { } -void test_nans() { +static void test_nans() { const char *nans[] = { "0/0", @@ -222,7 +222,7 @@ void test_nans() { "npr(2, -4)", }; - int i; + size_t i; for (i = 0; i < sizeof(nans) / sizeof(const char *); ++i) { const char *expr = nans[i]; @@ -241,7 +241,7 @@ void test_nans() { } -void test_infs() { +static void test_infs() { const char *infs[] = { "1/0", @@ -256,7 +256,7 @@ void test_infs() { "npr(30,25)", }; - int i; + size_t i; for (i = 0; i < sizeof(infs) / sizeof(const char *); ++i) { const char *expr = infs[i]; @@ -275,10 +275,13 @@ void test_infs() { } -void test_variables() { +static void test_variables() { - double x, y, test; - te_variable lookup[] = {{"x", &x}, {"y", &y}, {"te_st", &test}}; + static double x, y, test; + te_variable lookup[] = + {{"x", {&x}, TE_VARIABLE, NULL}, + {"y", {&y}, TE_VARIABLE, NULL}, + {"te_st", {&test}, TE_VARIABLE, NULL}}; int err; @@ -351,10 +354,11 @@ void test_variables() { te_free(expr);\ }while(0) -void test_functions() { +static void test_functions() { - double x, y; - te_variable lookup[] = {{"x", &x}, {"y", &y}}; + static double x, y; + te_variable lookup[] = + {{"x", {&x}, TE_VARIABLE, NULL}, {"y", {&y}, TE_VARIABLE, NULL}}; int err; te_expr *expr; @@ -386,46 +390,46 @@ void test_functions() { } -double sum0() { +static double sum0(void) { return 6; } -double sum1(double a) { +static double sum1(double a) { return a * 2; } -double sum2(double a, double b) { +static double sum2(double a, double b) { return a + b; } -double sum3(double a, double b, double c) { +static double sum3(double a, double b, double c) { return a + b + c; } -double sum4(double a, double b, double c, double d) { +static 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) { +static 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) { +static 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) { +static 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() { +static void test_dynamic() { - double x, f; + static 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}, + {"x", {&x}, TE_VARIABLE, NULL}, + {"f", {&f}, TE_VARIABLE, NULL}, + {"sum0", {.f0=sum0}, TE_FUNCTION0, NULL}, + {"sum1", {.f1=sum1}, TE_FUNCTION1, NULL}, + {"sum2", {.f2=sum2}, TE_FUNCTION2, NULL}, + {"sum3", {.f3=sum3}, TE_FUNCTION3, NULL}, + {"sum4", {.f4=sum4}, TE_FUNCTION4, NULL}, + {"sum5", {.f5=sum5}, TE_FUNCTION5, NULL}, + {"sum6", {.f6=sum6}, TE_FUNCTION6, NULL}, + {"sum7", {.f7=sum7}, TE_FUNCTION7, NULL}, }; test_case cases[] = { @@ -456,7 +460,7 @@ void test_dynamic() { x = 2; f = 5; - int i; + size_t i; for (i = 0; i < sizeof(cases) / sizeof(test_case); ++i) { const char *expr = cases[i].expr; const double answer = cases[i].answer; @@ -470,34 +474,34 @@ void test_dynamic() { } -double clo0(void *context) { +static double clo0(void *context) { if (context) return *((double*)context) + 6; return 6; } -double clo1(void *context, double a) { +static double clo1(void *context, double a) { if (context) return *((double*)context) + a * 2; return a * 2; } -double clo2(void *context, double a, double b) { +static double clo2(void *context, double a, double b) { if (context) return *((double*)context) + a + b; return a + b; } -double cell(void *context, double a) { +static double cell(void *context, double a) { double *c = context; return c[(int)a]; } -void test_closure() { +static void test_closure() { - double extra; - double c[] = {5,6,7,8,9}; + static double extra; + static 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}, + {"c0", {.cl0=clo0}, TE_CLOSURE0, &extra}, + {"c1", {.cl1=clo1}, TE_CLOSURE1, &extra}, + {"c2", {.cl2=clo2}, TE_CLOSURE2, &extra}, + {"cell", {.cl1=cell}, TE_CLOSURE1, c}, }; test_case cases[] = { @@ -506,7 +510,7 @@ void test_closure() { {"c2 (10, 20)", 30}, }; - int i; + size_t i; for (i = 0; i < sizeof(cases) / sizeof(test_case); ++i) { const char *expr = cases[i].expr; const double answer = cases[i].answer; @@ -544,7 +548,7 @@ void test_closure() { } } -void test_optimize() { +static void test_optimize() { test_case cases[] = { {"5+5", 10}, @@ -553,7 +557,7 @@ void test_optimize() { {"pi * 2", 6.2832}, }; - int i; + size_t i; for (i = 0; i < sizeof(cases) / sizeof(test_case); ++i) { const char *expr = cases[i].expr; const double answer = cases[i].answer; @@ -564,14 +568,14 @@ void test_optimize() { /* The answer should be know without * even running eval. */ - lfequal(ex->value, answer); + lfequal(ex->v.value, answer); lfequal(te_eval(ex), answer); te_free(ex); } } -void test_pow() { +static void test_pow() { #ifdef TE_POW_FROM_RIGHT test_equ cases[] = { {"2^3^4", "2^(3^4)"}, @@ -598,14 +602,14 @@ void test_pow() { }; #endif - double a = 2, b = 3; + static double a = 2, b = 3; te_variable lookup[] = { - {"a", &a}, - {"b", &b} + {"a", {&a}, TE_VARIABLE, NULL}, + {"b", {&b}, TE_VARIABLE, NULL} }; - int i; + size_t i; for (i = 0; i < sizeof(cases) / sizeof(test_equ); ++i) { const char *expr1 = cases[i].expr1; const char *expr2 = cases[i].expr2; @@ -628,7 +632,7 @@ void test_pow() { } -void test_combinatorics() { +static void test_combinatorics() { test_case cases[] = { {"fac(0)", 1}, {"fac(0.2)", 1}, @@ -655,7 +659,7 @@ void test_combinatorics() { }; - int i; + size_t i; for (i = 0; i < sizeof(cases) / sizeof(test_case); ++i) { const char *expr = cases[i].expr; const double answer = cases[i].answer; @@ -674,6 +678,7 @@ void test_combinatorics() { int main(int argc, char *argv[]) { + (void)argc; (void)argv; lrun("Results", test_results); lrun("Syntax", test_syntax); lrun("NaNs", test_nans); diff --git a/tinyexpr.c b/tinyexpr.c old mode 100755 new mode 100644 index 90ed8fc..e070750 --- a/tinyexpr.c +++ b/tinyexpr.c @@ -40,6 +40,7 @@ For log = natural log uncomment the next line. */ #include #include #include +#include #ifndef NAN #define NAN (0.0/0.0) @@ -65,7 +66,7 @@ typedef struct state { const char *start; const char *next; int type; - union {double value; const double *bound; const void *function;}; + union value v; void *context; const te_variable *lookup; @@ -82,21 +83,43 @@ typedef struct state { #define NEW_EXPR(type, ...) new_expr((type), (const te_expr*[]){__VA_ARGS__}) static te_expr *new_expr(const int type, const te_expr *parameters[]) { - const int arity = ARITY(type); - const int psize = sizeof(void*) * arity; - const int size = (sizeof(te_expr) - sizeof(void*)) + psize + (IS_CLOSURE(type) ? sizeof(void*) : 0); + const size_t arity = ARITY(type); + const size_t psize = sizeof(void*) * arity; + const size_t size = (sizeof(te_expr) - sizeof(void*)) + psize + (IS_CLOSURE(type) ? sizeof(void*) : 0); te_expr *ret = malloc(size); memset(ret, 0, size); if (arity && parameters) { memcpy(ret->parameters, parameters, psize); } ret->type = type; - ret->bound = 0; + ret->v.bound = 0; return ret; } +static te_expr *new_expr1(const int type, te_expr *p1) { + const size_t size = sizeof(te_expr) + (IS_CLOSURE(type) ? sizeof(void*) : 0); + assert(p1 && ARITY(type) == 1); + te_expr *ret = malloc(size); + ret->type = type; + ret->v.bound = 0; + ret->parameters[0] = p1; + if (IS_CLOSURE(type)) ret->parameters[1] = NULL; + return ret; +} -void te_free_parameters(te_expr *n) { +static te_expr *new_expr2(const int type, te_expr *p1, te_expr *p2) { + const size_t size = sizeof(te_expr) + sizeof(void*) + (IS_CLOSURE(type) ? sizeof(void*) : 0); + assert(p1 && p2 && ARITY(type) == 2); + te_expr *ret = malloc(size); + ret->type = type; + ret->v.bound = 0; + ret->parameters[0] = p1; + ret->parameters[1] = p2; + if (IS_CLOSURE(type)) ret->parameters[2] = NULL; + return ret; +} + +static void te_free_parameters(te_expr *n) { if (!n) return; switch (TYPE_MASK(n->type)) { case TE_FUNCTION7: case TE_CLOSURE7: te_free(n->parameters[6]); /* Falls through. */ @@ -145,44 +168,48 @@ static double ncr(double n, double r) { result *= un - ur + i; result /= i; } - return result; + return (double)result; } static double npr(double n, double r) {return ncr(n, r) * fac(r);} +/* Workaround for a VC 2017 problem */ +static double ceil_(double x) { return ceil(x); } +static double floor_(double x) { return floor(x); } + static const te_variable functions[] = { /* must be in alphabetical order */ - {"abs", fabs, TE_FUNCTION1 | TE_FLAG_PURE, 0}, - {"acos", acos, TE_FUNCTION1 | TE_FLAG_PURE, 0}, - {"asin", asin, TE_FUNCTION1 | TE_FLAG_PURE, 0}, - {"atan", atan, TE_FUNCTION1 | TE_FLAG_PURE, 0}, - {"atan2", atan2, TE_FUNCTION2 | TE_FLAG_PURE, 0}, - {"ceil", ceil, TE_FUNCTION1 | TE_FLAG_PURE, 0}, - {"cos", cos, TE_FUNCTION1 | TE_FLAG_PURE, 0}, - {"cosh", cosh, TE_FUNCTION1 | TE_FLAG_PURE, 0}, - {"e", e, TE_FUNCTION0 | TE_FLAG_PURE, 0}, - {"exp", exp, TE_FUNCTION1 | TE_FLAG_PURE, 0}, - {"fac", fac, TE_FUNCTION1 | TE_FLAG_PURE, 0}, - {"floor", floor, TE_FUNCTION1 | TE_FLAG_PURE, 0}, - {"ln", log, TE_FUNCTION1 | TE_FLAG_PURE, 0}, + {"abs", {.f1=fabs}, TE_FUNCTION1 | TE_FLAG_PURE, 0}, + {"acos", {.f1=acos}, TE_FUNCTION1 | TE_FLAG_PURE, 0}, + {"asin", {.f1=asin}, TE_FUNCTION1 | TE_FLAG_PURE, 0}, + {"atan", {.f1=atan}, TE_FUNCTION1 | TE_FLAG_PURE, 0}, + {"atan2", {.f2=atan2}, TE_FUNCTION2 | TE_FLAG_PURE, 0}, + {"ceil", {.f1=ceil_}, TE_FUNCTION1 | TE_FLAG_PURE, 0}, + {"cos", {.f1=cos}, TE_FUNCTION1 | TE_FLAG_PURE, 0}, + {"cosh", {.f1=cosh}, TE_FUNCTION1 | TE_FLAG_PURE, 0}, + {"e", {.f0=e}, TE_FUNCTION0 | TE_FLAG_PURE, 0}, + {"exp", {.f1=exp}, TE_FUNCTION1 | TE_FLAG_PURE, 0}, + {"fac", {.f1=fac}, TE_FUNCTION1 | TE_FLAG_PURE, 0}, + {"floor", {.f1=floor_}, TE_FUNCTION1 | TE_FLAG_PURE, 0}, + {"ln", {.f1=log}, TE_FUNCTION1 | TE_FLAG_PURE, 0}, #ifdef TE_NAT_LOG - {"log", log, TE_FUNCTION1 | TE_FLAG_PURE, 0}, + {"log", {.f1=log}, TE_FUNCTION1 | TE_FLAG_PURE, 0}, #else - {"log", log10, TE_FUNCTION1 | TE_FLAG_PURE, 0}, + {"log", {.f1=log10}, TE_FUNCTION1 | TE_FLAG_PURE, 0}, #endif - {"log10", log10, TE_FUNCTION1 | TE_FLAG_PURE, 0}, - {"ncr", ncr, TE_FUNCTION2 | TE_FLAG_PURE, 0}, - {"npr", npr, TE_FUNCTION2 | TE_FLAG_PURE, 0}, - {"pi", pi, TE_FUNCTION0 | TE_FLAG_PURE, 0}, - {"pow", pow, TE_FUNCTION2 | TE_FLAG_PURE, 0}, - {"sin", sin, TE_FUNCTION1 | TE_FLAG_PURE, 0}, - {"sinh", sinh, TE_FUNCTION1 | TE_FLAG_PURE, 0}, - {"sqrt", sqrt, TE_FUNCTION1 | TE_FLAG_PURE, 0}, - {"tan", tan, TE_FUNCTION1 | TE_FLAG_PURE, 0}, - {"tanh", tanh, TE_FUNCTION1 | TE_FLAG_PURE, 0}, - {0, 0, 0, 0} + {"log10", {.f1=log10}, TE_FUNCTION1 | TE_FLAG_PURE, 0}, + {"ncr", {.f2=ncr}, TE_FUNCTION2 | TE_FLAG_PURE, 0}, + {"npr", {.f2=npr}, TE_FUNCTION2 | TE_FLAG_PURE, 0}, + {"pi", {.f0=pi}, TE_FUNCTION0 | TE_FLAG_PURE, 0}, + {"pow", {.f2=pow}, TE_FUNCTION2 | TE_FLAG_PURE, 0}, + {"sin", {.f1=sin}, TE_FUNCTION1 | TE_FLAG_PURE, 0}, + {"sinh", {.f1=sinh}, TE_FUNCTION1 | TE_FLAG_PURE, 0}, + {"sqrt", {.f1=sqrt}, TE_FUNCTION1 | TE_FLAG_PURE, 0}, + {"tan", {.f1=tan}, TE_FUNCTION1 | TE_FLAG_PURE, 0}, + {"tanh", {.f1=tanh}, TE_FUNCTION1 | TE_FLAG_PURE, 0}, + {0, {0}, 0, 0} }; -static const te_variable *find_builtin(const char *name, int len) { +static const te_variable *find_builtin(const char *name, size_t len) { int imin = 0; int imax = sizeof(functions) / sizeof(te_variable) - 2; @@ -203,7 +230,7 @@ static const te_variable *find_builtin(const char *name, int len) { return 0; } -static const te_variable *find_lookup(const state *s, const char *name, int len) { +static const te_variable *find_lookup(const state *s, const char *name, size_t len) { int iters; const te_variable *var; if (!s->lookup) return 0; @@ -226,7 +253,7 @@ static double negate(double a) {return -a;} static double comma(double a, double b) {(void)a; return b;} -void next_token(state *s) { +static void next_token(state *s) { s->type = TOK_NULL; do { @@ -238,7 +265,7 @@ void next_token(state *s) { /* Try reading a number. */ if ((s->next[0] >= '0' && s->next[0] <= '9') || s->next[0] == '.') { - s->value = strtod(s->next, (char**)&s->next); + s->v.value = strtod(s->next, (char**)&s->next); s->type = TOK_NUMBER; } else { /* Look for a variable or builtin function call. */ @@ -247,8 +274,8 @@ void next_token(state *s) { start = s->next; while ((s->next[0] >= 'a' && s->next[0] <= 'z') || (s->next[0] >= '0' && s->next[0] <= '9') || (s->next[0] == '_')) s->next++; - const te_variable *var = find_lookup(s, start, s->next - start); - if (!var) var = find_builtin(start, s->next - start); + const te_variable *var = find_lookup(s, start, (size_t)(s->next - start)); + if (!var) var = find_builtin(start, (size_t)(s->next - start)); if (!var) { s->type = TOK_ERROR; @@ -257,7 +284,7 @@ void next_token(state *s) { { case TE_VARIABLE: s->type = TOK_VARIABLE; - s->bound = var->address; + s->v.bound = var->address.any; break; case TE_CLOSURE0: case TE_CLOSURE1: case TE_CLOSURE2: case TE_CLOSURE3: /* Falls through. */ @@ -267,7 +294,7 @@ void next_token(state *s) { case TE_FUNCTION0: case TE_FUNCTION1: case TE_FUNCTION2: case TE_FUNCTION3: /* Falls through. */ case TE_FUNCTION4: case TE_FUNCTION5: case TE_FUNCTION6: case TE_FUNCTION7: /* Falls through. */ s->type = var->type; - s->function = var->address; + s->v.f.any = var->address.any; break; } } @@ -275,12 +302,12 @@ void next_token(state *s) { } else { /* Look for an operator or special character. */ switch (s->next++[0]) { - case '+': s->type = TOK_INFIX; s->function = add; break; - case '-': s->type = TOK_INFIX; s->function = sub; break; - case '*': s->type = TOK_INFIX; s->function = mul; break; - case '/': s->type = TOK_INFIX; s->function = divide; break; - case '^': s->type = TOK_INFIX; s->function = pow; break; - case '%': s->type = TOK_INFIX; s->function = fmod; break; + case '+': s->type = TOK_INFIX; s->v.f.f2 = add; break; + case '-': s->type = TOK_INFIX; s->v.f.f2 = sub; break; + case '*': s->type = TOK_INFIX; s->v.f.f2 = mul; break; + case '/': s->type = TOK_INFIX; s->v.f.f2 = divide; break; + case '^': s->type = TOK_INFIX; s->v.f.f2 = pow; break; + case '%': s->type = TOK_INFIX; s->v.f.f2 = fmod; break; case '(': s->type = TOK_OPEN; break; case ')': s->type = TOK_CLOSE; break; case ',': s->type = TOK_SEP; break; @@ -305,20 +332,20 @@ static te_expr *base(state *s) { switch (TYPE_MASK(s->type)) { case TOK_NUMBER: ret = new_expr(TE_CONSTANT, 0); - ret->value = s->value; + ret->v.value = s->v.value; next_token(s); break; case TOK_VARIABLE: ret = new_expr(TE_VARIABLE, 0); - ret->bound = s->bound; + ret->v.bound = s->v.bound; next_token(s); break; case TE_FUNCTION0: case TE_CLOSURE0: ret = new_expr(s->type, 0); - ret->function = s->function; + ret->v.f.any = s->v.f.any; if (IS_CLOSURE(s->type)) ret->parameters[0] = s->context; next_token(s); if (s->type == TOK_OPEN) { @@ -334,7 +361,7 @@ static te_expr *base(state *s) { case TE_FUNCTION1: case TE_CLOSURE1: ret = new_expr(s->type, 0); - ret->function = s->function; + ret->v.f.any = s->v.f.any; if (IS_CLOSURE(s->type)) ret->parameters[1] = s->context; next_token(s); ret->parameters[0] = power(s); @@ -347,7 +374,7 @@ static te_expr *base(state *s) { arity = ARITY(s->type); ret = new_expr(s->type, 0); - ret->function = s->function; + ret->v.f.any = s->v.f.any; if (IS_CLOSURE(s->type)) ret->parameters[arity] = s->context; next_token(s); @@ -384,7 +411,7 @@ static te_expr *base(state *s) { default: ret = new_expr(0, 0); s->type = TOK_ERROR; - ret->value = NAN; + ret->v.value = NAN; break; } @@ -395,8 +422,8 @@ static te_expr *base(state *s) { static te_expr *power(state *s) { /* = {("-" | "+")} */ int sign = 1; - while (s->type == TOK_INFIX && (s->function == add || s->function == sub)) { - if (s->function == sub) sign = -sign; + while (s->type == TOK_INFIX && (s->v.f.f2 == add || s->v.f.f2 == sub)) { + if (s->v.f.f2 == sub) sign = -sign; next_token(s); } @@ -405,8 +432,8 @@ static te_expr *power(state *s) { if (sign == 1) { ret = base(s); } else { - ret = NEW_EXPR(TE_FUNCTION1 | TE_FLAG_PURE, base(s)); - ret->function = negate; + ret = new_expr1(TE_FUNCTION1 | TE_FLAG_PURE, base(s)); + ret->v.f.f1 = negate; } return ret; @@ -420,33 +447,33 @@ static te_expr *factor(state *s) { int neg = 0; te_expr *insertion = 0; - if (ret->type == (TE_FUNCTION1 | TE_FLAG_PURE) && ret->function == negate) { + if (ret->type == (TE_FUNCTION1 | TE_FLAG_PURE) && ret->v.f.f1 == negate) { te_expr *se = ret->parameters[0]; free(ret); ret = se; neg = 1; } - while (s->type == TOK_INFIX && (s->function == pow)) { - te_fun2 t = s->function; + while (s->type == TOK_INFIX && (s->v.f.f2 == pow)) { + te_fun2 t = s->v.f.f2; next_token(s); if (insertion) { /* Make exponentiation go right-to-left. */ - te_expr *insert = NEW_EXPR(TE_FUNCTION2 | TE_FLAG_PURE, insertion->parameters[1], power(s)); - insert->function = t; + te_expr *insert = new_expr2(TE_FUNCTION2 | TE_FLAG_PURE, insertion->parameters[1], power(s)); + insert->v.f.f2 = t; insertion->parameters[1] = insert; insertion = insert; } else { - ret = NEW_EXPR(TE_FUNCTION2 | TE_FLAG_PURE, ret, power(s)); - ret->function = t; + ret = new_expr2(TE_FUNCTION2 | TE_FLAG_PURE, ret, power(s)); + ret->v.f.f2 = t; insertion = ret; } } if (neg) { - ret = NEW_EXPR(TE_FUNCTION1 | TE_FLAG_PURE, ret); - ret->function = negate; + ret = new_expr1(TE_FUNCTION1 | TE_FLAG_PURE, ret); + ret->v.f.f1 = negate; } return ret; @@ -456,11 +483,11 @@ static te_expr *factor(state *s) { /* = {"^" } */ te_expr *ret = power(s); - while (s->type == TOK_INFIX && (s->function == pow)) { - te_fun2 t = s->function; + while (s->type == TOK_INFIX && (s->v.f.f2 == pow)) { + te_fun2 t = s->v.f.f2; next_token(s); - ret = NEW_EXPR(TE_FUNCTION2 | TE_FLAG_PURE, ret, power(s)); - ret->function = t; + ret = new_expr2(TE_FUNCTION2 | TE_FLAG_PURE, ret, power(s)); + ret->v.f.f2 = t; } return ret; @@ -473,11 +500,11 @@ static te_expr *term(state *s) { /* = {("*" | "/" | "%") } */ te_expr *ret = factor(s); - while (s->type == TOK_INFIX && (s->function == mul || s->function == divide || s->function == fmod)) { - te_fun2 t = s->function; + while (s->type == TOK_INFIX && (s->v.f.f2 == mul || s->v.f.f2 == divide || s->v.f.f2 == fmod)) { + te_fun2 t = s->v.f.f2; next_token(s); - ret = NEW_EXPR(TE_FUNCTION2 | TE_FLAG_PURE, ret, factor(s)); - ret->function = t; + ret = new_expr2(TE_FUNCTION2 | TE_FLAG_PURE, ret, factor(s)); + ret->v.f.f2 = t; } return ret; @@ -488,11 +515,11 @@ static te_expr *expr(state *s) { /* = {("+" | "-") } */ te_expr *ret = term(s); - while (s->type == TOK_INFIX && (s->function == add || s->function == sub)) { - te_fun2 t = s->function; + while (s->type == TOK_INFIX && (s->v.f.f2 == add || s->v.f.f2 == sub)) { + te_fun2 t = s->v.f.f2; next_token(s); - ret = NEW_EXPR(TE_FUNCTION2 | TE_FLAG_PURE, ret, term(s)); - ret->function = t; + ret = new_expr2(TE_FUNCTION2 | TE_FLAG_PURE, ret, term(s)); + ret->v.f.f2 = t; } return ret; @@ -505,15 +532,14 @@ static te_expr *list(state *s) { while (s->type == TOK_SEP) { next_token(s); - ret = NEW_EXPR(TE_FUNCTION2 | TE_FLAG_PURE, ret, expr(s)); - ret->function = comma; + ret = new_expr2(TE_FUNCTION2 | TE_FLAG_PURE, ret, expr(s)); + ret->v.f.f2 = comma; } return ret; } -#define TE_FUN(...) ((double(*)(__VA_ARGS__))n->function) #define M(e) te_eval(n->parameters[e]) @@ -521,34 +547,34 @@ double te_eval(const te_expr *n) { if (!n) return NAN; switch(TYPE_MASK(n->type)) { - case TE_CONSTANT: return n->value; - case TE_VARIABLE: return *n->bound; + case TE_CONSTANT: return n->v.value; + case TE_VARIABLE: return *n->v.bound; case TE_FUNCTION0: case TE_FUNCTION1: case TE_FUNCTION2: case TE_FUNCTION3: case TE_FUNCTION4: case TE_FUNCTION5: case TE_FUNCTION6: case TE_FUNCTION7: switch(ARITY(n->type)) { - case 0: return TE_FUN(void)(); - case 1: return TE_FUN(double)(M(0)); - case 2: return TE_FUN(double, double)(M(0), M(1)); - case 3: return TE_FUN(double, double, double)(M(0), M(1), M(2)); - case 4: return TE_FUN(double, double, double, double)(M(0), M(1), M(2), M(3)); - case 5: return TE_FUN(double, double, double, double, double)(M(0), M(1), M(2), M(3), M(4)); - case 6: return TE_FUN(double, double, double, double, double, double)(M(0), M(1), M(2), M(3), M(4), M(5)); - case 7: return TE_FUN(double, double, double, double, double, double, double)(M(0), M(1), M(2), M(3), M(4), M(5), M(6)); + case 0: return n->v.f.f0(); + case 1: return n->v.f.f1(M(0)); + case 2: return n->v.f.f2(M(0), M(1)); + case 3: return n->v.f.f3(M(0), M(1), M(2)); + case 4: return n->v.f.f4(M(0), M(1), M(2), M(3)); + case 5: return n->v.f.f5(M(0), M(1), M(2), M(3), M(4)); + case 6: return n->v.f.f6(M(0), M(1), M(2), M(3), M(4), M(5)); + case 7: return n->v.f.f7(M(0), M(1), M(2), M(3), M(4), M(5), M(6)); default: return NAN; } case TE_CLOSURE0: case TE_CLOSURE1: case TE_CLOSURE2: case TE_CLOSURE3: case TE_CLOSURE4: case TE_CLOSURE5: case TE_CLOSURE6: case TE_CLOSURE7: switch(ARITY(n->type)) { - case 0: return TE_FUN(void*)(n->parameters[0]); - case 1: return TE_FUN(void*, double)(n->parameters[1], M(0)); - case 2: return TE_FUN(void*, double, double)(n->parameters[2], M(0), M(1)); - case 3: return TE_FUN(void*, double, double, double)(n->parameters[3], M(0), M(1), M(2)); - case 4: return TE_FUN(void*, double, double, double, double)(n->parameters[4], M(0), M(1), M(2), M(3)); - case 5: return TE_FUN(void*, double, double, double, double, double)(n->parameters[5], M(0), M(1), M(2), M(3), M(4)); - case 6: return TE_FUN(void*, double, double, double, double, double, double)(n->parameters[6], M(0), M(1), M(2), M(3), M(4), M(5)); - case 7: return TE_FUN(void*, double, double, double, double, double, double, double)(n->parameters[7], M(0), M(1), M(2), M(3), M(4), M(5), M(6)); + case 0: return n->v.f.cl0(n->parameters[0]); + case 1: return n->v.f.cl1(n->parameters[1], M(0)); + case 2: return n->v.f.cl2(n->parameters[2], M(0), M(1)); + case 3: return n->v.f.cl3(n->parameters[3], M(0), M(1), M(2)); + case 4: return n->v.f.cl4(n->parameters[4], M(0), M(1), M(2), M(3)); + case 5: return n->v.f.cl5(n->parameters[5], M(0), M(1), M(2), M(3), M(4)); + case 6: return n->v.f.cl6(n->parameters[6], M(0), M(1), M(2), M(3), M(4), M(5)); + case 7: return n->v.f.cl7(n->parameters[7], M(0), M(1), M(2), M(3), M(4), M(5), M(6)); default: return NAN; } @@ -557,7 +583,6 @@ double te_eval(const te_expr *n) { } -#undef TE_FUN #undef M static void optimize(te_expr *n) { @@ -580,7 +605,7 @@ static void optimize(te_expr *n) { const double value = te_eval(n); te_free_parameters(n); n->type = TE_CONSTANT; - n->value = value; + n->v.value = value; } } } @@ -598,7 +623,7 @@ te_expr *te_compile(const char *expression, const te_variable *variables, int va if (s.type != TOK_END) { te_free(root); if (error) { - *error = (s.next - s.start); + *error = (int)(s.next - s.start); if (*error == 0) *error = 1; } return 0; @@ -627,8 +652,8 @@ static void pn (const te_expr *n, int depth) { printf("%*s", depth, ""); switch(TYPE_MASK(n->type)) { - case TE_CONSTANT: printf("%f\n", n->value); break; - case TE_VARIABLE: printf("bound %p\n", n->bound); break; + case TE_CONSTANT: printf("%f\n", n->v.value); break; + case TE_VARIABLE: printf("bound %p\n", (void*)n->v.bound); break; case TE_FUNCTION0: case TE_FUNCTION1: case TE_FUNCTION2: case TE_FUNCTION3: case TE_FUNCTION4: case TE_FUNCTION5: case TE_FUNCTION6: case TE_FUNCTION7: diff --git a/tinyexpr.h b/tinyexpr.h index 8278633..b5c58dc 100644 --- a/tinyexpr.h +++ b/tinyexpr.h @@ -31,10 +31,35 @@ extern "C" { #endif +union fun { + void *any; + double (*f0)(void); + double (*f1)(double); + double (*f2)(double,double); + double (*f3)(double,double,double); + double (*f4)(double,double,double,double); + double (*f5)(double,double,double,double,double); + double (*f6)(double,double,double,double,double,double); + double (*f7)(double,double,double,double,double,double,double); + double (*cl0)(void*); + double (*cl1)(void*,double); + double (*cl2)(void*,double,double); + double (*cl3)(void*,double,double,double); + double (*cl4)(void*,double,double,double,double); + double (*cl5)(void*,double,double,double,double,double); + double (*cl6)(void*,double,double,double,double,double,double); + double (*cl7)(void*,double,double,double,double,double,double,double); +}; + +union value { + double value; + const double *bound; + union fun f; +}; typedef struct te_expr { int type; - union {double value; const double *bound; const void *function;}; + union value v; void *parameters[1]; } te_expr; @@ -53,7 +78,7 @@ enum { typedef struct te_variable { const char *name; - const void *address; + const union fun address; int type; void *context; } te_variable;