Introduction
Introduction Statistics Contact Development Disclaimer Help
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 }
You are viewing proxied material from suckless.org. The copyright of proxied material belongs to its original authors. Any comments or complaints in relation to proxied material should be directed to the original authors of the content concerned. Please see the disclaimer for more details.