mirror of
https://github.com/stedolan/jq.git
synced 2024-05-11 05:55:39 +00:00
2nd order functions
This commit is contained in:
@@ -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);
|
||||
|
62
c/compile.c
62
c/compile.c
@@ -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) {
|
||||
|
@@ -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;
|
||||
|
@@ -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},
|
||||
|
@@ -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)
|
||||
|
13
c/parser.y
13
c/parser.y
@@ -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:
|
||||
|
@@ -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]
|
||||
|
Reference in New Issue
Block a user