aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBen Winston2023-05-20 18:58:51 -0400
committerBen Winston2023-05-20 18:58:51 -0400
commit38dc63a67879a42f208b5642a8590e1192e8e2e5 (patch)
treee36a71c752d8d0c89e343ef02e295115d3e19ce7
parent0c70372774297272dd14133d48e40e9a3624420a (diff)
global variables
-rw-r--r--chunk.d2
-rw-r--r--compiler.d38
-rw-r--r--dbg.d6
-rw-r--r--main.d34
-rw-r--r--parser.d33
-rw-r--r--vm.d44
6 files changed, 122 insertions, 35 deletions
diff --git a/chunk.d b/chunk.d
index f6dce01..daced26 100644
--- a/chunk.d
+++ b/chunk.d
@@ -37,6 +37,8 @@ enum OpCode {
OP_NEGATE,
OP_RETURN,
OP_CONSTANT,
+ OP_DEFINE_GLOBAL,
+ OP_GET_GLOBAL,
OP_POP,
OP_SUBTRACT,
OP_NIL,
diff --git a/compiler.d b/compiler.d
index 51e45ba..739f887 100644
--- a/compiler.d
+++ b/compiler.d
@@ -1,5 +1,7 @@
import std.stdio;
import std.string;
+import std.conv;
+import std.algorithm : canFind;
import parser;
import chunk;
@@ -86,6 +88,33 @@ class Compiler {
func.chunk.writeOp(OpCode.OP_LESS, current.line);
}
+ void compileDef(Form form) {
+ Def def = cast(Def)form;
+
+ // add the variable name to the chunk
+ int addr = func.chunk.addConstant(makeStringValue(def.name.name));
+
+ // resolve the value
+ resolve(def.val);
+
+ // define the variable
+ func.chunk.writeOp(OpCode.OP_DEFINE_GLOBAL, current.line);
+ func.chunk.writeOp(to!ubyte(addr), current.line);
+ }
+
+ void compileSymbol(Form form) {
+ Symbol sym = cast(Symbol)form;
+
+ // add the symbol name to the chunk
+ int addr = func.chunk.addConstant(makeStringValue(sym.name));
+
+ // get the variable
+ func.chunk.writeOp(OpCode.OP_GET_GLOBAL, sym.line);
+ func.chunk.writeOp(to!ubyte(addr), sym.line);
+
+ advance();
+ }
+
void compileCons(Form form) {
Cons cons = cast(Cons)form;
Form head = cons.head;
@@ -110,8 +139,11 @@ class Compiler {
compileLess(cons.tail);
break;
default:
+ /*
writefln("unsure how to compile %s", sym.name);
advance();
+ */
+ resolve(head);
break;
}
@@ -130,6 +162,12 @@ class Compiler {
case FormType.NIL:
this.compileNil(form);
break;
+ case FormType.DEF:
+ this.compileDef(form);
+ break;
+ case FormType.SYMBOL:
+ this.compileSymbol(form);
+ break;
default:
write("not sure how to resolve: ");
printForm(form);
diff --git a/dbg.d b/dbg.d
index dba6070..1290cf0 100644
--- a/dbg.d
+++ b/dbg.d
@@ -47,6 +47,10 @@ void printForm(Form f, string prefix = "") {
writefln("%s with %d body lines", prefix, func.funcBody.length);
writefln("%s <end fn %s>", prefix, func.name.name);
break;
+ case FormType.DEF:
+ Def def = cast(Def)f;
+ writefln("%s var: %s", prefix, def.name.name);
+ break;
default:
writeln("printFormDefault");
break;
@@ -101,6 +105,8 @@ int disassemble(Chunk chunk, int offset) {
return simpleInstruction("OP_SUBTRACT", offset);
case OpCode.OP_CONSTANT:
return constantInstruction("OP_CONSTANT", chunk, offset);
+ case OpCode.OP_DEFINE_GLOBAL:
+ return constantInstruction("OP_DEFINE_GLOBAL", chunk, offset);
case OpCode.OP_NEGATE:
return simpleInstruction("OP_NEGATE", offset);
case OpCode.OP_POP:
diff --git a/main.d b/main.d
index 0bdc74b..678bfb0 100644
--- a/main.d
+++ b/main.d
@@ -18,6 +18,7 @@ import vm;
void repl() {
while(true) {
+
write("> ");
string input = strip(stdin.readln());
@@ -27,45 +28,12 @@ void repl() {
Parser parser = new Parser(input);
- /*
- Chunk chunk = new Chunk();
- Form f;
- while (true) {
- f = parser.parseForm();
- printForm(f, "");
-
- f.compile(chunk);
-
-
- if (f.type == FormType.EOF) {
- break;
- }
- }
- */
-
Compiler compiler = new Compiler(FunctionType.SCRIPT, &parser);
Function func = compiler.compile();
VM vm = new VM(func);
vm.run();
- /*
- writeln("== disassembling chunk ==");
- int off = 0;
- while (off < func.chunk.code.length) {
- off = disassemble(func.chunk, off);
- }
- */
-
- /*
- Compiler compiler = new Compiler(lex);
-
- ObjFunction func = compiler.compile();
-
- VM vm = new VM(func);
- vm.run();
- */
-
}
}
diff --git a/parser.d b/parser.d
index 20231c9..ef1d736 100644
--- a/parser.d
+++ b/parser.d
@@ -12,6 +12,7 @@ enum FormType {
NIL,
SYMBOL,
FUNC,
+ DEF,
EOF,
PARSE_ERROR
@@ -134,6 +135,19 @@ class Func : Form {
}
}
+class Def : Form {
+
+ Symbol name;
+ Form val;
+
+ this(Symbol name, Form val, int line) {
+ this.name = name;
+ this.val = val;
+ this.line = line;
+ this.type = FormType.DEF;
+ }
+}
+
class Atom : Form {
Value value;
@@ -310,6 +324,23 @@ class Parser {
return new Atom(to!double(to!string(acc)), line);
}
+ Form parseDef() {
+ // we've parsed `def`, but not the symbol yet
+ Form sym = parseForm();
+ if (sym.type != FormType.SYMBOL) {
+ return new ParseError("func definitions expect a symbol name", line);
+ }
+
+ // get the value
+ Form val = parseForm();
+
+ Def def = new Def(cast(Symbol)sym, val, line);
+
+ advance(); // closing paren
+
+ return def;
+ }
+
Form parseFunc() {
// we've parsed `func`, but not the symbol yet
Form sym = parseForm();
@@ -362,6 +393,8 @@ class Parser {
switch (s.name) {
case "func":
return parseFunc();
+ case "def":
+ return parseDef();
default:
break;
}
diff --git a/vm.d b/vm.d
index 6011928..7955963 100644
--- a/vm.d
+++ b/vm.d
@@ -1,5 +1,6 @@
import std.stdio;
import std.string;
+import std.conv;
import chunk;
import parser;
@@ -19,6 +20,7 @@ class VM {
Value[] stack;
//ObjFunction func;
Function func;
+ Value[string] globals;
this(Function func) {
this.func = func;
@@ -26,6 +28,7 @@ class VM {
Value peek(int offset) {
if (offset >= this.topOffset) {
+ //if (offset > this.topOffset) {
writefln("offset of %d greater than stack size %d", offset, topOffset);
}
return this.stack[topOffset - offset - 1];
@@ -58,22 +61,55 @@ class VM {
return value.type == ValueType.NUMBER;
}
+ bool isString(Value value) {
+ return value.type == ValueType.STRING;
+ }
+
double asNumber(Value value) {
return value.as.number;
}
+ string asString(Value value) {
+ return value.as.str;
+ }
+
InterpretResult run() {
//int ip = 0; // TODO this is wrong
while (true) {
write(" ");
- //foreach (Value val; this.stack) {
for (int i = 0; i < this.topOffset; i++) {
writef("[ %s ]", printableValue(this.stack[i]));
}
writeln("\n--");
+
disassemble(this.func.chunk, this.ip);
ubyte inst;
switch (inst = this.readByte()) {
+ case OpCode.OP_DEFINE_GLOBAL:
+ Value name = func.chunk.constants[readByte()];
+ if (!isString(name)) {
+ writefln("variables must be strings, got %s", printableValue(name));
+ return InterpretResult.RUNTIME_ERROR; // TODO error
+ }
+ Value val = pop();
+ globals[asString(name)] = val;
+ break;
+ case OpCode.OP_GET_GLOBAL:
+ writeln("in OP_GET_GLOBAL");
+ Value name = func.chunk.constants[readByte()];
+ writefln(asString(name));
+ if (!isString(name)) {
+ writefln("variables must be strings, got %s", printableValue(name));
+ return InterpretResult.RUNTIME_ERROR; // TODO error
+ }
+ writefln(asString(name));
+ Value* member = asString(name) in globals;
+ if (member is null) {
+ writefln("no such variable: %s", printableValue(name));
+ return InterpretResult.RUNTIME_ERROR; // TODO error
+ }
+ push(*member); // do i need to dereference?
+ break;
case OpCode.OP_CONSTANT:
Value constant = this.func.chunk.constants[this.readByte()];
this.push(constant);
@@ -142,5 +178,9 @@ class VM {
}
}
-
}
+
+/*
+InterpretResult interpret(string source) {
+ Parser parser = new Parser(source);
+*/