1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56#define FOR_expr
57#include "toys.h"
58
59GLOBALS(
60 char **tok;
61
62 char *refree;
63)
64
65
66struct value {
67 char *s;
68 long long i;
69};
70
71
72char *get_str(struct value *v)
73{
74 if (v->s) return v->s;
75 else return xmprintf("%lld", v->i);
76}
77
78
79int get_int(struct value *v, long long *ret)
80{
81 if (v->s) {
82 char *endp;
83
84 *ret = strtoll(v->s, &endp, 10);
85
86 if (*endp) return 0;
87 } else *ret = v->i;
88
89 return 1;
90}
91
92
93void assign_int(struct value *v, long long i)
94{
95 v->i = i;
96 v->s = NULL;
97}
98
99
100static int is_false(struct value *v)
101{
102 return get_int(v, &v->i) && !v->i;
103}
104
105
106static void re(char *target, char *pattern, struct value *ret)
107{
108 regex_t pat;
109 regmatch_t m[2];
110
111 xregcomp(&pat, pattern, 0);
112
113 if (!regexec(&pat, target, 2, m, 0) && !m[0].rm_so) {
114
115 if (pat.re_nsub>0) {
116 ret->s = xmprintf("%.*s", (int)(m[1].rm_eo-m[1].rm_so),
117 target+m[1].rm_so);
118 if (TT.refree) free(TT.refree);
119 TT.refree = ret->s;
120 } else assign_int(ret, m[0].rm_eo);
121 } else {
122 if (pat.re_nsub>0) ret->s = "";
123 else assign_int(ret, 0);
124 }
125 regfree(&pat);
126}
127
128
129
130enum { SI_TO_SI = 1, SI_TO_I, I_TO_I, S_TO_SI };
131
132enum { OR = 1, AND, EQ, NE, GT, GTE, LT, LTE, ADD, SUB, MUL, DIVI, MOD, RE };
133
134
135static struct op_def {
136 char *tok;
137 char prec, sig, op;
138} OPS[] = {
139
140 {"|", 1, SI_TO_SI, OR },
141 {"&", 2, SI_TO_SI, AND },
142
143 {"=", 3, SI_TO_I, EQ }, {"==", 3, SI_TO_I, EQ }, {"!=", 3, SI_TO_I, NE },
144 {">", 3, SI_TO_I, GT }, {">=", 3, SI_TO_I, GTE },
145 {"<", 3, SI_TO_I, LT }, {"<=", 3, SI_TO_I, LTE },
146
147 {"+", 4, I_TO_I, ADD }, {"-", 4, I_TO_I, SUB },
148 {"*", 5, I_TO_I, MUL }, {"/", 5, I_TO_I, DIVI }, {"%", 5, I_TO_I, MOD },
149
150 {":", 6, S_TO_SI, RE },
151 {NULL, 0, 0, 0},
152};
153
154void eval_op(struct op_def *o, struct value *ret, struct value *rhs)
155{
156 long long a, b, x = 0;
157 char *s, *t;
158 int cmp;
159
160 switch (o->sig) {
161
162 case SI_TO_SI:
163 switch (o->op) {
164 case OR: if (is_false(ret)) *ret = *rhs; break;
165 case AND: if (is_false(ret) || is_false(rhs)) assign_int(ret, 0); break;
166 }
167 break;
168
169 case SI_TO_I:
170 if (get_int(ret, &a) && get_int(rhs, &b)) {
171 cmp = a - b;
172 } else {
173 cmp = strcmp(s = get_str(ret), t = get_str(rhs));
174 if (ret->s != s) free(s);
175 if (rhs->s != t) free(t);
176 }
177 switch (o->op) {
178 case EQ: x = cmp == 0; break;
179 case NE: x = cmp != 0; break;
180 case GT: x = cmp > 0; break;
181 case GTE: x = cmp >= 0; break;
182 case LT: x = cmp < 0; break;
183 case LTE: x = cmp <= 0; break;
184 }
185 assign_int(ret, x);
186 break;
187
188 case I_TO_I:
189 if (!get_int(ret, &a) || !get_int(rhs, &b))
190 error_exit("non-integer argument");
191 switch (o->op) {
192 case ADD: x = a + b; break;
193 case SUB: x = a - b; break;
194 case MUL: x = a * b; break;
195 case DIVI: if (b == 0) error_exit("division by zero"); x = a / b; break;
196 case MOD: if (b == 0) error_exit("division by zero"); x = a % b; break;
197 }
198 assign_int(ret, x);
199 break;
200
201 case S_TO_SI:
202 s = get_str(ret);
203 cmp = ret->s!=s;
204 re(s, t = get_str(rhs), ret);
205 if (cmp) free(s);
206 if (rhs->s!=t) free(t);
207 break;
208 }
209}
210
211
212
213static void eval_expr(struct value *ret, int min_prec)
214{
215 if (!*TT.tok) error_exit("Unexpected end of input");
216
217
218 if (!strcmp(*TT.tok, "(")) {
219 TT.tok++;
220
221 eval_expr(ret, 1);
222 if (ret->s && !strcmp(ret->s, ")")) error_exit("empty ( )");
223 if (!*TT.tok) error_exit("Expected )");
224 if (strcmp(*TT.tok, ")")) error_exit("Expected ) but got %s", *TT.tok);
225 } else ret->s = *TT.tok;
226 TT.tok++;
227
228
229 struct value rhs;
230 while (*TT.tok) {
231 struct op_def *o = OPS;
232
233 while (o->tok) {
234 if (!strcmp(*TT.tok, o->tok)) break;
235 o++;
236 }
237 if (!o->tok) break;
238 if (o->prec < min_prec) break;
239 TT.tok++;
240
241 eval_expr(&rhs, o->prec + 1);
242 eval_op(o, ret, &rhs);
243 }
244}
245
246void expr_main(void)
247{
248 struct value ret = {0};
249
250 toys.exitval = 2;
251 TT.tok = toys.optargs;
252 eval_expr(&ret, 1);
253 if (*TT.tok) error_exit("Unexpected extra input '%s'\n", *TT.tok);
254
255 if (ret.s) printf("%s\n", ret.s);
256 else printf("%lld\n", ret.i);
257
258 toys.exitval = is_false(&ret);
259
260 if (TT.refree) free(TT.refree);
261}
262