]> granicus.if.org Git - jq/commitdiff
if-then-else and defined-or operators
authorStephen Dolan <mu@netsoc.tcd.ie>
Tue, 4 Sep 2012 14:34:34 +0000 (15:34 +0100)
committerStephen Dolan <mu@netsoc.tcd.ie>
Tue, 4 Sep 2012 14:34:34 +0000 (15:34 +0100)
c/compile.c
c/compile.h
c/execute.c
c/lexer.l
c/opcode_list.h
c/parser.y
c/testdata

index b7fcc4f2b258ed35f632a26e69f0919825ab1bd4..c83a5583dd5a96f11ad39b814d5c3f0e8949e724 100644 (file)
@@ -312,8 +312,51 @@ block gen_assign(block expr) {
   return c;
 }
 
-block gen_else(block a, block b) {
-  assert(0);
+block gen_definedor(block a, block b) {
+  // var found := false
+  block c = gen_op_simple(DUP);
+  block_append(&c, gen_op_const(LOADK, jv_false()));
+  block found_var = block_bind(gen_op_var_unbound(STOREV, "found"),
+                               gen_noop(), OP_HAS_VARIABLE);
+  block_append(&c, found_var);
+
+  // if found, backtrack. Otherwise execute b
+  block tail = gen_op_simple(DUP);
+  block_append(&tail, gen_op_var_bound(LOADV, found_var));
+  block backtrack = gen_op_simple(BACKTRACK);
+  block_append(&tail, gen_op_target(JUMP_F, backtrack));
+  block_append(&tail, backtrack);
+  block_append(&tail, gen_op_simple(POP));
+  block_append(&tail, b);
+
+  // try again
+  block if_notfound = gen_op_simple(BACKTRACK);
+
+  // found := true, produce result
+  block if_found = gen_op_simple(DUP);
+  block_append(&if_found, gen_op_const(LOADK, jv_true()));
+  block_append(&if_found, gen_op_var_bound(STOREV, found_var));
+  block_append(&if_found, gen_op_target(JUMP, tail));
+
+  block_append(&c, gen_op_target(FORK, if_notfound));
+  block_append(&c, a);
+  block_append(&c, gen_op_target(JUMP_F, if_found));
+  block_append(&c, if_found);
+  block_append(&c, if_notfound);
+  block_append(&c, tail);
+
+  return c;
+}
+
+block gen_cond(block cond, block iftrue, block iffalse) {
+  block b = gen_op_simple(DUP);
+  block_append(&b, cond);
+
+  block_append(&iftrue, gen_op_target(JUMP, iffalse));
+  block_append(&b, gen_op_target(JUMP_F, iftrue));
+  block_append(&b, block_join(gen_op_simple(POP), iftrue));
+  block_append(&b, block_join(gen_op_simple(POP), iffalse));
+  return b;
 }
 
 block gen_cbinding(struct symbol_table* t, block code) {
index 5f0be89b8aa8d5de390889d3ae2429cd021356e7..5a2faf7b85569924319b71fdc921ae9e6cf7da41 100644 (file)
@@ -26,7 +26,9 @@ block gen_subexp(block a);
 block gen_both(block a, block b);
 block gen_collect(block expr);
 block gen_assign(block expr);
-block gen_else(block a, block b);
+block gen_definedor(block a, block b);
+
+block gen_cond(block cond, block iftrue, block iffalse);
 
 block gen_cbinding(struct symbol_table* functions, block b);
 
index b7818d12f57c64bf019ef2d81a7d1396adec5795..5e6a2c5121e274308bd42030ecdb651640a9d375 100644 (file)
@@ -59,6 +59,7 @@ typedef struct {
 } data_stk_elem;
 
 void stack_push(stackval val) {
+  assert(jv_is_valid(val.value));
   data_stk_elem* s = forkable_stack_push(&data_stk, sizeof(data_stk_elem));
   s->sv = val;
 }
@@ -70,6 +71,7 @@ stackval stack_pop() {
     sv.value = jv_copy(sv.value);
   }
   forkable_stack_pop(&data_stk);
+  assert(jv_is_valid(sv.value));
   return sv;
 }
 
@@ -292,6 +294,17 @@ jv jq_next() {
       break;
     }
 
+    case JUMP_F: {
+      uint16_t offset = *pc++;
+      stackval t = stack_pop();
+      jv_kind kind = jv_get_kind(t.value);
+      if (kind == JV_KIND_FALSE || kind == JV_KIND_NULL) {
+        pc += offset;
+      }
+      stack_push(t); // FIXME do this better
+      break;
+    }
+
     case EACH: 
       stack_push(stackval_root(jv_number(0)));
       // fallthrough
index 4acb026af7a6d4303915b48a113dc62273c92c0e..e3c140b6925447e1177edd5a8f50c46ff5112b3f 100644 (file)
--- a/c/lexer.l
+++ b/c/lexer.l
 "as" { return AS; }
 "def" { return DEF; }
 "|=" { return SETPIPE; }
+"if" { return IF; }
+"then" { return THEN; }
+"else" { return ELSE; }
+"end" { return END; }
+"//" { return DEFINEDOR; }
 "."|"="|";"|"["|"]"|","|":"|"("|")"|"{"|"}"|"|"|"+"|"\$" { return yytext[0];}
 
 \"(\\.|[^\\"])*\" |
