Bug fixing; Finished Parser in Chapter 6; Starting Interpreter

This commit is contained in:
Garrett Dickinson 2022-07-04 20:33:01 -05:00
parent da88ff4071
commit 5cd5dda34d
6 changed files with 219 additions and 29 deletions

View File

@ -0,0 +1,54 @@
package cobalt.lang;
// Creates an unambiguous, if ugly, string representation of AST nodes.
class AstPrinter implements Expr.Visitor<String> {
String print(Expr expr) {
return expr.accept(this);
}
@Override
public String visitBinaryExpr(Expr.Binary expr) {
return parenthesize(expr.operator.lexeme, expr.left, expr.right);
}
@Override
public String visitGroupingExpr(Expr.Grouping expr) {
return parenthesize("group", expr.expression);
}
@Override
public String visitLiteralExpr(Expr.Literal expr) {
if (expr.value == null) return "nil";
return expr.value.toString();
}
@Override
public String visitUnaryExpr(Expr.Unary expr) {
return parenthesize(expr.operator.lexeme, expr.right);
}
private String parenthesize(String name, Expr... exprs) {
StringBuilder builder = new StringBuilder();
builder.append("(").append(name);
for (Expr expr : exprs) {
builder.append(" ");
builder.append(expr.accept(this));
}
builder.append(")");
return builder.toString();
}
public static void main(String[] args) {
Expr expression = new Expr.Binary(
new Expr.Unary(
new Token(TokenType.MINUS, "-", null, 1),
new Expr.Literal(123)),
new Token(TokenType.STAR, "*", null, 1),
new Expr.Grouping(
new Expr.Literal(45.67)));
System.out.println(new AstPrinter().print(expression));
}
}

View File

@ -1,4 +1,4 @@
package cobalt;
package cobalt.lang;
import java.io.BufferedReader;
import java.io.IOException;
@ -23,12 +23,14 @@ public class Cobalt {
}
}
private static void runFile(String path) throws IOException {
byte[] bytes = Files.readAllBytes(Paths.get(path));
run(new String(bytes, Charset.defaultCharset()));
if (hadError) System.exit(65);
}
private static void runPrompt() throws IOException {
InputStreamReader input = new InputStreamReader(System.in);
BufferedReader reader = new BufferedReader(input);
@ -42,27 +44,37 @@ public class Cobalt {
}
}
private static void run(String source) {
// Wont compile since we don't use the standard Java Scanner,
// gets implemented further in the textbook
Scanner scanner = new Scanner(source);
// Same situation for Token type
List<Token> tokens = scanner.scanTokens();
for (Token token : tokens) {
System.out.println(token);
}
private static void run(String source) {
Scanner scanner = new Scanner(source);
List<Token> tokens = scanner.scanTokens();
Parser parser = new Parser(tokens);
Expr expression = parser.parse();
if (hadError) return;
System.out.println(new AstPrinter().print(expression));
}
static void error(int line, String message) {
report(line, "", message);
}
private static void report(int line, String where, String message) {
System.err.println(
"[line " + line + "] Error" + where + ": " + message
);
hadError = true;
}
static 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);
}
}
}

View File

