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;