aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--chunk.d64
-rw-r--r--main.d118
-rw-r--r--parser.d398
3 files changed, 580 insertions, 0 deletions
diff --git a/chunk.d b/chunk.d
new file mode 100644
index 0000000..5233a87
--- /dev/null
+++ b/chunk.d
@@ -0,0 +1,64 @@
+import std.stdio;
+import std.string;
+import std.conv;
+
+import parser;
+
+enum ObjType {
+ FUNCTION,
+}
+
+abstract class Obj {
+ ObjType type;
+}
+
+class Function : Obj {
+ Chunk chunk;
+ string name;
+
+ this() {
+ this.type = ObjType.FUNCTION;
+ this.chunk = new Chunk();
+ this.name = "";
+ }
+
+ override string toString() {
+ if (name == "") {
+ return "<neb>";
+ } else {
+ return name;
+ }
+ }
+}
+
+enum OpCode {
+ OP_ADD,
+ OP_RETURN,
+ OP_CONSTANT,
+ OP_POP,
+}
+
+class Chunk {
+ int count = 0;
+ ubyte[] code;
+ int[] lines;
+ Value[] constants;
+
+ //int writeOp(OpCode opCode, int line) {
+ 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);
+ }
+
+}
+
+
diff --git a/main.d b/main.d
new file mode 100644
index 0000000..18927d8
--- /dev/null
+++ b/main.d
@@ -0,0 +1,118 @@
+import std.stdio;
+import std.string;
+
+import parser;
+/*
+import compiler;
+import obj;
+import dbg;
+import chunk;
+import vm;
+*/
+
+void printForm(Form f, string prefix) {
+ switch (f.type) {
+ case FormType.EOF:
+ writeln("eof");
+ return;
+ case FormType.ATOM:
+ writefln("%s atom: %s", prefix, atomAsString(cast(Atom)f));
+ break;
+ case FormType.CONS:
+ Cons c = cast(Cons)f;
+ writef("%s cons <", prefix);
+ if (c.evaluate) {
+ writeln("true>");
+ } else {
+ writeln("false>");
+ }
+
+ printForm(c.head, format("%s>", prefix));
+ foreach (Form i ; c.tail) {
+ printForm(i, format("%s>", prefix));
+ }
+ break;
+ case FormType.NIL:
+ writefln("%s NIL", prefix);
+ break;
+ case FormType.PARSE_ERROR:
+ ParseError pe = cast(ParseError)f;
+ writefln("ERROR: %s", pe.message);
+ break;
+ case FormType.SYMBOL:
+ Symbol s = cast(Symbol)f;
+ writefln("%s sym: %s", prefix, s.name);
+ break;
+ case FormType.FUNC:
+ Func func = cast(Func)f;
+ writefln("%s <fn %s>", prefix, func.name.name);
+ printForm(func.args, format("%s -", prefix));
+ writefln("%s with %d body lines", prefix, func.funcBody.length);
+ writefln("%s <end fn %s>", prefix, func.name.name);
+ break;
+ default:
+ writeln("printFormDefault");
+ break;
+ }
+
+}
+
+
+void repl() {
+ while(true) {
+ write("> ");
+ string input = strip(stdin.readln());
+
+ if (input.length == 0) {
+ continue;
+ }
+
+ Parser parser = new Parser(input);
+ Form f;
+ while (true) {
+ f = parser.parseForm();
+
+ printForm(f, "");
+ if (f.type == FormType.EOF) {
+ break;
+ }
+
+
+ /*
+ if (is(typeof(f) == Eof)) {
+ writeln("eof");
+ break;
+ } else if (is(typeof(f) == Atom)) {
+ writeln("atom");
+ } else {
+ writeln("other");
+ }
+ */
+
+ /*
+ if (typeof(f) == EOF) {
+ writeln("reached the end");
+ break;
+ } else if (typeof(f) == ParseError) {
+ writefln("got a parse error: %s", f.message);
+ break;
+ }
+ */
+ }
+
+ /*
+ Compiler compiler = new Compiler(lex);
+
+ ObjFunction func = compiler.compile();
+
+ VM vm = new VM(func);
+ vm.run();
+ */
+
+ }
+}
+
+
+void main() {
+ repl();
+}
diff --git a/parser.d b/parser.d
new file mode 100644
index 0000000..d471f4d
--- /dev/null
+++ b/parser.d
@@ -0,0 +1,398 @@
+import std.stdio;
+import std.string;
+import std.algorithm : canFind;
+import std.conv : to;
+
+import chunk;
+
+enum FormType {
+ ATOM,
+ CONS,
+ NIL,
+ SYMBOL,
+ FUNC,
+
+ EOF,
+ PARSE_ERROR
+}
+
+/*
+struct Form {
+ FormType type;
+ bool evaluate;
+}
+*/
+
+abstract class Form {
+ FormType type;
+ bool evaluate;
+ int line;
+}
+
+class Symbol : Form {
+
+ string name;
+
+ this(string name, int line) {
+ this(name, false, line);
+ }
+
+ this(string name, bool quoted, int line) {
+ this.name = name;
+ this.line = line;
+ this.evaluate = !quoted;
+ this.type = FormType.SYMBOL;
+ }
+}
+
+class Eof : Form {
+ this(int line) {
+ this.line = line;
+ this.type = FormType.EOF;
+ }
+}
+
+class ParseError : Form {
+
+ string message;
+
+ this(string message, int line) {
+ this.message = message;
+ this.line = line;
+ this.type = FormType.PARSE_ERROR;
+ }
+}
+
+class Cons : Form {
+
+ Form head;
+ Form[] tail;
+ int argCount;
+
+ this(int line) {
+ this.line = line;
+ this.type = FormType.NIL;
+ this.argCount = 0;
+ this.evaluate = false;
+ }
+
+ void append(Form form) {
+ if (argCount == 0) {
+ head = form;
+
+ type = FormType.CONS;
+ } else {
+ tail ~= form;
+ }
+ argCount++;
+ }
+}
+
+class Func : Form {
+
+ Symbol name;
+ Cons args;
+ Form[] funcBody;
+
+ this(int line) {
+ this.line = line;
+ this.type = FormType.FUNC;
+ }
+
+ void addToBody(Form f) {
+ this.funcBody ~= f;
+ }
+}
+
+class Atom : Form {
+ Value value;
+
+ this(string value, int line) {
+ this.value = makeStringValue(value);
+ this.line = line;
+ this.type = FormType.ATOM;
+ this.evaluate = false;
+ }
+
+ this(double value, int line) {
+ this.value = makeNumberValue(value);
+ this.line = line;
+ this.type = FormType.ATOM;
+ this.evaluate = false;
+ }
+
+ this(bool value, int line) {
+ this.value = makeBooleanValue(value);
+ this.line = line;
+ this.type = FormType.ATOM;
+ this.evaluate = false;
+ }
+
+}
+
+enum ValueType {
+ STRING,
+ NUMBER,
+ BOOLEAN,
+ OBJ,
+}
+
+union As {
+ bool boolean;
+ double number;
+ string str;
+ Obj obj;
+}
+
+
+struct Value {
+ ValueType type;
+ As as;
+}
+
+Value makeStringValue(string str) {
+ As as = { str: str };
+ Value val = { ValueType.STRING, as };
+ return val;
+}
+
+Value makeNumberValue(double number) {
+ As as = { number: number };
+ Value val = { ValueType.NUMBER, as };
+ return val;
+}
+
+Value makeBooleanValue(bool boolean) {
+ As as = { boolean: boolean };
+ Value val = { ValueType.BOOLEAN };
+ return val;
+}
+
+Value makeObjValue(Obj obj) {
+ As as = { obj: obj };
+ Value val = { ValueType.OBJ };
+ return val;
+}
+
+string atomAsString(Atom a) {
+ Value val = a.value;
+ switch (val.type) {
+ case ValueType.STRING:
+ return val.as.str;
+ case ValueType.NUMBER:
+ return format("%g", val.as.number);
+ case ValueType.BOOLEAN:
+ if (val.as.boolean) {
+ return "true";
+ } else {
+ return "false";
+ }
+ default:
+ return "! unknown value type !";
+ }
+}
+
+class Parser {
+
+ string source;
+ int pos;
+ int line;
+
+ bool peekable() {
+ return (pos < source.length);
+ }
+
+ char peek() {
+ if (pos < source.length) {
+ return source[pos];
+ } else {
+ //writeln("peek returns null");
+ return '\0';
+ }
+ }
+
+ char advance() {
+ char ret = peek();
+ if (ret != '\0') {
+ pos++;
+ }
+ return ret;
+ }
+
+ void skipWhitespace() {
+ while (canFind([' ', '\t', '\r', '\n'], peek())) {
+ if (peek() == '\n') {
+ line++;
+ }
+ pos++;
+ }
+ }
+
+ Form parseSymbol() {
+ char[] acc;
+ char next;
+ while (peekable()) {
+ next = peek();
+ if (isBoundary(next)) {
+ break;
+ }
+ acc ~= next;
+ advance();
+ }
+ return new Symbol(to!string(acc), line);
+ }
+
+ Form parseString() {
+ char[] acc;
+ char next;
+ while (peekable()) {
+ next = peek();
+ if (next == '"') {
+ break;
+ } else if (next == '\n') {
+ line++;
+ }
+ acc ~= advance();
+ }
+ if (!peekable() && next != '"') {
+ return new ParseError("unterminated string!", line);
+ }
+ advance(); // go past last quote
+ return new Atom(to!string(acc), line);
+ }
+
+ bool isBoundary(char ch) {
+ return canFind([' ', '\r', '\t', '\n', ')'], ch);
+ }
+
+ bool isDigit(char ch) {
+ return '0' <= ch && '9' >= ch;
+ }
+
+ Form parseNumber() {
+ char[] acc;
+ char next;
+ while (peekable()) {
+ next = peek();
+ if (isBoundary(next)) {
+ break;
+ } else if (isDigit(next)) {
+ acc ~= next;
+ } else {
+ return new ParseError("unparseable number", line);
+ }
+ advance();
+ }
+ return new Atom(to!double(to!string(acc)), line);
+ }
+
+ Form parseFunc() {
+ // we've parsed `func`, but not the symbol yet
+ Form sym = parseForm();
+ if (sym.type != FormType.SYMBOL) {
+ return new ParseError("func definitions expect a symbol name", line);
+ }
+
+ // args should be a cons
+ Form args = parseForm();
+ if (args.type != FormType.CONS && args.type != FormType.NIL) {
+ return new ParseError("func definitions expect a list of arguments", line);
+ }
+
+ Func func = new Func(line);
+ func.name = cast(Symbol)sym;
+ func.args = cast(Cons)args;
+
+ char next;
+ while(peekable()) {
+ next = peek();
+ if (next == ')') {
+ break;
+ }
+ func.addToBody(parseForm());
+ }
+
+ if (!peekable()) {
+ return new ParseError("unterminated cons", line);
+ }
+
+ advance(); // consume closing paren
+
+ return func;
+ }
+
+
+ Form parseCons() {
+ skipWhitespace();
+ char next;
+ Cons cons = new Cons(line);
+
+ if (peekable() && peek() == ')') {
+ advance();
+ return cons;
+ }
+
+ Form f = parseForm();
+ if (f.type == FormType.SYMBOL) {
+ Symbol s = cast(Symbol)f;
+ switch (s.name) {
+ case "func":
+ return parseFunc();
+ default:
+ break;
+ }
+ }
+
+ // if it's not a special symbol, add it to the cons and keep parsing
+ cons.append(f);
+ while(peekable()) {
+ next = peek();
+ if (next == ')') {
+ break;
+ }
+ cons.append(parseForm());
+ }
+
+ if (!peekable()) {
+ return new ParseError("unterminated cons", line);
+ }
+
+ advance(); // go past closing paren
+
+ return cons;
+ }
+
+ Form parseForm() {
+ skipWhitespace();
+
+ if (!peekable()) {
+ return new Eof(line);
+ }
+
+ char next = peek();
+
+ if (isDigit(next)) {
+ return parseNumber();
+ }
+
+ switch (next) {
+ case '"':
+ advance(); // go past first quote
+ return parseString();
+ case '(':
+ advance(); // go past the open paren
+ return parseCons();
+ default:
+ return parseSymbol();
+ }
+ advance();
+
+ return new Atom(0, -1);
+ }
+
+ this(string source) {
+ this.source = source;
+ this.pos = 0;
+ this.line = 1;
+ }
+}