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