diff options
Diffstat (limited to 'src/com/craftinginterpreters/lox')
-rw-r--r-- | src/com/craftinginterpreters/lox/AstPrinter.java | 5 | ||||
-rw-r--r-- | src/com/craftinginterpreters/lox/Break.java | 5 | ||||
-rw-r--r-- | src/com/craftinginterpreters/lox/CMakeLists.txt | 1 | ||||
-rw-r--r-- | src/com/craftinginterpreters/lox/Interpreter.java | 13 | ||||
-rw-r--r-- | src/com/craftinginterpreters/lox/Parser.java | 15 | ||||
-rw-r--r-- | src/com/craftinginterpreters/lox/Scanner.java | 160 | ||||
-rw-r--r-- | src/com/craftinginterpreters/lox/TokenType.java | 9 |
7 files changed, 130 insertions, 78 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 } |