summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGravatar Tom Willemse2021-01-18 17:42:02 -0800
committerGravatar Tom Willemse2021-01-18 17:42:02 -0800
commit80394e184274eb8ec3ce03cd901e5c817ec82e2a (patch)
tree67c0b023af49684ef79c1428c1b6fc9618f159c4
parent613c2388bedd7a9ad98587877d9f15c7dc99bc84 (diff)
downloadcrafting-interpreters-chapter-9-challenges.tar.gz
crafting-interpreters-chapter-9-challenges.zip
Add break statementchapter-9-challenges
-rw-r--r--src/com/craftinginterpreters/lox/AstPrinter.java5
-rw-r--r--src/com/craftinginterpreters/lox/Break.java5
-rw-r--r--src/com/craftinginterpreters/lox/CMakeLists.txt1
-rw-r--r--src/com/craftinginterpreters/lox/Interpreter.java13
-rw-r--r--src/com/craftinginterpreters/lox/Parser.java15
-rw-r--r--src/com/craftinginterpreters/lox/Scanner.java160
-rw-r--r--src/com/craftinginterpreters/lox/TokenType.java9
-rw-r--r--src/com/craftinginterpreters/tool/GenerateAst.java12
8 files changed, 138 insertions, 82 deletions
diff --git a/src/com/craftinginterpreters/lox/AstPrinter.java b/src/com/craftinginterpreters/lox/AstPrinter.java
index 65f1916..a72dc3a 100644
--- a/src/com/craftinginterpreters/lox/AstPrinter.java
+++ b/src/com/craftinginterpreters/lox/AstPrinter.java
@@ -65,6 +65,11 @@ class AstPrinter implements Expr.Visitor<String>, Stmt.Visitor<String> {
}
@Override
+ public String visitBreakStmt(Stmt.Break statement) {
+ return "break";
+ }
+
+ @Override
public String visitPrintStmt(Stmt.Print statement) {
return parenthesize("print", statement.expression);
}
diff --git a/src/com/craftinginterpreters/lox/Break.java b/src/com/craftinginterpreters/lox/Break.java
new file mode 100644
index 0000000..708915f
--- /dev/null
+++ b/src/com/craftinginterpreters/lox/Break.java
@@ -0,0 +1,5 @@
+package com.craftinginterpreters.lox;
+
+class Break extends RuntimeException {
+
+}
diff --git a/src/com/craftinginterpreters/lox/CMakeLists.txt b/src/com/craftinginterpreters/lox/CMakeLists.txt
index 447b2cf..18cdc77 100644
--- a/src/com/craftinginterpreters/lox/CMakeLists.txt
+++ b/src/com/craftinginterpreters/lox/CMakeLists.txt
@@ -20,4 +20,5 @@ add_jar(Lox
Interpreter.java
RuntimeError.java
Environment.java
+ Break.java
ENTRY_POINT com/craftinginterpreters/lox/Lox)
diff --git a/src/com/craftinginterpreters/lox/Interpreter.java b/src/com/craftinginterpreters/lox/Interpreter.java
index 9d0cfb3..22c8d4a 100644
--- a/src/com/craftinginterpreters/lox/Interpreter.java
+++ b/src/com/craftinginterpreters/lox/Interpreter.java
@@ -161,14 +161,23 @@ class Interpreter implements Expr.Visitor<Object>, Stmt.Visitor<Void> {
@Override
public Void visitWhileStmt(Stmt.While stmt) {
- while (isTruthy(evaluate(stmt.condition))) {
- execute(stmt.body);
+ try {
+ while (isTruthy(evaluate(stmt.condition))) {
+ execute(stmt.body);
+ }
+ } catch (Break ex) {
+ // We continue on.
}
return null;
}
@Override
+ public Void visitBreakStmt(Stmt.Break stmt) {
+ throw new Break();
+ }
+
+ @Override
public Object visitAssignExpr(Expr.Assign expr) {
Object value = evaluate(expr.value);
environment.assign(expr.name, value);
diff --git a/src/com/craftinginterpreters/lox/Parser.java b/src/com/craftinginterpreters/lox/Parser.java
index 7d7b27a..786d317 100644
--- a/src/com/craftinginterpreters/lox/Parser.java
+++ b/src/com/craftinginterpreters/lox/Parser.java
@@ -44,6 +44,8 @@ class Parser {
}
private Stmt statement() {
+ if (match(BREAK))
+ return breakStatement();
if (match(FOR))
return forStatement();
if (match(IF))
@@ -84,13 +86,11 @@ class Parser {
Stmt body = statement();
if (increment != null) {
- body = new Stmt.Block(
- Arrays.asList(
- body,
- new Stmt.Expression(increment)));
+ body = new Stmt.Block(Arrays.asList(body, new Stmt.Expression(increment)));
}
- if (condition == null) condition = new Expr.Literal(true);
+ if (condition == null)
+ condition = new Expr.Literal(true);
body = new Stmt.While(condition, body);
if (initializer != null) {
@@ -100,6 +100,11 @@ class Parser {
return body;
}
+ private Stmt breakStatement() {
+ consume(SEMICOLON, "Expect ';' after break statement.");
+ return new Stmt.Break();
+ }
+
private Stmt ifStatement() {
consume(LEFT_PAREN, "Expect '(' after 'if'.");
Expr condition = expression();
diff --git a/src/com/craftinginterpreters/lox/Scanner.java b/src/com/craftinginterpreters/lox/Scanner.java
index e21f701..b0aba3f 100644
--- a/src/com/craftinginterpreters/lox/Scanner.java
+++ b/src/com/craftinginterpreters/lox/Scanner.java
@@ -33,6 +33,7 @@ class Scanner {
keywords.put("true", TRUE);
keywords.put("var", VAR);
keywords.put("while", WHILE);
+ keywords.put("break", BREAK);
}
Scanner(String source) {
@@ -54,91 +55,118 @@ class Scanner {
char c = advance();
switch (c) {
- case '(': addToken(LEFT_PAREN); break;
- case ')': addToken(RIGHT_PAREN); break;
- case '{': addToken(LEFT_BRACE); break;
- case '}': addToken(RIGHT_BRACE); break;
- case ',': addToken(COMMA); break;
- case '.': addToken(DOT); break;
- case '-': addToken(MINUS); break;
- case '+': addToken(PLUS); break;
- case ';': addToken(SEMICOLON); break;
- case '*': addToken(STAR); break;
- case '!':
- addToken(match('=') ? BANG_EQUAL : BANG);
- break;
- case '=':
- addToken(match('=') ? EQUAL_EQUAL : EQUAL);
- break;
- case '<':
- addToken(match('=') ? LESS_EQUAL : LESS);
- break;
- case '>':
- addToken(match('=') ? GREATER_EQUAL : GREATER);
- break;
- case '/':
- if (match('/')) {
- // A comment goes until the end of the line.
- while (peek() != '\n' && !isAtEnd()) advance();
- } else {
- addToken(SLASH);
- }
- break;
-
- case ' ':
- case '\r':
- case '\t':
- // Ignore whitespace.
- break;
+ case '(':
+ addToken(LEFT_PAREN);
+ break;
+ case ')':
+ addToken(RIGHT_PAREN);
+ break;
+ case '{':
+ addToken(LEFT_BRACE);
+ break;
+ case '}':
+ addToken(RIGHT_BRACE);
+ break;
+ case ',':
+ addToken(COMMA);
+ break;
+ case '.':
+ addToken(DOT);
+ break;
+ case '-':
+ addToken(MINUS);
+ break;
+ case '+':
+ addToken(PLUS);
+ break;
+ case ';':
+ addToken(SEMICOLON);
+ break;
+ case '*':
+ addToken(STAR);
+ break;
+ case '!':
+ addToken(match('=') ? BANG_EQUAL : BANG);
+ break;
+ case '=':
+ addToken(match('=') ? EQUAL_EQUAL : EQUAL);
+ break;
+ case '<':
+ addToken(match('=') ? LESS_EQUAL : LESS);
+ break;
+ case '>':
+ addToken(match('=') ? GREATER_EQUAL : GREATER);
+ break;
+ case '/':
+ if (match('/')) {
+ // A comment goes until the end of the line.
+ while (peek() != '\n' && !isAtEnd())
+ advance();
+ } else {
+ addToken(SLASH);
+ }
+ break;
+
+ case ' ':
+ case '\r':
+ case '\t':
+ // Ignore whitespace.
+ break;
// I guess this code isn't meant to run on Mac OS 9 and before.
- case '\n':
- line++;
- break;
-
- case '"': string(); break;
-
- default:
- if (isDigit(c)) {
- number();
- } else if (isAlpha(c)) {
- identifier();
- } else {
- Lox.error(line, "Unexpected character.");
- }
- break;
+ case '\n':
+ line++;
+ break;
+
+ case '"':
+ string();
+ break;
+
+ default:
+ if (isDigit(c)) {
+ number();
+ } else if (isAlpha(c)) {
+ identifier();
+ } else {
+ Lox.error(line, "Unexpected character.");
+ }
+ break;
}
}
private void identifier() {
- while (isAlphaNumeric(peek())) advance();
+ while (isAlphaNumeric(peek()))
+ advance();
String text = source.substring(start, current);
TokenType type = keywords.get(text);
- if (type == null) type = IDENTIFIER;
+ if (type == null)
+ type = IDENTIFIER;
addToken(type);
}
private void number() {
- while (isDigit(peek())) advance();
+ while (isDigit(peek()))
+ advance();
// Look for a fractional part.
if (peek() == '.' && isDigit(peekNext())) {
// Consume the "."
advance();
- while (isDigit(peek())) advance();
+ while (isDigit(peek()))
+ advance();
}
- addToken(NUMBER,
- Double.parseDouble(source.substring(start, current)));
+ addToken(NUMBER, Double.parseDouble(source.substring(start, current)));
}
// I guess we won't be able to include escaped characters in the string? At
// least not escaped " characters.
private void string() {
while (peek() != '"' && !isAtEnd()) {
- if (peek() == '\n') line++;
+ if (peek() == '\n')
+ line++;
advance();
}
@@ -156,27 +184,29 @@ class Scanner {
}
private boolean match(char expected) {
- if (isAtEnd()) return false;
- if (source.charAt(current) != expected) return false;
+ if (isAtEnd())
+ return false;
+ if (source.charAt(current) != expected)
+ return false;
current++;
return true;
}
private char peek() {
- if (isAtEnd()) return '\0';
+ if (isAtEnd())
+ return '\0';
return source.charAt(current);
}
private char peekNext() {
- if (current + 1 >= source.length()) return '\0';
+ if (current + 1 >= source.length())
+ return '\0';
return source.charAt(current + 1);
}
private boolean isAlpha(char c) {
- return (c >= 'a' && c <= 'z') ||
- (c >= 'A' && c <= 'Z') ||
- c == '_';
+ return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_';
}
private boolean isAlphaNumeric(char c) {
diff --git a/src/com/craftinginterpreters/lox/TokenType.java b/src/com/craftinginterpreters/lox/TokenType.java
index 9ab2a8b..f32ab81 100644
--- a/src/com/craftinginterpreters/lox/TokenType.java
+++ b/src/com/craftinginterpreters/lox/TokenType.java
@@ -2,19 +2,16 @@ package com.craftinginterpreters.lox;
enum TokenType {
// Single-character tokens.
- LEFT_PAREN, RIGHT_PAREN, LEFT_BRACE, RIGHT_BRACE, COMMA, DOT, MINUS, PLUS,
- SEMICOLON, SLASH, STAR,
+ LEFT_PAREN, RIGHT_PAREN, LEFT_BRACE, RIGHT_BRACE, COMMA, DOT, MINUS, PLUS, SEMICOLON, SLASH, STAR,
// One or two character tokens.
- BANG, BANG_EQUAL, EQUAL, EQUAL_EQUAL, GREATER, GREATER_EQUAL, LESS,
- LESS_EQUAL,
+ BANG, BANG_EQUAL, EQUAL, EQUAL_EQUAL, GREATER, GREATER_EQUAL, LESS, LESS_EQUAL,
// Literals.
IDENTIFIER, STRING, NUMBER,
// Keywords.
- AND, CLASS, ELSE, FALSE, FUN, FOR, IF, NIL, OR, PRINT, RETURN, SUPER, THIS,
- TRUE, VAR, WHILE,
+ AND, CLASS, ELSE, FALSE, FUN, FOR, IF, NIL, OR, PRINT, RETURN, SUPER, THIS, TRUE, VAR, WHILE, BREAK,
EOF
}
diff --git a/src/com/craftinginterpreters/tool/GenerateAst.java b/src/com/craftinginterpreters/tool/GenerateAst.java
index 8845c6e..51b4ce3 100644
--- a/src/com/craftinginterpreters/tool/GenerateAst.java
+++ b/src/com/craftinginterpreters/tool/GenerateAst.java
@@ -22,7 +22,7 @@ public class GenerateAst {
defineAst(outputDir, "Stmt",
Arrays.asList("Block : List<Stmt> statements", "Expression : Expr expression",
"If : Expr condition, Stmt thenBranch, Stmt elseBranch", "Print : Expr expression",
- "Var : Token name, Expr initializer", "While : Expr condition, Stmt body"));
+ "Var : Token name, Expr initializer", "While : Expr condition, Stmt body", "Break : "));
}
private static void defineAst(String outputDir, String baseName, List<String> types) throws IOException {
@@ -71,8 +71,10 @@ public class GenerateAst {
// Store parameters in fields.
String[] fields = fieldList.split(", ");
for (String field : fields) {
- String name = field.split(" ")[1];
- writer.println(" this." + name + " = " + name + ";");
+ if (!field.isEmpty()) {
+ String name = field.split(" ")[1];
+ writer.println(" this." + name + " = " + name + ";");
+ }
}
writer.println(" }");
@@ -87,7 +89,9 @@ public class GenerateAst {
// Fields
writer.println();
for (String field : fields) {
- writer.println(" final " + field + ";");
+ if (!field.isEmpty()) {
+ writer.println(" final " + field + ";");
+ }
}
writer.println(" }");