Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Logical operators #38

Merged
merged 10 commits into from
Jul 10, 2018
63 changes: 63 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
cmake_minimum_required(VERSION 2.8.4)

project(tinyexpr)

option(TE_POW_FROM_RIGHT "Evaluate exponents from right to left." OFF)
option(TE_NAT_LOG "Define the log function as natural logarithm." OFF)
option(build_tinyexpr_test "Build TinyExpr tests." OFF)
option(build_tinyexpr_test_pr "Build TinyExpr tests PR." OFF)
option(build_tinyexpr_bench "Build TinyExpr benchmark." OFF)
option(build_tinyexpr_example "Build TinyExpr example." OFF)
option(build_tinyexpr_example2 "Build TinyExpr example 2." OFF)
option(build_tinyexpr_example3 "Build TinyExpr example 3." OFF)

find_library(MATH_LIB m)

set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -ansi -Wall -Wshadow -fPIC -O3")

set(SOURCE_FILES
tinyexpr.c
tinyexpr.h
)

add_library(tinyexpr STATIC ${SOURCE_FILES})
if (TE_POW_FROM_RIGHT)
target_compile_definitions(tinyexpr PRIVATE TE_POW_FROM_RIGHT)
endif()
if (TE_NAT_LOG)
target_compile_definitions(tinyexpr PRIVATE TE_NAT_LOG)
endif()
target_link_libraries(tinyexpr ${MATH_LIB})
install(TARGETS tinyexpr ARCHIVE DESTINATION lib)
install(FILES tinyexpr.h DESTINATION include COMPONENT Devel)

if (build_tinyexpr_test)
add_executable(tinyexpr_test test.c tinyexpr.c)
target_link_libraries(tinyexpr_test ${MATH_LIB})
endif()

if (build_tinyexpr_test_pr)
add_executable(tinyexpr_test_pr test.c tinyexpr.c)
target_compile_definitions(tinyexpr_test_pr PRIVATE TE_POW_FROM_RIGHT TE_NAT_LOG)
target_link_libraries(tinyexpr_test_pr ${MATH_LIB})
endif()

if (build_tinyexpr_bench)
add_executable(tinyexpr_benchmark benchmark.c tinyexpr.c)
target_link_libraries(tinyexpr_benchmark ${MATH_LIB})
endif()

if (build_tinyexpr_example)
add_executable(tinyexpr_example example.c tinyexpr.c)
target_link_libraries(tinyexpr_example ${MATH_LIB})
endif()

if (build_tinyexpr_example2)
add_executable(tinyexpr_example2 example2.c tinyexpr.c)
target_link_libraries(tinyexpr_example2 ${MATH_LIB})
endif()

if (build_tinyexpr_example3)
add_executable(tinyexpr_example3 example3.c tinyexpr.c)
target_link_libraries(tinyexpr_example3 ${MATH_LIB})
endif()
95 changes: 95 additions & 0 deletions test.c
Original file line number Diff line number Diff line change
Expand Up @@ -672,6 +672,100 @@ void test_combinatorics() {
}


