• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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