diff options
| author | mryouse | 2023-06-08 20:03:38 -0400 |
|---|---|---|
| committer | mryouse | 2023-06-08 20:03:38 -0400 |
| commit | 2990e709e410f4f41f80802cf31e930950cf499c (patch) | |
| tree | da8513086079f7c6a1690ecd2d80212a98af910b | |
| parent | a1a7eb31c2ded8e3f1f6ed205fc3c4c2fd5d67b5 (diff) | |
initial commit of reduce
| -rw-r--r-- | README.md | 1 | ||||
| -rw-r--r-- | compiler.d | 69 |
2 files changed, 70 insertions, 0 deletions
@@ -27,6 +27,7 @@ now in bytecode - [ ] bounds issues (`first`/`last` on empty sequences) crashes entirely - [ ] constants get duplicated in chunks - [ ] a space at the end of (definitions? lists?) crashes + - [ ] HOF can't take in builtins (they're not actually in the global environment) - [ ] i mean, nearly nothing works ## things that hopefully work @@ -401,6 +401,72 @@ class Compiler { this.func.chunk.writeOp(OpCode.OP_LIST_N, args[0].line); } + void compileReduce(Form[] args) { + // TODO how do we identify/propagate errors? + if (args.length != 3) { + this.error(format("'reduce': expected [3] arguments, received %d", args.length), -1); + return; + } + ValueType vt = this.resolve(args[0], ValueType.ANY); // resolves the function + vt = this.resolve(args[1], ValueType.ANY); // resolves the list + + vt = this.resolve(args[2], ValueType.ANY); // resolves the accumulator + + this.func.chunk.writeOp(OpCode.OP_ROTATE_N, args[0].line); + this.func.chunk.writeOp(to!ubyte(3), args[0].line); // | [ acc ] [ fn ] [ list ] + + // we're going to jump back to this point, so let's keep track of our location + int jumpBack = to!int(this.func.chunk.code.length); + + // with our list on top of the stack, let's check for nil to see if we're done + this.func.chunk.writeOp(OpCode.OP_DUPLICATE, args[0].line); // | [ acc ] [ fn ] [ list ] [ list ] + this.func.chunk.writeOp(OpCode.OP_IS_NIL, args[0].line); // | [ acc ] [ fn ] [ list ] [ bool ] + + // we'll jump from here to the end if we have nil + int nilJump = this.jump(OpCode.OP_JUMP_IF_TRUE); // TODO if we're using "falsey", we could cut an instruction here + + // get rid of the boolean on top + this.func.chunk.writeOp(OpCode.OP_POP, args[0].line); // | [ acc ] [ fn ] [ list ] + + // duplicate the function and list, and rotate it below the length (we'll need it later) + this.func.chunk.writeOp(OpCode.OP_DUPLICATE_2, args[0].line); // | [ acc ] [ fn ] [ list ] [ fn ] [ list ] + this.func.chunk.writeOp(OpCode.OP_ROTATE_N, args[0].line); + this.func.chunk.writeOp(to!ubyte(5), args[0].line); // | [ list ] [ acc ] [ fn ] [ list ] [ fn ] + this.func.chunk.writeOp(OpCode.OP_ROTATE_N, args[0].line); + this.func.chunk.writeOp(to!ubyte(5), args[0].line); // | [ fn ] [ list ] [ acc ] [ fn ] [ list ] + + // get the first item from the top list, then run the function on it (we know there's always 1 argument) + this.func.chunk.writeOp(OpCode.OP_FIRST, args[0].line); // | [ fn ] [ list ] [ acc ] [ fn ] [ item ] + + // rotate the accumulator and item to the top + this.func.chunk.writeOp(OpCode.OP_ROTATE_N, args[0].line); + this.func.chunk.writeOp(to!ubyte(3), args[0].line); // | [ fn ] [ list ] [ item ] [ acc ] [ fn ] + this.func.chunk.writeOp(OpCode.OP_ROTATE_N, args[0].line); + this.func.chunk.writeOp(to!ubyte(3), args[0].line); // | [ fn ] [ list ] [ fn ] [ item ] [ acc ] + + // call the function + this.func.chunk.writeOp(OpCode.OP_CALL, args[0].line); + this.func.chunk.writeOp(to!ubyte(2), args[0].line); // | [ fn ] [ list ] [ fn(item, acc) ] + + // rotate the accumulator behind the fn/list + this.func.chunk.writeOp(OpCode.OP_ROTATE_N, args[0].line); + this.func.chunk.writeOp(to!ubyte(3), args[0].line); // | [ fn(item, acc) ] [ fn ] [ list ] + + // get the rest of the list + this.func.chunk.writeOp(OpCode.OP_REST, args[0].line); // | [ fn(item, acc) ] [ fn ] [ rest(list) ] + + // jump back to the top of the loop + this.jumpBackTo(OpCode.OP_JUMP_TO, jumpBack); + + // jump here when we have a nil + this.patchJump(nilJump); // | [ fn(first) ] ... [ fn(last) ] [ length ] [ fn ] [ nil ] [ true ] + + // pop the unneeded state at the top, leaving the result at the top + this.func.chunk.writeOp(OpCode.OP_POP, args[0].line); + this.func.chunk.writeOp(OpCode.OP_POP, args[0].line); + this.func.chunk.writeOp(OpCode.OP_POP, args[0].line); // | [ acc ] + } + // LISP-Y void compileBlock(Form form) { Block block = cast(Block)form; @@ -854,6 +920,9 @@ class Compiler { case "map": this.compileMap(cons.tail); break; + case "reduce": + this.compileReduce(cons.tail); + break; // STRINGS case "concat": |
