132526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley/* expr.c - evaluate expression 232526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley * 3e6912f90d663120b32b894d1f10a0d8cd530e2e7Andy Chu * Copyright 2016 Google Inc. 432526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley * Copyright 2013 Daniel Verkamp <daniel@drv.nu> 532526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley * 632526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley * http://pubs.opengroup.org/onlinepubs/9699919799/utilities/expr.html 7bce8a514aaf0a7e8b98ff20217c7a44a50aa46deRob Landley * 8bce8a514aaf0a7e8b98ff20217c7a44a50aa46deRob Landley * The web standard is incomplete (precedence grouping missing), see: 9bce8a514aaf0a7e8b98ff20217c7a44a50aa46deRob Landley * http://permalink.gmane.org/gmane.comp.standards.posix.austin.general/10141 101af3bad8b63b2b6886123d9e681782781a62efcaRob Landley * 111af3bad8b63b2b6886123d9e681782781a62efcaRob Landley * eval_expr() uses the recursive "Precedence Climbing" algorithm: 121af3bad8b63b2b6886123d9e681782781a62efcaRob Landley * 131af3bad8b63b2b6886123d9e681782781a62efcaRob Landley * Clarke, Keith. "The top-down parsing of expressions." University of London. 141af3bad8b63b2b6886123d9e681782781a62efcaRob Landley * Queen Mary College. Department of Computer Science and Statistics, 1986. 151af3bad8b63b2b6886123d9e681782781a62efcaRob Landley * 161af3bad8b63b2b6886123d9e681782781a62efcaRob Landley * http://www.antlr.org/papers/Clarke-expr-parsing-1986.pdf 171af3bad8b63b2b6886123d9e681782781a62efcaRob Landley * 181af3bad8b63b2b6886123d9e681782781a62efcaRob Landley * Nice explanation and Python implementation: 191af3bad8b63b2b6886123d9e681782781a62efcaRob Landley * http://eli.thegreenplace.net/2012/08/02/parsing-expressions-by-precedence-climbing 2032526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley 2132526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob LandleyUSE_EXPR(NEWTOY(expr, NULL, TOYFLAG_USR|TOYFLAG_BIN)) 2232526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley 2332526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landleyconfig EXPR 2432526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley bool "expr" 2532526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley default n 2632526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley help 277f24174da2eb388168b0320cb095b834b57992e8Rob Landley usage: expr ARG1 OPERATOR ARG2... 2832526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley 297f24174da2eb388168b0320cb095b834b57992e8Rob Landley Evaluate expression and print result. For example, "expr 1 + 2". 3032526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley 317f24174da2eb388168b0320cb095b834b57992e8Rob Landley The supported operators are (grouped from highest to lowest priority): 3232526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley 337f24174da2eb388168b0320cb095b834b57992e8Rob Landley ( ) : * / % + - != <= < >= > = & | 3432526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley 35eeff24f941eee0696b36ec7115bb9e958e38ac10Elliott Hughes Each constant and operator must be a separate command line argument. 367f24174da2eb388168b0320cb095b834b57992e8Rob Landley All operators are infix, meaning they expect a constant (or expression 377f24174da2eb388168b0320cb095b834b57992e8Rob Landley that resolves to a constant) on each side of the operator. Operators of 387f24174da2eb388168b0320cb095b834b57992e8Rob Landley the same priority (within each group above) are evaluated left to right. 39eeff24f941eee0696b36ec7115bb9e958e38ac10Elliott Hughes Parentheses may be used (as separate arguments) to elevate the priority 407f24174da2eb388168b0320cb095b834b57992e8Rob Landley of expressions. 417f24174da2eb388168b0320cb095b834b57992e8Rob Landley 427f24174da2eb388168b0320cb095b834b57992e8Rob Landley Calling expr from a command shell requires a lot of \( or '*' escaping 437f24174da2eb388168b0320cb095b834b57992e8Rob Landley to avoid interpreting shell control characters. 447f24174da2eb388168b0320cb095b834b57992e8Rob Landley 45eeff24f941eee0696b36ec7115bb9e958e38ac10Elliott Hughes The & and | operators are logical (not bitwise) and may operate on 467f24174da2eb388168b0320cb095b834b57992e8Rob Landley strings (a blank string is "false"). Comparison operators may also 477f24174da2eb388168b0320cb095b834b57992e8Rob Landley operate on strings (alphabetical sort). 487f24174da2eb388168b0320cb095b834b57992e8Rob Landley 497f24174da2eb388168b0320cb095b834b57992e8Rob Landley Constants may be strings or integers. Comparison, logical, and regex 507f24174da2eb388168b0320cb095b834b57992e8Rob Landley operators may operate on strings (a blank string is "false"), other 517f24174da2eb388168b0320cb095b834b57992e8Rob Landley operators require integers. 5232526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley*/ 5332526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley 5432526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley// TODO: int overflow checking 5532526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley 5632526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley#define FOR_expr 5732526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley#include "toys.h" 5832526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley 5932526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob LandleyGLOBALS( 60438fa71547d791c5bee3201b42b16415627cdb45Rob Landley char **tok; // current token, not on the stack since recursive calls mutate it 616aba814b28cddd45c7301c39dccd65b316eb5c82Rob Landley 626aba814b28cddd45c7301c39dccd65b316eb5c82Rob Landley char *refree; 6332526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley) 6432526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley 65e6912f90d663120b32b894d1f10a0d8cd530e2e7Andy Chu// Scalar value. If s != NULL, it's a string, otherwise it's an int. 6632526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landleystruct value { 6732526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley char *s; 68bc9cfe08cfa2c47b2d106eda7b5d0ecc73f47238Rob Landley long long i; 6932526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley}; 7032526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley 71e6912f90d663120b32b894d1f10a0d8cd530e2e7Andy Chu// Get the value as a string. 720ec95b7c2e20cd7be33bae6adba20bf89c5f3e86Rob Landleychar *get_str(struct value *v) 7332526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley{ 740ec95b7c2e20cd7be33bae6adba20bf89c5f3e86Rob Landley if (v->s) return v->s; 750ec95b7c2e20cd7be33bae6adba20bf89c5f3e86Rob Landley else return xmprintf("%lld", v->i); 7632526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley} 7732526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley 78e6912f90d663120b32b894d1f10a0d8cd530e2e7Andy Chu// Get the value as an integer and return 1, or return 0 on error. 79e6912f90d663120b32b894d1f10a0d8cd530e2e7Andy Chuint get_int(struct value *v, long long *ret) 8032526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley{ 81e6912f90d663120b32b894d1f10a0d8cd530e2e7Andy Chu if (v->s) { 82e6912f90d663120b32b894d1f10a0d8cd530e2e7Andy Chu char *endp; 831af3bad8b63b2b6886123d9e681782781a62efcaRob Landley 84e6912f90d663120b32b894d1f10a0d8cd530e2e7Andy Chu *ret = strtoll(v->s, &endp, 10); 851af3bad8b63b2b6886123d9e681782781a62efcaRob Landley 861af3bad8b63b2b6886123d9e681782781a62efcaRob Landley if (*endp) return 0; // If endp points to NUL, all chars were converted 871af3bad8b63b2b6886123d9e681782781a62efcaRob Landley } else *ret = v->i; 881af3bad8b63b2b6886123d9e681782781a62efcaRob Landley 891af3bad8b63b2b6886123d9e681782781a62efcaRob Landley return 1; 9032526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley} 9132526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley 92e6912f90d663120b32b894d1f10a0d8cd530e2e7Andy Chu// Preserve the invariant that v.s is NULL when the value is an integer. 93e6912f90d663120b32b894d1f10a0d8cd530e2e7Andy Chuvoid assign_int(struct value *v, long long i) 9432526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley{ 95e6912f90d663120b32b894d1f10a0d8cd530e2e7Andy Chu v->i = i; 96e6912f90d663120b32b894d1f10a0d8cd530e2e7Andy Chu v->s = NULL; 9732526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley} 9832526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley 99e6912f90d663120b32b894d1f10a0d8cd530e2e7Andy Chu// Check if v is 0 or the empty string. 100e6912f90d663120b32b894d1f10a0d8cd530e2e7Andy Chustatic int is_false(struct value *v) 10132526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley{ 1026aba814b28cddd45c7301c39dccd65b316eb5c82Rob Landley return get_int(v, &v->i) && !v->i; 10332526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley} 10432526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley 105e6912f90d663120b32b894d1f10a0d8cd530e2e7Andy Chu// 'ret' is filled with a string capture or int match position. 106e6912f90d663120b32b894d1f10a0d8cd530e2e7Andy Chustatic void re(char *target, char *pattern, struct value *ret) 10732526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley{ 108e6912f90d663120b32b894d1f10a0d8cd530e2e7Andy Chu regex_t pat; 109e6912f90d663120b32b894d1f10a0d8cd530e2e7Andy Chu regmatch_t m[2]; 110e6912f90d663120b32b894d1f10a0d8cd530e2e7Andy Chu 111e6912f90d663120b32b894d1f10a0d8cd530e2e7Andy Chu xregcomp(&pat, pattern, 0); 1126aba814b28cddd45c7301c39dccd65b316eb5c82Rob Landley // must match at pos 0 1136aba814b28cddd45c7301c39dccd65b316eb5c82Rob Landley if (!regexec(&pat, target, 2, m, 0) && !m[0].rm_so) { 1146aba814b28cddd45c7301c39dccd65b316eb5c82Rob Landley // Return first parenthesized subexpression as string, or length of match 1156aba814b28cddd45c7301c39dccd65b316eb5c82Rob Landley if (pat.re_nsub>0) { 116352efdf18d98be859d19bbae2cd2732d2b7f01cfElliott Hughes ret->s = xmprintf("%.*s", (int)(m[1].rm_eo-m[1].rm_so), 117352efdf18d98be859d19bbae2cd2732d2b7f01cfElliott Hughes target+m[1].rm_so); 1186aba814b28cddd45c7301c39dccd65b316eb5c82Rob Landley if (TT.refree) free(TT.refree); 1196aba814b28cddd45c7301c39dccd65b316eb5c82Rob Landley TT.refree = ret->s; 1206aba814b28cddd45c7301c39dccd65b316eb5c82Rob Landley } else assign_int(ret, m[0].rm_eo); 1216aba814b28cddd45c7301c39dccd65b316eb5c82Rob Landley } else { 1226aba814b28cddd45c7301c39dccd65b316eb5c82Rob Landley if (pat.re_nsub>0) ret->s = ""; 1231af3bad8b63b2b6886123d9e681782781a62efcaRob Landley else assign_int(ret, 0); 12432526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley } 1256aba814b28cddd45c7301c39dccd65b316eb5c82Rob Landley regfree(&pat); 12632526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley} 12732526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley 128e6912f90d663120b32b894d1f10a0d8cd530e2e7Andy Chu// 4 different signatures of operators. S = string, I = int, SI = string or 129e6912f90d663120b32b894d1f10a0d8cd530e2e7Andy Chu// int. 130e6912f90d663120b32b894d1f10a0d8cd530e2e7Andy Chuenum { SI_TO_SI = 1, SI_TO_I, I_TO_I, S_TO_SI }; 13132526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley 132e6912f90d663120b32b894d1f10a0d8cd530e2e7Andy Chuenum { OR = 1, AND, EQ, NE, GT, GTE, LT, LTE, ADD, SUB, MUL, DIVI, MOD, RE }; 13332526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley 13414c91c1ebd68daacce9794cf8894dcfea68efd7bAndy Chu// operators grouped by precedence 135e6912f90d663120b32b894d1f10a0d8cd530e2e7Andy Chustatic struct op_def { 136ef6a773198040a05a56dec2261516fb149823cf6Rob Landley char *tok; 137e6912f90d663120b32b894d1f10a0d8cd530e2e7Andy Chu char prec, sig, op; // precedence, signature for type coercion, operator ID 13814c91c1ebd68daacce9794cf8894dcfea68efd7bAndy Chu} OPS[] = { 139e6912f90d663120b32b894d1f10a0d8cd530e2e7Andy Chu // logical ops, precedence 1 and 2, signature SI_TO_SI 140e6912f90d663120b32b894d1f10a0d8cd530e2e7Andy Chu {"|", 1, SI_TO_SI, OR }, 141e6912f90d663120b32b894d1f10a0d8cd530e2e7Andy Chu {"&", 2, SI_TO_SI, AND }, 142e6912f90d663120b32b894d1f10a0d8cd530e2e7Andy Chu // comparison ops, precedence 3, signature SI_TO_I 143e6912f90d663120b32b894d1f10a0d8cd530e2e7Andy Chu {"=", 3, SI_TO_I, EQ }, {"==", 3, SI_TO_I, EQ }, {"!=", 3, SI_TO_I, NE }, 144e6912f90d663120b32b894d1f10a0d8cd530e2e7Andy Chu {">", 3, SI_TO_I, GT }, {">=", 3, SI_TO_I, GTE }, 145e6912f90d663120b32b894d1f10a0d8cd530e2e7Andy Chu {"<", 3, SI_TO_I, LT }, {"<=", 3, SI_TO_I, LTE }, 146e6912f90d663120b32b894d1f10a0d8cd530e2e7Andy Chu // arithmetic ops, precedence 4 and 5, signature I_TO_I 147e6912f90d663120b32b894d1f10a0d8cd530e2e7Andy Chu {"+", 4, I_TO_I, ADD }, {"-", 4, I_TO_I, SUB }, 148e6912f90d663120b32b894d1f10a0d8cd530e2e7Andy Chu {"*", 5, I_TO_I, MUL }, {"/", 5, I_TO_I, DIVI }, {"%", 5, I_TO_I, MOD }, 149e6912f90d663120b32b894d1f10a0d8cd530e2e7Andy Chu // regex match, precedence 6, signature S_TO_SI 150e6912f90d663120b32b894d1f10a0d8cd530e2e7Andy Chu {":", 6, S_TO_SI, RE }, 151e6912f90d663120b32b894d1f10a0d8cd530e2e7Andy Chu {NULL, 0, 0, 0}, // sentinel 152ef6a773198040a05a56dec2261516fb149823cf6Rob Landley}; 153ef6a773198040a05a56dec2261516fb149823cf6Rob Landley 1541af3bad8b63b2b6886123d9e681782781a62efcaRob Landleyvoid eval_op(struct op_def *o, struct value *ret, struct value *rhs) 1551af3bad8b63b2b6886123d9e681782781a62efcaRob Landley{ 156e6912f90d663120b32b894d1f10a0d8cd530e2e7Andy Chu long long a, b, x = 0; // x = a OP b for ints. 157e6912f90d663120b32b894d1f10a0d8cd530e2e7Andy Chu char *s, *t; // string operands 158e6912f90d663120b32b894d1f10a0d8cd530e2e7Andy Chu int cmp; 159e6912f90d663120b32b894d1f10a0d8cd530e2e7Andy Chu 160e6912f90d663120b32b894d1f10a0d8cd530e2e7Andy Chu switch (o->sig) { 161e6912f90d663120b32b894d1f10a0d8cd530e2e7Andy Chu 162e6912f90d663120b32b894d1f10a0d8cd530e2e7Andy Chu case SI_TO_SI: 163e6912f90d663120b32b894d1f10a0d8cd530e2e7Andy Chu switch (o->op) { 164e6912f90d663120b32b894d1f10a0d8cd530e2e7Andy Chu case OR: if (is_false(ret)) *ret = *rhs; break; 165e6912f90d663120b32b894d1f10a0d8cd530e2e7Andy Chu case AND: if (is_false(ret) || is_false(rhs)) assign_int(ret, 0); break; 166e6912f90d663120b32b894d1f10a0d8cd530e2e7Andy Chu } 167e6912f90d663120b32b894d1f10a0d8cd530e2e7Andy Chu break; 168e6912f90d663120b32b894d1f10a0d8cd530e2e7Andy Chu 169e6912f90d663120b32b894d1f10a0d8cd530e2e7Andy Chu case SI_TO_I: 170e6912f90d663120b32b894d1f10a0d8cd530e2e7Andy Chu if (get_int(ret, &a) && get_int(rhs, &b)) { // both are ints 171e6912f90d663120b32b894d1f10a0d8cd530e2e7Andy Chu cmp = a - b; 172e6912f90d663120b32b894d1f10a0d8cd530e2e7Andy Chu } else { // otherwise compare both as strings 1730ec95b7c2e20cd7be33bae6adba20bf89c5f3e86Rob Landley cmp = strcmp(s = get_str(ret), t = get_str(rhs)); 1740ec95b7c2e20cd7be33bae6adba20bf89c5f3e86Rob Landley if (ret->s != s) free(s); 1750ec95b7c2e20cd7be33bae6adba20bf89c5f3e86Rob Landley if (rhs->s != t) free(t); 176e6912f90d663120b32b894d1f10a0d8cd530e2e7Andy Chu } 177e6912f90d663120b32b894d1f10a0d8cd530e2e7Andy Chu switch (o->op) { 178e6912f90d663120b32b894d1f10a0d8cd530e2e7Andy Chu case EQ: x = cmp == 0; break; 179e6912f90d663120b32b894d1f10a0d8cd530e2e7Andy Chu case NE: x = cmp != 0; break; 180e6912f90d663120b32b894d1f10a0d8cd530e2e7Andy Chu case GT: x = cmp > 0; break; 181e6912f90d663120b32b894d1f10a0d8cd530e2e7Andy Chu case GTE: x = cmp >= 0; break; 182e6912f90d663120b32b894d1f10a0d8cd530e2e7Andy Chu case LT: x = cmp < 0; break; 183e6912f90d663120b32b894d1f10a0d8cd530e2e7Andy Chu case LTE: x = cmp <= 0; break; 184e6912f90d663120b32b894d1f10a0d8cd530e2e7Andy Chu } 185e6912f90d663120b32b894d1f10a0d8cd530e2e7Andy Chu assign_int(ret, x); 186e6912f90d663120b32b894d1f10a0d8cd530e2e7Andy Chu break; 187e6912f90d663120b32b894d1f10a0d8cd530e2e7Andy Chu 188e6912f90d663120b32b894d1f10a0d8cd530e2e7Andy Chu case I_TO_I: 189e6912f90d663120b32b894d1f10a0d8cd530e2e7Andy Chu if (!get_int(ret, &a) || !get_int(rhs, &b)) 190e6912f90d663120b32b894d1f10a0d8cd530e2e7Andy Chu error_exit("non-integer argument"); 191e6912f90d663120b32b894d1f10a0d8cd530e2e7Andy Chu switch (o->op) { 192e6912f90d663120b32b894d1f10a0d8cd530e2e7Andy Chu case ADD: x = a + b; break; 193e6912f90d663120b32b894d1f10a0d8cd530e2e7Andy Chu case SUB: x = a - b; break; 194e6912f90d663120b32b894d1f10a0d8cd530e2e7Andy Chu case MUL: x = a * b; break; 195e6912f90d663120b32b894d1f10a0d8cd530e2e7Andy Chu case DIVI: if (b == 0) error_exit("division by zero"); x = a / b; break; 196e6912f90d663120b32b894d1f10a0d8cd530e2e7Andy Chu case MOD: if (b == 0) error_exit("division by zero"); x = a % b; break; 197e6912f90d663120b32b894d1f10a0d8cd530e2e7Andy Chu } 198e6912f90d663120b32b894d1f10a0d8cd530e2e7Andy Chu assign_int(ret, x); 199e6912f90d663120b32b894d1f10a0d8cd530e2e7Andy Chu break; 200e6912f90d663120b32b894d1f10a0d8cd530e2e7Andy Chu 201e6912f90d663120b32b894d1f10a0d8cd530e2e7Andy Chu case S_TO_SI: // op == RE 2020ec95b7c2e20cd7be33bae6adba20bf89c5f3e86Rob Landley s = get_str(ret); 2030ec95b7c2e20cd7be33bae6adba20bf89c5f3e86Rob Landley cmp = ret->s!=s; // ret overwritten by re so check now 2040ec95b7c2e20cd7be33bae6adba20bf89c5f3e86Rob Landley re(s, t = get_str(rhs), ret); 2050ec95b7c2e20cd7be33bae6adba20bf89c5f3e86Rob Landley if (cmp) free(s); 2060ec95b7c2e20cd7be33bae6adba20bf89c5f3e86Rob Landley if (rhs->s!=t) free(t); 207e6912f90d663120b32b894d1f10a0d8cd530e2e7Andy Chu break; 208e6912f90d663120b32b894d1f10a0d8cd530e2e7Andy Chu } 209e6912f90d663120b32b894d1f10a0d8cd530e2e7Andy Chu} 210e6912f90d663120b32b894d1f10a0d8cd530e2e7Andy Chu 2111af3bad8b63b2b6886123d9e681782781a62efcaRob Landley// Evalute a compound expression using recursive "Precedence Climbing" 2121af3bad8b63b2b6886123d9e681782781a62efcaRob Landley// algorithm, setting 'ret'. 21314c91c1ebd68daacce9794cf8894dcfea68efd7bAndy Chustatic void eval_expr(struct value *ret, int min_prec) 21432526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley{ 215438fa71547d791c5bee3201b42b16415627cdb45Rob Landley if (!*TT.tok) error_exit("Unexpected end of input"); 21614c91c1ebd68daacce9794cf8894dcfea68efd7bAndy Chu 21714c91c1ebd68daacce9794cf8894dcfea68efd7bAndy Chu // Evaluate LHS atom, setting 'ret'. 218438fa71547d791c5bee3201b42b16415627cdb45Rob Landley if (!strcmp(*TT.tok, "(")) { // parenthesized expression 219438fa71547d791c5bee3201b42b16415627cdb45Rob Landley TT.tok++; // consume ( 220438fa71547d791c5bee3201b42b16415627cdb45Rob Landley 221e6912f90d663120b32b894d1f10a0d8cd530e2e7Andy Chu eval_expr(ret, 1); // We're inside ( ), so min_prec = 1 222438fa71547d791c5bee3201b42b16415627cdb45Rob Landley if (ret->s && !strcmp(ret->s, ")")) error_exit("empty ( )"); 223438fa71547d791c5bee3201b42b16415627cdb45Rob Landley if (!*TT.tok) error_exit("Expected )"); 224438fa71547d791c5bee3201b42b16415627cdb45Rob Landley if (strcmp(*TT.tok, ")")) error_exit("Expected ) but got %s", *TT.tok); 225438fa71547d791c5bee3201b42b16415627cdb45Rob Landley } else ret->s = *TT.tok; // simple literal, all values start as strings 226438fa71547d791c5bee3201b42b16415627cdb45Rob Landley TT.tok++; 22732526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley 22814c91c1ebd68daacce9794cf8894dcfea68efd7bAndy Chu // Evaluate RHS and apply operator until precedence is too low. 22914c91c1ebd68daacce9794cf8894dcfea68efd7bAndy Chu struct value rhs; 230438fa71547d791c5bee3201b42b16415627cdb45Rob Landley while (*TT.tok) { 231e6912f90d663120b32b894d1f10a0d8cd530e2e7Andy Chu struct op_def *o = OPS; 232438fa71547d791c5bee3201b42b16415627cdb45Rob Landley 233e6912f90d663120b32b894d1f10a0d8cd530e2e7Andy Chu while (o->tok) { // Look up operator 234438fa71547d791c5bee3201b42b16415627cdb45Rob Landley if (!strcmp(*TT.tok, o->tok)) break; 23514c91c1ebd68daacce9794cf8894dcfea68efd7bAndy Chu o++; 23614c91c1ebd68daacce9794cf8894dcfea68efd7bAndy Chu } 237e6912f90d663120b32b894d1f10a0d8cd530e2e7Andy Chu if (!o->tok) break; // Not an operator (extra input will fail later) 23814c91c1ebd68daacce9794cf8894dcfea68efd7bAndy Chu if (o->prec < min_prec) break; // Precedence too low, pop a stack frame 239438fa71547d791c5bee3201b42b16415627cdb45Rob Landley TT.tok++; 24014c91c1ebd68daacce9794cf8894dcfea68efd7bAndy Chu 24114c91c1ebd68daacce9794cf8894dcfea68efd7bAndy Chu eval_expr(&rhs, o->prec + 1); // Evaluate RHS, with higher min precedence 242e6912f90d663120b32b894d1f10a0d8cd530e2e7Andy Chu eval_op(o, ret, &rhs); // Apply operator, setting 'ret' 24332526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley } 24432526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley} 24532526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley 24632526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landleyvoid expr_main(void) 24732526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley{ 24814c91c1ebd68daacce9794cf8894dcfea68efd7bAndy Chu struct value ret = {0}; 249438fa71547d791c5bee3201b42b16415627cdb45Rob Landley 250e6912f90d663120b32b894d1f10a0d8cd530e2e7Andy Chu toys.exitval = 2; // if exiting early, indicate error 251438fa71547d791c5bee3201b42b16415627cdb45Rob Landley TT.tok = toys.optargs; // initialize global token 25214c91c1ebd68daacce9794cf8894dcfea68efd7bAndy Chu eval_expr(&ret, 1); 253438fa71547d791c5bee3201b42b16415627cdb45Rob Landley if (*TT.tok) error_exit("Unexpected extra input '%s'\n", *TT.tok); 25432526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley 25532526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley if (ret.s) printf("%s\n", ret.s); 256bc9cfe08cfa2c47b2d106eda7b5d0ecc73f47238Rob Landley else printf("%lld\n", ret.i); 25732526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley 2586aba814b28cddd45c7301c39dccd65b316eb5c82Rob Landley toys.exitval = is_false(&ret); 2596aba814b28cddd45c7301c39dccd65b316eb5c82Rob Landley 2606aba814b28cddd45c7301c39dccd65b316eb5c82Rob Landley if (TT.refree) free(TT.refree); 26132526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley} 262