]> granicus.if.org Git - jq/commitdiff
Fold constants (fix #504)
authorNicolas Williams <nico@cryptonector.com>
Sun, 27 Jul 2014 22:33:22 +0000 (17:33 -0500)
committerNicolas Williams <nico@cryptonector.com>
Sun, 27 Jul 2014 22:48:49 +0000 (17:48 -0500)
compile.c
compile.h
parser.y
tests/run

index 0b0208ee70fae2f2d11f7af3199191a50b4c3ada..03d03c3ed63e5fea2ca39086c5f2d296f001fee4 100644 (file)
--- a/compile.c
+++ b/compile.c
@@ -97,7 +97,7 @@ static block inst_block(inst* i) {
   return b;
 }
 
-static int block_is_single(block b) {
+int block_is_single(block b) {
   return b.first && b.first == b.last;
 }
 
@@ -131,6 +131,10 @@ block gen_noop() {
   return b;
 }
 
+int block_is_noop(block b) {
+  return (b.first == 0 && b.last == 0);
+}
+
 block gen_op_simple(opcode op) {
   assert(opcode_describe(op)->length == 1);
   return inst_block(inst_new(op));
@@ -144,6 +148,20 @@ block gen_const(jv constant) {
   return inst_block(i);
 }
 
+int block_is_const(block b) {
+  return (block_is_single(b) && b.first->op == LOADK);
+}
+
+jv_kind block_const_kind(block b) {
+  assert(block_is_const(b));
+  return jv_get_kind(b.first->imm.constant);
+}
+
+jv block_const(block b) {
+  assert(block_is_const(b));
+  return jv_copy(b.first->imm.constant);
+}
+
 block gen_op_target(opcode op, block target) {
   assert(opcode_describe(op)->flags & OP_HAS_BRANCH);
   assert(target.last);
index aba6a4d268813466b1f45a16eb0758a629147052..a487c14c06aeb16937a5f16428130cb6ff7bd702 100644 (file)
--- a/compile.h
+++ b/compile.h
@@ -17,8 +17,12 @@ typedef struct block {
 block gen_location(location, struct locfile*, block);
 
 block gen_noop();
+int block_is_noop(block b);
 block gen_op_simple(opcode op);
 block gen_const(jv constant);
+int block_is_const(block b);
+jv_kind block_const_kind(block b);
+jv block_const(block b);
 block gen_op_target(opcode op, block target);
 block gen_op_unbound(opcode op, const char* name);
 block gen_op_bound(opcode op, block binder);
@@ -52,6 +56,7 @@ int block_has_only_binders_and_imports(block, int bindflags);
 int block_has_only_binders(block, int bindflags);
 int block_has_main(block);
 int block_is_funcdef(block b);
+int block_is_single(block b);
 block block_bind(block binder, block body, int bindflags);
 block block_bind_library(block binder, block body, int bindflags, const char* libname);
 block block_bind_referenced(block binder, block body, int bindflags);
index 4b4ad2468f3c6127b57e65bdd304ae9a4ce5a27d..0117e1a2c1733768d2e0d329ab51f04dffd79fcd 100644 (file)
--- a/parser.y
+++ b/parser.y
@@ -162,7 +162,50 @@ static block gen_slice_index(block obj, block start, block end, opcode idx_op) {
   return BLOCK(key, obj, gen_op_simple(idx_op));
 }
 
+static block constant_fold(block a, block b, int op) {
+  if (!block_is_single(a) || !block_is_const(a) ||
+      !block_is_single(b) || !block_is_const(b))
+    return gen_noop();
+  if (block_const_kind(a) != block_const_kind(b))
+    return gen_noop();
+
+  jv res = jv_invalid();
+
+  if (block_const_kind(a) == JV_KIND_NUMBER) {
+    double na = jv_number_value(block_const(a));
+    double nb = jv_number_value(block_const(b));
+    switch (op) {
+    case '+': res = jv_number(na + nb); break;
+    case '-': res = jv_number(na - nb); break;
+    case '*': res = jv_number(na * nb); break;
+    case '/': res = jv_number(na / nb); break;
+    case EQ:  res = (na == nb ? jv_true() : jv_false()); break;
+    case NEQ: res = (na != nb ? jv_true() : jv_false()); break;
+    case '<': res = (na < nb ? jv_true() : jv_false()); break;
+    case '>': res = (na > nb ? jv_true() : jv_false()); break;
+    case LESSEQ: res = (na <= nb ? jv_true() : jv_false()); break;
+    case GREATEREQ: res = (na >= nb ? jv_true() : jv_false()); break;
+    default: break;
+    }
+  } else if (op == '+' && block_const_kind(a) == JV_KIND_STRING) {
+    res = jv_string_concat(block_const(a),  block_const(b));
+  } else {
+    return gen_noop();
+  }
+
+  if (jv_get_kind(res) == JV_KIND_INVALID)
+    return gen_noop();
+
+  block_free(a);
+  block_free(b);
+  return gen_const(res);
+}
+
 static block gen_binop(block a, block b, int op) {
+  block folded = constant_fold(a, b, op);
+  if (!block_is_noop(folded))
+    return folded;
+
   const char* funcname = 0;
   switch (op) {
   case '+': funcname = "_plus"; break;
index a7ef7df3760a97a4a06262482f133fa246c514dd..923478c6ae6da66875ebab899544d8a3508e9c90 100755 (executable)
--- a/tests/run
+++ b/tests/run
@@ -20,6 +20,59 @@ if [ -z "$d" ]; then
     exit 0
 fi
 
+## Test constant folding
+
+# String constant folding (addition only)
+n=`$VALGRIND $Q ./jq -n --debug-dump-disasm '"foo"' | wc -l`
+if [ $n -ne 5 ]; then
+    echo "Constant expression folding for strings didn't work"
+    exit 1
+fi
+
+# Numeric constant folding (not all ops yet)
+n=`$VALGRIND $Q ./jq -n --debug-dump-disasm '1+1' | wc -l`
+if [ $n -ne 5 ]; then
+    echo "Constant expression folding for strings didn't work"
+    exit 1
+fi
+n=`$VALGRIND $Q ./jq -n --debug-dump-disasm '1-1' | wc -l`
+if [ $n -ne 5 ]; then
+    echo "Constant expression folding for strings didn't work"
+    exit 1
+fi
+n=`$VALGRIND $Q ./jq -n --debug-dump-disasm '2*3' | wc -l`
+if [ $n -ne 5 ]; then
+    echo "Constant expression folding for strings didn't work"
+    exit 1
+fi
+n=`$VALGRIND $Q ./jq -n --debug-dump-disasm '9/3' | wc -l`
+if [ $n -ne 5 ]; then
+    echo "Constant expression folding for strings didn't work"
+    exit 1
+fi
+n=`$VALGRIND $Q ./jq -n --debug-dump-disasm '9==3' | wc -l`
+if [ $n -ne 5 ]; then
+    echo "Constant expression folding for strings didn't work"
+    exit 1
+fi
+n=`$VALGRIND $Q ./jq -n --debug-dump-disasm '9!=3' | wc -l`
+if [ $n -ne 5 ]; then
+    echo "Constant expression folding for strings didn't work"
+    exit 1
+fi
+n=`$VALGRIND $Q ./jq -n --debug-dump-disasm '9<=3' | wc -l`
+if [ $n -ne 5 ]; then
+    echo "Constant expression folding for strings didn't work"
+    exit 1
+fi
+n=`$VALGRIND $Q ./jq -n --debug-dump-disasm '9>=3' | wc -l`
+if [ $n -ne 5 ]; then
+    echo "Constant expression folding for strings didn't work"
+    exit 1
+fi
+
+## Test library/module system
+
 cat > "$d/.jq" <<EOF
 def foo: "baz";
 def f: "wat";