diff options
author | ubq323 <ubq323@ubq323.website> | 2024-07-01 20:49:14 +0100 |
---|---|---|
committer | ubq323 <ubq323@ubq323.website> | 2024-07-01 21:17:44 +0100 |
commit | 629416ffb6e30836f4de4a0f2401ccd66b4ce4a6 (patch) | |
tree | cf7c27e0322338abd3291f3a7ac8d2a0e2bbba54 | |
parent | 4571c3cb409808942ecbe353b1ffa6794cabc557 (diff) |
add numeric for form
-rw-r--r-- | com.c | 64 | ||||
-rw-r--r-- | tests/for.bth | 2 | ||||
-rw-r--r-- | tests/for.out | 10 |
3 files changed, 76 insertions, 0 deletions
@@ -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 |