diff options
-rw-r--r-- | tests/expr.test | 2 | ||||
-rw-r--r-- | toys/pending/expr.c | 178 |
2 files changed, 75 insertions, 105 deletions
diff --git a/tests/expr.test b/tests/expr.test index 1c038a5c..33900d7a 100644 --- a/tests/expr.test +++ b/tests/expr.test @@ -17,3 +17,5 @@ testing "expr % * same priority" "expr 3 % 2 \* 4" "4\n" "" "" testing "expr * % same priority" "expr 3 \* 2 % 4" "2\n" "" "" testing "expr = > same priority" "expr 0 = 2 \> 3" "0\n" "" "" testing "expr > = same priority" "expr 3 \> 2 = 1" "1\n" "" "" +testing "expr string becomes integer" "expr ab21xx : '[^0-9]*\([0-9]*\)' + 3" \ + "24\n" "" "" diff --git a/toys/pending/expr.c b/toys/pending/expr.c index 6742ff68..c7da4d1c 100644 --- a/toys/pending/expr.c +++ b/toys/pending/expr.c @@ -16,7 +16,7 @@ config EXPR The supported operators, in order of increasing precedence, are: - | & = > >= < <= != + - * / % + | & = > >= < <= != + - * / % : In addition, parentheses () are supported for grouping. */ @@ -39,44 +39,10 @@ struct value { long long i; }; -static void parse_expr(struct value *ret, struct value *v); - -static void get_value(struct value *v) -{ - char *endp, *arg; - - if (TT.argidx == toys.optc) { - v->i = 0; - v->s = ""; // signal end of expression - return; - } - - if (TT.argidx >= toys.optc) { - error_exit("syntax error"); - } - - arg = toys.optargs[TT.argidx++]; - - v->i = strtoll(arg, &endp, 10); - v->s = *endp ? arg : NULL; -} - - -// check if v matches a token, and consume it if so -static int match(struct value *v, const char *tok) -{ - if (v->s && !strcmp(v->s, tok)) { - get_value(v); - return 1; - } - - return 0; -} - // check if v is the integer 0 or the empty string -static int is_zero(const struct value *v) +static int is_zero(struct value *v) { - return ((v->s && *v->s == '\0') || v->i == 0); + return v->s ? !*v->s : !v->i; } static char *num_to_str(long long num) @@ -86,30 +52,17 @@ static char *num_to_str(long long num) return num_buf; } -static int cmp(const struct value *lhs, const struct value *rhs) +static int cmp(struct value *lhs, struct value *rhs) { if (lhs->s || rhs->s) { // at least one operand is a string char *ls = lhs->s ? lhs->s : num_to_str(lhs->i); char *rs = rhs->s ? rhs->s : num_to_str(rhs->i); return strcmp(ls, rs); - } else { - return lhs->i - rhs->i; - } + } else return lhs->i - rhs->i; } - -// operators - -struct op { - const char *tok; - - // calculate "lhs op rhs" (e.g. lhs + rhs) and store result in lhs - void (*calc)(struct value *lhs, const struct value *rhs); -}; - - -static void re(struct value *lhs, const struct value *rhs) +static void re(struct value *lhs, struct value *rhs) { regex_t rp; regmatch_t rm[2]; @@ -130,75 +83,75 @@ static void re(struct value *lhs, const struct value *rhs) } } -static void mod(struct value *lhs, const struct value *rhs) +static void mod(struct value *lhs, struct value *rhs) { if (lhs->s || rhs->s) error_exit("non-integer argument"); if (is_zero(rhs)) error_exit("division by zero"); lhs->i %= rhs->i; } -static void divi(struct value *lhs, const struct value *rhs) +static void divi(struct value *lhs, struct value *rhs) { if (lhs->s || rhs->s) error_exit("non-integer argument"); if (is_zero(rhs)) error_exit("division by zero"); lhs->i /= rhs->i; } -static void mul(struct value *lhs, const struct value *rhs) +static void mul(struct value *lhs, struct value *rhs) { if (lhs->s || rhs->s) error_exit("non-integer argument"); lhs->i *= rhs->i; } -static void sub(struct value *lhs, const struct value *rhs) +static void sub(struct value *lhs, struct value *rhs) { if (lhs->s || rhs->s) error_exit("non-integer argument"); lhs->i -= rhs->i; } -static void add(struct value *lhs, const struct value *rhs) +static void add(struct value *lhs, struct value *rhs) { if (lhs->s || rhs->s) error_exit("non-integer argument"); lhs->i += rhs->i; } -static void ne(struct value *lhs, const struct value *rhs) +static void ne(struct value *lhs, struct value *rhs) { lhs->i = cmp(lhs, rhs) != 0; lhs->s = NULL; } -static void lte(struct value *lhs, const struct value *rhs) +static void lte(struct value *lhs, struct value *rhs) { lhs->i = cmp(lhs, rhs) <= 0; lhs->s = NULL; } -static void lt(struct value *lhs, const struct value *rhs) +static void lt(struct value *lhs, struct value *rhs) { lhs->i = cmp(lhs, rhs) < 0; lhs->s = NULL; } -static void gte(struct value *lhs, const struct value *rhs) +static void gte(struct value *lhs, struct value *rhs) { lhs->i = cmp(lhs, rhs) >= 0; lhs->s = NULL; } -static void gt(struct value *lhs, const struct value *rhs) +static void gt(struct value *lhs, struct value *rhs) { lhs->i = cmp(lhs, rhs) > 0; lhs->s = NULL; } -static void eq(struct value *lhs, const struct value *rhs) +static void eq(struct value *lhs, struct value *rhs) { - lhs->i = cmp(lhs, rhs) == 0; + lhs->i = !cmp(lhs, rhs); lhs->s = NULL; } -static void and(struct value *lhs, const struct value *rhs) +static void and(struct value *lhs, struct value *rhs) { if (is_zero(lhs) || is_zero(rhs)) { lhs->i = 0; @@ -206,52 +159,71 @@ static void and(struct value *lhs, const struct value *rhs) } } -static void or(struct value *lhs, const struct value *rhs) +static void or(struct value *lhs, struct value *rhs) { - if (is_zero(lhs)) { - *lhs = *rhs; - } + if (is_zero(lhs)) *lhs = *rhs; } +static void get_value(struct value *v) +{ + char *endp, *arg; -// operators in order of increasing precedence -static const struct op ops[] = { - {"|", or }, - {"&", and }, - {"=", eq }, - {"==", eq }, - {">", gt }, - {">=", gte }, - {"<", lt }, - {"<=", lte }, - {"!=", ne }, - {"+", add }, - {"-", sub }, - {"*", mul }, - {"/", divi}, - {"%", mod }, - {":", re }, - {"(", NULL}, // special case - must be last -}; + if (TT.argidx == toys.optc) { + v->i = 0; + v->s = ""; // signal end of expression + return; + } +// can't happen, the increment is after the == test +// if (TT.argidx >= toys.optc) error_exit("syntax error"); + + arg = toys.optargs[TT.argidx++]; + + v->i = strtoll(arg, &endp, 10); + v->s = *endp ? arg : NULL; +} -static void parse_parens(struct value *ret, struct value *v) +// check if v matches a token, and consume it if so +static int match(struct value *v, char *tok) { - if (match(v, "(")) { - parse_expr(ret, v); - if (!match(v, ")")) error_exit("syntax error"); // missing closing paren - } else { - // v is a string or integer - return it and get the next token - *ret = *v; + if (v->s && !strcmp(v->s, tok)) { get_value(v); + return 1; } + + return 0; } -static void parse_op(struct value *lhs, struct value *tok, const struct op *op) +// operators in order of increasing precedence +static struct op { + char *tok; + + // calculate "lhs op rhs" (e.g. lhs + rhs) and store result in lhs + void (*calc)(struct value *lhs, struct value *rhs); +} ops[] = { + {"|", or }, {"&", and }, {"=", eq }, {"==", eq }, {">", gt }, + {">=", gte }, {"<", lt }, {"<=", lte }, {"!=", ne }, {"+", add }, + {"-", sub }, {"*", mul }, {"/", divi}, {"%", mod }, {":", re }, + {"(", NULL}, // special case - must be last +}; + +// "|,&,= ==> >=< <= !=,+-,*/%,:" + +static void parse_op(struct value *lhs, struct value *tok, struct op *op) { + if (!op) op = ops; + // special case parsing for parentheses if (*op->tok == '(') { - parse_parens(lhs, tok); + if (match(tok, "(")) { + parse_op(lhs, tok, 0); + if (!match(tok, ")")) error_exit("syntax error"); // missing closing paren + } else { + // tok is a string or integer - return it and get the next token + *lhs = *tok; + get_value(tok); + } + return; } @@ -264,11 +236,6 @@ static void parse_op(struct value *lhs, struct value *tok, const struct op *op) } } -static void parse_expr(struct value *ret, struct value *v) -{ - parse_op(ret, v, ops); // start at the top of the ops table -} - void expr_main(void) { struct value tok, ret = {0}; @@ -278,9 +245,10 @@ void expr_main(void) TT.argidx = 0; get_value(&tok); // warm up the parser with the initial value - parse_expr(&ret, &tok); + parse_op(&ret, &tok, 0); - if (!tok.s || *tok.s) error_exit("syntax error"); // final token should be end of expression + // final token should be end of expression + if (!tok.s || *tok.s) error_exit("syntax error"); if (ret.s) printf("%s\n", ret.s); else printf("%lld\n", ret.i); |