void test_logic() {
test_case cases[] = {
{"1 && 1", 1},
{"1 && 0", 0},
{"0 && 1", 0},
{"0 && 0", 0},
{"1 || 1", 1},
{"1 || 0", 1},
{"0 || 1", 1},
{"0 || 0", 0},
{"!0", 1},
{"!1", 0},
{"!2", 0},

{"!-2", 0},
{"-!2", 0},
{"!!0", 0},
{"!!1", 1},
{"!!2", 1},
{"!!-2", 1},
{"!-!2", 1},
{"-!!2", -1},
{"--!!2", 1},

{"1 < 2", 1},
{"2 < 2", 0},
{"2 <= 2", 1},
{"2 > 1", 1},
{"2 > 2", 0},
{"2 >= 2", 1},
{"2 > -2", 1},
{"-2 < 2", 1},

{"0 == 0", 1},
{"0 != 0", 0},
{"2 == 2", 1},
{"2 != 2", 0},
{"2 == 3", 0},
{"2 != 3", 1},
{"2 == 2.0001", 0},
{"2 != 2.0001", 1},

{"1 < 2 && 2 < 3", 1},
{"1 < 2 && 3 < 2", 0},
{"2 < 1 && 2 < 3", 0},
{"2 < 1 && 3 < 2", 0},
{"1 < 2 || 2 < 3", 1},
{"1 < 2 || 3 < 2", 1},
{"2 < 1 || 2 < 3", 1},
{"2 < 1 || 3 < 2", 0},

{"1 < 1+1", 1},
{"1 < 1*2", 1},
{"1 < 2/2", 0},
{"1 < 2^2", 1},

{"5+5 < 4+10", 1},
{"5+(5 < 4)+10", 15},
{"5+(5 < 4+10)", 6},
{"(5+5 < 4)+10", 10},
{"5+!(5 < 4)+10", 16},
{"5+!(5 < 4+10)", 5},
{"!(5+5 < 4)+10", 11},

#ifdef TE_POW_FROM_RIGHT
{"!0^2", 1},
{"!0^-1", 0},
{"-!0^2", -1},
#else
{"!0^2", 1},
{"!0^-1", 1},
{"-!0^2", 1},
#endif

};


int i;
for (i = 0; i < sizeof(cases) / sizeof(test_case); ++i) {
const char *expr = cases[i].expr;
const double answer = cases[i].answer;

int err;
const double ev = te_interp(expr, &err);
lok(!err);
lfequal(ev, answer);

if (err) {
printf("FAILED: %s (%d)\n", expr, err);
}
}
}


