master xplshn/aruu / cmd / posix / expr.c
  1/* See LICENSE file for copyright and license details. */
  2
  3#include <limits.h>
  4#include <stdio.h>
  5#include <stdlib.h>
  6#include <string.h>
  7
  8#include "utf.h"
  9#include "util.h"
 10
 11/* tokens, one-character operators represent themselves */
 12enum { VAL = CHAR_MAX + 1, GE, LE, NE };
 13
 14struct val {
 15  char     *str;
 16  long long num;
 17};
 18
 19static void
 20tonum(struct val *v)
 21{
 22  const char *errstr;
 23  long long   d;
 24
 25  /* check if val is the result of an earlier calculation */
 26  if (!v->str)
 27    return;
 28
 29  d = strtonum(v->str, LLONG_MIN, LLONG_MAX, &errstr);
 30  if (errstr)
 31    enprintf(2, "error: expected integer, got %s\n", v->str);
 32  v->num = d;
 33}
 34
 35static void
 36ezero(struct val *v)
 37{
 38  if (v->num != 0)
 39    return;
 40  enprintf(2, "division by zero\n");
 41}
 42
 43static int
 44valcmp(struct val *a, struct val *b)
 45{
 46  int         ret;
 47  const char *err1, *err2;
 48  long long   d1, d2;
 49
 50  d1 = strtonum(a->str, LLONG_MIN, LLONG_MAX, &err1);
 51  d2 = strtonum(b->str, LLONG_MIN, LLONG_MAX, &err2);
 52
 53  if (!err1 && !err2) {
 54    ret = (d1 > d2) - (d1 < d2);
 55  } else {
 56    ret = strcmp(a->str, b->str);
 57  }
 58
 59  return ret;
 60}
 61
 62static void
 63match(struct val *vstr, struct val *vregx, struct val *ret)
 64{
 65  regex_t    re;
 66  regmatch_t matches[2];
 67  size_t     anchlen;
 68  char      *s, *p, *anchreg;
 69  char      *str = vstr->str, *regx = vregx->str;
 70
 71  /* anchored regex */
 72  anchlen = strlen(regx) + 1 + 1;
 73  anchreg = emalloc(anchlen);
 74  estrlcpy(anchreg, "^", anchlen);
 75  estrlcat(anchreg, regx, anchlen);
 76  enregcomp(3, &re, anchreg, 0);
 77  free(anchreg);
 78
 79  if (regexec(&re, str, 2, matches, 0)) {
 80    regfree(&re);
 81    ret->str = re.re_nsub ? "" : NULL;
 82    return;
 83  } else if (re.re_nsub) {
 84    regfree(&re);
 85
 86    s        = str + matches[1].rm_so;
 87    p        = str + matches[1].rm_eo;
 88    *p       = '\0';
 89    ret->str = enstrdup(3, s);
 90    return;
 91  } else {
 92    regfree(&re);
 93    str += matches[0].rm_so;
 94    ret->num = utfnlen(str, matches[0].rm_eo - matches[0].rm_so);
 95    return;
 96  }
 97}
 98
 99static void
