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