@ -1,6 +1,16 @@
package cobalt.lang;
import java.util.List;
abstract class Expr {
interface Visitor<R> {
R visitBinaryExpr(Binary expr);
R visitGroupingExpr(Grouping expr);
R visitLiteralExpr(Literal expr);
R visitUnaryExpr(Unary expr);
}
static class Binary extends Expr {
Binary(Expr left, Token operator, Expr right) {
this.left = left;
@ -8,34 +18,55 @@ abstract class Expr {
this.right = right;
}
<R> R accept(Visitor<R> visitor) {
return visitor.visitBinaryExpr(this);
}
final Expr left;
final Token operator;
final Expr right;
}
static class Grouping extends Expr {
Grouping(Expr expression) {
this.expression = expression;
}
<R> R accept(Visitor<R> visitor) {
return visitor.visitGroupingExpr(this);
}
final Expr expression;
}
static class Literal extends Expr {
Literal(Object value) {
this.value = value;
}
<R> R accept(Visitor<R> visitor) {
return visitor.visitLiteralExpr(this);
}
final Object value;
}
static class Unary extends Expr {
Unary(Token operator, Expr right) {
this.operator = operator;
this.right = right;
}
<R> R accept(Visitor<R> visitor) {
return visitor.visitUnaryExpr(this);
}
final Token operator;
final Expr right;
}
static class Grouping extends Expr {
Grouping(Expr expression) {
this.expression = expression;
}
final Expr expression;
}
static class Literal extends Expr {
Literal(Object value) {
this.value = value;
}
final Object value;
}
abstract <R> R accept(Visitor<R> visitor);
}

View File

@ -0,0 +1,34 @@
//
// Continue at Section 7.2.3
// Page 100
//
package cobalt.lang;
class Interpreter implements Expr.Visitor<Object> {
@Override
public Object visitLiteralExpr(Expr.Literal expr) {
return expr.value;
}
@Override
public Object visitUnaryExpr(Expr.Unary expr) {
Object right = evaluate(expr.right);
switch (expr.operator.type) {
case MINUS:
return -(double)right;
}
return null;
}
@Override
public Object visitGroupingExpr(Expr.Grouping expr) {
return evaluate(expr.expression);
}
private Object evaluate(Expr expr) {
return expr.accept(this);
}
}

View File

@ -1,16 +1,11 @@
///////////
//
// Continue at page 89, Section 6.3
//
///////////
package cobalt.lang;
import java.util.List;
import static cobalt.lang.TokenType.*;
class Parser {
private static class ParseError extends RuntimeException {}
private final List<Token> tokens;
private int current = 0;
@ -18,6 +13,16 @@ class Parser {
this.tokens = tokens;
}
Expr parse() {
try {
return expression();
} catch (ParseError error) {
return null;
}
}
private Expr expression() {
return equality();
}
@ -38,6 +43,7 @@ class Parser {
return expr;
}
// Parser definition for handling comparisons
//
// comparison -> term ( ( ">" | ">=" | "<" | "<=" ) term )* ;
@ -81,6 +87,11 @@ class Parser {
}
// Parser definition for handling unary statements
//
// unary -> ( "!" | "-" ) unary
// | primary ;
//
private Expr unary() {
if (match(BANG, MINUS)) {
Token operator = previous();
@ -92,6 +103,11 @@ class Parser {
}
// Parser definition for handling primary statements
//
// primary -> NUMBER | STRING | "true" | "false" | "nil"
// | "(" expression ")" ;
//
private Expr primary() {
if (match(FALSE)) return new Expr.Literal(false);
if (match(TRUE)) return new Expr.Literal(true);
@ -106,6 +122,8 @@ class Parser {
consume(RIGHT_PAREN, "Expect ')' after expression.");
return new Expr.Grouping(expr);
}
throw error(peek(), "Expected expression.");
}
@ -123,6 +141,14 @@ class Parser {
}
// Consume function
private Token consume(TokenType type, String message) {
if (check(type)) return advance();
throw error(peek(), message);
}
// Returns true if the current token is of the given type, false if otherwise
private boolean check(TokenType type) {
if (isAtEnd()) return false;
@ -154,6 +180,36 @@ class Parser {
return tokens.get(current - 1);
}
// Throw a new parse error based on the incorrect token
private ParseError error(Token token, String message) {
Cobalt.error(token, message);
return new ParseError();
}
// Once finding a parse error, discard the rest of the tokens in
// a statement in order to prevent cascading errors.
private void synchronize() {
advance();
while (!isAtEnd()) {
if (previous().type == SEMICOLON) return;
switch (peek().type) {
case CLASS:
case FOR:
case FUNC:
case IF:
case PRINT:
case RETURN:
case VAR:
case WHILE:
return;
}
advance();
}
}
}

View File

@ -18,7 +18,8 @@ class Scanner {
static {
keywords = new HashMap<>();
keywords.put("and", AND);
keywords.put("class", ELSE);
keywords.put("class", CLASS);
keywords.put("else", ELSE);
keywords.put("false", FALSE);
keywords.put("for", FOR);
keywords.put("func", FUNC);
@ -142,6 +143,8 @@ class Scanner {
while (isDigit(peek())) advance();
}
addToken(NUMBER, Double.parseDouble(source.substring(start, current)));
}