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));