comparison toys/pending/expr.c @ 920:74b5cf0309fc

Start of expr, by Daniel Verkamp.
author Rob Landley <rob@landley.net>
date Wed, 05 Jun 2013 00:59:01 -0500
parents
children 32eec4a8f363
comparison
equal deleted inserted replaced
919:a186f9a1406d 920:74b5cf0309fc
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 USE_EXPR(NEWTOY(expr, NULL, TOYFLAG_USR|TOYFLAG_BIN))
8
9 config EXPR
10 bool "expr"
11 default n
12 help
13 usage: expr args
14
15 Evaluate expression and print result.
16
17 The supported operators, in order of increasing precedence, are:
18
19 | & = > >= < <= != + - * / %
20
21 In addition, parentheses () are supported for grouping.
22 */
23
24 // TODO: int overflow checking
25
26 #define FOR_expr
27 #include "toys.h"
28
29
30 GLOBALS(
31 int argidx;
32 )
33
34 // Scalar value.
35 // If s is NULL, the value is an integer (i).
36 // If s is not NULL, the value is a string (s).
37 struct value {
38 char *s;
39 long i;
40 };
41
42 static void parse_expr(struct value *ret, struct value *v);
43
44 static void get_value(struct value *v)
45 {
46 char *endp, *arg;
47
48 if (TT.argidx == toys.optc) {
49 v->i = 0;
50 v->s = ""; // signal end of expression
51 return;
52 }
53
54 if (TT.argidx >= toys.optc) {
55 error_exit("syntax error");
56 }
57
58 arg = toys.optargs[TT.argidx++];
59
60 v->i = strtol(arg, &endp, 10);
61 v->s = *endp ? arg : NULL;
62 }
63
64
65 // check if v matches a token, and consume it if so
66 static int match(struct value *v, const char *tok)
67 {
68 if (v->s && !strcmp(v->s, tok)) {
69 get_value(v);
70 return 1;
71 }
72
73 return 0;
74 }
75
76 // check if v is the integer 0 or the empty string
77 static int is_zero(const struct value *v)
78 {
79 return ((v->s && *v->s == '\0') || v->i == 0);
80 }
81
82 static char *num_to_str(long num)
83 {
84 static char num_buf[21];
85 snprintf(num_buf, sizeof(num_buf), "%ld", num);
86 return num_buf;
87 }
88
89 static int cmp(const struct value *lhs, const struct value *rhs)
90 {
91 if (lhs->s || rhs->s) {
92 // at least one operand is a string
93 char *ls = lhs->s ? lhs->s : num_to_str(lhs->i);
94 char *rs = rhs->s ? rhs->s : num_to_str(rhs->i);
95 return strcmp(ls, rs);
96 } else {
97 return lhs->i - rhs->i;
98 }
99 }
100
101
102 // operators
103
104 struct op {
105 const char *tok;
106
107 // calculate "lhs op rhs" (e.g. lhs + rhs) and store result in lhs
108 void (*calc)(struct value *lhs, const struct value *rhs);
109 };
110
111
112 static void re(struct value *lhs, const struct value *rhs)
113 {
114 error_exit("regular expression match not implemented");
115 }
116
117 static void mod(struct value *lhs, const struct value *rhs)
118 {
119 if (lhs->s || rhs->s) error_exit("non-integer argument");
120 if (is_zero(rhs)) error_exit("division by zero");
121 lhs->i %= rhs->i;
122 }
123
124 static void divi(struct value *lhs, const struct value *rhs)
125 {
126 if (lhs->s || rhs->s) error_exit("non-integer argument");
127 if (is_zero(rhs)) error_exit("division by zero");
128 lhs->i /= rhs->i;
129 }
130
131 static void mul(struct value *lhs, const struct value *rhs)
132 {
133 if (lhs->s || rhs->s) error_exit("non-integer argument");
134 lhs->i *= rhs->i;
135 }
136
137 static void sub(struct value *lhs, const struct value *rhs)
138 {
139 if (lhs->s || rhs->s) error_exit("non-integer argument");
140 lhs->i -= rhs->i;
141 }
142
143 static void add(struct value *lhs, const struct value *rhs)
144 {
145 if (lhs->s || rhs->s) error_exit("non-integer argument");
146 lhs->i += rhs->i;
147 }
148
149 static void ne(struct value *lhs, const struct value *rhs)
150 {
151 lhs->i = cmp(lhs, rhs) != 0;
152 lhs->s = NULL;
153 }
154
155 static void lte(struct value *lhs, const struct value *rhs)
156 {
157 lhs->i = cmp(lhs, rhs) <= 0;
158 lhs->s = NULL;
159 }
160
161 static void lt(struct value *lhs, const struct value *rhs)
162 {
163 lhs->i = cmp(lhs, rhs) < 0;
164 lhs->s = NULL;
165 }
166
167 static void gte(struct value *lhs, const struct value *rhs)
168 {
169 lhs->i = cmp(lhs, rhs) >= 0;
170 lhs->s = NULL;
171 }
172
173 static void gt(struct value *lhs, const struct value *rhs)
174 {
175 lhs->i = cmp(lhs, rhs) > 0;
176 lhs->s = NULL;
177 }
178
179 static void eq(struct value *lhs, const struct value *rhs)
180 {
181 lhs->i = cmp(lhs, rhs) == 0;
182 lhs->s = NULL;
183 }
184
185 static void and(struct value *lhs, const struct value *rhs)
186 {
187 if (is_zero(lhs) || is_zero(rhs)) {
188 lhs->i = 0;
189 lhs->s = NULL;
190 }
191 }
192
193 static void or(struct value *lhs, const struct value *rhs)
194 {
195 if (is_zero(lhs)) {
196 *lhs = *rhs;
197 }
198 }
199
200
201 // operators in order of increasing precedence
202 static const struct op ops[] = {
203 {"|", or },
204 {"&", and },
205 {"=", eq },
206 {">", gt },
207 {">=", gte },
208 {"<", lt },
209 {"<=", lte },
210 {"!=", ne },
211 {"+", add },
212 {"-", sub },
213 {"*", mul },
214 {"/", divi},
215 {"%", mod },
216 {":", re },
217 {"(", NULL}, // special case - must be last
218 };
219
220
221 static void parse_parens(struct value *ret, struct value *v)
222 {
223 if (match(v, "(")) {
224 parse_expr(ret, v);
225 if (!match(v, ")")) error_exit("syntax error"); // missing closing paren
226 } else {
227 // v is a string or integer - return it and get the next token
228 *ret = *v;
229 get_value(v);
230 }
231 }
232
233 static void parse_op(struct value *lhs, struct value *tok, const struct op *op)
234 {
235 // special case parsing for parentheses
236 if (*op->tok == '(') {
237 parse_parens(lhs, tok);
238 return;
239 }
240
241 parse_op(lhs, tok, op + 1);
242 while (match(tok, op->tok)) {
243 struct value rhs;
244 parse_op(&rhs, tok, op + 1);
245 if (rhs.s && !*rhs.s) error_exit("syntax error"); // premature end of expression
246 op->calc(lhs, &rhs);
247 }
248 }
249
250 static void parse_expr(struct value *ret, struct value *v)
251 {
252 parse_op(ret, v, ops); // start at the top of the ops table
253 }
254
255 void expr_main(void)
256 {
257 struct value tok, ret = {0};
258
259 toys.exitval = 2; // if exiting early, indicate invalid expression
260
261 TT.argidx = 0;
262
263 get_value(&tok); // warm up the parser with the initial value
264 parse_expr(&ret, &tok);
265
266 if (!tok.s || *tok.s) error_exit("syntax error"); // final token should be end of expression
267
268 if (ret.s) printf("%s\n", ret.s);
269 else printf("%ld\n", ret.i);
270
271 exit(is_zero(&ret));
272 }