100doop(int *ophead, int *opp, struct val *valhead, struct val *valp)
101{
102  struct val ret = {.str = NULL, .num = 0}, *a, *b;
103  int        op;
104
105  (void)ophead;
106
107  /* an operation "a op b" needs an operator and two values */
108  if (opp[-1] == '(')
109    enprintf(2, "syntax error: extra (\n");
110  if (valp - valhead < 2)
111    enprintf(2, "syntax error: missing expression or extra operator\n");
112
113  a  = valp - 2;
114  b  = valp - 1;
115  op = opp[-1];
116
117  switch (op) {
118    case '|':
119      if (a->str && *a->str)
120        ret.str = a->str;
121      else if (!a->str && a->num)
122        ret.num = a->num;
123      else if (b->str && *b->str)
124        ret.str = b->str;
125      else
126        ret.num = b->num;
127      break;
128    case '&':
129      if (((a->str && *a->str) || a->num) && ((b->str && *b->str) || b->num)) {
130        ret.str = a->str;
131        ret.num = a->num;
132      }
133      break;
134
135    case '=':
136      ret.num = (valcmp(a, b) == 0);
137      break;
138    case '>':
139      ret.num = (valcmp(a, b) > 0);
140      break;
141    case GE:
142      ret.num = (valcmp(a, b) >= 0);
143      break;
144    case '<':
145      ret.num = (valcmp(a, b) < 0);
146      break;
147    case LE:
148      ret.num = (valcmp(a, b) <= 0);
149      break;
150    case NE:
151      ret.num = (valcmp(a, b) != 0);
152      break;
153
154    case '+':
155      tonum(a);
156      tonum(b);
157      ret.num = a->num + b->num;
158      break;
159    case '-':
160      tonum(a);
161      tonum(b);
162      ret.num = a->num - b->num;
163      break;
164    case '*':
165      tonum(a);
166      tonum(b);
167      ret.num = a->num * b->num;
168      break;
169    case '/':
170      tonum(a);
171      tonum(b);
172      ezero(b);
173      ret.num = a->num / b->num;
174      break;
175    case '%':
176      tonum(a);
177      tonum(b);
178      ezero(b);
179      ret.num = a->num % b->num;
180      break;
181
182    case ':':
183      match(a, b, &ret);
184      break;
185  }
186
187  valp[-2] = ret;
188}
189
190static int
191lex(char *s, struct val *v)
192{
193  int   type = VAL;
194  char *ops  = "|&=><+-*/%():";
195
196  (void)v;
197
198  if (s[0] && strchr(ops, s[0]) && !s[1]) {
199    /* one-char operand */
200    type = s[0];
201  } else if (s[0] && strchr("><!", s[0]) && s[1] == '=' && !s[2]) {
202    /* two-char operand */
203    type = (s[0] == '>') ? GE : (s[0] == '<') ? LE : NE;
204  }
205
206  return type;
207}
208
209static int
210parse(char *expr[], int numexpr)
211{
212  struct val *valhead, *valp, v = {.str = NULL, .num = 0};
213  int        *ophead, *opp, type, lasttype = 0;
214  char        prec[] = {
215      [0]   = 0,
216      [VAL] = 0,
217      ['('] = 0,
218      [')'] = 0,
219      ['|'] = 1,
220      ['&'] = 2,
221      ['='] = 3,
222      ['>'] = 3,
223      [GE]  = 3,
224      ['<'] = 3,
225      [LE]  = 3,
226      [NE]  = 3,
227      ['+'] = 4,
228      ['-'] = 4,
229      ['*'] = 5,
230      ['/'] = 5,
231      ['%'] = 5,
232      [':'] = 6,
233  };
234
235  valp = valhead = enreallocarray(3, NULL, numexpr, sizeof(*valp));
236  opp = ophead = enreallocarray(3, NULL, numexpr, sizeof(*opp));
237  for (; *expr; expr++) {
238    switch ((type = lex(*expr, &v))) {
239      case VAL:
240        /* treatment of *expr is not known until
241         * doop(); treat as a string for now */
242        valp->str = *expr;
243        valp++;
244        break;
245      case '(':
246        *opp++ = type;
247        break;
248      case ')':
249        if (lasttype == '(')
250          enprintf(2, "syntax error: empty ( )\n");
251        while (opp > ophead && opp[-1] != '(')
252          doop(ophead, opp--, valhead, valp--);
253        if (opp == ophead)
254          enprintf(2, "syntax error: extra )\n");
255        opp--;
256        break;
257      default: /* operator */
258        if (prec[lasttype])
259          enprintf(2, "syntax error: extra operator\n");
260        while (opp > ophead && prec[opp[-1]] >= prec[type])
261          doop(ophead, opp--, valhead, valp--);
262        *opp++ = type;
263        break;
264    }
265    lasttype = type;
266    v.str    = NULL;
267    v.num    = 0;
268  }
269  while (opp > ophead)
270    doop(ophead, opp--, valhead, valp--);
271  if (valp == valhead)
272    enprintf(2, "syntax error: missing expression\n");
273  if (--valp > valhead)
274    enprintf(2, "syntax error: extra expression\n");
275
276  if (valp->str)
277    puts(valp->str);
278  else
279    printf("%lld\n", valp->num);
280
281  return (valp->str && *valp->str) || valp->num;
282}
283
284// ?man expr: evaluate expression
285// ?man evaluate a command line expression and print the result
286int
287main(int argc, char *argv[])
288{
289  int ret;
290
291  argv0 = *argv, argv0 ? (argc--, argv++) : (void *)0;
292
293  ret = !parse(argv, argc);
294
295  if (fshut(stdout, "<stdout>"))
296    ret = 3;
297
298  return ret;
299}