changeset 1590:d5931155cafe draft

Refactor expr and add another test entry that works with TEST_HOST=1 but not with the one in pending.
author Rob Landley <rob@landley.net>
date Mon, 01 Dec 2014 03:15:25 -0600
parents a97f69a1e406
children a016421051e4
files tests/expr.test toys/pending/expr.c
diffstat 2 files changed, 77 insertions(+), 107 deletions(-) [+]
line wrap: on
line diff
--- a/tests/expr.test	Sun Nov 30 14:10:36 2014 -0600
+++ b/tests/expr.test	Mon Dec 01 03:15:25 2014 -0600
@@ -17,3 +17,5 @@
 testing "expr * % same priority" "expr 3 \* 2 % 4" "2\n" "" ""
 testing "expr = > same priority" "expr 0 = 2 \> 3" "0\n" "" ""
 testing "expr > = same priority" "expr 3 \> 2 = 1" "1\n" "" ""
+testing "expr string becomes integer" "expr ab21xx : '[^0-9]*\([0-9]*\)' + 3" \
+	"24\n" "" ""
--- a/toys/pending/expr.c	Sun Nov 30 14:10:36 2014 -0600
+++ b/toys/pending/expr.c	Mon Dec 01 03:15:25 2014 -0600
@@ -16,7 +16,7 @@
 
     The supported operators, in order of increasing precedence, are:
 
-    | & = > >= < <= != + - * / %
+    | & = > >= < <= != + - * / % :
 
     In addition, parentheses () are supported for grouping.
 */
@@ -39,44 +39,10 @@
   long long i;
 };
 
-static void parse_expr(struct value *ret, struct value *v);
-
-static void get_value(struct value *v)
+// check if v is the integer 0 or the empty string
+static int is_zero(struct value *v)
 {
-  char *endp, *arg;
-
-  if (TT.argidx == toys.optc) {
-    v->i = 0;
-    v->s = ""; // signal end of expression
-    return;
-  }
-
-  if (TT.argidx >= toys.optc) {
-    error_exit("syntax error");
-  }
-
-  arg = toys.optargs[TT.argidx++];
-
-  v->i = strtoll(arg, &endp, 10);
-  v->s = *endp ? arg : NULL;
-}
-
-
-// check if v matches a token, and consume it if so
-static int match(struct value *v, const char *tok)
-{
-  if (v->s && !strcmp(v->s, tok)) {
-    get_value(v);
-    return 1;
-  }
-
-  return 0;
-}
-
-// check if v is the integer 0 or the empty string
-static int is_zero(const struct value *v)
-{
-  return ((v->s && *v->s == '\0') || v->i == 0);
+  return v->s ? !*v->s : !v->i;
 }
 
 static char *num_to_str(long long num)
@@ -86,30 +52,17 @@
   return num_buf;
 }
 
-static int cmp(const struct value *lhs, const struct value *rhs)
+static int cmp(struct value *lhs, struct value *rhs)
 {
   if (lhs->s || rhs->s) {
     // at least one operand is a string
     char *ls = lhs->s ? lhs->s : num_to_str(lhs->i);
     char *rs = rhs->s ? rhs->s : num_to_str(rhs->i);
     return strcmp(ls, rs);
-  } else {
-    return lhs->i - rhs->i;
-  }
+  } else return lhs->i - rhs->i;
 }
 
-
-// operators
-
-struct op {
-  const char *tok;
-
-  // calculate "lhs op rhs" (e.g. lhs + rhs) and store result in lhs
-  void (*calc)(struct value *lhs, const struct value *rhs);
-};
-
-
-static void re(struct value *lhs, const struct value *rhs)
+static void re(struct value *lhs, struct value *rhs)
 {
   regex_t rp;
   regmatch_t rm[2];
@@ -130,75 +83,75 @@
   }
 }
 
-static void mod(struct value *lhs, const struct value *rhs)
+static void mod(struct value *lhs, struct value *rhs)
 {
   if (lhs->s || rhs->s) error_exit("non-integer argument");
   if (is_zero(rhs)) error_exit("division by zero");
   lhs->i %= rhs->i;
 }
 
-static void divi(struct value *lhs, const struct value *rhs)
+static void divi(struct value *lhs, struct value *rhs)
 {
   if (lhs->s || rhs->s) error_exit("non-integer argument");
   if (is_zero(rhs)) error_exit("division by zero");
   lhs->i /= rhs->i;
 }
 
-static void mul(struct value *lhs, const struct value *rhs)
+static void mul(struct value *lhs, struct value *rhs)
 {
   if (lhs->s || rhs->s) error_exit("non-integer argument");
   lhs->i *= rhs->i;
 }
 
-static void sub(struct value *lhs, const struct value *rhs)
+static void sub(struct value *lhs, struct value *rhs)
 {
   if (lhs->s || rhs->s) error_exit("non-integer argument");
   lhs->i -= rhs->i;
 }
 
-static void add(struct value *lhs, const struct value *rhs)
+static void add(struct value *lhs, struct value *rhs)
 {
   if (lhs->s || rhs->s) error_exit("non-integer argument");
   lhs->i += rhs->i;
 }
 
-static void ne(struct value *lhs, const struct value *rhs)
+static void ne(struct value *lhs, struct value *rhs)
 {
   lhs->i = cmp(lhs, rhs) != 0;
   lhs->s = NULL;
 }
 
