expr.c revision 14c91c1ebd68daacce9794cf8894dcfea68efd7b
132526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley/* expr.c - evaluate expression
232526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley *
332526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley * Copyright 2013 Daniel Verkamp <daniel@drv.nu>
432526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley *
532526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley * http://pubs.opengroup.org/onlinepubs/9699919799/utilities/expr.html
6bce8a514aaf0a7e8b98ff20217c7a44a50aa46deRob Landley *
7bce8a514aaf0a7e8b98ff20217c7a44a50aa46deRob Landley * The web standard is incomplete (precedence grouping missing), see:
8bce8a514aaf0a7e8b98ff20217c7a44a50aa46deRob Landley * http://permalink.gmane.org/gmane.comp.standards.posix.austin.general/10141
932526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley
1032526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob LandleyUSE_EXPR(NEWTOY(expr, NULL, TOYFLAG_USR|TOYFLAG_BIN))
1132526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley
1232526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landleyconfig EXPR
1332526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley  bool "expr"
1432526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley  default n
1532526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley  help
167f24174da2eb388168b0320cb095b834b57992e8Rob Landley    usage: expr ARG1 OPERATOR ARG2...
1732526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley
187f24174da2eb388168b0320cb095b834b57992e8Rob Landley    Evaluate expression and print result. For example, "expr 1 + 2".
1932526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley
207f24174da2eb388168b0320cb095b834b57992e8Rob Landley    The supported operators are (grouped from highest to lowest priority):
2132526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley
227f24174da2eb388168b0320cb095b834b57992e8Rob Landley      ( )    :    * / %    + -    != <= < >= > =    &    |
2332526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley
24eeff24f941eee0696b36ec7115bb9e958e38ac10Elliott Hughes    Each constant and operator must be a separate command line argument.
257f24174da2eb388168b0320cb095b834b57992e8Rob Landley    All operators are infix, meaning they expect a constant (or expression
267f24174da2eb388168b0320cb095b834b57992e8Rob Landley    that resolves to a constant) on each side of the operator. Operators of
277f24174da2eb388168b0320cb095b834b57992e8Rob Landley    the same priority (within each group above) are evaluated left to right.
28eeff24f941eee0696b36ec7115bb9e958e38ac10Elliott Hughes    Parentheses may be used (as separate arguments) to elevate the priority
297f24174da2eb388168b0320cb095b834b57992e8Rob Landley    of expressions.
307f24174da2eb388168b0320cb095b834b57992e8Rob Landley
317f24174da2eb388168b0320cb095b834b57992e8Rob Landley    Calling expr from a command shell requires a lot of \( or '*' escaping
327f24174da2eb388168b0320cb095b834b57992e8Rob Landley    to avoid interpreting shell control characters.
337f24174da2eb388168b0320cb095b834b57992e8Rob Landley
34eeff24f941eee0696b36ec7115bb9e958e38ac10Elliott Hughes    The & and | operators are logical (not bitwise) and may operate on
357f24174da2eb388168b0320cb095b834b57992e8Rob Landley    strings (a blank string is "false"). Comparison operators may also
367f24174da2eb388168b0320cb095b834b57992e8Rob Landley    operate on strings (alphabetical sort).
377f24174da2eb388168b0320cb095b834b57992e8Rob Landley
387f24174da2eb388168b0320cb095b834b57992e8Rob Landley    Constants may be strings or integers. Comparison, logical, and regex
397f24174da2eb388168b0320cb095b834b57992e8Rob Landley    operators may operate on strings (a blank string is "false"), other
407f24174da2eb388168b0320cb095b834b57992e8Rob Landley    operators require integers.
4132526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley*/
4232526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley
4332526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley// TODO: int overflow checking
4432526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley
4532526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley#define FOR_expr
4632526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley#include "toys.h"
4732526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley
4832526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob LandleyGLOBALS(
4914c91c1ebd68daacce9794cf8894dcfea68efd7bAndy Chu  char* tok; // current token, not on the stack since recursive calls mutate it
5032526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley)
5132526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley
5232526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley// Scalar value.
5332526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley// If s is NULL, the value is an integer (i).
5432526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley// If s is not NULL, the value is a string (s).
5532526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landleystruct value {
5632526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley  char *s;
57bc9cfe08cfa2c47b2d106eda7b5d0ecc73f47238Rob Landley  long long i;
5832526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley};
5932526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley
6032526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley// check if v is the integer 0 or the empty string
61ef6a773198040a05a56dec2261516fb149823cf6Rob Landleystatic int is_zero(struct value *v)
6232526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley{
63ef6a773198040a05a56dec2261516fb149823cf6Rob Landley  return v->s ? !*v->s : !v->i;
6432526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley}
6532526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley
66bc9cfe08cfa2c47b2d106eda7b5d0ecc73f47238Rob Landleystatic char *num_to_str(long long num)
6732526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley{
6832526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley  static char num_buf[21];
69bc9cfe08cfa2c47b2d106eda7b5d0ecc73f47238Rob Landley  snprintf(num_buf, sizeof(num_buf), "%lld", num);
7032526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley  return num_buf;
7132526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley}
7232526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley
73ef6a773198040a05a56dec2261516fb149823cf6Rob Landleystatic int cmp(struct value *lhs, struct value *rhs)
7432526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley{
7532526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley  if (lhs->s || rhs->s) {
7632526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley    // at least one operand is a string
7732526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley    char *ls = lhs->s ? lhs->s : num_to_str(lhs->i);
7832526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley    char *rs = rhs->s ? rhs->s : num_to_str(rhs->i);
7932526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley    return strcmp(ls, rs);
80ef6a773198040a05a56dec2261516fb149823cf6Rob Landley  } else return lhs->i - rhs->i;
8132526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley}
8232526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley
83ef6a773198040a05a56dec2261516fb149823cf6Rob Landleystatic void re(struct value *lhs, struct value *rhs)
8432526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley{
85c3657fd41199ca97b74e66e80734f5c4a9509da4Ashwini Sharma  regex_t rp;
86c3657fd41199ca97b74e66e80734f5c4a9509da4Ashwini Sharma  regmatch_t rm[2];
87c3657fd41199ca97b74e66e80734f5c4a9509da4Ashwini Sharma
88c3657fd41199ca97b74e66e80734f5c4a9509da4Ashwini Sharma  xregcomp(&rp, rhs->s, 0);
8914c91c1ebd68daacce9794cf8894dcfea68efd7bAndy Chu  // BUG: lhs->s is NULL when it looks like an integer, causing a segfault.
90c3657fd41199ca97b74e66e80734f5c4a9509da4Ashwini Sharma  if (!regexec(&rp, lhs->s, 2, rm, 0) && rm[0].rm_so == 0) {
91c3657fd41199ca97b74e66e80734f5c4a9509da4Ashwini Sharma    if (rp.re_nsub > 0 && rm[1].rm_so >= 0)
92c3657fd41199ca97b74e66e80734f5c4a9509da4Ashwini Sharma      lhs->s = xmprintf("%.*s", rm[1].rm_eo - rm[1].rm_so, lhs->s+rm[1].rm_so);
93c3657fd41199ca97b74e66e80734f5c4a9509da4Ashwini Sharma    else {
94c3657fd41199ca97b74e66e80734f5c4a9509da4Ashwini Sharma      lhs->i = rm[0].rm_eo;
95c3657fd41199ca97b74e66e80734f5c4a9509da4Ashwini Sharma      lhs->s = 0;
96c3657fd41199ca97b74e66e80734f5c4a9509da4Ashwini Sharma    }
97c3657fd41199ca97b74e66e80734f5c4a9509da4Ashwini Sharma  } else {
98c3657fd41199ca97b74e66e80734f5c4a9509da4Ashwini Sharma    if (!rp.re_nsub) {
99c3657fd41199ca97b74e66e80734f5c4a9509da4Ashwini Sharma      lhs->i = 0;
100c3657fd41199ca97b74e66e80734f5c4a9509da4Ashwini Sharma      lhs->s = 0;
101c3657fd41199ca97b74e66e80734f5c4a9509da4Ashwini Sharma    } else lhs->s = "";
102c3657fd41199ca97b74e66e80734f5c4a9509da4Ashwini Sharma  }
10332526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley}
10432526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley
105ef6a773198040a05a56dec2261516fb149823cf6Rob Landleystatic void mod(struct value *lhs, struct value *rhs)
10632526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley{
10732526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley  if (lhs->s || rhs->s) error_exit("non-integer argument");
10832526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley  if (is_zero(rhs)) error_exit("division by zero");
10932526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley  lhs->i %= rhs->i;
11032526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley}
11132526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley
112ef6a773198040a05a56dec2261516fb149823cf6Rob Landleystatic void divi(struct value *lhs, struct value *rhs)
11332526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley{
11432526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley  if (lhs->s || rhs->s) error_exit("non-integer argument");
11532526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley  if (is_zero(rhs)) error_exit("division by zero");
11632526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley  lhs->i /= rhs->i;
11732526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley}
11832526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley
119ef6a773198040a05a56dec2261516fb149823cf6Rob Landleystatic void mul(struct value *lhs, struct value *rhs)
12032526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley{
12132526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley  if (lhs->s || rhs->s) error_exit("non-integer argument");
12232526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley  lhs->i *= rhs->i;
12332526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley}
12432526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley
125ef6a773198040a05a56dec2261516fb149823cf6Rob Landleystatic void sub(struct value *lhs, struct value *rhs)
12632526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley{
12732526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley  if (lhs->s || rhs->s) error_exit("non-integer argument");
12832526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley  lhs->i -= rhs->i;
12932526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley}
13032526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley
131ef6a773198040a05a56dec2261516fb149823cf6Rob Landleystatic void add(struct value *lhs, struct value *rhs)
13232526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley{
13332526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley  if (lhs->s || rhs->s) error_exit("non-integer argument");
13432526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley  lhs->i += rhs->i;
13532526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley}
13632526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley
137ef6a773198040a05a56dec2261516fb149823cf6Rob Landleystatic void ne(struct value *lhs, struct value *rhs)
13832526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley{
13932526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley  lhs->i = cmp(lhs, rhs) != 0;
14032526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley  lhs->s = NULL;
14132526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley}
14232526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley
143ef6a773198040a05a56dec2261516fb149823cf6Rob Landleystatic void lte(struct value *lhs, struct value *rhs)
14432526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley{
14532526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley  lhs->i = cmp(lhs, rhs) <= 0;
14632526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley  lhs->s = NULL;
14732526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley}
14832526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley
149ef6a773198040a05a56dec2261516fb149823cf6Rob Landleystatic void lt(struct value *lhs, struct value *rhs)
15032526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley{
15132526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley  lhs->i = cmp(lhs, rhs) < 0;
15232526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley  lhs->s = NULL;
15332526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley}
15432526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley
155ef6a773198040a05a56dec2261516fb149823cf6Rob Landleystatic void gte(struct value *lhs, struct value *rhs)
15632526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley{
15732526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley  lhs->i = cmp(lhs, rhs) >= 0;
15832526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley  lhs->s = NULL;
15932526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley}
16032526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley
161ef6a773198040a05a56dec2261516fb149823cf6Rob Landleystatic void gt(struct value *lhs, struct value *rhs)
16232526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley{
16332526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley  lhs->i = cmp(lhs, rhs) > 0;
16432526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley  lhs->s = NULL;
16532526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley}
16632526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley
167ef6a773198040a05a56dec2261516fb149823cf6Rob Landleystatic void eq(struct value *lhs, struct value *rhs)
16832526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley{
169ef6a773198040a05a56dec2261516fb149823cf6Rob Landley  lhs->i = !cmp(lhs, rhs);
17032526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley  lhs->s = NULL;
17132526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley}
17232526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley
173ef6a773198040a05a56dec2261516fb149823cf6Rob Landleystatic void and(struct value *lhs, struct value *rhs)
17432526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley{
17532526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley  if (is_zero(lhs) || is_zero(rhs)) {
17632526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley    lhs->i = 0;
17732526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley    lhs->s = NULL;
17832526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley  }
17932526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley}
18032526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley
181ef6a773198040a05a56dec2261516fb149823cf6Rob Landleystatic void or(struct value *lhs, struct value *rhs)
18232526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley{
183ef6a773198040a05a56dec2261516fb149823cf6Rob Landley  if (is_zero(lhs)) *lhs = *rhs;
18432526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley}
18532526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley
18614c91c1ebd68daacce9794cf8894dcfea68efd7bAndy Chu// Converts an arg string to a value struct.  Assumes arg != NULL.
18714c91c1ebd68daacce9794cf8894dcfea68efd7bAndy Chustatic void parse_value(char* arg, struct value *v)
188ef6a773198040a05a56dec2261516fb149823cf6Rob Landley{
18914c91c1ebd68daacce9794cf8894dcfea68efd7bAndy Chu  char *endp;
190ef6a773198040a05a56dec2261516fb149823cf6Rob Landley  v->i = strtoll(arg, &endp, 10);
19114c91c1ebd68daacce9794cf8894dcfea68efd7bAndy Chu  // if non-NULL, there's still stuff left, and it's a string.  Otherwise no
19214c91c1ebd68daacce9794cf8894dcfea68efd7bAndy Chu  // string.
193ef6a773198040a05a56dec2261516fb149823cf6Rob Landley  v->s = *endp ? arg : NULL;
194ef6a773198040a05a56dec2261516fb149823cf6Rob Landley}
19532526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley
19614c91c1ebd68daacce9794cf8894dcfea68efd7bAndy Chuvoid syntax_error(char *msg, ...) {
19714c91c1ebd68daacce9794cf8894dcfea68efd7bAndy Chu  if (0) { // detailed message for debugging.  TODO: add CFG_ var to enable
19814c91c1ebd68daacce9794cf8894dcfea68efd7bAndy Chu    va_list va;
19914c91c1ebd68daacce9794cf8894dcfea68efd7bAndy Chu    va_start(va, msg);
20014c91c1ebd68daacce9794cf8894dcfea68efd7bAndy Chu    verror_msg(msg, 0, va);
20114c91c1ebd68daacce9794cf8894dcfea68efd7bAndy Chu    va_end(va);
20214c91c1ebd68daacce9794cf8894dcfea68efd7bAndy Chu    xexit();
20314c91c1ebd68daacce9794cf8894dcfea68efd7bAndy Chu  } else
20414c91c1ebd68daacce9794cf8894dcfea68efd7bAndy Chu    error_exit("syntax error");
20532526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley}
20632526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley
20714c91c1ebd68daacce9794cf8894dcfea68efd7bAndy Chu// operators grouped by precedence
208ef6a773198040a05a56dec2261516fb149823cf6Rob Landleystatic struct op {
209ef6a773198040a05a56dec2261516fb149823cf6Rob Landley  char *tok;
21014c91c1ebd68daacce9794cf8894dcfea68efd7bAndy Chu  char prec;
211ef6a773198040a05a56dec2261516fb149823cf6Rob Landley  // calculate "lhs op rhs" (e.g. lhs + rhs) and store result in lhs
212ef6a773198040a05a56dec2261516fb149823cf6Rob Landley  void (*calc)(struct value *lhs, struct value *rhs);
21314c91c1ebd68daacce9794cf8894dcfea68efd7bAndy Chu} OPS[] = {
21414c91c1ebd68daacce9794cf8894dcfea68efd7bAndy Chu  {"|", 1, or  },
21514c91c1ebd68daacce9794cf8894dcfea68efd7bAndy Chu  {"&", 2, and },
21614c91c1ebd68daacce9794cf8894dcfea68efd7bAndy Chu  {"=", 3, eq  }, {"==", 3, eq  }, {">",  3, gt  }, {">=", 3, gte },
21714c91c1ebd68daacce9794cf8894dcfea68efd7bAndy Chu  {"<", 3, lt  }, {"<=", 3, lte }, {"!=", 3, ne  },
21814c91c1ebd68daacce9794cf8894dcfea68efd7bAndy Chu  {"+", 4, add }, {"-",  4, sub },
21914c91c1ebd68daacce9794cf8894dcfea68efd7bAndy Chu  {"*", 5, mul }, {"/",  5, divi }, {"%", 5, mod },
22014c91c1ebd68daacce9794cf8894dcfea68efd7bAndy Chu  {":", 6, re  },
22114c91c1ebd68daacce9794cf8894dcfea68efd7bAndy Chu  {"",  0, NULL}, // sentinel
222ef6a773198040a05a56dec2261516fb149823cf6Rob Landley};
223ef6a773198040a05a56dec2261516fb149823cf6Rob Landley
22414c91c1ebd68daacce9794cf8894dcfea68efd7bAndy Chu// Point TT.tok at the next token.  It's NULL when there are no more tokens.
22514c91c1ebd68daacce9794cf8894dcfea68efd7bAndy Chuvoid advance() {
22614c91c1ebd68daacce9794cf8894dcfea68efd7bAndy Chu  TT.tok = *toys.optargs++;
22714c91c1ebd68daacce9794cf8894dcfea68efd7bAndy Chu}
228ef6a773198040a05a56dec2261516fb149823cf6Rob Landley
22914c91c1ebd68daacce9794cf8894dcfea68efd7bAndy Chu// Evalute a compound expression, setting 'ret'.
23014c91c1ebd68daacce9794cf8894dcfea68efd7bAndy Chu//
23114c91c1ebd68daacce9794cf8894dcfea68efd7bAndy Chu// This function uses the recursive "Precedence Climbing" algorithm:
23214c91c1ebd68daacce9794cf8894dcfea68efd7bAndy Chu//
23314c91c1ebd68daacce9794cf8894dcfea68efd7bAndy Chu// Clarke, Keith. "The top-down parsing of expressions." University of London.
23414c91c1ebd68daacce9794cf8894dcfea68efd7bAndy Chu// Queen Mary College. Department of Computer Science and Statistics, 1986.
23514c91c1ebd68daacce9794cf8894dcfea68efd7bAndy Chu//
23614c91c1ebd68daacce9794cf8894dcfea68efd7bAndy Chu// http://www.antlr.org/papers/Clarke-expr-parsing-1986.pdf
23714c91c1ebd68daacce9794cf8894dcfea68efd7bAndy Chu//
23814c91c1ebd68daacce9794cf8894dcfea68efd7bAndy Chu// Nice explanation and Python implementation:
23914c91c1ebd68daacce9794cf8894dcfea68efd7bAndy Chu// http://eli.thegreenplace.net/2012/08/02/parsing-expressions-by-precedence-climbing
24014c91c1ebd68daacce9794cf8894dcfea68efd7bAndy Chustatic void eval_expr(struct value *ret, int min_prec)
24132526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley{
24214c91c1ebd68daacce9794cf8894dcfea68efd7bAndy Chu  if (!TT.tok) syntax_error("Unexpected end of input");
24314c91c1ebd68daacce9794cf8894dcfea68efd7bAndy Chu
24414c91c1ebd68daacce9794cf8894dcfea68efd7bAndy Chu  // Evaluate LHS atom, setting 'ret'.
24514c91c1ebd68daacce9794cf8894dcfea68efd7bAndy Chu  if (!strcmp(TT.tok, "(")) { // parenthesized expression
24614c91c1ebd68daacce9794cf8894dcfea68efd7bAndy Chu    advance(); // consume (
24714c91c1ebd68daacce9794cf8894dcfea68efd7bAndy Chu    eval_expr(ret, 1); // We're inside ( ), so start with min_prec = 1
24814c91c1ebd68daacce9794cf8894dcfea68efd7bAndy Chu    if (!TT.tok)             syntax_error("Expected )");
24914c91c1ebd68daacce9794cf8894dcfea68efd7bAndy Chu    if (strcmp(TT.tok, ")")) syntax_error("Expected ) but got %s", TT.tok);
25014c91c1ebd68daacce9794cf8894dcfea68efd7bAndy Chu    advance(); // consume )
25114c91c1ebd68daacce9794cf8894dcfea68efd7bAndy Chu  } else { // simple literal
25214c91c1ebd68daacce9794cf8894dcfea68efd7bAndy Chu    parse_value(TT.tok, ret);
25314c91c1ebd68daacce9794cf8894dcfea68efd7bAndy Chu    advance();
25432526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley  }
25532526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley
25614c91c1ebd68daacce9794cf8894dcfea68efd7bAndy Chu  // Evaluate RHS and apply operator until precedence is too low.
25714c91c1ebd68daacce9794cf8894dcfea68efd7bAndy Chu  struct value rhs;
25814c91c1ebd68daacce9794cf8894dcfea68efd7bAndy Chu  while (TT.tok) {
25914c91c1ebd68daacce9794cf8894dcfea68efd7bAndy Chu    struct op *o = OPS;
26014c91c1ebd68daacce9794cf8894dcfea68efd7bAndy Chu    while (o->calc) {  // Look up the precedence of operator TT.tok
26114c91c1ebd68daacce9794cf8894dcfea68efd7bAndy Chu      if (!strcmp(TT.tok, o->tok)) break;
26214c91c1ebd68daacce9794cf8894dcfea68efd7bAndy Chu      o++;
26314c91c1ebd68daacce9794cf8894dcfea68efd7bAndy Chu    }
26414c91c1ebd68daacce9794cf8894dcfea68efd7bAndy Chu    if (!o->calc) break; // Not an operator (extra input will fail later)
26514c91c1ebd68daacce9794cf8894dcfea68efd7bAndy Chu    if (o->prec < min_prec) break; // Precedence too low, pop a stack frame
26614c91c1ebd68daacce9794cf8894dcfea68efd7bAndy Chu    advance();
26714c91c1ebd68daacce9794cf8894dcfea68efd7bAndy Chu
26814c91c1ebd68daacce9794cf8894dcfea68efd7bAndy Chu    eval_expr(&rhs, o->prec + 1); // Evaluate RHS, with higher min precedence
26914c91c1ebd68daacce9794cf8894dcfea68efd7bAndy Chu    o->calc(ret, &rhs); // Apply operator, setting 'ret'.
27032526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley  }
27132526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley}
27232526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley
27332526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landleyvoid expr_main(void)
27432526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley{
27514c91c1ebd68daacce9794cf8894dcfea68efd7bAndy Chu  struct value ret = {0};
27632526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley  toys.exitval = 2; // if exiting early, indicate invalid expression
27732526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley
27814c91c1ebd68daacce9794cf8894dcfea68efd7bAndy Chu  advance(); // initialize global token
27914c91c1ebd68daacce9794cf8894dcfea68efd7bAndy Chu  eval_expr(&ret, 1);
28032526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley
28114c91c1ebd68daacce9794cf8894dcfea68efd7bAndy Chu  if (TT.tok) syntax_error("Unexpected extra input '%s'\n", TT.tok);
28232526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley
28332526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley  if (ret.s) printf("%s\n", ret.s);
284bc9cfe08cfa2c47b2d106eda7b5d0ecc73f47238Rob Landley  else printf("%lld\n", ret.i);
28532526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley
28632526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley  exit(is_zero(&ret));
28732526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley}
288