]> granicus.if.org Git - jq/commitdiff
2nd order functions
authorStephen Dolan <mu@netsoc.tcd.ie>
Sun, 26 Aug 2012 13:25:56 +0000 (14:25 +0100)
committerStephen Dolan <mu@netsoc.tcd.ie>
Sun, 26 Aug 2012 13:25:56 +0000 (14:25 +0100)
c/bytecode.c
c/compile.c
c/execute.c
c/opcode.c
c/opcode_list.h
c/parser.y
c/testdata

index c85be8046c28c102e64a2ae1e3af76d2bd7aa5ea..ef585bd76da8e25cc95784fadc99cd820d5a101e 100644 (file)
@@ -13,6 +13,9 @@ static int bytecode_operation_length(uint16_t* codeptr) {
 }
 
 void dump_disassembly(int indent, struct bytecode* bc) {
+  if (bc->nclosures > 0) {
+    printf("%*s[params: %d]\n", indent, "", bc->nclosures);
+  }
   dump_code(indent, bc);
   for (int i=0; i<bc->nsubfunctions; i++) {
     printf("%*ssubfn[%d]:\n", indent, "", i);
index 71988c4ad7e2decf5376c0e56573e359dc3ce207..d2b9cee7ca44b8a9adc6bc5ad98ea770f815653a 100644 (file)
@@ -59,6 +59,24 @@ static block inst_block(inst* i) {
   return b;
 }
 
+static int block_is_single(block b) {
+  return b.first && b.first == b.last;
+}
+
+static inst* block_take(block* b) {
+  if (b->first == 0) return 0;
+  inst* i = b->first;
+  if (i->next) {
+    i->next->prev = 0;
+    b->first = i->next;
+    i->next = 0;
+  } else {
+    b->first = 0;
+    b->last = 0;
+  }
+  return i;
+}
+
 block gen_noop() {
   block b = {0,0};
   return b;
@@ -92,8 +110,7 @@ block gen_op_targetlater(opcode op) {
   return inst_block(i);
 }
 void inst_set_target(block b, block target) {
-  assert(b.first);
-  assert(b.first == b.last);
+  assert(block_is_single(b));
   assert(opcode_describe(b.first->op)->flags & OP_HAS_BRANCH);
   assert(target.last);
   b.first->imm.target = target.last;
@@ -107,9 +124,7 @@ block gen_op_var_unbound(opcode op, const char* name) {
 }
 
 block gen_op_var_bound(opcode op, block binder) {
-  assert(opcode_describe(op)->flags & OP_HAS_VARIABLE);
-  assert(binder.first);
-  assert(binder.first == binder.last);
+  assert(block_is_single(binder));
   block b = gen_op_var_unbound(op, binder.first->symbol);
   b.first->bound_by = binder.first;
   return b;
@@ -138,18 +153,38 @@ block gen_op_block_unbound(opcode op, const char* name) {
   return inst_block(i);
 }
 
+block gen_op_block_bound(opcode op, block binder) {
+  assert(block_is_single(binder));
+  block b = gen_op_block_unbound(op, binder.first->symbol);
+  b.first->bound_by = binder.first;
+  return b;
+}
 
 block gen_op_call(opcode op, block arglist) {
   assert(opcode_describe(op)->flags & OP_HAS_VARIABLE_LENGTH_ARGLIST);
   inst* i = inst_new(op);
+  block prelude = gen_noop();
+  block call = inst_block(i);
   int nargs = 0;
-  for (inst* curr = arglist.first; curr; curr = curr->next) {
+  inst* curr = 0;
+  while ((curr = block_take(&arglist))) {
     assert(opcode_describe(curr->op)->flags & OP_IS_CALL_PSEUDO);
+    block bcurr = inst_block(curr);
+    switch (curr->op) {
+    default: assert(0 && "Unknown type of parameter"); break;
+    case CLOSURE_REF:
+      block_append(&call, bcurr);
+      break;
+    case CLOSURE_CREATE:
+      block_append(&prelude, bcurr);
+      block_append(&call, gen_op_block_bound(CLOSURE_REF, bcurr));
+      break;
+    }
     nargs++;
   }
   assert(nargs < 100); //FIXME
   i->imm.intval = nargs;
-  return block_join(inst_block(i), arglist);
+  return block_join(prelude, call);
 }
 
 static void inst_join(inst* a, inst* b) {
@@ -178,8 +213,7 @@ block block_join(block a, block b) {
 }
 
 static void block_bind_subblock(block binder, block body, int bindflags) {
-  assert(binder.first);
-  assert(binder.first == binder.last);
+  assert(block_is_single(binder));
   assert((opcode_describe(binder.first->op)->flags & bindflags) == bindflags);
   assert(binder.first->symbol);
   assert(binder.first->bound_by == 0 || binder.first->bound_by == binder.first);
@@ -194,6 +228,7 @@ static void block_bind_subblock(block binder, block body, int bindflags) {
       i->bound_by = binder.first;
     }
     if (flags & OP_HAS_BLOCK) {
+      // binding recurses into closures
       block_bind_subblock(binder, i->subfn, bindflags);
     }
   }
@@ -272,6 +307,7 @@ static void compile(struct bytecode* bc, block b) {
   int pos = 0;
   int var_frame_idx = 0;
   bc->nsubfunctions = 0;
+  bc->nclosures = 0;
   for (inst* curr = b.first; curr; curr = curr->next) {
     if (!curr->next) assert(curr == b.last);
     pos += opcode_length(curr->op);
@@ -290,6 +326,10 @@ static void compile(struct bytecode* bc, block b) {
       assert(curr->bound_by == curr);
       curr->imm.intval = bc->nsubfunctions++;
     }
+    if (curr->op == CLOSURE_PARAM) {
+      assert(curr->bound_by == curr);
+      curr->imm.intval = bc->nclosures++;
+    }
   }
   if (bc->nsubfunctions) {
     bc->subfunctions = malloc(sizeof(struct bytecode*) * bc->nsubfunctions);
@@ -331,6 +371,9 @@ static void compile(struct bytecode* bc, block b) {
         case CLOSURE_CREATE:
           code[pos++] = curr->bound_by->imm.intval | ARG_NEWCLOSURE;
           break;
+        case CLOSURE_PARAM:
+          code[pos++] = curr->bound_by->imm.intval;
+          break;
         }
       }
     } else if (opflags & OP_HAS_CONSTANT) {
@@ -363,7 +406,6 @@ static void compile(struct bytecode* bc, block b) {
   }
   bc->constants = constant_pool;
   bc->nlocals = maxvar + 2; // FIXME: frames of size zero?
-  bc->nclosures = 0;
 }
 
 struct bytecode* block_compile(struct symbol_table* syms, block b) {
index a8dfc5e797e0c2f39a7d0da3e369bfb0d933eff1..9d952d6b8198ed396d52a4b33641751102189b38 100644 (file)
@@ -357,6 +357,7 @@ json_t* jq_next() {
                                        pc + nclosures * 2);
       pc += 2;
       frame_ptr old_frame = forkable_stack_peek_next(&frame_stk, new_frame);
+      assert(nclosures - 1 == frame_self(new_frame)->bc->nclosures);
       for (int i=0; i<nclosures-1; i++) {
         *frame_closure_arg(new_frame, i) = make_closure(&frame_stk, old_frame, pc);
         pc += 2;
index 5a7bfb2cceb4d81b6009e35ac80f90946749961b..62624c92eab530d7e191e825d328c1f52a0d22db 100644 (file)
@@ -7,8 +7,9 @@
 #define BRANCH OP_HAS_BRANCH, 2
 #define CFUNC (OP_HAS_SYMBOL | OP_HAS_CFUNC), 2
 #define UFUNC (OP_HAS_UFUNC | OP_HAS_VARIABLE_LENGTH_ARGLIST), 2
-#define CLOSURE_DEFINE (OP_HAS_BLOCK | OP_IS_CALL_PSEUDO | OP_HAS_BINDING), 0
-#define CLOSURE_ACCESS (OP_IS_CALL_PSEUDO | OP_HAS_BINDING), 2
+#define CLOSURE_PARAM_IMM (OP_IS_CALL_PSEUDO | OP_HAS_BINDING), 0
+#define CLOSURE_CREATE_IMM (OP_HAS_BLOCK | OP_IS_CALL_PSEUDO | OP_HAS_BINDING), 0
+#define CLOSURE_REF_IMM (OP_IS_CALL_PSEUDO | OP_HAS_BINDING), 2
 
 #define OP(name, imm, in, out) \
   {name, #name, imm, in, out},
index 497ae88a25f268a1fb535f7a7ba7c6f51bfa5fb0..1d6d277366b530389ec9a1b242733e300448f824 100644 (file)
@@ -20,6 +20,6 @@ OP(CALL_BUILTIN_3_1, CFUNC, 3, 1)
 OP(CALL_1_1, UFUNC, 1, 1)
 OP(RET, NONE, 1, 1)
 
-OP(CLOSURE_PARAM, CLOSURE_ACCESS, 0, 0)
-OP(CLOSURE_REF, CLOSURE_ACCESS, 0, 0)
-OP(CLOSURE_CREATE, CLOSURE_DEFINE, 0, 0)
+OP(CLOSURE_PARAM, CLOSURE_PARAM_IMM, 0, 0)
+OP(CLOSURE_REF, CLOSURE_REF_IMM, 0, 0)
+OP(CLOSURE_CREATE, CLOSURE_CREATE_IMM, 0, 0)
index ca82fcd5f9cebdba61bce7d1ca704a1baf9c616a..ebc90a32176f6be242b5e0db1b5542981ea07bf2 100644 (file)
@@ -65,6 +65,11 @@ Exp:
   $$ = block_bind(gen_op_block_defn(CLOSURE_CREATE, $2, body), $6, OP_IS_CALL_PSEUDO);
 } |
 
+"def" IDENT '(' IDENT ')' ':' Exp ';' Exp {
+  block body = block_bind(gen_op_block_unbound(CLOSURE_PARAM, $4), block_join($7, gen_op_simple(RET)), OP_IS_CALL_PSEUDO);
+  $$ = block_bind(gen_op_block_defn(CLOSURE_CREATE, $2, body), $9, OP_IS_CALL_PSEUDO);
+} |
+
 Term "as" '$' IDENT '|' Exp {
   $$ = gen_op_simple(DUP);
   block_append(&$$, $1);
@@ -143,6 +148,14 @@ IDENT {
 } | 
 '$' '$' IDENT {
   $$ = gen_op_call(CALL_1_1, gen_op_block_unbound(CLOSURE_REF, $3));
+} |
+'$' '$' IDENT '(' Exp ')' {
+  $$ = gen_op_call(CALL_1_1, 
+                   block_join(gen_op_block_unbound(CLOSURE_REF, $3),
+                              block_bind(gen_op_block_defn(CLOSURE_CREATE,
+                                                "lambda",
+                                                           block_join($5, gen_op_simple(RET))),
+                                         gen_noop(), OP_IS_CALL_PSEUDO)));
 }
 
 MkDict:
index e22f09ce006536a9de7f62287c1730b753eafe56..f2bd4ee31336766280b0551db8762bb0da8bcb8a 100644 (file)
@@ -146,3 +146,10 @@ def f: (1000,2000); $$f
 [[20,10][1,0] as $x | def f: (100,200) as $y | def g: [$x + $y, .]; . + $x | $$g; $$f[0] | [$$f][0][1] | $$f]
 "woo, testing!"
 [[110.0, 130.0], [210.0, 130.0], [110.0, 230.0], [210.0, 230.0], [120.0, 160.0], [220.0, 160.0], [120.0, 260.0], [220.0, 260.0]]
+
+def f(x): $$x | $$x; $$f([.], . + [42])
+[1,2,3]
+[[[1,2,3]]]
+[[1,2,3],42]
+[[1,2,3,42]]
+[1,2,3,42,42]