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