index 7f2c144af9709c27f4afc5ec26cf93a9ae92f915..30e7203d6f4e4b39893083439f41c562ddf72a22 100644 (file)
@@ -10,6 +10,7 @@ OP(YIELD, NONE, 1, 0)
 OP(EACH,  NONE,     1, 1)
 OP(FORK,  BRANCH,   0, 0)
 OP(JUMP,  BRANCH,   0, 0)
+OP(JUMP_F,BRANCH,   1, 0)
 OP(BACKTRACK, NONE, 0, 0)
 OP(APPEND, NONE,    2, 1)
 OP(INSERT, NONE,    4, 2)
index 3629245397f378d6dd983c2c1ea17a92cd21b9e8..d4e8d8f43195e35556f268b0024c842840d60853 100644 (file)
 %left '|'
 %left ','
 %token EQ "=="
+%token DEFINEDOR "//"
 %token AS "as"
 %token DEF "def"
 %token SETPIPE "|="
+%token IF "if"
+%token THEN "then"
+%token ELSE "else"
+%token END "end"
+%right "//"
 %nonassoc '=' SETPIPE
 %nonassoc EQ
 %left '+'
 
+
 %type <blk> Exp Term MkDict MkDictPair ExpD
 
 %{
@@ -78,6 +85,10 @@ Term "as" '$' IDENT '|' Exp {
   jv_free($4);
 } |
 
+"if" Exp "then" Exp "else" Exp "end" {
+  $$ = gen_cond($2, $4, $6);
+} |
+
 Exp '=' Exp {
   block assign = gen_op_simple(DUP);
   block_append(&assign, $3);
@@ -87,6 +98,10 @@ Exp '=' Exp {
   $$ = gen_assign(assign);
 } |
 
+Exp "//" Exp {
+  $$ = gen_definedor($1, $3);
+} |
+
 Exp "|=" Exp {
   block assign = $1;
   block_append(&assign, gen_op_simple(DUP));
index 6ddf09fdee7b88a97d7473dde11304990b056230..888d8b74d2e3944042414feeb00797f27a15c4d2 100644 (file)
@@ -234,3 +234,18 @@ def inc(x): x |= .+1; inc(.[].a)
 .foo[2].bar = 1
 {"foo":[11], "bar":42}
 {"foo":[11,null,{"bar":1}], "bar":42}
+
+#
+# Conditionals
+#
+
+[.[] | if .foo then "yep" else "nope" end]
+[{"foo":0},{"foo":1},{"foo":[]},{"foo":true},{"foo":false},{"foo":null},{"foo":"foo"},{}]
+["yep","yep","yep","yep","nope","nope","yep","nope"]
+
+# FIXME: define/test behaviour of 'if (.foo,.bar) then A else B end'
+
+[.[] | [.foo[] // .bar]]
+[{"foo":[1,2], "bar": 42}, {"foo":[1], "bar": null}, {"foo":[null,false,3], "bar": 18}, {"foo":[], "bar":42}, {"foo": [null,false,null], "bar": 41}]
+[[1,2], [1], [3], [42], [41]]
+