summaryrefslogtreecommitdiff
path: root/com.c
diff options
context:
space:
mode:
Diffstat (limited to 'com.c')
-rw-r--r--com.c101
1 files changed, 93 insertions, 8 deletions
diff --git a/com.c b/com.c
index 2c44883..cd097df 100644
--- a/com.c
+++ b/com.c
@@ -47,6 +47,7 @@ static int stack_effect_of(Op opcode) {
case OP_SETLOCAL:
case OP_RET:
case OP_HALT:
+ case OP_ARRLEN:
return 0;
case OP_DROP:
case OP_0BRANCH:
@@ -298,15 +299,18 @@ static void end_scope(Compiler *C) {
free(sc);
}
-static void declare_local(Compiler *C, char *name) {
+// returns slot of declared local
+static uint8_t declare_local(Compiler *C, char *name) {
Scope *sc = C->scope;
CHECK(sc != NULL, "can't declare local outside of scope");
Local *l = &sc->locals[sc->nlocals++];
l->name = name;
// -1 because local is expected to be already on the stack
// ie sitting just below where stack_cur points
- l->slot = C->stack_cur - 1;
+ uint8_t slot = C->stack_cur - 1;
+ l->slot = slot;
// printf("declaring local %s at %d, stack_cur is %d\n",l->name, l->slot, C->stack_cur);
+ return slot;
}
static Local *locate_local(Compiler *C, char *name) {
for (Scope *sc = C->scope; sc != NULL; sc = sc->outer) {
@@ -353,14 +357,10 @@ void for_form(Compiler *C, AstVec l, Op _, int flags) {
compile_opcode(C, OP_LOADK);
compile_byte(C, compile_constant(C, VAL_NUM(0)));
- declare_local(C, ivar);
- Local *loc = locate_local(C, ivar);
- int islot = loc->slot;
+ uint8_t islot = declare_local(C, ivar);
compile_node(C, blist.vals[1], 0);
- declare_local(C, "__max__");
- loc = locate_local(C, "__max__");
- int mslot = loc->slot;
+ uint8_t mslot = declare_local(C, "__max__");
// A
// getlocal ivar
@@ -401,6 +401,90 @@ void for_form(Compiler *C, AstVec l, Op _, int flags) {
end_scope(C);
}
+void each_form(Compiler *C, AstVec l, Op _, int flags) {
+ // (each (x a) ...)
+ // returns nil, for now
+ CHECK(l.vals[1].ty == AST_LIST, "each needs binding list");
+ AstVec blist = l.vals[1].as.list;
+ CHECK(blist.len == 2, "each binding list must have length 2");
+ CHECK(blist.vals[0].ty == AST_IDENT, "can only bind to ident");
+ char *ivar = blist.vals[0].as.str;
+
+ begin_scope(C);
+
+ compile_opcode(C, OP_LOADK);
+ compile_byte(C, compile_constant(C, VAL_NUM(0)));
+ uint8_t islot = declare_local(C, "__idx__");
+
+ compile_node(C, blist.vals[1], 0);
+ uint8_t aslot = declare_local(C, "__arr__");
+
+ compile_opcode(C, OP_GETLOCAL);
+ compile_byte(C, aslot);
+ compile_opcode(C, OP_ARRLEN);
+ uint8_t mslot = declare_local(C, "__max__");
+
+ compile_opcode(C, OP_NIL);
+ uint8_t vslot = declare_local(C, ivar);
+
+
+ // A
+ // getlocal idx
+ // getlocal max
+ // cmp
+ // 0branch -> B
+ // getlocal arr
+ // getlocal idx
+ // call 2
+ // setlocal ivar
+ // body ...
+ // incr idx
+ // redo -> A
+ // B:
+ // nil
+
+ size_t dest_A = BYTECODE(C).len;
+ compile_opcode(C, OP_GETLOCAL);
+ compile_byte(C, islot);
+ compile_opcode(C, OP_GETLOCAL);
+ compile_byte(C, mslot);
+ compile_opcode(C, OP_CMP);
+ compile_opcode(C, OP_0BRANCH);
+ size_t ph_B = placeholder(C);
+
+ compile_opcode(C, OP_GETLOCAL);
+ compile_byte(C, aslot);
+ compile_opcode(C, OP_GETLOCAL);
+ compile_byte(C, islot);
+ compile_call_instr(C, 2);
+ compile_opcode(C, OP_SETLOCAL);
+ compile_byte(C, vslot);
+ compile_opcode(C, OP_DROP);
+
+ compile_body(C, l, 2, flags & ~F_tail);
+ compile_opcode(C, OP_DROP);
+
+ compile_opcode(C, OP_GETLOCAL);
+ compile_byte(C, islot);
+ compile_opcode(C, OP_LOADK);
+ compile_byte(C, compile_constant(C, VAL_NUM(1)));
+ compile_opcode(C, OP_ADD);
+ compile_opcode(C, OP_SETLOCAL);
+ compile_byte(C, islot);
+ compile_opcode(C, OP_DROP);
+
+ compile_opcode(C, OP_REDO);
+ size_t ph_A = placeholder(C);
+
+ size_t dest_B = BYTECODE(C).len;
+ compile_opcode(C, OP_NIL);
+
+ patch(C, ph_A, ph_A - dest_A + 2);
+ patch(C, ph_B, dest_B - ph_B - 2);
+
+ end_scope(C);
+}
+
void def_form(Compiler *C, AstVec l, Op _, int flags) {
CHECK(l.vals[1].ty == AST_IDENT, "def's first argument must be ident");
@@ -459,6 +543,7 @@ static BuiltinForm builtin_forms[] = {
{ "if", 3, false, if_form, 0 },
{ "while", 2, true, while_form, 0 },
{ "for", 2, true, for_form, 0 },
+ { "each", 2, true, each_form, 0 },
{ "fn", 2, true, fn_form, 0 },
{ "let", 2, true, let_form, 0 },
{ "def", 2, false, def_form, 0 },