expr.c - sbase - suckless unix tools | |
git clone git://git.suckless.org/sbase | |
Log | |
Files | |
Refs | |
README | |
LICENSE | |
--- | |
expr.c (5514B) | |
--- | |
1 /* See LICENSE file for copyright and license details. */ | |
2 #include <limits.h> | |
3 #include <stdio.h> | |
4 #include <stdlib.h> | |
5 #include <string.h> | |
6 | |
7 #include "utf.h" | |
8 #include "util.h" | |
9 | |
10 /* tokens, one-character operators represent themselves */ | |
11 enum { | |
12 VAL = CHAR_MAX + 1, GE, LE, NE | |
13 }; | |
14 | |
15 struct val { | |
16 char *str; | |
17 long long num; | |
18 }; | |
19 | |
20 static void | |
21 tonum(struct val *v) | |
22 { | |
23 const char *errstr; | |
24 long long d; | |
25 | |
26 /* check if val is the result of an earlier calculation */ | |
27 if (!v->str) | |
28 return; | |
29 | |
30 d = strtonum(v->str, LLONG_MIN, LLONG_MAX, &errstr); | |
31 if (errstr) | |
32 enprintf(2, "error: expected integer, got %s\n", v->str); | |
33 v->num = d; | |
34 } | |
35 | |
36 static void | |
37 ezero(struct val *v) | |
38 { | |
39 if (v->num != 0) | |
40 return; | |
41 enprintf(2, "division by zero\n"); | |
42 } | |
43 | |
44 static int | |
45 valcmp(struct val *a, struct val *b) | |
46 { | |
47 int ret; | |
48 const char *err1, *err2; | |
49 long long d1, d2; | |
50 | |
51 d1 = strtonum(a->str, LLONG_MIN, LLONG_MAX, &err1); | |
52 d2 = strtonum(b->str, LLONG_MIN, LLONG_MAX, &err2); | |
53 | |
54 if (!err1 && !err2) { | |
55 ret = (d1 > d2) - (d1 < d2); | |
56 } else { | |
57 ret = strcmp(a->str, b->str); | |
58 } | |
59 | |
60 return ret; | |
61 } | |
62 | |
63 static void | |
64 match(struct val *vstr, struct val *vregx, struct val *ret) | |
65 { | |
66 regex_t re; | |
67 regmatch_t matches[2]; | |
68 size_t anchlen; | |
69 char *s, *p, *anchreg; | |
70 char *str = vstr->str, *regx = vregx->str; | |
71 | |
72 /* anchored regex */ | |
73 anchlen = strlen(regx) + 1 + 1; | |
74 anchreg = emalloc(anchlen); | |
75 estrlcpy(anchreg, "^", anchlen); | |
76 estrlcat(anchreg, regx, anchlen); | |
77 enregcomp(3, &re, anchreg, 0); | |
78 free(anchreg); | |
79 | |
80 if (regexec(&re, str, 2, matches, 0)) { | |
81 regfree(&re); | |
82 ret->str = re.re_nsub ? "" : NULL; | |
83 return; | |
84 } else if (re.re_nsub) { | |
85 regfree(&re); | |
86 | |
87 s = str + matches[1].rm_so; | |
88 p = str + matches[1].rm_eo; | |
89 *p = '\0'; | |
90 ret->str = enstrdup(3, s); | |
91 return; | |
92 } else { | |
93 regfree(&re); | |
94 str += matches[0].rm_so; | |
95 ret->num = utfnlen(str, matches[0].rm_eo - matches[0].rm… | |
96 return; | |
97 } | |
98 } | |
99 | |
100 static void | |
101 doop(int *ophead, int *opp, struct val *valhead, struct val *valp) | |
102 { | |
103 struct val ret = { .str = NULL, .num = 0 }, *a, *b; | |
104 int op; | |
105 | |
106 /* an operation "a op b" needs an operator and two values */ | |
107 if (opp[-1] == '(') | |
108 enprintf(2, "syntax error: extra (\n"); | |
109 if (valp - valhead < 2) | |
110 enprintf(2, "syntax error: missing expression or extra o… | |
111 | |
112 a = valp - 2; | |
113 b = valp - 1; | |
114 op = opp[-1]; | |
115 | |
116 switch (op) { | |
117 case '|': | |
118 if ( a->str && *a->str) ret.str = a->str; | |
119 else if (!a->str && a->num) ret.num = a->num; | |
120 else if ( b->str && *b->str) ret.str = b->str; | |
121 else ret.num = b->num; | |
122 break; | |
123 case '&': | |
124 if (((a->str && *a->str) || a->num) && | |
125 ((b->str && *b->str) || b->num)) { | |
126 ret.str = a->str; | |
127 ret.num = a->num; | |
128 } | |
129 break; | |
130 | |
131 case '=': ret.num = (valcmp(a, b) == 0); break; | |
132 case '>': ret.num = (valcmp(a, b) > 0); break; | |
133 case GE : ret.num = (valcmp(a, b) >= 0); break; | |
134 case '<': ret.num = (valcmp(a, b) < 0); break; | |
135 case LE : ret.num = (valcmp(a, b) <= 0); break; | |
136 case NE : ret.num = (valcmp(a, b) != 0); break; | |
137 | |
138 case '+': tonum(a); tonum(b); ret.num = a->num + b->nu… | |
139 case '-': tonum(a); tonum(b); ret.num = a->num - b->nu… | |
140 case '*': tonum(a); tonum(b); ret.num = a->num * b->nu… | |
141 case '/': tonum(a); tonum(b); ezero(b); ret.num = a->num / b->nu… | |
142 case '%': tonum(a); tonum(b); ezero(b); ret.num = a->num % b->nu… | |
143 | |
144 case ':': match(a, b, &ret); break; | |
145 } | |
146 | |
147 valp[-2] = ret; | |
148 } | |
149 | |
150 static int | |
151 lex(char *s, struct val *v) | |
152 { | |
153 int type = VAL; | |
154 char *ops = "|&=><+-*/%():"; | |
155 | |
156 if (s[0] && strchr(ops, s[0]) && !s[1]) { | |
157 /* one-char operand */ | |
158 type = s[0]; | |
159 } else if (s[0] && strchr("><!", s[0]) && s[1] == '=' && !s[2]) { | |
160 /* two-char operand */ | |
161 type = (s[0] == '>') ? GE : (s[0] == '<') ? LE : NE; | |
162 } | |
163 | |
164 return type; | |
165 } | |
166 | |
167 static int | |
168 parse(char *expr[], int numexpr) | |
169 { | |
170 struct val *valhead, *valp, v = { .str = NULL, .num = 0 }; | |
171 int *ophead, *opp, type, lasttype = 0; | |
172 char prec[] = { | |
173 [ 0 ] = 0, [VAL] = 0, ['('] = 0, [')'] = 0, | |
174 ['|'] = 1, | |
175 ['&'] = 2, | |
176 ['='] = 3, ['>'] = 3, [GE] = 3, ['<'] = 3, [LE] = 3, [NE… | |
177 ['+'] = 4, ['-'] = 4, | |
178 ['*'] = 5, ['/'] = 5, ['%'] = 5, | |
179 [':'] = 6, | |
180 }; | |
181 | |
182 valp = valhead = enreallocarray(3, NULL, numexpr, sizeof(*valp)); | |
183 opp = ophead = enreallocarray(3, NULL, numexpr, sizeof(*opp)); | |
184 for (; *expr; expr++) { | |
185 switch ((type = lex(*expr, &v))) { | |
186 case VAL: | |
187 /* treatment of *expr is not known until | |
188 * doop(); treat as a string for now */ | |
189 valp->str = *expr; | |
190 valp++; | |
191 break; | |
192 case '(': | |
193 *opp++ = type; | |
194 break; | |
195 case ')': | |
196 if (lasttype == '(') | |
197 enprintf(2, "syntax error: empty ( )\n"); | |
198 while (opp > ophead && opp[-1] != '(') | |
199 doop(ophead, opp--, valhead, valp--); | |
200 if (opp == ophead) | |
201 enprintf(2, "syntax error: extra )\n"); | |
202 opp--; | |
203 break; | |
204 default: /* operator */ | |
205 if (prec[lasttype]) | |
206 enprintf(2, "syntax error: extra operato… | |
207 while (opp > ophead && prec[opp[-1]] >= prec[typ… | |
208 doop(ophead, opp--, valhead, valp--); | |
209 *opp++ = type; | |
210 break; | |
211 } | |
212 lasttype = type; | |
213 v.str = NULL; | |
214 v.num = 0; | |
215 } | |
216 while (opp > ophead) | |
217 doop(ophead, opp--, valhead, valp--); | |
218 if (valp == valhead) | |
219 enprintf(2, "syntax error: missing expression\n"); | |
220 if (--valp > valhead) | |
221 enprintf(2, "syntax error: extra expression\n"); | |
222 | |
223 if (valp->str) | |
224 puts(valp->str); | |
225 else | |
226 printf("%lld\n", valp->num); | |
227 | |
228 return (valp->str && *valp->str) || valp->num; | |
229 } | |
230 | |
231 int | |
232 main(int argc, char *argv[]) | |
233 { | |
234 int ret; | |
235 | |
236 argv0 = *argv, argv0 ? (argc--, argv++) : (void *)0; | |
237 | |
238 ret = !parse(argv, argc); | |
239 | |
240 if (fshut(stdout, "<stdout>")) | |
241 ret = 3; | |
242 | |
243 return ret; | |
244 } |