Merge pull request #38 from cschreib/master

Logical operators
This commit is contained in:
Lewis Van Winkle
2018-07-10 01:20:55 -05:00
committed by GitHub
3 changed files with 287 additions and 10 deletions

63
CMakeLists.txt Normal file
View File

@@ -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
test.c
View File

@@ -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);
@@ -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;

View File

@@ -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;
@@ -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;
@@ -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;
@@ -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)) {
@@ -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;
@@ -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);
@@ -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);