Home | History | Annotate | Download | only in pending
      1 /* expr.c - evaluate expression
      2  *
      3  * Copyright 2013 Daniel Verkamp <daniel (at) drv.nu>
      4  *
      5  * http://pubs.opengroup.org/onlinepubs/9699919799/utilities/expr.html
      6  *
      7  * The web standard is incomplete (precedence grouping missing), see:
      8  * http://permalink.gmane.org/gmane.comp.standards.posix.austin.general/10141
      9 
     10 USE_EXPR(NEWTOY(expr, NULL, TOYFLAG_USR|TOYFLAG_BIN))
     11 
     12 config EXPR
     13   bool "expr"
     14   default n
     15   help
     16     usage: expr ARG1 OPERATOR ARG2...
     17 
     18     Evaluate expression and print result. For example, "expr 1 + 2".
     19 
     20     The supported operators are (grouped from highest to lowest priority):
     21 
     22       ( )    :    * / %    + -    != <= < >= > =    &    |
     23 
     24     Each constant and operator must be a separate command line argument.
     25     All operators are infix, meaning they expect a constant (or expression
     26     that resolves to a constant) on each side of the operator. Operators of
     27     the same priority (within each group above) are evaluated left to right.
     28     Parentheses may be used (as separate arguments) to elevate the priority
     29     of expressions.
     30 
     31     Calling expr from a command shell requires a lot of \( or '*' escaping
     32     to avoid interpreting shell control characters.
     33 
     34     The & and | operators are logical (not bitwise) and may operate on
     35     strings (a blank string is "false"). Comparison operators may also
     36     operate on strings (alphabetical sort).
     37 
     38     Constants may be strings or integers. Comparison, logical, and regex
     39     operators may operate on strings (a blank string is "false"), other
     40     operators require integers.
     41 */
     42 
     43 // TODO: int overflow checking
     44 
     45 #define FOR_expr
     46 #include "toys.h"
     47 
     48 
     49 GLOBALS(
     50   int argidx;
     51 )
     52 
     53 // Scalar value.
     54 // If s is NULL, the value is an integer (i).
     55 // If s is not NULL, the value is a string (s).
     56 struct value {
     57   char *s;
     58   long long i;
     59 };
     60 
     61 // check if v is the integer 0 or the empty string
     62 static int is_zero(struct value *v)
     63 {
     64   return v->s ? !*v->s : !v->i;
     65 }
     66 
     67 static char *num_to_str(long long num)
     68 {
     69   static char num_buf[21];
     70   snprintf(num_buf, sizeof(num_buf), "%lld", num);
     71   return num_buf;
     72 }
     73 
     74 static int cmp(struct value *lhs, struct value *rhs)
     75 {
     76   if (lhs->s || rhs->s) {
     77     // at least one operand is a string
     78     char *ls = lhs->s ? lhs->s : num_to_str(lhs->i);
     79     char *rs = rhs->s ? rhs->s : num_to_str(rhs->i);
     80     return strcmp(ls, rs);
     81   } else return lhs->i - rhs->i;
     82 }
     83 
     84 static void re(struct value *lhs, struct value *rhs)
     85 {
     86   regex_t rp;
     87   regmatch_t rm[2];
     88 
     89   xregcomp(&rp, rhs->s, 0);
     90   if (!regexec(&rp, lhs->s, 2, rm, 0) && rm[0].rm_so == 0) {
     91     if (rp.re_nsub > 0 && rm[1].rm_so >= 0)
     92       lhs->s = xmprintf("%.*s", rm[1].rm_eo - rm[1].rm_so, lhs->s+rm[1].rm_so);
     93     else {
     94       lhs->i = rm[0].rm_eo;
     95       lhs->s = 0;
     96     }
     97   } else {
     98     if (!rp.re_nsub) {
     99       lhs->i = 0;
    100       lhs->s = 0;
    101     } else lhs->s = "";
    102   }
    103 }
    104 
    105 static void mod(struct value *lhs, struct value *rhs)
    106 {
    107   if (lhs->s || rhs->s) error_exit("non-integer argument");
    108   if (is_zero(rhs)) error_exit("division by zero");
    109   lhs->i %= rhs->i;
    110 }
    111 
    112 static void divi(struct value *lhs, struct value *rhs)
    113 {
    114   if (lhs->s || rhs->s) error_exit("non-integer argument");
    115   if (is_zero(rhs)) error_exit("division by zero");
    116   lhs->i /= rhs->i;
    117 }
    118 
    119 static void mul(struct value *lhs, struct value *rhs)
    120 {
    121   if (lhs->s || rhs->s) error_exit("non-integer argument");
    122   lhs->i *= rhs->i;
    123 }
    124 
    125 static void sub(struct value *lhs, struct value *rhs)
    126 {
    127   if (lhs->s || rhs->s) error_exit("non-integer argument");
    128   lhs->i -= rhs->i;
    129 }
    130 
    131 static void add(struct value *lhs, struct value *rhs)
    132 {
    133   if (lhs->s || rhs->s) error_exit("non-integer argument");
    134   lhs->i += rhs->i;
    135 }
    136 
    137 static void ne(struct value *lhs, struct value *rhs)
    138 {
    139   lhs->i = cmp(lhs, rhs) != 0;
    140   lhs->s = NULL;
    141 }
    142 
    143 static void lte(struct value *lhs, struct value *rhs)
    144 {
    145   lhs->i = cmp(lhs, rhs) <= 0;
    146   lhs->s = NULL;
    147 }
    148 
    149 static void lt(struct value *lhs, struct value *rhs)
    150 {
    151   lhs->i = cmp(lhs, rhs) < 0;
    152   lhs->s = NULL;
    153 }
    154 
    155 static void gte(struct value *lhs, struct value *rhs)
    156 {
    157   lhs->i = cmp(lhs, rhs) >= 0;
    158   lhs->s = NULL;
    159 }
    160 
    161 static void gt(struct value *lhs, struct value *rhs)
    162 {
    163   lhs->i = cmp(lhs, rhs) > 0;
    164   lhs->s = NULL;
    165 }
    166 
    167 static void eq(struct value *lhs, struct value *rhs)
    168 {
    169   lhs->i = !cmp(lhs, rhs);
    170   lhs->s = NULL;
    171 }
    172 
    173 static void and(struct value *lhs, struct value *rhs)
    174 {
    175   if (is_zero(lhs) || is_zero(rhs)) {
    176     lhs->i = 0;
    177     lhs->s = NULL;
    178   }
    179 }
    180 
    181 static void or(struct value *lhs, struct value *rhs)
    182 {
    183   if (is_zero(lhs)) *lhs = *rhs;
    184 }
    185 
    186 static void get_value(struct value *v)
    187 {
    188   char *endp, *arg;
    189 
    190   if (TT.argidx == toys.optc) {
    191     v->i = 0;
    192     v->s = ""; // signal end of expression
    193     return;
    194   }
    195 
    196 //  can't happen, the increment is after the == test
    197 //  if (TT.argidx >= toys.optc) error_exit("syntax error");
    198 
    199   arg = toys.optargs[TT.argidx++];
    200 
    201   v->i = strtoll(arg, &endp, 10);
    202   v->s = *endp ? arg : NULL;
    203 }
    204 
    205 // check if v matches a token, and consume it if so
    206 static int match(struct value *v, char *tok)
    207 {
    208   if (v->s && !strcmp(v->s, tok)) {
    209     get_value(v);
    210     return 1;
    211   }
    212 
    213   return 0;
    214 }
    215 
    216 // operators in order of increasing precedence
    217 static struct op {
    218   char *tok;
    219 
    220   // calculate "lhs op rhs" (e.g. lhs + rhs) and store result in lhs
    221   void (*calc)(struct value *lhs, struct value *rhs);
    222 } ops[] = {
    223   {"|",   or  }, {"&",   and }, {"=",   eq  }, {"==",  eq  }, {">",   gt  },
    224   {">=",  gte }, {"<",   lt  }, {"<=",  lte }, {"!=",  ne  }, {"+",   add },
    225   {"-",   sub }, {"*",   mul }, {"/",   divi}, {"%",   mod }, {":",   re  },
    226   {"(",   NULL}, // special case - must be last
    227 };
    228 
    229 // "|,&,= ==> >=< <= !=,+-,*/%,:"
    230 
    231 static void parse_op(struct value *lhs, struct value *tok, struct op *op)
    232 {
    233   if (!op) op = ops;
    234 
    235   // special case parsing for parentheses
    236   if (*op->tok == '(') {
    237     if (match(tok, "(")) {
    238       parse_op(lhs, tok, 0);
    239       if (!match(tok, ")")) error_exit("syntax error"); // missing closing paren
    240     } else {
    241       // tok is a string or integer - return it and get the next token
    242       *lhs = *tok;
    243       get_value(tok);
    244     }
    245 
    246     return;
    247   }
    248 
    249   parse_op(lhs, tok, op + 1);
    250   while (match(tok, op->tok)) {
    251     struct value rhs;
    252     parse_op(&rhs, tok, op + 1);
    253     if (rhs.s && !*rhs.s) error_exit("syntax error"); // premature end of expression
    254     op->calc(lhs, &rhs);
    255   }
    256 }
    257 
    258 void expr_main(void)
    259 {
    260   struct value tok, ret = {0};
    261 
    262   toys.exitval = 2; // if exiting early, indicate invalid expression
    263 
    264   TT.argidx = 0;
    265 
    266   get_value(&tok); // warm up the parser with the initial value
    267   parse_op(&ret, &tok, 0);
    268 
    269   // final token should be end of expression
    270   if (!tok.s || *tok.s) error_exit("syntax error");
    271 
    272   if (ret.s) printf("%s\n", ret.s);
    273   else printf("%lld\n", ret.i);
    274 
    275   exit(is_zero(&ret));
    276 }
    277