Mercurial > hg > toybox
comparison toys/pending/expr.c @ 1590:d5931155cafe draft
Refactor expr and add another test entry that works with TEST_HOST=1 but not with the one in pending.
author | Rob Landley <rob@landley.net> |
---|---|
date | Mon, 01 Dec 2014 03:15:25 -0600 |
parents | cff20c82a797 |
children | 9db327a6920d |
comparison
equal
deleted
inserted
replaced
1589:a97f69a1e406 | 1590:d5931155cafe |
---|---|
14 | 14 |
15 Evaluate expression and print result. | 15 Evaluate expression and print result. |
16 | 16 |
17 The supported operators, in order of increasing precedence, are: | 17 The supported operators, in order of increasing precedence, are: |
18 | 18 |
19 | & = > >= < <= != + - * / % | 19 | & = > >= < <= != + - * / % : |
20 | 20 |
21 In addition, parentheses () are supported for grouping. | 21 In addition, parentheses () are supported for grouping. |
22 */ | 22 */ |
23 | 23 |
24 // TODO: int overflow checking | 24 // TODO: int overflow checking |
37 struct value { | 37 struct value { |
38 char *s; | 38 char *s; |
39 long long i; | 39 long long i; |
40 }; | 40 }; |
41 | 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 = strtoll(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 | 42 // check if v is the integer 0 or the empty string |
77 static int is_zero(const struct value *v) | 43 static int is_zero(struct value *v) |
78 { | 44 { |
79 return ((v->s && *v->s == '\0') || v->i == 0); | 45 return v->s ? !*v->s : !v->i; |
80 } | 46 } |
81 | 47 |
82 static char *num_to_str(long long num) | 48 static char *num_to_str(long long num) |
83 { | 49 { |
84 static char num_buf[21]; | 50 static char num_buf[21]; |
85 snprintf(num_buf, sizeof(num_buf), "%lld", num); | 51 snprintf(num_buf, sizeof(num_buf), "%lld", num); |
86 return num_buf; | 52 return num_buf; |
87 } | 53 } |
88 | 54 |
89 static int cmp(const struct value *lhs, const struct value *rhs) | 55 static int cmp(struct value *lhs, struct value *rhs) |
90 { | 56 { |
91 if (lhs->s || rhs->s) { | 57 if (lhs->s || rhs->s) { |
92 // at least one operand is a string | 58 // at least one operand is a string |
93 char *ls = lhs->s ? lhs->s : num_to_str(lhs->i); | 59 char *ls = lhs->s ? lhs->s : num_to_str(lhs->i); |
94 char *rs = rhs->s ? rhs->s : num_to_str(rhs->i); | 60 char *rs = rhs->s ? rhs->s : num_to_str(rhs->i); |
95 return strcmp(ls, rs); | 61 return strcmp(ls, rs); |
96 } else { | 62 } else return lhs->i - rhs->i; |
97 return lhs->i - rhs->i; | 63 } |
98 } | 64 |
99 } | 65 static void re(struct value *lhs, struct value *rhs) |
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 { | 66 { |
114 regex_t rp; | 67 regex_t rp; |
115 regmatch_t rm[2]; | 68 regmatch_t rm[2]; |
116 | 69 |
117 xregcomp(&rp, rhs->s, 0); | 70 xregcomp(&rp, rhs->s, 0); |
128 lhs->s = 0; | 81 lhs->s = 0; |
129 } else lhs->s = ""; | 82 } else lhs->s = ""; |
130 } | 83 } |
131 } | 84 } |
132 | 85 |
133 static void mod(struct value *lhs, const struct value *rhs) | 86 static void mod(struct value *lhs, struct value *rhs) |
134 { | 87 { |
135 if (lhs->s || rhs->s) error_exit("non-integer argument"); | 88 if (lhs->s || rhs->s) error_exit("non-integer argument"); |
136 if (is_zero(rhs)) error_exit("division by zero"); | 89 if (is_zero(rhs)) error_exit("division by zero"); |
137 lhs->i %= rhs->i; | 90 lhs->i %= rhs->i; |
138 } | 91 } |
139 | 92 |
140 static void divi(struct value *lhs, const struct value *rhs) | 93 static void divi(struct value *lhs, struct value *rhs) |
141 { | 94 { |
142 if (lhs->s || rhs->s) error_exit("non-integer argument"); | 95 if (lhs->s || rhs->s) error_exit("non-integer argument"); |
143 if (is_zero(rhs)) error_exit("division by zero"); | 96 if (is_zero(rhs)) error_exit("division by zero"); |
144 lhs->i /= rhs->i; | 97 lhs->i /= rhs->i; |
145 } | 98 } |
146 | 99 |
147 static void mul(struct value *lhs, const struct value *rhs) | 100 static void mul(struct value *lhs, struct value *rhs) |
148 { | 101 { |
149 if (lhs->s || rhs->s) error_exit("non-integer argument"); | 102 if (lhs->s || rhs->s) error_exit("non-integer argument"); |
150 lhs->i *= rhs->i; | 103 lhs->i *= rhs->i; |
151 } | 104 } |
152 | 105 |
153 static void sub(struct value *lhs, const struct value *rhs) | 106 static void sub(struct value *lhs, struct value *rhs) |
154 { | 107 { |
155 if (lhs->s || rhs->s) error_exit("non-integer argument"); | 108 if (lhs->s || rhs->s) error_exit("non-integer argument"); |
156 lhs->i -= rhs->i; | 109 lhs->i -= rhs->i; |
157 } | 110 } |
158 | 111 |
159 static void add(struct value *lhs, const struct value *rhs) | 112 static void add(struct value *lhs, struct value *rhs) |
160 { | 113 { |
161 if (lhs->s || rhs->s) error_exit("non-integer argument"); | 114 if (lhs->s || rhs->s) error_exit("non-integer argument"); |
162 lhs->i += rhs->i; | 115 lhs->i += rhs->i; |
163 } | 116 } |
164 | 117 |
165 static void ne(struct value *lhs, const struct value *rhs) | 118 static void ne(struct value *lhs, struct value *rhs) |
166 { | 119 { |
167 lhs->i = cmp(lhs, rhs) != 0; | 120 lhs->i = cmp(lhs, rhs) != 0; |
168 lhs->s = NULL; | 121 lhs->s = NULL; |
169 } | 122 } |
170 | 123 |
171 static void lte(struct value *lhs, const struct value *rhs) | 124 static void lte(struct value *lhs, struct value *rhs) |
172 { | 125 { |
173 lhs->i = cmp(lhs, rhs) <= 0; | 126 lhs->i = cmp(lhs, rhs) <= 0; |
174 lhs->s = NULL; | 127 lhs->s = NULL; |
175 } | 128 } |
176 | 129 |
177 static void lt(struct value *lhs, const struct value *rhs) | 130 static void lt(struct value *lhs, struct value *rhs) |
178 { | 131 { |
179 lhs->i = cmp(lhs, rhs) < 0; | 132 lhs->i = cmp(lhs, rhs) < 0; |
180 lhs->s = NULL; | 133 lhs->s = NULL; |
181 } | 134 } |
182 | 135 |
183 static void gte(struct value *lhs, const struct value *rhs) | 136 static void gte(struct value *lhs, struct value *rhs) |
184 { | 137 { |
185 lhs->i = cmp(lhs, rhs) >= 0; | 138 lhs->i = cmp(lhs, rhs) >= 0; |
186 lhs->s = NULL; | 139 lhs->s = NULL; |
187 } | 140 } |
188 | 141 |
189 static void gt(struct value *lhs, const struct value *rhs) | 142 static void gt(struct value *lhs, struct value *rhs) |
190 { | 143 { |
191 lhs->i = cmp(lhs, rhs) > 0; | 144 lhs->i = cmp(lhs, rhs) > 0; |
192 lhs->s = NULL; | 145 lhs->s = NULL; |
193 } | 146 } |
194 | 147 |
195 static void eq(struct value *lhs, const struct value *rhs) | 148 static void eq(struct value *lhs, struct value *rhs) |
196 { | 149 { |
197 lhs->i = cmp(lhs, rhs) == 0; | 150 lhs->i = !cmp(lhs, rhs); |
198 lhs->s = NULL; | 151 lhs->s = NULL; |
199 } | 152 } |
200 | 153 |
201 static void and(struct value *lhs, const struct value *rhs) | 154 static void and(struct value *lhs, struct value *rhs) |
202 { | 155 { |
203 if (is_zero(lhs) || is_zero(rhs)) { | 156 if (is_zero(lhs) || is_zero(rhs)) { |
204 lhs->i = 0; | 157 lhs->i = 0; |
205 lhs->s = NULL; | 158 lhs->s = NULL; |
206 } | 159 } |
207 } | 160 } |
208 | 161 |
209 static void or(struct value *lhs, const struct value *rhs) | 162 static void or(struct value *lhs, struct value *rhs) |
210 { | 163 { |
211 if (is_zero(lhs)) { | 164 if (is_zero(lhs)) *lhs = *rhs; |
212 *lhs = *rhs; | 165 } |
213 } | 166 |
214 } | 167 static void get_value(struct value *v) |
215 | 168 { |
169 char *endp, *arg; | |
170 | |
171 if (TT.argidx == toys.optc) { | |
172 v->i = 0; | |
173 v->s = ""; // signal end of expression | |
174 return; | |
175 } | |
176 | |
177 // can't happen, the increment is after the == test | |
178 // if (TT.argidx >= toys.optc) error_exit("syntax error"); | |
179 | |
180 arg = toys.optargs[TT.argidx++]; | |
181 | |
182 v->i = strtoll(arg, &endp, 10); | |
183 v->s = *endp ? arg : NULL; | |
184 } | |
185 | |
186 // check if v matches a token, and consume it if so | |
187 static int match(struct value *v, char *tok) | |
188 { | |
189 if (v->s && !strcmp(v->s, tok)) { | |
190 get_value(v); | |
191 return 1; | |
192 } | |
193 | |
194 return 0; | |
195 } | |
216 | 196 |
217 // operators in order of increasing precedence | 197 // operators in order of increasing precedence |
218 static const struct op ops[] = { | 198 static struct op { |
219 {"|", or }, | 199 char *tok; |
220 {"&", and }, | 200 |
221 {"=", eq }, | 201 // calculate "lhs op rhs" (e.g. lhs + rhs) and store result in lhs |
222 {"==", eq }, | 202 void (*calc)(struct value *lhs, struct value *rhs); |
223 {">", gt }, | 203 } ops[] = { |
224 {">=", gte }, | 204 {"|", or }, {"&", and }, {"=", eq }, {"==", eq }, {">", gt }, |
225 {"<", lt }, | 205 {">=", gte }, {"<", lt }, {"<=", lte }, {"!=", ne }, {"+", add }, |
226 {"<=", lte }, | 206 {"-", sub }, {"*", mul }, {"/", divi}, {"%", mod }, {":", re }, |
227 {"!=", ne }, | |
228 {"+", add }, | |
229 {"-", sub }, | |
230 {"*", mul }, | |
231 {"/", divi}, | |
232 {"%", mod }, | |
233 {":", re }, | |
234 {"(", NULL}, // special case - must be last | 207 {"(", NULL}, // special case - must be last |
235 }; | 208 }; |
236 | 209 |
237 | 210 // "|,&,= ==> >=< <= !=,+-,*/%,:" |
238 static void parse_parens(struct value *ret, struct value *v) | 211 |
239 { | 212 static void parse_op(struct value *lhs, struct value *tok, struct op *op) |
240 if (match(v, "(")) { | 213 { |
241 parse_expr(ret, v); | 214 if (!op) op = ops; |
242 if (!match(v, ")")) error_exit("syntax error"); // missing closing paren | 215 |
243 } else { | |
244 // v is a string or integer - return it and get the next token | |
245 *ret = *v; | |
246 get_value(v); | |
247 } | |
248 } | |
249 | |
250 static void parse_op(struct value *lhs, struct value *tok, const struct op *op) | |
251 { | |
252 // special case parsing for parentheses | 216 // special case parsing for parentheses |
253 if (*op->tok == '(') { | 217 if (*op->tok == '(') { |
254 parse_parens(lhs, tok); | 218 if (match(tok, "(")) { |
219 parse_op(lhs, tok, 0); | |
220 if (!match(tok, ")")) error_exit("syntax error"); // missing closing paren | |
221 } else { | |
222 // tok is a string or integer - return it and get the next token | |
223 *lhs = *tok; | |
224 get_value(tok); | |
225 } | |
226 | |
255 return; | 227 return; |
256 } | 228 } |
257 | 229 |
258 parse_op(lhs, tok, op + 1); | 230 parse_op(lhs, tok, op + 1); |
259 while (match(tok, op->tok)) { | 231 while (match(tok, op->tok)) { |
262 if (rhs.s && !*rhs.s) error_exit("syntax error"); // premature end of expression | 234 if (rhs.s && !*rhs.s) error_exit("syntax error"); // premature end of expression |
263 op->calc(lhs, &rhs); | 235 op->calc(lhs, &rhs); |
264 } | 236 } |
265 } | 237 } |
266 | 238 |
267 static void parse_expr(struct value *ret, struct value *v) | |
268 { | |
269 parse_op(ret, v, ops); // start at the top of the ops table | |
270 } | |
271 | |
272 void expr_main(void) | 239 void expr_main(void) |
273 { | 240 { |
274 struct value tok, ret = {0}; | 241 struct value tok, ret = {0}; |
275 | 242 |
276 toys.exitval = 2; // if exiting early, indicate invalid expression | 243 toys.exitval = 2; // if exiting early, indicate invalid expression |
277 | 244 |
278 TT.argidx = 0; | 245 TT.argidx = 0; |
279 | 246 |
280 get_value(&tok); // warm up the parser with the initial value | 247 get_value(&tok); // warm up the parser with the initial value |
281 parse_expr(&ret, &tok); | 248 parse_op(&ret, &tok, 0); |
282 | 249 |
283 if (!tok.s || *tok.s) error_exit("syntax error"); // final token should be end of expression | 250 // final token should be end of expression |
251 if (!tok.s || *tok.s) error_exit("syntax error"); | |
284 | 252 |
285 if (ret.s) printf("%s\n", ret.s); | 253 if (ret.s) printf("%s\n", ret.s); |
286 else printf("%lld\n", ret.i); | 254 else printf("%lld\n", ret.i); |
287 | 255 |
288 exit(is_zero(&ret)); | 256 exit(is_zero(&ret)); |