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.BufferedReader;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
@ -23,12 +23,14 @@ public class Cobalt {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private static void runFile(String path) throws IOException {
|
private static void runFile(String path) throws IOException {
|
||||||
byte[] bytes = Files.readAllBytes(Paths.get(path));
|
byte[] bytes = Files.readAllBytes(Paths.get(path));
|
||||||
run(new String(bytes, Charset.defaultCharset()));
|
run(new String(bytes, Charset.defaultCharset()));
|
||||||
if (hadError) System.exit(65);
|
if (hadError) System.exit(65);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private static void runPrompt() throws IOException {
|
private static void runPrompt() throws IOException {
|
||||||
InputStreamReader input = new InputStreamReader(System.in);
|
InputStreamReader input = new InputStreamReader(System.in);
|
||||||
BufferedReader reader = new BufferedReader(input);
|
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) {
|
private static void run(String source) {
|
||||||
System.out.println(token);
|
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) {
|
static void error(int line, String message) {
|
||||||
report(line, "", message);
|
report(line, "", message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private static void report(int line, String where, String message) {
|
private static void report(int line, String where, String message) {
|
||||||
System.err.println(
|
System.err.println(
|
||||||
"[line " + line + "] Error" + where + ": " + message
|
"[line " + line + "] Error" + where + ": " + message
|
||||||
);
|
);
|
||||||
hadError = true;
|
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;
|
package cobalt.lang;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
abstract class Expr {
|
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 {
|
static class Binary extends Expr {
|
||||||
Binary(Expr left, Token operator, Expr right) {
|
Binary(Expr left, Token operator, Expr right) {
|
||||||
this.left = left;
|
this.left = left;
|
||||||
|
|
@ -8,34 +18,55 @@ abstract class Expr {
|
||||||
this.right = right;
|
this.right = right;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
<R> R accept(Visitor<R> visitor) {
|
||||||
|
return visitor.visitBinaryExpr(this);
|
||||||
|
}
|
||||||
|
|
||||||
final Expr left;
|
final Expr left;
|
||||||
final Token operator;
|
final Token operator;
|
||||||
final Expr right;
|
final Expr right;
|
||||||
}
|
}
|
||||||
|
|
||||||
static class Unary extends Expr {
|
|
||||||
Unary(Token operator, Expr right) {
|
|
||||||
this.operator = operator;
|
|
||||||
this.right = right;
|
|
||||||
}
|
|
||||||
|
|
||||||
final Token operator;
|
|
||||||
final Expr right;
|
|
||||||
}
|
|
||||||
|
|
||||||
static class Grouping extends Expr {
|
static class Grouping extends Expr {
|
||||||
Grouping(Expr expression) {
|
Grouping(Expr expression) {
|
||||||
this.expression = expression;
|
this.expression = expression;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
<R> R accept(Visitor<R> visitor) {
|
||||||
|
return visitor.visitGroupingExpr(this);
|
||||||
|
}
|
||||||
|
|
||||||
final Expr expression;
|
final Expr expression;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static class Literal extends Expr {
|
static class Literal extends Expr {
|
||||||
Literal(Object value) {
|
Literal(Object value) {
|
||||||
this.value = value;
|
this.value = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
<R> R accept(Visitor<R> visitor) {
|
||||||
|
return visitor.visitLiteralExpr(this);
|
||||||
|
}
|
||||||
|
|
||||||
final Object value;
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
package cobalt.lang;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import static cobalt.lang.TokenType.*;
|
import static cobalt.lang.TokenType.*;
|
||||||
|
|
||||||
class Parser {
|
class Parser {
|
||||||
|
private static class ParseError extends RuntimeException {}
|
||||||
|
|
||||||
private final List<Token> tokens;
|
private final List<Token> tokens;
|
||||||
private int current = 0;
|
private int current = 0;
|
||||||
|
|
||||||
|
|
@ -18,6 +13,16 @@ class Parser {
|
||||||
this.tokens = tokens;
|
this.tokens = tokens;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Expr parse() {
|
||||||
|
try {
|
||||||
|
return expression();
|
||||||
|
} catch (ParseError error) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
private Expr expression() {
|
private Expr expression() {
|
||||||
return equality();
|
return equality();
|
||||||
}
|
}
|
||||||
|
|
@ -38,6 +43,7 @@ class Parser {
|
||||||
return expr;
|
return expr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Parser definition for handling comparisons
|
// Parser definition for handling comparisons
|
||||||
//
|
//
|
||||||
// comparison -> term ( ( ">" | ">=" | "<" | "<=" ) term )* ;
|
// comparison -> term ( ( ">" | ">=" | "<" | "<=" ) term )* ;
|
||||||
|
|
@ -80,7 +86,12 @@ class Parser {
|
||||||
return expr;
|
return expr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Parser definition for handling unary statements
|
||||||
|
//
|
||||||
|
// unary -> ( "!" | "-" ) unary
|
||||||
|
// | primary ;
|
||||||
|
//
|
||||||
private Expr unary() {
|
private Expr unary() {
|
||||||
if (match(BANG, MINUS)) {
|
if (match(BANG, MINUS)) {
|
||||||
Token operator = previous();
|
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() {
|
private Expr primary() {
|
||||||
if (match(FALSE)) return new Expr.Literal(false);
|
if (match(FALSE)) return new Expr.Literal(false);
|
||||||
if (match(TRUE)) return new Expr.Literal(true);
|
if (match(TRUE)) return new Expr.Literal(true);
|
||||||
|
|
@ -106,6 +122,8 @@ class Parser {
|
||||||
consume(RIGHT_PAREN, "Expect ')' after expression.");
|
consume(RIGHT_PAREN, "Expect ')' after expression.");
|
||||||
return new Expr.Grouping(expr);
|
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
|
// Returns true if the current token is of the given type, false if otherwise
|
||||||
private boolean check(TokenType type) {
|
private boolean check(TokenType type) {
|
||||||
if (isAtEnd()) return false;
|
if (isAtEnd()) return false;
|
||||||
|
|
@ -153,7 +179,37 @@ class Parser {
|
||||||
private Token previous() {
|
private Token previous() {
|
||||||
return tokens.get(current - 1);
|
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 {
|
static {
|
||||||
keywords = new HashMap<>();
|
keywords = new HashMap<>();
|
||||||
keywords.put("and", AND);
|
keywords.put("and", AND);
|
||||||
keywords.put("class", ELSE);
|
keywords.put("class", CLASS);
|
||||||
|
keywords.put("else", ELSE);
|
||||||
keywords.put("false", FALSE);
|
keywords.put("false", FALSE);
|
||||||
keywords.put("for", FOR);
|
keywords.put("for", FOR);
|
||||||
keywords.put("func", FUNC);
|
keywords.put("func", FUNC);
|
||||||
|
|
@ -142,6 +143,8 @@ class Scanner {
|
||||||
|
|
||||||
while (isDigit(peek())) advance();
|
while (isDigit(peek())) advance();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
addToken(NUMBER, Double.parseDouble(source.substring(start, current)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user