diff options
| author | mryouse | 2023-05-25 21:15:04 +0000 |
|---|---|---|
| committer | mryouse | 2023-05-25 21:15:04 +0000 |
| commit | 9fe6496202bd95d252ed2323a88fd24781780b64 (patch) | |
| tree | 907f85973ab3e5b22b076a27a3933b88855e07ba | |
| parent | d95e4f6f988b16bbd95a9c78c0355d190390f003 (diff) | |
lists as sequences, first and rest
| -rw-r--r-- | chunk.d | 30 | ||||
| -rw-r--r-- | compiler.d | 27 | ||||
| -rw-r--r-- | dbg.d | 24 | ||||
| -rw-r--r-- | parser.d | 10 | ||||
| -rw-r--r-- | vm.d | 37 |
5 files changed, 110 insertions, 18 deletions
@@ -8,7 +8,6 @@ import dbg; enum ObjType { FUNCTION, SCRIPT, - LIST, } abstract class Obj { @@ -37,18 +36,41 @@ class Function : Obj { } } -class List : Obj { +enum SeqType { + LIST, + STRING, +} + +abstract class Seq { + SeqType type; + abstract Value first(); + abstract Seq rest(); +} + +class List : Seq { Value[] inner; this(int length) { this.inner = new Value[length]; - this.type = ObjType.LIST; + this.type = SeqType.LIST; } void addItemAtIndex(Value item, int idx) { this.inner[idx] = item; } + override Value first() { + return this.inner[0]; // this fails on NIL + } + + override Seq rest() { + List ret = new List(to!int(this.inner.length) - 1); + for (int i = 1; i < this.inner.length; i++) { + ret.addItemAtIndex(this.inner[i], i - 1); + } + return ret; + } + override string toString() { return format("list ('%s' + %d)", printableValue(this.inner[0]), this.inner.length - 1); } @@ -85,11 +107,13 @@ enum OpCode { OP_CONCAT, // No? OP_FIRST, // No? + OP_REST, OP_TYPE_CHECK_NUMBER, OP_TYPE_CHECK_BOOLEAN, OP_TYPE_CHECK_STRING, OP_TYPE_CHECK_LIST, + OP_TYPE_CHECK_SEQ, } class Chunk { @@ -345,9 +345,12 @@ class Compiler { */ int resolveLocal(Symbol sym) { + writefln("resolving local: %s", sym.name); for (int i = this.localCount - 1; i >= 0; i--) { Local local = this.locals[i]; + writefln(" > [%d] %s (%s)", i, local.sym.name, local.depth); if (local.sym.name == sym.name) { + writeln("got it!"); return i; } } @@ -524,8 +527,8 @@ class Compiler { writeln("COMPILE ERROR: 'first' expects exactly one argument"); return; } - ValueType vt = this.resolve(args[0], ValueType.OBJ); // TODO need a new type - this.func.chunk.writeOp(OpCode.OP_TYPE_CHECK_LIST, args[0].line); + ValueType vt = this.resolve(args[0], ValueType.SEQ); // TODO need a new type + this.func.chunk.writeOp(OpCode.OP_TYPE_CHECK_SEQ, args[0].line); // pop the value off the stack // get the address of the first item in the list @@ -535,6 +538,23 @@ class Compiler { this.func.chunk.writeOp(OpCode.OP_FIRST, args[0].line); } + void compileRest(Form[] args) { + // TODO how do we identify/propagate errors? + if (args.length != 1) { + writeln("COMPILE ERROR: 'first' expects exactly one argument"); + return; + } + ValueType vt = this.resolve(args[0], ValueType.OBJ); // TODO need a new type + this.func.chunk.writeOp(OpCode.OP_TYPE_CHECK_SEQ, args[0].line); + + this.func.chunk.writeOp(OpCode.OP_REST, args[0].line); + + // there's probably a nicer way to copy + //List lst = new List(); + + // create a copy of the list + } + void compileFunc(Form form) { Func f = cast(Func)form; @@ -629,6 +649,9 @@ class Compiler { //return compileList(cons.tail); this.compileList(cons.tail); break; + case "rest": + this.compileRest(cons.tail); + break; default: /* writefln("unsure how to compile %s", sym.name); @@ -8,8 +8,8 @@ import parser; //import dbg; //import value; -//bool DEBUG = true; -bool DEBUG = false; +bool DEBUG = true; +//bool DEBUG = false; void printForm(Form f, string prefix = "") { @@ -85,11 +85,17 @@ string printableValue(Value val) { return val.as.type; case ValueType.NIL: return "nil"; + case ValueType.SEQ: + return printableSeq(val.as.seq); default: return "! unknown value type !"; } } +string printableSeq(Seq seq) { + return format("%s", seq); +} + string printableFunction(Obj func) { return format("%s", func); } @@ -119,6 +125,12 @@ int constantInstruction(string message, Chunk chunk, int offset) { return offset + 2; } +int listInstruction(string message, Chunk chunk, int offset) { + ubyte idx = chunk.code[offset + 1]; + writefln("%-16s %4d", message, idx); + return offset + 2; +} + int simpleInstruction(string message, int offset) { writeln(message); return offset + 1; @@ -147,6 +159,8 @@ int disassemble(Chunk chunk, int offset) { return byteInstruction("OP_GET_LOCAL", chunk, offset); case OpCode.OP_SET_LOCAL: return byteInstruction("OP_SET_LOCAL", chunk, offset); + case OpCode.OP_LIST: + return listInstruction("OP_LIST", chunk, offset); case OpCode.OP_ADD: return simpleInstruction("OP_ADD", offset); case OpCode.OP_LESS: @@ -187,6 +201,12 @@ int disassemble(Chunk chunk, int offset) { return simpleInstruction("OP_TYPE_CHECK_NUMBER", offset); case OpCode.OP_TYPE_CHECK_BOOLEAN: return simpleInstruction("OP_TYPE_CHECK_BOOLEAN", offset); + case OpCode.OP_TYPE_CHECK_SEQ: + return simpleInstruction("OP_TYPE_CHECK_SEQ", offset); + case OpCode.OP_FIRST: + return simpleInstruction("OP_FIRST", offset); + case OpCode.OP_REST: + return simpleInstruction("OP_REST", offset); default: writeln("unknown opcode?"); return offset + 1; @@ -270,6 +270,7 @@ enum ValueType { NUMBER, BOOLEAN, TYPE, + SEQ, OBJ, ANY, NIL, @@ -281,6 +282,7 @@ union As { string str; string type; Obj obj; + Seq seq; } @@ -313,6 +315,12 @@ Value makeObjValue(Obj obj) { return val; } +Value makeSeqValue(Seq seq) { + As as = { seq: seq }; + Value val = { ValueType.SEQ, as }; + return val; +} + Value makeTypeValue(string name) { As as = { type: name }; Value val = { ValueType.TYPE, as }; @@ -464,7 +472,7 @@ class Parser { if (next == ')') { break; } - func.addToBody(parseForm()); + func.addToBody(this.parseForm()); } if (!peekable()) { @@ -168,14 +168,26 @@ class VM { return value.type == ValueType.OBJ; } + bool isSeq(Value value) { + return value.type == ValueType.SEQ; + } + bool isList(Value value) { - return isObj(value) && objTypeOf(value) == ObjType.LIST; + return isSeq(value) && seqTypeOf(value) == SeqType.LIST; } ObjType objTypeOf(Value value) { return value.as.obj.type; } + SeqType seqTypeOf(Value value) { + return value.as.seq.type; + } + + Seq asSeq(Value value) { + return value.as.seq; + } + double asNumber(Value value) { return value.as.number; } @@ -228,6 +240,7 @@ class VM { bool call(Function func, int argCount) { //CallFrame frame = { func, 0, this.bStack, this.bTop - argCount - 1 }; // i need to do sthg with argCount + //CallFrame frame = { func, 0, 0, this.bTop - argCount - 1 }; // i need to do sthg with argCount CallFrame frame = { func, 0, 0, this.bTop - argCount - 1 }; // i need to do sthg with argCount //frames ~= frame; //frameCount++; @@ -329,7 +342,7 @@ class VM { //lst.appendItem(this.popA()); lst.addItemAtIndex(this.popA(), i); } - this.pushA(makeObjValue(lst)); + this.pushA(makeSeqValue(lst)); break; case OpCode.OP_CONSTANT: Value constant = this.current.func.chunk.constants[this.readByte()]; @@ -344,6 +357,12 @@ class VM { double val = asNumber(this.popA()); this.pushA(makeNumberValue(val * -1)); break; + case OpCode.OP_TYPE_CHECK_SEQ: + if (!isSeq(this.peekA(0))) { + writeln("VM type check: not a seq!"); + return InterpretResult.RUNTIME_ERROR; // TODO error + } + break; case OpCode.OP_TYPE_CHECK_LIST: if (!isList(this.peekA(0))) { writeln("VM type check: not a list!"); @@ -368,16 +387,14 @@ class VM { return InterpretResult.RUNTIME_ERROR; // TODO error } break; - /* case OpCode.OP_FIRST: - Value val = this.popA(); - List lst = cast(List)val.as.obj; // TODO this needs better checking - int addr = lst.first(); - writefln("got this address: %d", addr); - Value first = this.current.func.chunk.constants[to!ubyte(addr)]; - this.pushA(first); + Seq seq = asSeq(this.popA()); + this.pushA(seq.first()); + break; + case OpCode.OP_REST: + Seq seq = asSeq(this.popA()); + this.pushA(makeSeqValue(seq.rest())); break; - */ case OpCode.OP_ADD: Value b = this.popA(); /* |
