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 Landley
4932526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob LandleyGLOBALS(
5032526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley  int argidx;
5132526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley)
5232526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley
5332526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley// Scalar value.
5432526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley// If s is NULL, the value is an integer (i).
5532526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley// If s is not NULL, the value is a string (s).
5632526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landleystruct value {
5732526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley  char *s;
58bc9cfe08cfa2c47b2d106eda7b5d0ecc73f47238Rob Landley  long long i;
5932526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley};
6032526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley
6132526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley// check if v is the integer 0 or the empty string
62ef6a773198040a05a56dec2261516fb149823cf6Rob Landleystatic int is_zero(struct value *v)
6332526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley{
64ef6a773198040a05a56dec2261516fb149823cf6Rob Landley  return v->s ? !*v->s : !v->i;
6532526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley}
6632526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley
67bc9cfe08cfa2c47b2d106eda7b5d0ecc73f47238Rob Landleystatic char *num_to_str(long long num)
6832526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley{
6932526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley  static char num_buf[21];
70bc9cfe08cfa2c47b2d106eda7b5d0ecc73f47238Rob Landley  snprintf(num_buf, sizeof(num_buf), "%lld", num);
7132526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley  return num_buf;
7232526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley}
7332526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley
74ef6a773198040a05a56dec2261516fb149823cf6Rob Landleystatic int cmp(struct value *lhs, struct value *rhs)
7532526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley{
7632526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley  if (lhs->s || rhs->s) {
7732526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley    // at least one operand is a string
7832526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley    char *ls = lhs->s ? lhs->s : num_to_str(lhs->i);
7932526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley    char *rs = rhs->s ? rhs->s : num_to_str(rhs->i);
8032526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley    return strcmp(ls, rs);
81ef6a773198040a05a56dec2261516fb149823cf6Rob Landley  } else return lhs->i - rhs->i;
8232526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley}
8332526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley
84ef6a773198040a05a56dec2261516fb149823cf6Rob Landleystatic void re(struct value *lhs, struct value *rhs)
8532526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley{
86c3657fd41199ca97b74e66e80734f5c4a9509da4Ashwini Sharma  regex_t rp;
87c3657fd41199ca97b74e66e80734f5c4a9509da4Ashwini Sharma  regmatch_t rm[2];
88c3657fd41199ca97b74e66e80734f5c4a9509da4Ashwini Sharma
89c3657fd41199ca97b74e66e80734f5c4a9509da4Ashwini Sharma  xregcomp(&rp, rhs->s, 0);
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
186ef6a773198040a05a56dec2261516fb149823cf6Rob Landleystatic void get_value(struct value *v)
187ef6a773198040a05a56dec2261516fb149823cf6Rob Landley{
188ef6a773198040a05a56dec2261516fb149823cf6Rob Landley  char *endp, *arg;
18932526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley
190ef6a773198040a05a56dec2261516fb149823cf6Rob Landley  if (TT.argidx == toys.optc) {
191ef6a773198040a05a56dec2261516fb149823cf6Rob Landley    v->i = 0;
192ef6a773198040a05a56dec2261516fb149823cf6Rob Landley    v->s = ""; // signal end of expression
193ef6a773198040a05a56dec2261516fb149823cf6Rob Landley    return;
194ef6a773198040a05a56dec2261516fb149823cf6Rob Landley  }
19532526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley
196ef6a773198040a05a56dec2261516fb149823cf6Rob Landley//  can't happen, the increment is after the == test
197ef6a773198040a05a56dec2261516fb149823cf6Rob Landley//  if (TT.argidx >= toys.optc) error_exit("syntax error");
198ef6a773198040a05a56dec2261516fb149823cf6Rob Landley
199ef6a773198040a05a56dec2261516fb149823cf6Rob Landley  arg = toys.optargs[TT.argidx++];
200ef6a773198040a05a56dec2261516fb149823cf6Rob Landley
201ef6a773198040a05a56dec2261516fb149823cf6Rob Landley  v->i = strtoll(arg, &endp, 10);
202ef6a773198040a05a56dec2261516fb149823cf6Rob Landley  v->s = *endp ? arg : NULL;
203ef6a773198040a05a56dec2261516fb149823cf6Rob Landley}
20432526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley
205ef6a773198040a05a56dec2261516fb149823cf6Rob Landley// check if v matches a token, and consume it if so
206ef6a773198040a05a56dec2261516fb149823cf6Rob Landleystatic int match(struct value *v, char *tok)
20732526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley{
208ef6a773198040a05a56dec2261516fb149823cf6Rob Landley  if (v->s && !strcmp(v->s, tok)) {
20932526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley    get_value(v);
210ef6a773198040a05a56dec2261516fb149823cf6Rob Landley    return 1;
21132526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley  }
212ef6a773198040a05a56dec2261516fb149823cf6Rob Landley
213ef6a773198040a05a56dec2261516fb149823cf6Rob Landley  return 0;
21432526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley}
21532526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley
216ef6a773198040a05a56dec2261516fb149823cf6Rob Landley// operators in order of increasing precedence
217ef6a773198040a05a56dec2261516fb149823cf6Rob Landleystatic struct op {
218ef6a773198040a05a56dec2261516fb149823cf6Rob Landley  char *tok;
219ef6a773198040a05a56dec2261516fb149823cf6Rob Landley
220ef6a773198040a05a56dec2261516fb149823cf6Rob Landley  // calculate "lhs op rhs" (e.g. lhs + rhs) and store result in lhs
221ef6a773198040a05a56dec2261516fb149823cf6Rob Landley  void (*calc)(struct value *lhs, struct value *rhs);
222ef6a773198040a05a56dec2261516fb149823cf6Rob Landley} ops[] = {
223ef6a773198040a05a56dec2261516fb149823cf6Rob Landley  {"|",   or  }, {"&",   and }, {"=",   eq  }, {"==",  eq  }, {">",   gt  },
224ef6a773198040a05a56dec2261516fb149823cf6Rob Landley  {">=",  gte }, {"<",   lt  }, {"<=",  lte }, {"!=",  ne  }, {"+",   add },
225ef6a773198040a05a56dec2261516fb149823cf6Rob Landley  {"-",   sub }, {"*",   mul }, {"/",   divi}, {"%",   mod }, {":",   re  },
226ef6a773198040a05a56dec2261516fb149823cf6Rob Landley  {"(",   NULL}, // special case - must be last
227ef6a773198040a05a56dec2261516fb149823cf6Rob Landley};
228ef6a773198040a05a56dec2261516fb149823cf6Rob Landley
229ef6a773198040a05a56dec2261516fb149823cf6Rob Landley// "|,&,= ==> >=< <= !=,+-,*/%,:"
230ef6a773198040a05a56dec2261516fb149823cf6Rob Landley
231ef6a773198040a05a56dec2261516fb149823cf6Rob Landleystatic void parse_op(struct value *lhs, struct value *tok, struct op *op)
23232526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley{
233ef6a773198040a05a56dec2261516fb149823cf6Rob Landley  if (!op) op = ops;
234ef6a773198040a05a56dec2261516fb149823cf6Rob Landley
23532526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley  // special case parsing for parentheses
23632526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley  if (*op->tok == '(') {
237ef6a773198040a05a56dec2261516fb149823cf6Rob Landley    if (match(tok, "(")) {
238ef6a773198040a05a56dec2261516fb149823cf6Rob Landley      parse_op(lhs, tok, 0);
239ef6a773198040a05a56dec2261516fb149823cf6Rob Landley      if (!match(tok, ")")) error_exit("syntax error"); // missing closing paren
240ef6a773198040a05a56dec2261516fb149823cf6Rob Landley    } else {
241ef6a773198040a05a56dec2261516fb149823cf6Rob Landley      // tok is a string or integer - return it and get the next token
242ef6a773198040a05a56dec2261516fb149823cf6Rob Landley      *lhs = *tok;
243ef6a773198040a05a56dec2261516fb149823cf6Rob Landley      get_value(tok);
244ef6a773198040a05a56dec2261516fb149823cf6Rob Landley    }
245ef6a773198040a05a56dec2261516fb149823cf6Rob Landley
24632526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley    return;
24732526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley  }
24832526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley
24932526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley  parse_op(lhs, tok, op + 1);
25032526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley  while (match(tok, op->tok)) {
25132526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley    struct value rhs;
25232526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley    parse_op(&rhs, tok, op + 1);
25332526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley    if (rhs.s && !*rhs.s) error_exit("syntax error"); // premature end of expression
25432526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley    op->calc(lhs, &rhs);
25532526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley  }
25632526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley}
25732526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley
25832526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landleyvoid expr_main(void)
25932526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley{
26032526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley  struct value tok, ret = {0};
26132526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley
26232526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley  toys.exitval = 2; // if exiting early, indicate invalid expression
26332526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley
26432526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley  TT.argidx = 0;
26532526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley
26632526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley  get_value(&tok); // warm up the parser with the initial value
267ef6a773198040a05a56dec2261516fb149823cf6Rob Landley  parse_op(&ret, &tok, 0);
26832526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley
269ef6a773198040a05a56dec2261516fb149823cf6Rob Landley  // final token should be end of expression
270ef6a773198040a05a56dec2261516fb149823cf6Rob Landley  if (!tok.s || *tok.s) error_exit("syntax error");
27132526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley
27232526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley  if (ret.s) printf("%s\n", ret.s);
273bc9cfe08cfa2c47b2d106eda7b5d0ecc73f47238Rob Landley  else printf("%lld\n", ret.i);
27432526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley
27532526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley  exit(is_zero(&ret));
27632526f25a7e62b1fe82d1ea30dc4a8506d0ee0d4Rob Landley}
277