/* expr.c - evaluate expression * * Copyright 2013 Daniel Verkamp * * http://pubs.opengroup.org/onlinepubs/9699919799/utilities/expr.html USE_EXPR(NEWTOY(expr, NULL, TOYFLAG_USR|TOYFLAG_BIN)) config EXPR bool "expr" default n help usage: expr args Evaluate expression and print result. The supported operators, in order of increasing precedence, are: | & = > >= < <= != + - * / % In addition, parentheses () are supported for grouping. */ // TODO: int overflow checking #define FOR_expr #include "toys.h" GLOBALS( int argidx; ) // Scalar value. // If s is NULL, the value is an integer (i). // If s is not NULL, the value is a string (s). struct value { char *s; 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) { return ((v->s && *v->s == '\0') || v->i == 0); } static char *num_to_str(long long num) { static char num_buf[21]; snprintf(num_buf, sizeof(num_buf), "%lld", num); return num_buf; } static int cmp(const struct value *lhs, const 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; } } // 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) { error_exit("regular expression match not implemented"); } static void mod(struct value *lhs, const 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) { 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) { if (lhs->s || rhs->s) error_exit("non-integer argument"); lhs->i *= rhs->i; } static void sub(struct value *lhs, const 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) { if (lhs->s || rhs->s) error_exit("non-integer argument"); lhs->i += rhs->i; } static void ne(struct value *lhs, const struct value *rhs) { lhs->i = cmp(lhs, rhs) != 0; lhs->s = NULL; } static void lte(struct value *lhs, const struct value *rhs) { lhs->i = cmp(lhs, rhs) <= 0; lhs->s = NULL; } static void lt(struct value *lhs, const struct value *rhs) { lhs->i = cmp(lhs, rhs) < 0; lhs->s = NULL; } static void gte(struct value *lhs, const struct value *rhs) { lhs->i = cmp(lhs, rhs) >= 0; lhs->s = NULL; } static void gt(struct value *lhs, const struct value *rhs) { lhs->i = cmp(lhs, rhs) > 0; lhs->s = NULL; } static void eq(struct value *lhs, const struct value *rhs) { lhs->i = cmp(lhs, rhs) == 0; lhs->s = NULL; } static void and(struct value *lhs, const struct value *rhs) { if (is_zero(lhs) || is_zero(rhs)) { lhs->i = 0; lhs->s = NULL; } } static void or(struct value *lhs, const struct value *rhs) { if (is_zero(lhs)) { *lhs = *rhs; } } // operators in order of increasing precedence static const struct op ops[] = { {"|", or }, {"&", and }, {"=", eq }, {">", gt }, {">=", gte }, {"<", lt }, {"<=", lte }, {"!=", ne }, {"+", add }, {"-", sub }, {"*", mul }, {"/", divi}, {"%", mod }, {":", re }, {"(", NULL}, // special case - must be last }; static void parse_parens(struct value *ret, struct value *v) { 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; get_value(v); } } static void parse_op(struct value *lhs, struct value *tok, const struct op *op) { // special case parsing for parentheses if (*op->tok == '(') { parse_parens(lhs, tok); return; } parse_op(lhs, tok, op + 1); while (match(tok, op->tok)) { struct value rhs; parse_op(&rhs, tok, op + 1); if (rhs.s && !*rhs.s) error_exit("syntax error"); // premature end of expression op->calc(lhs, &rhs); } } 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}; toys.exitval = 2; // if exiting early, indicate invalid expression TT.argidx = 0; get_value(&tok); // warm up the parser with the initial value parse_expr(&ret, &tok); if (!tok.s || *tok.s) error_exit("syntax error"); // final token should be end of expression if (ret.s) printf("%s\n", ret.s); else printf("%lld\n", ret.i); exit(is_zero(&ret)); }