diff --git a/test.c b/test.c index c772950..a7f07f4 100644 --- a/test.c +++ b/test.c @@ -672,6 +672,69 @@ 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}, + + {"1 < 2", 1}, + {"2 < 2", 0}, + {"2 <= 2", 1}, + {"2 > 1", 1}, + {"2 > 2", 0}, + {"2 >= 2", 1}, + {"2 > -2", 1}, + {"-2 < 2", 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}, + }; + + + 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 +748,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; diff --git a/tinyexpr.c b/tinyexpr.c index 91a1848..44def87 100755 --- a/tinyexpr.c +++ b/tinyexpr.c @@ -225,6 +225,14 @@ 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 logical_and(double a, double b) {return (int)(a + 0.5) == 1 && (int)(b + 0.5) == 1;} +static double logical_or(double a, double b) {return (int)(a + 0.5) == 1 || (int)(b + 0.5) == 1;} +static double logical_not(double a) {return (int)(a + 0.5) == 0;} + void next_token(state *s) { s->type = TOK_NULL; @@ -281,6 +289,37 @@ 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 '!': s->type = TOK_INFIX; s->function = logical_not; 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,16 +432,21 @@ 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)) { + int logical = 0; + while (s->type == TOK_INFIX && (s->function == add || s->function == sub || s->function == logical_not)) { if (s->function == sub) sign = -sign; + if (s->function == logical_not) logical = 1; next_token(s); } te_expr *ret; - if (sign == 1) { + if (logical) { + ret = NEW_EXPR(TE_FUNCTION1 | TE_FLAG_PURE, base(s)); + ret->function = logical_not; + } else if (sign == 1) { ret = base(s); } else { ret = NEW_EXPR(TE_FUNCTION1 | TE_FLAG_PURE, base(s)); @@ -418,13 +462,19 @@ static te_expr *factor(state *s) { te_expr *ret = power(s); int neg = 0; + int lnot = 0; te_expr *insertion = 0; - if (ret->type == (TE_FUNCTION1 | TE_FLAG_PURE) && ret->function == negate) { + if (ret->type == (TE_FUNCTION1 | TE_FLAG_PURE)) { te_expr *se = ret->parameters[0]; free(ret); ret = se; - neg = 1; + + if (ret->function == negate) { + neg = 1; + } else if (ret->function == logical_not) { + lnot = 1; + } } while (s->type == TOK_INFIX && (s->function == pow)) { @@ -448,6 +498,10 @@ static te_expr *factor(state *s) { ret = NEW_EXPR(TE_FUNCTION1 | TE_FLAG_PURE, ret); ret->function = negate; } + if (lnot) { + ret = NEW_EXPR(TE_FUNCTION1 | TE_FLAG_PURE, ret); + ret->function = logical_not; + } return ret; } @@ -484,7 +538,7 @@ static te_expr *term(state *s) { } -static te_expr *expr(state *s) { +static te_expr *sum_expr(state *s) { /* = {("+" | "-") } */ te_expr *ret = term(s); @@ -499,6 +553,37 @@ static te_expr *expr(state *s) { } +static te_expr *test_expr(state *s) { + /* = {(">" | ">=" | "<" | "<=") } */ + 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)) { + 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) { + /* = {("&&" | "||") } */ + 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) { /* = {"," } */ te_expr *ret = expr(s);