diff --git a/.gitignore b/.gitignore index f9dffc0..a8ba56d 100644 --- a/.gitignore +++ b/.gitignore @@ -14,3 +14,6 @@ lox-test-* *.o *.obj *.lst + +.msc/ + diff --git a/src/jlox/expr.d b/src/jlox/expr.d index 4d78df2..674a826 100644 --- a/src/jlox/expr.d +++ b/src/jlox/expr.d @@ -8,6 +8,7 @@ import taggedalgebraic; import jlox.token; import jlox.tokentype; +import jlox.util; abstract class Expr{ interface Visitor(R){ @@ -20,10 +21,7 @@ abstract class Expr{ static foreach(T; rTypes) abstract T accept(Visitor!T visitor); private template defCtorAndAccept(){ - this(Args...)(auto ref Args args){ - static foreach(i, a; args) - this.tupleof[i] = a; - } + mixin defaultCtor; static foreach(T; rTypes) override T accept(Visitor!T visitor) => visitor.visit(this); } diff --git a/src/jlox/main.d b/src/jlox/main.d index 1da72ac..08d0b6a 100644 --- a/src/jlox/main.d +++ b/src/jlox/main.d @@ -10,12 +10,21 @@ import commandr; import jlox.token; import jlox.tokentype; import jlox.scanner; +import jlox.parser; +import jlox.expr; static bool hadError = false; void error(int line, string message){ report(line, "", message); } +void error(Token token, string message){ + if (token.type == TokenType.EOF) { + report(token.line, " at end", message); + } else { + report(token.line, " at '" ~ token.lexeme ~ "'", message); + } +} void report(int line, string where, string message){ stderr.writeln("[line " ~ line.to!string ~ "] Error" ~ where ~ ": " ~ message); hadError = true; @@ -25,8 +34,13 @@ void run(string source){ Scanner scanner = new Scanner(source); Token[] tokens = scanner.scanTokens(); - foreach(token; tokens) - writeln(token); + Parser parser = new Parser(tokens); + Expr expression = parser.parse(); + + if(hadError) + return; + + writeln((new AstPrinter).print(expression)); } void runFile(string path){ diff --git a/src/jlox/parser.d b/src/jlox/parser.d new file mode 100644 index 0000000..09487c4 --- /dev/null +++ b/src/jlox/parser.d @@ -0,0 +1,148 @@ +module jlox.parser; + +import std.stdio; + +import jlox.token; +import jlox.tokentype; +import jlox.util; +import jlox.expr; +import jlox.main : loxError = error; + +class Parser{ + private Token[] tokens; + private int current = 0; + + mixin defaultCtor; + Expr parse(){ + try { + return expression(); + } catch (ParseError error) { + return null; + } + } + + private bool isAtEnd() => peek().type == EOF; + private Token peek() => tokens[current]; + private Token previous() => tokens[current-1]; + private bool check(TokenType type){ + if(isAtEnd) + return false; + return peek().type == type; + } + private Token advance(){ + if(!isAtEnd()) + current++; + return previous(); + } + private bool match(TokenType[] types...){ + foreach(TokenType type; types){ + if(check(type)){ + advance(); + return true; + } + } + return false; + } + private Token consume(TokenType type, string message){ + if(check(type)) + return advance(); + throw error(peek(), message); + } + private static class ParseError : Exception { + this(string s){ + super(s); + } + } + private ParseError error(Token token, string message){ + loxError(token, message); + return new ParseError("hello"); + } + private void synchronize(){ + advance(); + with(TokenType) while(!isAtEnd()){ + if(previous().type == SEMICOLON) + return; + switch(peek().type){ + case CLASS: + case FUN: + case VAR: + case FOR: + case IF: + case WHILE: + case PRINT: + case RETURN: + return; + default: + assert(0); + } + advance(); + } + } + + + private Expr expression(){ + return equality(); + } + private Expr equality(){ + Expr expr = comparison(); + while(match(TokenType.BANG_EQUAL, TokenType.EQUAL_EQUAL)){ + Token operator = previous(); + Expr right = comparison(); + expr = new Expr.Binary(expr, operator, right); + } + return expr; + } + private Expr comparison(){ + Expr expr = term(); + while(match(TokenType.GREATER, TokenType.GREATER_EQUAL, TokenType.LESS, TokenType.LESS_EQUAL)){ + Token operator = previous(); + Expr right = term(); + expr = new Expr.Binary(expr, operator, right); + } + return expr; + } + private Expr term(){ + Expr expr = factor(); + while(match(TokenType.MINUS, TokenType.PLUS)){ + Token operator = previous(); + Expr right = factor(); + expr = new Expr.Binary(expr, operator, right); + } + return expr; + } + private Expr factor(){ + Expr expr = unary(); + while(match(TokenType.SLASH, TokenType.STAR)){ + Token operator = previous(); + Expr right = unary(); + expr = new Expr.Binary(expr, operator, right); + } + return expr; + } + private Expr unary(){ + if(match(TokenType.BANG, TokenType.MINUS)){ + Token operator = previous(); + Expr right = unary(); + return new Expr.Unary(operator, right); + } + return primary(); + } + private Expr primary(){ + if(match(TokenType.FALSE)) + return new Expr.Literal(false); + if(match(TokenType.TRUE)) + return new Expr.Literal(true); + if(match(TokenType.NIL)) + return new Expr.Literal(null); + if(match(TokenType.NUMBER, TokenType.STRING)) + return new Expr.Literal(previous().literal); + if(match(TokenType.LEFT_PAREN)){ + Expr expr = expression(); + consume(TokenType.RIGHT_PAREN, "Expect ')' after expression."); + return new Expr.Grouping(expr); + } + throw error(peek(), "Expect expression."); + assert(0); + } + +} diff --git a/src/jlox/util.d b/src/jlox/util.d new file mode 100644 index 0000000..0a650cd --- /dev/null +++ b/src/jlox/util.d @@ -0,0 +1,8 @@ +module jlox.util; + +template defaultCtor(){ + this(Args...)(auto ref Args args){ + static foreach(i, a; args) + this.tupleof[i] = a; + } +}