Parsing Expressions 6

This commit is contained in:
nazrin 2025-05-31 15:20:43 +00:00
parent 2de4381fae
commit d937226553
5 changed files with 177 additions and 6 deletions

3
.gitignore vendored
View file

@ -14,3 +14,6 @@ lox-test-*
*.o
*.obj
*.lst
.msc/

View file

@ -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);
}

View file

@ -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){

148
src/jlox/parser.d Normal file
View 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
View 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;
}
}