}
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);
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;
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;
}
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;
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) {
}
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);
i->bound_by = binder.first;
}
if (flags & OP_HAS_BLOCK) {
+ // binding recurses into closures
block_bind_subblock(binder, i->subfn, bindflags);
}
}
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);
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);
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) {
}
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) {
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;
#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},
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)
$$ = 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);
} |
'$' '$' 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:
[[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]