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_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 }