/* See LICENSE file for copyright and license details. */ #include #include #include #include #include #include #include #include "util.h" enum { VAL = CHAR_MAX + 1, GE, LE, NE }; typedef struct { char *s; intmax_t n; } Val; static void doop(int*, int**, Val*, Val**); static Val match(Val, Val); static void num(Val); static int valcmp(Val, Val); static char *valstr(Val, char*); static int yylex(void); static int yyparse(int); static char **args; static size_t intlen; static Val yylval; // otop points to one past last op // vtop points to one past last val // guaranteed otop != ops // pop two vals, pop op, apply op, push val static void doop(int *ops, int **otop, Val *vals, Val **vtop) { Val ret, a, b; int op; if ((*otop)[-1] == '(') enprintf(2, "syntax error: extra (\n"); if (*vtop - vals < 2) enprintf(2, "syntax error: missing expression or extra operator\n"); a = (*vtop)[-2]; b = (*vtop)[-1]; op = (*otop)[-1]; switch (op) { case '|': if ( a.s && *a.s) ret = (Val){ a.s , 0 }; else if (!a.s && a.n) ret = (Val){ NULL, a.n }; else if ( b.s && *b.s) ret = (Val){ b.s , 0 }; else ret = (Val){ NULL, b.n }; break; case '&': if (((a.s && *a.s) || a.n) && ((b.s && *b.s) || b.n)) ret = a; else ret = (Val){ NULL, 0 }; break; case '=': ret = (Val){ NULL, valcmp(a, b) == 0 }; break; case '>': ret = (Val){ NULL, valcmp(a, b) > 0 }; break; case GE : ret = (Val){ NULL, valcmp(a, b) >= 0 }; break; case '<': ret = (Val){ NULL, valcmp(a, b) < 0 }; break; case LE : ret = (Val){ NULL, valcmp(a, b) <= 0 }; break; case NE : ret = (Val){ NULL, valcmp(a, b) != 0 }; break; case '+': num(a); num(b); ret = (Val){ NULL, a.n + b.n }; break; case '-': num(a); num(b); ret = (Val){ NULL, a.n - b.n }; break; case '*': num(a); num(b); ret = (Val){ NULL, a.n * b.n }; break; case '/': num(a); num(b); ret = (Val){ NULL, a.n / b.n }; break; case '%': num(a); num(b); ret = (Val){ NULL, a.n % b.n }; break; case ':': ret = match(a, b); break; } (*vtop)[-2] = ret; (*otop)--; (*vtop)--; } static Val match(Val vstr, Val vregx) { char b1[intlen], *str = valstr(vstr , b1); char b2[intlen], *regx = valstr(vregx, b2); char anchreg[strlen(regx) + 2]; char *ret, *p; intmax_t d; regex_t re; regmatch_t matches[2]; sprintf(anchreg, "^%s", regx); if (regcomp(&re, anchreg, 0)) enprintf(3, "regcomp failed\n"); if (regexec(&re, str, 2, matches, 0)) return (Val){ (re.re_nsub ? "" : NULL), 0 }; if (re.re_nsub) { regoff_t len = matches[1].rm_eo - matches[1].rm_so + 1; if (!(ret = malloc(len))) // FIXME: free enprintf(3, "malloc failed\n"); d = strtoimax(ret, &p, 10); strlcpy(ret, str + matches[1].rm_so, len); if (*ret && !*p) return (Val){ NULL, d }; return (Val){ ret, 0 }; } return (Val){ NULL, matches[0].rm_eo - matches[0].rm_so }; } static void num(Val v) { if (v.s) enprintf(2, "syntax error: expected integer got '%s'\n", v.s); } static int valcmp(Val a, Val b) { char b1[intlen], *p = valstr(a, b1); char b2[intlen], *q = valstr(b, b2); if (!a.s && !b.s) return (a.n > b.n) - (a.n < b.n); return strcmp(p, q); } static char * valstr(Val val, char *buf) { char *p = val.s; if (!p) { sprintf(buf, "%"PRIdMAX, val.n); p = buf; } return p; } static int yylex(void) { intmax_t d; char *q, *p, *ops = "|&=><+-*/%():"; /* Reached end of expression */ if (!(p = *args++)) return 0; /* Check if p is a clean integer */ d = strtoimax(p, &q, 10); if(*p && !*q) { yylval = (Val){ NULL, d }; return VAL; } /* Single character operator */ if (*p && !*(p+1) && strchr(ops, *p)) return *p; /* Double character operator */ if (*p && *(p+1) == '=' && !*(p+2)) { switch (*p) { case '>': return GE; case '<': return LE; case '!': return NE; } } /* Nothing matches, return raw val */ yylval = (Val){ p, 0 }; return VAL; } static int yyparse(int argc) { Val vals[argc], *vtop = vals; int ops [argc], *otop = ops; int type, last = 0; char prec[] = { [VAL] = 0, ['|'] = 1, ['&'] = 2, ['='] = 3, ['>'] = 3, [GE] = 3, ['<'] = 3, [LE] = 3, [NE] = 3, ['+'] = 4, ['-'] = 4, ['*'] = 5, ['/'] = 5, ['%'] = 5, [':'] = 6, }; while ((type = yylex())) { switch (type) { case VAL: *vtop++ = yylval; break; case '(': *otop++ = '('; break; case ')': if (last == '(') enprintf(2, "syntax error: empty ( )\n"); while(otop > ops && otop[-1] != '(') doop(ops, &otop, vals, &vtop); if (otop == ops) enprintf(2, "syntax error: extra )\n"); otop--; break; default: if (prec[last]) enprintf(2, "syntax error: extra operator\n"); while (otop > ops && prec[otop[-1]] >= prec[type]) doop(ops, &otop, vals, &vtop); *otop++ = type; break; } last = type; } while (otop > ops) doop(ops, &otop, vals, &vtop); if (vtop == vals) { enprintf(2, "syntax error: missing expression\n"); } else if (vtop - vals > 1) { enprintf(2, "syntax error: extra expression\n"); } vtop--; if (vtop->s) { printf("%s\n" , vtop->s); } else { printf("%"PRIdMAX"\n", vtop->n); } return (vtop->s && *vtop->s) || vtop->n; } int main(int argc, char **argv) { if (!(intlen = snprintf(NULL, 0, "%"PRIdMAX, INTMAX_MIN) + 1)) enprintf(3, "failed to get max digits\n"); args = argv + 1; if (*args && !strcmp("--", *args)) ++args; return !yyparse(argc); }