expr.c - sbase - suckless unix tools
 (HTM) git clone git://git.suckless.org/sbase
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) README
 (DIR) 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 }