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