Skip to content

Commit 4dfb202

Browse files
authored
Merge pull request #38 from cschreib/master
Logical operators
2 parents ffb0d41 + 9de3ed2 commit 4dfb202

File tree

3 files changed

+287
-10
lines changed

3 files changed

+287
-10
lines changed

CMakeLists.txt

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
cmake_minimum_required(VERSION 2.8.4)
2+
3+
project(tinyexpr)
4+
5+
option(TE_POW_FROM_RIGHT "Evaluate exponents from right to left." OFF)
6+
option(TE_NAT_LOG "Define the log function as natural logarithm." OFF)
7+
option(build_tinyexpr_test "Build TinyExpr tests." OFF)
8+
option(build_tinyexpr_test_pr "Build TinyExpr tests PR." OFF)
9+
option(build_tinyexpr_bench "Build TinyExpr benchmark." OFF)
10+
option(build_tinyexpr_example "Build TinyExpr example." OFF)
11+
option(build_tinyexpr_example2 "Build TinyExpr example 2." OFF)
12+
option(build_tinyexpr_example3 "Build TinyExpr example 3." OFF)
13+
14+
find_library(MATH_LIB m)
15+
16+
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -ansi -Wall -Wshadow -fPIC -O3")
17+
18+
set(SOURCE_FILES
19+
tinyexpr.c
20+
tinyexpr.h
21+
)
22+
23+
add_library(tinyexpr STATIC ${SOURCE_FILES})
24+
if (TE_POW_FROM_RIGHT)
25+
target_compile_definitions(tinyexpr PRIVATE TE_POW_FROM_RIGHT)
26+
endif()
27+
if (TE_NAT_LOG)
28+
target_compile_definitions(tinyexpr PRIVATE TE_NAT_LOG)
29+
endif()
30+
target_link_libraries(tinyexpr ${MATH_LIB})
31+
install(TARGETS tinyexpr ARCHIVE DESTINATION lib)
32+
install(FILES tinyexpr.h DESTINATION include COMPONENT Devel)
33+
34+
if (build_tinyexpr_test)
35+
add_executable(tinyexpr_test test.c tinyexpr.c)
36+
target_link_libraries(tinyexpr_test ${MATH_LIB})
37+
endif()
38+
39+
if (build_tinyexpr_test_pr)
40+
add_executable(tinyexpr_test_pr test.c tinyexpr.c)
41+
target_compile_definitions(tinyexpr_test_pr PRIVATE TE_POW_FROM_RIGHT TE_NAT_LOG)
42+
target_link_libraries(tinyexpr_test_pr ${MATH_LIB})
43+
endif()
44+
45+
if (build_tinyexpr_bench)
46+
add_executable(tinyexpr_benchmark benchmark.c tinyexpr.c)
47+
target_link_libraries(tinyexpr_benchmark ${MATH_LIB})
48+
endif()
49+
50+
if (build_tinyexpr_example)
51+
add_executable(tinyexpr_example example.c tinyexpr.c)
52+
target_link_libraries(tinyexpr_example ${MATH_LIB})
53+
endif()
54+
55+
if (build_tinyexpr_example2)
56+
add_executable(tinyexpr_example2 example2.c tinyexpr.c)
57+
target_link_libraries(tinyexpr_example2 ${MATH_LIB})
58+
endif()
59+
60+
if (build_tinyexpr_example3)
61+
add_executable(tinyexpr_example3 example3.c tinyexpr.c)
62+
target_link_libraries(tinyexpr_example3 ${MATH_LIB})
63+
endif()

test.c

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -672,6 +672,100 @@ void test_combinatorics() {
672672
}
673673

674674