-static void lte(struct value *lhs, const struct value *rhs)
+static void lte(struct value *lhs, struct value *rhs)
 {
   lhs->i = cmp(lhs, rhs) <= 0;
   lhs->s = NULL;
 }
 
-static void lt(struct value *lhs, const struct value *rhs)
+static void lt(struct value *lhs, struct value *rhs)
 {
   lhs->i = cmp(lhs, rhs) < 0;
   lhs->s = NULL;
 }
 
-static void gte(struct value *lhs, const struct value *rhs)
+static void gte(struct value *lhs, struct value *rhs)
 {
   lhs->i = cmp(lhs, rhs) >= 0;
   lhs->s = NULL;
 }
 
-static void gt(struct value *lhs, const struct value *rhs)
+static void gt(struct value *lhs, struct value *rhs)
 {
   lhs->i = cmp(lhs, rhs) > 0;
   lhs->s = NULL;
 }
 
-static void eq(struct value *lhs, const struct value *rhs)
+static void eq(struct value *lhs, struct value *rhs)
 {
-  lhs->i = cmp(lhs, rhs) == 0;
+  lhs->i = !cmp(lhs, rhs);
   lhs->s = NULL;
 }
 
-static void and(struct value *lhs, const struct value *rhs)
+static void and(struct value *lhs, struct value *rhs)
 {
   if (is_zero(lhs) || is_zero(rhs)) {
     lhs->i = 0;
@@ -206,52 +159,71 @@
   }
 }
 
-static void or(struct value *lhs, const struct value *rhs)
+static void or(struct value *lhs, struct value *rhs)
 {
-  if (is_zero(lhs)) {
-    *lhs = *rhs;
-  }
+  if (is_zero(lhs)) *lhs = *rhs;
 }
 
+static void get_value(struct value *v)
+{
+  char *endp, *arg;
+
+  if (TT.argidx == toys.optc) {
+    v->i = 0;
+    v->s = ""; // signal end of expression
+    return;
+  }
+
+//  can't happen, the increment is after the == test
+//  if (TT.argidx >= toys.optc) error_exit("syntax error");
+
+  arg = toys.optargs[TT.argidx++];
+
+  v->i = strtoll(arg, &endp, 10);
+  v->s = *endp ? arg : NULL;
+}
+
+// check if v matches a token, and consume it if so
+static int match(struct value *v, char *tok)
+{
+  if (v->s && !strcmp(v->s, tok)) {
+    get_value(v);
+    return 1;
+  }
+
+  return 0;
+}
 
 // operators in order of increasing precedence
-static const struct op ops[] = {
-  {"|",   or  },
-  {"&",   and },
-  {"=",   eq  },
-  {"==",  eq  },
-  {">",   gt  },
-  {">=",  gte },
-  {"<",   lt  },
-  {"<=",  lte },
-  {"!=",  ne  },
-  {"+",   add },
-  {"-",   sub },
-  {"*",   mul },
-  {"/",   divi},
-  {"%",   mod },
-  {":",   re  },
+static struct op {
+  char *tok;
+
+  // calculate "lhs op rhs" (e.g. lhs + rhs) and store result in lhs
+  void (*calc)(struct value *lhs, struct value *rhs);
+} ops[] = {
+  {"|",   or  }, {"&",   and }, {"=",   eq  }, {"==",  eq  }, {">",   gt  },
+  {">=",  gte }, {"<",   lt  }, {"<=",  lte }, {"!=",  ne  }, {"+",   add },
+  {"-",   sub }, {"*",   mul }, {"/",   divi}, {"%",   mod }, {":",   re  },
   {"(",   NULL}, // special case - must be last
 };
 
+// "|,&,= ==> >=< <= !=,+-,*/%,:"
 
-static void parse_parens(struct value *ret, struct value *v)
+static void parse_op(struct value *lhs, struct value *tok, struct op *op)
 {
-  if (match(v, "(")) {
-    parse_expr(ret, v);
-    if (!match(v, ")")) error_exit("syntax error"); // missing closing paren
-  } else {
-    // v is a string or integer - return it and get the next token
-    *ret = *v;
-    get_value(v);
-  }
-}
+  if (!op) op = ops;
 
-static void parse_op(struct value *lhs, struct value *tok, const struct op *op)
-{
   // special case parsing for parentheses
   if (*op->tok == '(') {
-    parse_parens(lhs, tok);
+    if (match(tok, "(")) {
+      parse_op(lhs, tok, 0);
+      if (!match(tok, ")")) error_exit("syntax error"); // missing closing paren
+    } else {
+      // tok is a string or integer - return it and get the next token
+      *lhs = *tok;
+      get_value(tok);
+    }
+
     return;
   }
 
@@ -264,11 +236,6 @@
   }
 }
 
-static void parse_expr(struct value *ret, struct value *v)
-{
-  parse_op(ret, v, ops); // start at the top of the ops table
-}
-
 void expr_main(void)
 {
   struct value tok, ret = {0};
@@ -278,9 +245,10 @@
   TT.argidx = 0;
 
   get_value(&tok); // warm up the parser with the initial value
-  parse_expr(&ret, &tok);
+  parse_op(&ret, &tok, 0);
 
-  if (!tok.s || *tok.s) error_exit("syntax error"); // final token should be end of expression
+  // final token should be end of expression
+  if (!tok.s || *tok.s) error_exit("syntax error");
 
   if (ret.s) printf("%s\n", ret.s);
   else printf("%lld\n", ret.i);