1 /* expr.c - evaluate expression
3 * Copyright 2016 Google Inc.
4 * Copyright 2013 Daniel Verkamp <daniel@drv.nu>
6 * http://pubs.opengroup.org/onlinepubs/9699919799/utilities/expr.html
8 * The web standard is incomplete (precedence grouping missing), see:
9 * http://permalink.gmane.org/gmane.comp.standards.posix.austin.general/10141
11 * eval_expr() uses the recursive "Precedence Climbing" algorithm:
13 * Clarke, Keith. "The top-down parsing of expressions." University of London.
14 * Queen Mary College. Department of Computer Science and Statistics, 1986.
16 * http://www.antlr.org/papers/Clarke-expr-parsing-1986.pdf
18 * Nice explanation and Python implementation:
19 * http://eli.thegreenplace.net/2012/08/02/parsing-expressions-by-precedence-climbing
21 USE_EXPR(NEWTOY(expr, NULL, TOYFLAG_USR|TOYFLAG_BIN))
27 usage: expr ARG1 OPERATOR ARG2...
29 Evaluate expression and print result. For example, "expr 1 + 2".
31 The supported operators are (grouped from highest to lowest priority):
33 ( ) : * / % + - != <= < >= > = & |
35 Each constant and operator must be a separate command line argument.
36 All operators are infix, meaning they expect a constant (or expression
37 that resolves to a constant) on each side of the operator. Operators of
38 the same priority (within each group above) are evaluated left to right.
39 Parentheses may be used (as separate arguments) to elevate the priority
42 Calling expr from a command shell requires a lot of \( or '*' escaping
43 to avoid interpreting shell control characters.
45 The & and | operators are logical (not bitwise) and may operate on
46 strings (a blank string is "false"). Comparison operators may also
47 operate on strings (alphabetical sort).
49 Constants may be strings or integers. Comparison, logical, and regex
50 operators may operate on strings (a blank string is "false"), other
51 operators require integers.
54 // TODO: int overflow checking
60 char **tok; // current token, not on the stack since recursive calls mutate it
65 // Scalar value. If s != NULL, it's a string, otherwise it's an int.
71 // Get the value as a string.
72 char *get_str(struct value *v)
74 if (v->s) return v->s;
75 else return xmprintf("%lld", v->i);
78 // Get the value as an integer and return 1, or return 0 on error.
79 int get_int(struct value *v, long long *ret)
84 *ret = strtoll(v->s, &endp, 10);
86 if (*endp) return 0; // If endp points to NUL, all chars were converted
92 // Preserve the invariant that v.s is NULL when the value is an integer.
93 void assign_int(struct value *v, long long i)
99 // Check if v is 0 or the empty string.
100 static int is_false(struct value *v)
102 return get_int(v, &v->i) && !v->i;
105 // 'ret' is filled with a string capture or int match position.
106 static void re(char *target, char *pattern, struct value *ret)
111 xregcomp(&pat, pattern, 0);
112 // must match at pos 0
113 if (!regexec(&pat, target, 2, m, 0) && !m[0].rm_so) {
114 // Return first parenthesized subexpression as string, or length of match
116 ret->s = xmprintf("%.*s", m[1].rm_eo-m[1].rm_so, target+m[1].rm_so);
117 if (TT.refree) free(TT.refree);
119 } else assign_int(ret, m[0].rm_eo);
121 if (pat.re_nsub>0) ret->s = "";
122 else assign_int(ret, 0);
127 // 4 different signatures of operators. S = string, I = int, SI = string or
129 enum { SI_TO_SI = 1, SI_TO_I, I_TO_I, S_TO_SI };
131 enum { OR = 1, AND, EQ, NE, GT, GTE, LT, LTE, ADD, SUB, MUL, DIVI, MOD, RE };
133 // operators grouped by precedence
134 static struct op_def {
136 char prec, sig, op; // precedence, signature for type coercion, operator ID
138 // logical ops, precedence 1 and 2, signature SI_TO_SI
139 {"|", 1, SI_TO_SI, OR },
140 {"&", 2, SI_TO_SI, AND },
141 // comparison ops, precedence 3, signature SI_TO_I
142 {"=", 3, SI_TO_I, EQ }, {"==", 3, SI_TO_I, EQ }, {"!=", 3, SI_TO_I, NE },
143 {">", 3, SI_TO_I, GT }, {">=", 3, SI_TO_I, GTE },
144 {"<", 3, SI_TO_I, LT }, {"<=", 3, SI_TO_I, LTE },
145 // arithmetic ops, precedence 4 and 5, signature I_TO_I
146 {"+", 4, I_TO_I, ADD }, {"-", 4, I_TO_I, SUB },
147 {"*", 5, I_TO_I, MUL }, {"/", 5, I_TO_I, DIVI }, {"%", 5, I_TO_I, MOD },
148 // regex match, precedence 6, signature S_TO_SI
149 {":", 6, S_TO_SI, RE },
150 {NULL, 0, 0, 0}, // sentinel
153 void eval_op(struct op_def *o, struct value *ret, struct value *rhs)
155 long long a, b, x = 0; // x = a OP b for ints.
156 char *s, *t; // string operands
163 case OR: if (is_false(ret)) *ret = *rhs; break;
164 case AND: if (is_false(ret) || is_false(rhs)) assign_int(ret, 0); break;
169 if (get_int(ret, &a) && get_int(rhs, &b)) { // both are ints
171 } else { // otherwise compare both as strings
172 cmp = strcmp(s = get_str(ret), t = get_str(rhs));
173 if (ret->s != s) free(s);
174 if (rhs->s != t) free(t);
177 case EQ: x = cmp == 0; break;
178 case NE: x = cmp != 0; break;
179 case GT: x = cmp > 0; break;
180 case GTE: x = cmp >= 0; break;
181 case LT: x = cmp < 0; break;
182 case LTE: x = cmp <= 0; break;
188 if (!get_int(ret, &a) || !get_int(rhs, &b))
189 error_exit("non-integer argument");
191 case ADD: x = a + b; break;
192 case SUB: x = a - b; break;
193 case MUL: x = a * b; break;
194 case DIVI: if (b == 0) error_exit("division by zero"); x = a / b; break;
195 case MOD: if (b == 0) error_exit("division by zero"); x = a % b; break;
200 case S_TO_SI: // op == RE
202 cmp = ret->s!=s; // ret overwritten by re so check now
203 re(s, t = get_str(rhs), ret);
205 if (rhs->s!=t) free(t);
210 // Evalute a compound expression using recursive "Precedence Climbing"
211 // algorithm, setting 'ret'.
212 static void eval_expr(struct value *ret, int min_prec)
214 if (!*TT.tok) error_exit("Unexpected end of input");
216 // Evaluate LHS atom, setting 'ret'.
217 if (!strcmp(*TT.tok, "(")) { // parenthesized expression
218 TT.tok++; // consume (
220 eval_expr(ret, 1); // We're inside ( ), so min_prec = 1
221 if (ret->s && !strcmp(ret->s, ")")) error_exit("empty ( )");
222 if (!*TT.tok) error_exit("Expected )");
223 if (strcmp(*TT.tok, ")")) error_exit("Expected ) but got %s", *TT.tok);
224 } else ret->s = *TT.tok; // simple literal, all values start as strings
227 // Evaluate RHS and apply operator until precedence is too low.
230 struct op_def *o = OPS;
232 while (o->tok) { // Look up operator
233 if (!strcmp(*TT.tok, o->tok)) break;
236 if (!o->tok) break; // Not an operator (extra input will fail later)
237 if (o->prec < min_prec) break; // Precedence too low, pop a stack frame
240 eval_expr(&rhs, o->prec + 1); // Evaluate RHS, with higher min precedence
241 eval_op(o, ret, &rhs); // Apply operator, setting 'ret'
247 struct value ret = {0};
249 toys.exitval = 2; // if exiting early, indicate error
250 TT.tok = toys.optargs; // initialize global token
252 if (*TT.tok) error_exit("Unexpected extra input '%s'\n", *TT.tok);
254 if (ret.s) printf("%s\n", ret.s);
255 else printf("%lld\n", ret.i);
257 toys.exitval = is_false(&ret);
259 if (TT.refree) free(TT.refree);