1 /* expr.c - evaluate expression
2 *
3 * Copyright 2013 Daniel Verkamp <daniel@drv.nu>
4 *
5 * http://pubs.opengroup.org/onlinepubs/9699919799/utilities/expr.html
6 *
7 * The web standard is incomplete (precedence grouping missing), see:
8 * http://permalink.gmane.org/gmane.comp.standards.posix.austin.general/10141
9
10 USE_EXPR(NEWTOY(expr, NULL, TOYFLAG_USR|TOYFLAG_BIN))
11
12 config EXPR
13 bool "expr"
14 default n
15 help
16 usage: expr ARG1 OPERATOR ARG2...
17
18 Evaluate expression and print result. For example, "expr 1 + 2".
19
20 The supported operators are (grouped from highest to lowest priority):
21
22 ( ) : * / % + - != <= < >= > = & |
23
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
29 of expressions.
30
31 Calling expr from a command shell requires a lot of \( or '*' escaping
32 to avoid interpreting shell control characters.
33
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).
37
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.
41 */
42
43 // TODO: int overflow checking
44
45 #define FOR_expr
46 #include "toys.h"
47
48
49 GLOBALS(
50 int argidx;
51 )
52
53 // Scalar value.
54 // If s is NULL, the value is an integer (i).
55 // If s is not NULL, the value is a string (s).
56 struct value {
57 char *s;
58 long long i;
59 };
60
61 // check if v is the integer 0 or the empty string
is_zero(struct value * v)62 static int is_zero(struct value *v)
63 {
64 return v->s ? !*v->s : !v->i;
65 }
66
num_to_str(long long num)67 static char *num_to_str(long long num)
68 {
69 static char num_buf[21];
70 snprintf(num_buf, sizeof(num_buf), "%lld", num);
71 return num_buf;
72 }
73
cmp(struct value * lhs,struct value * rhs)74 static int cmp(struct value *lhs, struct value *rhs)
75 {
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;
82 }
83
re(struct value * lhs,struct value * rhs)84 static void re(struct value *lhs, struct value *rhs)
85 {
86 regex_t rp;
87 regmatch_t rm[2];
88
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);
93 else {
94 lhs->i = rm[0].rm_eo;
95 lhs->s = 0;
96 }
97 } else {
98 if (!rp.re_nsub) {
99 lhs->i = 0;
100 lhs->s = 0;
101 } else lhs->s = "";
102 }
103 }
104
mod(struct value * lhs,struct value * rhs)105 static void mod(struct value *lhs, struct value *rhs)
106 {
107 if (lhs->s || rhs->s) error_exit("non-integer argument");
108 if (is_zero(rhs)) error_exit("division by zero");
109 lhs->i %= rhs->i;
110 }
111
divi(struct value * lhs,struct value * rhs)112 static void divi(struct value *lhs, struct value *rhs)
113 {
114 if (lhs->s || rhs->s) error_exit("non-integer argument");
115 if (is_zero(rhs)) error_exit("division by zero");
116 lhs->i /= rhs->i;
117 }
118
mul(struct value * lhs,struct value * rhs)119 static void mul(struct value *lhs, struct value *rhs)
120 {
121 if (lhs->s || rhs->s) error_exit("non-integer argument");
122 lhs->i *= rhs->i;
123 }
124
sub(struct value * lhs,struct value * rhs)125 static void sub(struct value *lhs, struct value *rhs)
126 {
127 if (lhs->s || rhs->s) error_exit("non-integer argument");
128 lhs->i -= rhs->i;
129 }
130
add(struct value * lhs,struct value * rhs)131 static void add(struct value *lhs, struct value *rhs)
132 {
133 if (lhs->s || rhs->s) error_exit("non-integer argument");
134 lhs->i += rhs->i;
135 }
136
ne(struct value * lhs,struct value * rhs)137 static void ne(struct value *lhs, struct value *rhs)
138 {
139 lhs->i = cmp(lhs, rhs) != 0;
140 lhs->s = NULL;
141 }
142
lte(struct value * lhs,struct value * rhs)143 static void lte(struct value *lhs, struct value *rhs)
144 {
145 lhs->i = cmp(lhs, rhs) <= 0;
146 lhs->s = NULL;
147 }
148
lt(struct value * lhs,struct value * rhs)149 static void lt(struct value *lhs, struct value *rhs)
150 {
151 lhs->i = cmp(lhs, rhs) < 0;
152 lhs->s = NULL;
153 }
154
gte(struct value * lhs,struct value * rhs)155 static void gte(struct value *lhs, struct value *rhs)
156 {
157 lhs->i = cmp(lhs, rhs) >= 0;
158 lhs->s = NULL;
159 }
160
gt(struct value * lhs,struct value * rhs)161 static void gt(struct value *lhs, struct value *rhs)
162 {
163 lhs->i = cmp(lhs, rhs) > 0;
164 lhs->s = NULL;
165 }
166
eq(struct value * lhs,struct value * rhs)167 static void eq(struct value *lhs, struct value *rhs)
168 {
169 lhs->i = !cmp(lhs, rhs);
170 lhs->s = NULL;
171 }
172
and(struct value * lhs,struct value * rhs)173 static void and(struct value *lhs, struct value *rhs)
174 {
175 if (is_zero(lhs) || is_zero(rhs)) {
176 lhs->i = 0;
177 lhs->s = NULL;
178 }
179 }
180
or(struct value * lhs,struct value * rhs)181 static void or(struct value *lhs, struct value *rhs)
182 {
183 if (is_zero(lhs)) *lhs = *rhs;
184 }
185
get_value(struct value * v)186 static void get_value(struct value *v)
187 {
188 char *endp, *arg;
189
190 if (TT.argidx == toys.optc) {
191 v->i = 0;
192 v->s = ""; // signal end of expression
193 return;
194 }
195
196 // can't happen, the increment is after the == test
197 // if (TT.argidx >= toys.optc) error_exit("syntax error");
198
199 arg = toys.optargs[TT.argidx++];
200
201 v->i = strtoll(arg, &endp, 10);
202 v->s = *endp ? arg : NULL;
203 }
204
205 // check if v matches a token, and consume it if so
match(struct value * v,char * tok)206 static int match(struct value *v, char *tok)
207 {
208 if (v->s && !strcmp(v->s, tok)) {
209 get_value(v);
210 return 1;
211 }
212
213 return 0;
214 }
215
216 // operators in order of increasing precedence
217 static struct op {
218 char *tok;
219
220 // calculate "lhs op rhs" (e.g. lhs + rhs) and store result in lhs
221 void (*calc)(struct value *lhs, struct value *rhs);
222 } ops[] = {
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
227 };
228
229 // "|,&,= ==> >=< <= !=,+-,*/%,:"
230
parse_op(struct value * lhs,struct value * tok,struct op * op)231 static void parse_op(struct value *lhs, struct value *tok, struct op *op)
232 {
233 if (!op) op = ops;
234
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
240 } else {
241 // tok is a string or integer - return it and get the next token
242 *lhs = *tok;
243 get_value(tok);
244 }
245
246 return;
247 }
248
249 parse_op(lhs, tok, op + 1);
250 while (match(tok, op->tok)) {
251 struct value rhs;
252 parse_op(&rhs, tok, op + 1);
253 if (rhs.s && !*rhs.s) error_exit("syntax error"); // premature end of expression
254 op->calc(lhs, &rhs);
255 }
256 }
257
expr_main(void)258 void expr_main(void)
259 {
260 struct value tok, ret = {0};
261
262 toys.exitval = 2; // if exiting early, indicate invalid expression
263
264 TT.argidx = 0;
265
266 get_value(&tok); // warm up the parser with the initial value
267 parse_op(&ret, &tok, 0);
268
269 // final token should be end of expression
270 if (!tok.s || *tok.s) error_exit("syntax error");
271
272 if (ret.s) printf("%s\n", ret.s);
273 else printf("%lld\n", ret.i);
274
275 exit(is_zero(&ret));
276 }
277