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 }