sbase

suckless unix tools
git clone git://git.2f30.org/sbase
Log | Files | Refs | README | LICENSE

expr.c (5957B)


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