1 /* expr.c - evaluate expression
3 * Copyright 2013 Daniel Verkamp <daniel@drv.nu>
5 * http://pubs.opengroup.org/onlinepubs/9699919799/utilities/expr.html
7 * The web standard is incomplete (precedence grouping missing), see:
8 * http://permalink.gmane.org/gmane.comp.standards.posix.austin.general/10141
10 USE_EXPR(NEWTOY(expr, NULL, TOYFLAG_USR|TOYFLAG_BIN))
16 usage: expr ARG1 OPERATOR ARG2...
18 Evaluate expression and print result. For example, "expr 1 + 2".
20 The supported operators are (grouped from highest to lowest priority):
22 ( ) : * / % + - != <= < >= > = & |
24 Each constant and operator must be a separate command line argument.
25 All operators are infix, meaning they expect a constant (or expression
26 that resolves to a constant) on each side of the operator. Operators of
27 the same priority (within each group above) are evaluated left to right.
28 Parentheses may be used (as separate arguments) to elevate the priority
31 Calling expr from a command shell requires a lot of \( or '*' escaping
32 to avoid interpreting shell control characters.
34 The & and | operators are logical (not bitwise) and may operate on
35 strings (a blank string is "false"). Comparison operators may also
36 operate on strings (alphabetical sort).
38 Constants may be strings or integers. Comparison, logical, and regex
39 operators may operate on strings (a blank string is "false"), other
40 operators require integers.
43 // TODO: int overflow checking
54 // If s is NULL, the value is an integer (i).
55 // If s is not NULL, the value is a string (s).
61 // check if v is the integer 0 or the empty string
62 static int is_zero(struct value *v)
64 return v->s ? !*v->s : !v->i;
67 static char *num_to_str(long long num)
69 static char num_buf[21];
70 snprintf(num_buf, sizeof(num_buf), "%lld", num);
74 static int cmp(struct value *lhs, struct value *rhs)
76 if (lhs->s || rhs->s) {
77 // at least one operand is a string
78 char *ls = lhs->s ? lhs->s : num_to_str(lhs->i);
79 char *rs = rhs->s ? rhs->s : num_to_str(rhs->i);
80 return strcmp(ls, rs);
81 } else return lhs->i - rhs->i;
84 static void re(struct value *lhs, struct value *rhs)
89 xregcomp(&rp, rhs->s, 0);
90 if (!regexec(&rp, lhs->s, 2, rm, 0) && rm[0].rm_so == 0) {
91 if (rp.re_nsub > 0 && rm[1].rm_so >= 0)
92 lhs->s = xmprintf("%.*s", rm[1].rm_eo - rm[1].rm_so, lhs->s+rm[1].rm_so);
105 static void mod(struct value *lhs, struct value *rhs)
107 if (lhs->s || rhs->s) error_exit("non-integer argument");
108 if (is_zero(rhs)) error_exit("division by zero");
112 static void divi(struct value *lhs, struct value *rhs)
114 if (lhs->s || rhs->s) error_exit("non-integer argument");
115 if (is_zero(rhs)) error_exit("division by zero");
119 static void mul(struct value *lhs, struct value *rhs)
121 if (lhs->s || rhs->s) error_exit("non-integer argument");
125 static void sub(struct value *lhs, struct value *rhs)
127 if (lhs->s || rhs->s) error_exit("non-integer argument");
131 static void add(struct value *lhs, struct value *rhs)
133 if (lhs->s || rhs->s) error_exit("non-integer argument");
137 static void ne(struct value *lhs, struct value *rhs)
139 lhs->i = cmp(lhs, rhs) != 0;
143 static void lte(struct value *lhs, struct value *rhs)
145 lhs->i = cmp(lhs, rhs) <= 0;
149 static void lt(struct value *lhs, struct value *rhs)
151 lhs->i = cmp(lhs, rhs) < 0;
155 static void gte(struct value *lhs, struct value *rhs)
157 lhs->i = cmp(lhs, rhs) >= 0;
161 static void gt(struct value *lhs, struct value *rhs)
163 lhs->i = cmp(lhs, rhs) > 0;
167 static void eq(struct value *lhs, struct value *rhs)
169 lhs->i = !cmp(lhs, rhs);
173 static void and(struct value *lhs, struct value *rhs)
175 if (is_zero(lhs) || is_zero(rhs)) {
181 static void or(struct value *lhs, struct value *rhs)
183 if (is_zero(lhs)) *lhs = *rhs;
186 static void get_value(struct value *v)
190 if (TT.argidx == toys.optc) {
192 v->s = ""; // signal end of expression
196 // can't happen, the increment is after the == test
197 // if (TT.argidx >= toys.optc) error_exit("syntax error");
199 arg = toys.optargs[TT.argidx++];
201 v->i = strtoll(arg, &endp, 10);
202 v->s = *endp ? arg : NULL;
205 // check if v matches a token, and consume it if so
206 static int match(struct value *v, char *tok)
208 if (v->s && !strcmp(v->s, tok)) {
216 // operators in order of increasing precedence
220 // calculate "lhs op rhs" (e.g. lhs + rhs) and store result in lhs
221 void (*calc)(struct value *lhs, struct value *rhs);
223 {"|", or }, {"&", and }, {"=", eq }, {"==", eq }, {">", gt },
224 {">=", gte }, {"<", lt }, {"<=", lte }, {"!=", ne }, {"+", add },
225 {"-", sub }, {"*", mul }, {"/", divi}, {"%", mod }, {":", re },
226 {"(", NULL}, // special case - must be last
229 // "|,&,= ==> >=< <= !=,+-,*/%,:"
231 static void parse_op(struct value *lhs, struct value *tok, struct op *op)
235 // special case parsing for parentheses
236 if (*op->tok == '(') {
237 if (match(tok, "(")) {
238 parse_op(lhs, tok, 0);
239 if (!match(tok, ")")) error_exit("syntax error"); // missing closing paren
241 // tok is a string or integer - return it and get the next token
249 parse_op(lhs, tok, op + 1);
250 while (match(tok, op->tok)) {
252 parse_op(&rhs, tok, op + 1);
253 if (rhs.s && !*rhs.s) error_exit("syntax error"); // premature end of expression
260 struct value tok, ret = {0};
262 toys.exitval = 2; // if exiting early, indicate invalid expression
266 get_value(&tok); // warm up the parser with the initial value
267 parse_op(&ret, &tok, 0);
269 // final token should be end of expression
270 if (!tok.s || *tok.s) error_exit("syntax error");
272 if (ret.s) printf("%s\n", ret.s);
273 else printf("%lld\n", ret.i);