diff options
| -rw-r--r-- | chunk.d | 64 | ||||
| -rw-r--r-- | main.d | 118 | ||||
| -rw-r--r-- | parser.d | 398 |
3 files changed, 580 insertions, 0 deletions
@@ -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); + } + +} + + @@ -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; + } +} |