675+
void test_logic() {
676+
test_case cases[] = {
677+
{"1 && 1", 1},
678+
{"1 && 0", 0},
679+
{"0 && 1", 0},
680+
{"0 && 0", 0},
681+
{"1 || 1", 1},
682+
{"1 || 0", 1},
683+
{"0 || 1", 1},
684+
{"0 || 0", 0},
685+
{"!0", 1},
686+
{"!1", 0},
687+
{"!2", 0},
688+
689+
{"!-2", 0},
690+
{"-!2", 0},
691+
{"!!0", 0},
692+
{"!!1", 1},
693+
{"!!2", 1},
694+
{"!!-2", 1},
695+
{"!-!2", 1},
696+
{"-!!2", -1},
697+
{"--!!2", 1},
698+
699+
{"1 < 2", 1},
700+
{"2 < 2", 0},
701+
{"2 <= 2", 1},
702+
{"2 > 1", 1},
703+
{"2 > 2", 0},
704+
{"2 >= 2", 1},
705+
{"2 > -2", 1},
706+
{"-2 < 2", 1},
707+
708+
{"0 == 0", 1},
709+
{"0 != 0", 0},
710+
{"2 == 2", 1},
711+
{"2 != 2", 0},
712+
{"2 == 3", 0},
713+
{"2 != 3", 1},
714+
{"2 == 2.0001", 0},
715+
{"2 != 2.0001", 1},
716+
717+
{"1 < 2 && 2 < 3", 1},
718+
{"1 < 2 && 3 < 2", 0},
719+
{"2 < 1 && 2 < 3", 0},
720+
{"2 < 1 && 3 < 2", 0},
721+
{"1 < 2 || 2 < 3", 1},
722+
{"1 < 2 || 3 < 2", 1},
723+
{"2 < 1 || 2 < 3", 1},
724+
{"2 < 1 || 3 < 2", 0},
725+
726+
{"1 < 1+1", 1},
727+
{"1 < 1*2", 1},
728+
{"1 < 2/2", 0},
729+
{"1 < 2^2", 1},
730+
731+
{"5+5 < 4+10", 1},
732+
{"5+(5 < 4)+10", 15},
733+
{"5+(5 < 4+10)", 6},
734+
{"(5+5 < 4)+10", 10},
735+
{"5+!(5 < 4)+10", 16},
736+
{"5+!(5 < 4+10)", 5},
737+
{"!(5+5 < 4)+10", 11},
738+
739+
#ifdef TE_POW_FROM_RIGHT
740+
{"!0^2", 1},
741+
{"!0^-1", 0},
742+
{"-!0^2", -1},
743+
#else
744+
{"!0^2", 1},
745+
{"!0^-1", 1},
746+
{"-!0^2", 1},
747+
#endif
748+
749+
};
750+
751+
752+
int i;
753+
for (i = 0; i < sizeof(cases) / sizeof(test_case); ++i) {
754+
const char *expr = cases[i].expr;
755+
const double answer = cases[i].answer;
756+
757+
int err;
758+
const double ev = te_interp(expr, &err);
759+
lok(!err);
760+
lfequal(ev, answer);
761+
762+
if (err) {
763+
printf("FAILED: %s (%d)\n", expr, err);
764+
}
765+
}
766+
}
767+
768+
675769
int main(int argc, char *argv[])
676770
{
677771
lrun("Results", test_results);
@@ -685,6 +779,7 @@ int main(int argc, char *argv[])
685779
lrun("Optimize", test_optimize);
686780
lrun("Pow", test_pow);
687781
lrun("Combinatorics", test_combinatorics);
782+
lrun("Logic", test_logic);
688783
lresults();
689784

690785
return lfails != 0;

tinyexpr.c

Lines changed: 129 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,19 @@ static double divide(double a, double b) {return a / b;}
225225
static double negate(double a) {return -a;}
226226
static double comma(double a, double b) {(void)a; return b;}
227227

228+
static double greater(double a, double b) {return a > b;}
229+
static double greater_eq(double a, double b) {return a >= b;}
230+
static double lower(double a, double b) {return a < b;}
231+
static double lower_eq(double a, double b) {return a <= b;}
232+
static double equal(double a, double b) {return a == b;}
233+
static double not_equal(double a, double b) {return a != b;}
234+
static double logical_and(double a, double b) {return a != 0.0 && b != 0.0;}
235+
static double logical_or(double a, double b) {return a != 0.0 || b != 0.0;}
236+
static double logical_not(double a) {return a == 0.0;}
237+
static double logical_notnot(double a) {return a != 0.0;}
238+
static double negate_logical_not(double a) {return -(a == 0.0);}
239+
static double negate_logical_notnot(double a) {return -(a != 0.0);}
240+
228241

229242
void next_token(state *s) {
230243
s->type = TOK_NULL;
@@ -281,6 +294,51 @@ void next_token(state *s) {
281294
case '/': s->type = TOK_INFIX; s->function = divide; break;
282295
case '^': s->type = TOK_INFIX; s->function = pow; break;
283296
case '%': s->type = TOK_INFIX; s->function = fmod; break;
297+
case '!':
298+
if (s->next++[0] == '=') {
299+
s->type = TOK_INFIX; s->function = not_equal;
300+
} else {
301+
s->next--;
302+
s->type = TOK_INFIX; s->function = logical_not;
303+
}
304+
break;
305+
case '=':
306+
if (s->next++[0] == '=') {
307+
s->type = TOK_INFIX; s->function = equal;
308+
} else {
309+
s->type = TOK_ERROR;
310+
}
311+
break;
312+
case '<':
313+
if (s->next++[0] == '=') {
314+
s->type = TOK_INFIX; s->function = lower_eq;
315+
} else {
316+
s->next--;
317+
s->type = TOK_INFIX; s->function = lower;
318+
}
319+
break;
320+
case '>':
321+
if (s->next++[0] == '=') {
322+
s->type = TOK_INFIX; s->function = greater_eq;
323+
} else {
324+
s->next--;
325+
s->type = TOK_INFIX; s->function = greater;
326+
}
327+
break;
328+
case '&':
329+
if (s->next++[0] == '&') {
330+
s->type = TOK_INFIX; s->function = logical_and;
331+
} else {
332+
s->type = TOK_ERROR;
333+
}
334+
break;
335+
case '|':
336+
if (s->next++[0] == '|') {
337+
s->type = TOK_INFIX; s->function = logical_or;
338+
} else {
339+
s->type = TOK_ERROR;
340+
}
341+
break;
284342
case '(': s->type = TOK_OPEN; break;
285343
case ')': s->type = TOK_CLOSE; break;
286344
case ',': s->type = TOK_SEP; break;
@@ -393,20 +451,48 @@ static te_expr *base(state *s) {
393451

394452

395453
static te_expr *power(state *s) {
396-
/* <power> = {("-" | "+")} <base> */
454+
/* <power> = {("-" | "+" | "!")} <base> */
397455
int sign = 1;
398456
while (s->type == TOK_INFIX && (s->function == add || s->function == sub)) {
399457
if (s->function == sub) sign = -sign;
400458
next_token(s);
401459
}
402460

461+
int logical = 0;
462+
while (s->type == TOK_INFIX && (s->function == add || s->function == sub || s->function == logical_not)) {
463+
if (s->function == logical_not) {
464+
if (logical == 0) {
465+
logical = -1;
466+
} else {
467+
logical = -logical;
468+
}
469+
}
470+
next_token(s);
471+
}
472+
403473
te_expr *ret;
404474

405475
if (sign == 1) {
406-
ret = base(s);
476+
if (logical == 0) {
477+
ret = base(s);
478+
} else if (logical == -1) {
479+
ret = NEW_EXPR(TE_FUNCTION1 | TE_FLAG_PURE, base(s));
480+
ret->function = logical_not;
481+
} else {
482+
ret = NEW_EXPR(TE_FUNCTION1 | TE_FLAG_PURE, base(s));
483+
ret->function = logical_notnot;
484+
}
407485
} else {
408-
ret = NEW_EXPR(TE_FUNCTION1 | TE_FLAG_PURE, base(s));
409-
ret->function = negate;
486+
if (logical == 0) {
487+
ret = NEW_EXPR(TE_FUNCTION1 | TE_FLAG_PURE, base(s));
488+
ret->function = negate;
489+
} else if (logical == -1) {
490+
ret = NEW_EXPR(TE_FUNCTION1 | TE_FLAG_PURE, base(s));
491+
ret->function = negate_logical_not;
492+
} else {
493+
ret = NEW_EXPR(TE_FUNCTION1 | TE_FLAG_PURE, base(s));
494+
ret->function = negate_logical_notnot;
495+
}
410496
}
411497

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

420-
int neg = 0;
506+
const void *left_function = NULL;
421507
te_expr *insertion = 0;
422508

423-
if (ret->type == (TE_FUNCTION1 | TE_FLAG_PURE) && ret->function == negate) {
509+
if (ret->type == (TE_FUNCTION1 | TE_FLAG_PURE) &&
510+
(ret->function == negate || ret->function == logical_not || ret->function == logical_notnot ||
511+
ret->function == negate_logical_not || ret->function == negate_logical_notnot)) {
512+
left_function = ret->function;
424513
te_expr *se = ret->parameters[0];
425514
free(ret);
426515
ret = se;
427-
neg = 1;
428516
}
429517

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

447-
if (neg) {
535+
if (left_function) {
448536
ret = NEW_EXPR(TE_FUNCTION1 | TE_FLAG_PURE, ret);
449-
ret->function = negate;
537+
ret->function = left_function;
450538
}
451539

452540
return ret;
@@ -484,7 +572,7 @@ static te_expr *term(state *s) {
484572
}
485573

486574

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

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

501589

590+
static te_expr *test_expr(state *s) {
591+
/* <expr> = <sum_expr> {(">" | ">=" | "<" | "<=" | "==" | "!=") <sum_expr>} */
592+
te_expr *ret = sum_expr(s);
593+
594+
while (s->type == TOK_INFIX && (s->function == greater || s->function == greater_eq ||
595+
s->function == lower || s->function == lower_eq || s->function == equal || s->function == not_equal)) {
596+
te_fun2 t = s->function;
597+
next_token(s);
598+
ret = NEW_EXPR(TE_FUNCTION2 | TE_FLAG_PURE, ret, sum_expr(s));
599+
ret->function = t;
600+
}
601+
602+
return ret;
603+
}
604+
605+
606+
static te_expr *expr(state *s) {
607+
/* <expr> = <test_expr> {("&&" | "||") <test_expr>} */
608+
te_expr *ret = test_expr(s);
609+
610+
while (s->type == TOK_INFIX && (s->function == logical_and || s->function == logical_or)) {
611+
te_fun2 t = s->function;
612+
next_token(s);
613+
ret = NEW_EXPR(TE_FUNCTION2 | TE_FLAG_PURE, ret, test_expr(s));
614+
ret->function = t;
615+
}
616+
617+
return ret;
618+
}
619+
620+
502621
static te_expr *list(state *s) {
503622
/* <list> = <expr> {"," <expr>} */
504623
te_expr *ret = expr(s);

0 commit comments

Comments
 (0)