summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--com.c64
-rw-r--r--tests/for.bth2
-rw-r--r--tests/for.out10
3 files changed, 76 insertions, 0 deletions
diff --git a/com.c b/com.c
index 310117a..2c44883 100644
--- a/com.c
+++ b/com.c
@@ -268,6 +268,8 @@ void while_form(Compiler *C, AstVec l, Op _, int flags) {
patch(C, ph_b, dest_b - ph_b - 2);
}
+
+
void arith_form(Compiler *C, AstVec l, Op op, int flags) {
compile_node(C, l.vals[1], 0);
compile_node(C, l.vals[2], 0);
@@ -339,6 +341,67 @@ void let_form(Compiler *C, AstVec l, Op _, int flags) {
end_scope(C);
}
+void for_form(Compiler *C, AstVec l, Op _, int flags) {
+ // (for (x n) ...)
+ CHECK(l.vals[1].ty == AST_LIST, "for needs binding list");
+ AstVec blist = l.vals[1].as.list;
+ CHECK(blist.len == 2, "for 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)));
+ declare_local(C, ivar);
+ Local *loc = locate_local(C, ivar);
+ int islot = loc->slot;
+
+ compile_node(C, blist.vals[1], 0);
+ declare_local(C, "__max__");
+ loc = locate_local(C, "__max__");
+ int mslot = loc->slot;
+
+ // A
+ // getlocal ivar
+ // getlocal max
+ // cmp
+ // 0branch -> B
+ // body ...
+ // incr ivar
+ // 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_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_opcode(C, islot);
+ 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");
CHECK(flags & F_toplevel, "def only allowed at top level");
@@ -395,6 +458,7 @@ static BuiltinForm builtin_forms[] = {
{ "do", 1, true, do_form, 0 },
{ "if", 3, false, if_form, 0 },
{ "while", 2, true, while_form, 0 },
+ { "for", 2, true, for_form, 0 },
{ "fn", 2, true, fn_form, 0 },
{ "let", 2, true, let_form, 0 },
{ "def", 2, false, def_form, 0 },
diff --git a/tests/for.bth b/tests/for.bth
new file mode 100644
index 0000000..4d5f5b1
--- /dev/null
+++ b/tests/for.bth
@@ -0,0 +1,2 @@
+(for (x 10)
+ (say (* x x)))
diff --git a/tests/for.out b/tests/for.out
new file mode 100644
index 0000000..9b81ce7
--- /dev/null
+++ b/tests/for.out
@@ -0,0 +1,10 @@
+0
+1
+4
+9
+16
+25
+36
+49
+64
+81