1
0
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:
Stephen Dolan
2012-08-26 14:25:56 +01:00
parent befb103a12
commit a847d2250f
7 changed files with 82 additions and 15 deletions

View 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);

View 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) {

View 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;

View 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},

View 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)

View 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:

View 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]