expr.c - sbase - suckless unix tools
(HTM) git clone git://git.suckless.org/sbase
(DIR) Log
(DIR) Files
(DIR) Refs
(DIR) README
(DIR) 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_so);
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 operator\n");
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->num; break;
139 case '-': tonum(a); tonum(b); ret.num = a->num - b->num; break;
140 case '*': tonum(a); tonum(b); ret.num = a->num * b->num; break;
141 case '/': tonum(a); tonum(b); ezero(b); ret.num = a->num / b->num; break;
142 case '%': tonum(a); tonum(b); ezero(b); ret.num = a->num % b->num; break;
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] = 3,
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 operator\n");
207 while (opp > ophead && prec[opp[-1]] >= prec[type])
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 }