import std.stdio; import std.string; import std.conv; import parser; import dbg; enum ObjType { FUNCTION, SCRIPT, } abstract class Obj { ObjType type; } class Function : Obj { Chunk chunk; int arity; string name; ObjType type; this(ObjType type, string name = "") { this.type = type; this.chunk = new Chunk(); this.arity = 0; this.name = name; } override string toString() { if (type == ObjType.SCRIPT) { return ""; } else { return format("", name); } } } enum SeqType { LIST, STRING, } abstract class Seq { SeqType type; abstract Value first(); abstract Seq rest(); abstract Seq most(); abstract Value last(); abstract Seq reverse(); abstract int length(); abstract bool isIn(Value val); } class String : Seq { string str; this(Value value) { this.str = value.as.str; this.type = SeqType.STRING; } this(string str) { this.str = str; this.type = SeqType.STRING; } override Value first() { string ret = to!string(this.str[0]); return makeSeqValue(new String(ret)); } override Seq rest() { return new String(this.str[1..$]); } override Seq most() { return new String(this.str[0..$ - 1]); } override Seq reverse() { string s = ""; int end = to!int(this.str.length) - 1; for (int i = end; i >= 0; i--) { s ~= this.str[i]; } return new String(s); } override Value last() { string ret = to!string(this.str[$ - 1]); return makeSeqValue(new String(ret)); } override int length() { return to!int(str.length); } Seq concat(Seq seq) { if (seq.type != SeqType.STRING) { // how do i throw an error here? writeln("must concat strings to strings!"); return this; } String strSeq = cast(String)seq; return new String(this.str ~ strSeq.str); } override bool isIn(Value val) { if (val.type != ValueType.SEQ) { // how do i throw an error here? writeln("a non-string can't be 'in?' a string (this should be a compile error)"); return false; } Seq seq = val.as.seq; if (seq.type != SeqType.STRING) { // how do i throw an error here? writeln("a non-string can't be 'in?' a string (this should be a compile error)"); return false; } String strVal = cast(String)seq; return (indexOf(this.str, strVal.str) != -1); } override string toString() { return format("\"%s\"", this.str); } } class List : Seq { Value[] inner; this(int length) { this.inner = new Value[length]; 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() { if (this.inner.length == 0) { return new List(0); } 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 Seq most() { if (this.inner.length == 0) { return new List(0); } int newLength = to!int(this.inner.length) - 1; List ret = new List(newLength); for (int i = 0; i < newLength; i++) { ret.addItemAtIndex(this.inner[i], i); } return ret; } override Seq reverse() { int length = to!int(this.inner.length); List ret = new List(length); for (int i = 0; i < length; i++) { ret.addItemAtIndex(this.inner[i], length - i - 1); } return ret; } override Value last() { return this.inner[$ - 1]; // this fails on NIL } override int length() { return to!int(this.inner.length); } Seq append(Value val) { int length = to!int(this.inner.length); List ret = new List(length + 1); for (int i = 0; i < length; i++) { ret.addItemAtIndex(this.inner[i], i); } ret.addItemAtIndex(val, length); return ret; } override bool isIn(Value val) { foreach(Value mine ; this.inner) { if (areValuesEqual(mine, val)) { return true; } } return false; } override string toString() { if (this.inner.length == 0) { return "nil"; } else { string[] ret; foreach (Value val ; this.inner) { ret ~= printableValue(val); } return format("(%s)", join(ret, " ")); } } } enum OpCode { // MATH OP_ADD, OP_DIVIDE, OP_INCREMENT, OP_MULTIPLY, OP_NEGATE, OP_SUBTRACT, OP_ZERO, // BOOLEAN OP_EQUAL, OP_GREATER, OP_LESS, OP_NOT, // LISTS OP_LIST, OP_LIST_N, OP_UNGROUP, // SEQUENCES OP_CONCAT, // No? OP_APPEND, OP_FIRST, // No? OP_LENGTH, OP_LAST, OP_MEMBER, OP_MOST, OP_REST, OP_REVERSE, // DEFINITIONS/VARIABLES OP_CONSTANT, OP_DEF_GLOBAL, OP_DEF_LOCAL, OP_GET_GLOBAL, OP_GET_LOCAL, OP_SET_GLOBAL, OP_SET_LOCAL, // JUMPS OP_JUMP, OP_JUMP_IF_FALSE, OP_JUMP_IF_TRUE, OP_JUMP_TO, // STACK THINGS OP_DUPLICATE, OP_DUPLICATE_2, OP_POP, OP_POPB, OP_POP_SCOPE, OP_ROTATE_N, // RANDOM OP_NIL, OP_PRINT, // FUNCTIONS OP_CALL, OP_CALL_N, OP_RETURN, // TYPES OP_IS_NIL, OP_TYPE_CHECK_BOOLEAN, OP_TYPE_CHECK_LIST, OP_TYPE_CHECK_NUMBER, OP_TYPE_CHECK_SEQ, OP_TYPE_CHECK_STRING, } class Chunk { int count = 0; ubyte[] code; int[] lines; Value[] constants; int writeOp(ubyte opCode, int line) { this.code ~= opCode; this.lines ~= line; this.count++; assert(this.code.length == count); assert(this.lines.length == count); return count; } int addConstant(Value value) { this.constants ~= value; return to!int(this.constants.length - 1); } }