int main(int argc, char *argv[])
{
lrun("Results", test_results);
Expand All @@ -685,6 +779,7 @@ int main(int argc, char *argv[])
lrun("Optimize", test_optimize);
lrun("Pow", test_pow);
lrun("Combinatorics", test_combinatorics);
lrun("Logic", test_logic);
lresults();

return lfails != 0;
Expand Down
139 changes: 129 additions & 10 deletions tinyexpr.c
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,19 @@ static double divide(double a, double b) {return a / b;}
static double negate(double a) {return -a;}
static double comma(double a, double b) {(void)a; return b;}

static double greater(double a, double b) {return a > b;}
static double greater_eq(double a, double b) {return a >= b;}
static double lower(double a, double b) {return a < b;}
static double lower_eq(double a, double b) {return a <= b;}
static double equal(double a, double b) {return a == b;}
static double not_equal(double a, double b) {return a != b;}
static double logical_and(double a, double b) {return a != 0.0 && b != 0.0;}
static double logical_or(double a, double b) {return a != 0.0 || b != 0.0;}
static double logical_not(double a) {return a == 0.0;}
static double logical_notnot(double a) {return a != 0.0;}
static double negate_logical_not(double a) {return -(a == 0.0);}
static double negate_logical_notnot(double a) {return -(a != 0.0);}


void next_token(state *s) {
s->type = TOK_NULL;
Expand Down Expand Up @@ -281,6 +294,51 @@ void next_token(state *s) {
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 '!':
if (s->next++[0] == '=') {
s->type = TOK_INFIX; s->function = not_equal;
} else {
s->next--;
s->type = TOK_INFIX; s->function = logical_not;
}
break;
case '=':
if (s->next++[0] == '=') {
s->type = TOK_INFIX; s->function = equal;
} else {
s->type = TOK_ERROR;
}
break;
case '<':
if (s->next++[0] == '=') {
s->type = TOK_INFIX; s->function = lower_eq;
} else {
s->next--;
s->type = TOK_INFIX; s->function = lower;
}
break;
case '>':
if (s->next++[0] == '=') {
s->type = TOK_INFIX; s->function = greater_eq;
} else {
s->next--;
s->type = TOK_INFIX; s->function = greater;
}
break;
case '&':
if (s->next++[0] == '&') {
s->type = TOK_INFIX; s->function = logical_and;
} else {
s->type = TOK_ERROR;
}
break;
case '|':
if (s->next++[0] == '|') {
s->type = TOK_INFIX; s->function = logical_or;
} else {
s->type = TOK_ERROR;
}
break;
case '(': s->type = TOK_OPEN; break;
case ')': s->type = TOK_CLOSE; break;
case ',': s->type = TOK_SEP; break;
Expand Down Expand Up @@ -393,20 +451,48 @@ static te_expr *base(state *s) {


static te_expr *power(state *s) {
/* <power> = {("-" | "+")} <base> */
/* <power> = {("-" | "+" | "!")} <base> */
int sign = 1;
while (s->type == TOK_INFIX && (s->function == add || s->function == sub)) {
if (s->function == sub) sign = -sign;
next_token(s);
}

int logical = 0;
while (s->type == TOK_INFIX && (s->function == add || s->function == sub || s->function == logical_not)) {
if (s->function == logical_not) {
if (logical == 0) {
logical = -1;
} else {
logical = -logical;
}
}
next_token(s);
}

te_expr *ret;

if (sign == 1) {
ret = base(s);
if (logical == 0) {
ret = base(s);
} else if (logical == -1) {
ret = NEW_EXPR(TE_FUNCTION1 | TE_FLAG_PURE, base(s));
ret->function = logical_not;
} else {
ret = NEW_EXPR(TE_FUNCTION1 | TE_FLAG_PURE, base(s));
ret->function = logical_notnot;
}
} else {
ret = NEW_EXPR(TE_FUNCTION1 | TE_FLAG_PURE, base(s));
ret->function = negate;
if (logical == 0) {
ret = NEW_EXPR(TE_FUNCTION1 | TE_FLAG_PURE, base(s));
ret->function = negate;
} else if (logical == -1) {
ret = NEW_EXPR(TE_FUNCTION1 | TE_FLAG_PURE, base(s));
ret->function = negate_logical_not;
} else {
ret = NEW_EXPR(TE_FUNCTION1 | TE_FLAG_PURE, base(s));
ret->function = negate_logical_notnot;
}
}

return ret;
Expand All @@ -417,14 +503,16 @@ static te_expr *factor(state *s) {
/* <factor> = <power> {"^" <power>} */
te_expr *ret = power(s);

int neg = 0;
const void *left_function = NULL;
te_expr *insertion = 0;

if (ret->type == (TE_FUNCTION1 | TE_FLAG_PURE) && ret->function == negate) {
if (ret->type == (TE_FUNCTION1 | TE_FLAG_PURE) &&
(ret->function == negate || ret->function == logical_not || ret->function == logical_notnot ||
ret->function == negate_logical_not || ret->function == negate_logical_notnot)) {
left_function = ret->function;
te_expr *se = ret->parameters[0];
free(ret);
ret = se;
neg = 1;
}

while (s->type == TOK_INFIX && (s->function == pow)) {
Expand All @@ -444,9 +532,9 @@ static te_expr *factor(state *s) {
}
}

if (neg) {
if (left_function) {
ret = NEW_EXPR(TE_FUNCTION1 | TE_FLAG_PURE, ret);
ret->function = negate;
ret->function = left_function;
}

return ret;
Expand Down Expand Up @@ -484,7 +572,7 @@ static te_expr *term(state *s) {
}


static te_expr *expr(state *s) {
static te_expr *sum_expr(state *s) {
/* <expr> = <term> {("+" | "-") <term>} */
te_expr *ret = term(s);

Expand All @@ -499,6 +587,37 @@ static te_expr *expr(state *s) {
}


static te_expr *test_expr(state *s) {
/* <expr> = <sum_expr> {(">" | ">=" | "<" | "<=" | "==" | "!=") <sum_expr>} */
te_expr *ret = sum_expr(s);

while (s->type == TOK_INFIX && (s->function == greater || s->function == greater_eq ||
s->function == lower || s->function == lower_eq || s->function == equal || s->function == not_equal)) {
te_fun2 t = s->function;
next_token(s);
ret = NEW_EXPR(TE_FUNCTION2 | TE_FLAG_PURE, ret, sum_expr(s));
ret->function = t;
}

return ret;
}


static te_expr *expr(state *s) {
/* <expr> = <test_expr> {("&&" | "||") <test_expr>} */
te_expr *ret = test_expr(s);

while (s->type == TOK_INFIX && (s->function == logical_and || s->function == logical_or)) {
te_fun2 t = s->function;
next_token(s);
ret = NEW_EXPR(TE_FUNCTION2 | TE_FLAG_PURE, ret, test_expr(s));
ret->function = t;
}

return ret;
}


static te_expr *list(state *s) {
/* <list> = <expr> {"," <expr>} */
te_expr *ret = expr(s);
Expand Down