Parsing Expressions 6
This commit is contained in:
parent
2de4381fae
commit
d937226553
5 changed files with 177 additions and 6 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
|
@ -14,3 +14,6 @@ lox-test-*
|
||||||
*.o
|
*.o
|
||||||
*.obj
|
*.obj
|
||||||
*.lst
|
*.lst
|
||||||
|
|
||||||
|
.msc/
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ import taggedalgebraic;
|
||||||
|
|
||||||
import jlox.token;
|
import jlox.token;
|
||||||
import jlox.tokentype;
|
import jlox.tokentype;
|
||||||
|
import jlox.util;
|
||||||
|
|
||||||
abstract class Expr{
|
abstract class Expr{
|
||||||
interface Visitor(R){
|
interface Visitor(R){
|
||||||
|
|
@ -20,10 +21,7 @@ abstract class Expr{
|
||||||
static foreach(T; rTypes)
|
static foreach(T; rTypes)
|
||||||
abstract T accept(Visitor!T visitor);
|
abstract T accept(Visitor!T visitor);
|
||||||
private template defCtorAndAccept(){
|
private template defCtorAndAccept(){
|
||||||
this(Args...)(auto ref Args args){
|
mixin defaultCtor;
|
||||||
static foreach(i, a; args)
|
|
||||||
this.tupleof[i] = a;
|
|
||||||
}
|
|
||||||
static foreach(T; rTypes)
|
static foreach(T; rTypes)
|
||||||
override T accept(Visitor!T visitor) => visitor.visit(this);
|
override T accept(Visitor!T visitor) => visitor.visit(this);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,12 +10,21 @@ import commandr;
|
||||||
import jlox.token;
|
import jlox.token;
|
||||||
import jlox.tokentype;
|
import jlox.tokentype;
|
||||||
import jlox.scanner;
|
import jlox.scanner;
|
||||||
|
import jlox.parser;
|
||||||
|
import jlox.expr;
|
||||||
|
|
||||||
static bool hadError = false;
|
static bool hadError = false;
|
||||||
|
|
||||||
void error(int line, string message){
|
void error(int line, string message){
|
||||||
report(line, "", 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){
|
void report(int line, string where, string message){
|
||||||
stderr.writeln("[line " ~ line.to!string ~ "] Error" ~ where ~ ": " ~ message);
|
stderr.writeln("[line " ~ line.to!string ~ "] Error" ~ where ~ ": " ~ message);
|
||||||
hadError = true;
|
hadError = true;
|
||||||
|
|
@ -25,8 +34,13 @@ void run(string source){
|
||||||
Scanner scanner = new Scanner(source);
|
Scanner scanner = new Scanner(source);
|
||||||
Token[] tokens = scanner.scanTokens();
|
Token[] tokens = scanner.scanTokens();
|
||||||
|
|
||||||
foreach(token; tokens)
|
Parser parser = new Parser(tokens);
|
||||||
writeln(token);
|
Expr expression = parser.parse();
|
||||||
|
|
||||||
|
if(hadError)
|
||||||
|
return;
|
||||||
|
|
||||||
|
writeln((new AstPrinter).print(expression));
|
||||||
}
|
}
|
||||||
|
|
||||||
void runFile(string path){
|
void runFile(string path){
|
||||||
|
|
|
||||||
148
src/jlox/parser.d
Normal file
148
src/jlox/parser.d
Normal file
|
|
@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
8
src/jlox/util.d
Normal file
8
src/jlox/util.d
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
module jlox.util;
|
||||||
|
|
||||||
|
template defaultCtor(){
|
||||||
|
this(Args...)(auto ref Args args){
|
||||||
|
static foreach(i, a; args)
|
||||||
|
this.tupleof[i] = a;
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue