Bug fixing; Finished Parser in Chapter 6; Starting Interpreter
This commit is contained in:
parent
da88ff4071
commit
5cd5dda34d
54
cobalt/lang/AstPrinter.java
Normal file
54
cobalt/lang/AstPrinter.java
Normal 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));
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
34
cobalt/lang/Intepreter.java
Normal file
34
cobalt/lang/Intepreter.java
Normal 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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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)));
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user