Chapter 8: Add variable declarations

This commit is contained in:
Tom Willemse 2020-11-18 23:30:46 -08:00
parent 8019f6aa41
commit 899ecea236
6 changed files with 95 additions and 6 deletions

View file

@ -3,7 +3,7 @@ package com.craftinginterpreters.lox;
/** /**
* AstPrinter * AstPrinter
*/ */
class AstPrinter implements Expr.Visitor<String> { class AstPrinter implements Expr.Visitor<String>, Stmt.Visitor<String> {
String print(Expr expr) { String print(Expr expr) {
return expr.accept(this); return expr.accept(this);
} }
@ -30,6 +30,26 @@ class AstPrinter implements Expr.Visitor<String> {
return parenthesize(expr.operator.lexeme, expr.right); return parenthesize(expr.operator.lexeme, expr.right);
} }
@Override
public String visitVariableExpr(Expr.Variable expr) {
return expr.name.lexeme;
}
@Override
public String visitVarStmt(Stmt.Var statement) {
return parenthesize("define " + statement.name.lexeme + " = ", statement.initializer);
}
@Override
public String visitPrintStmt(Stmt.Print statement) {
return parenthesize("print", statement.expression);
}
@Override
public String visitExpressionStmt(Stmt.Expression expression) {
return expression.expression.accept(this);
}
private String parenthesize(String name, Expr... exprs) { private String parenthesize(String name, Expr... exprs) {
StringBuilder builder = new StringBuilder(); StringBuilder builder = new StringBuilder();

View file

@ -7,7 +7,7 @@ set(GENERATED_JAVA_FILENAMES
add_custom_command(OUTPUT ${GENERATED_JAVA_FILENAMES} add_custom_command(OUTPUT ${GENERATED_JAVA_FILENAMES}
COMMAND java -jar ${GENERATE_AST_JAR} ${CMAKE_CURRENT_BINARY_DIR} COMMAND java -jar ${GENERATE_AST_JAR} ${CMAKE_CURRENT_BINARY_DIR}
DEPENDS GenerateAst) DEPENDS GenerateAst ${GENERATE_AST_JAR})
add_jar(Lox add_jar(Lox
Lox.java Lox.java
@ -19,4 +19,5 @@ add_jar(Lox
Parser.java Parser.java
Interpreter.java Interpreter.java
RuntimeError.java RuntimeError.java
Environment.java
ENTRY_POINT com/craftinginterpreters/lox/Lox) ENTRY_POINT com/craftinginterpreters/lox/Lox)

View file

@ -0,0 +1,20 @@
package com.craftinginterpreters.lox;
import java.util.HashMap;
import java.util.Map;
class Environment {
private final Map<String, Object> values = new HashMap<>();
Object get(Token name) {
if (values.containsKey(name.lexeme)) {
return values.get(name.lexeme);
}
throw new RuntimeError(name, "Undefined variable '" + name.lexeme + "'.");
}
void define(String name, Object value) {
values.put(name, value);
}
}

View file

@ -3,6 +3,8 @@ package com.craftinginterpreters.lox;
import java.util.List; import java.util.List;
class Interpreter implements Expr.Visitor<Object>, Stmt.Visitor<Void> { class Interpreter implements Expr.Visitor<Object>, Stmt.Visitor<Void> {
private Environment environment = new Environment();
@Override @Override
public Object visitLiteralExpr(Expr.Literal expr) { public Object visitLiteralExpr(Expr.Literal expr) {
return expr.value; return expr.value;
@ -24,6 +26,11 @@ class Interpreter implements Expr.Visitor<Object>, Stmt.Visitor<Void> {
return null; return null;
} }
@Override
public Object visitVariableExpr(Expr.Variable expr) {
return environment.get(expr.name);
}
private void checkNumberOperand(Token operator, Object operand) { private void checkNumberOperand(Token operator, Object operand) {
if (operand instanceof Double) if (operand instanceof Double)
return; return;
@ -95,6 +102,17 @@ class Interpreter implements Expr.Visitor<Object>, Stmt.Visitor<Void> {
return null; return null;
} }
@Override
public Void visitVarStmt(Stmt.Var stmt) {
Object value = null;
if (stmt.initializer != null) {
value = evaluate(stmt.initializer);
}
environment.define(stmt.name.lexeme, value);
return null;
}
@Override @Override
public Object visitBinaryExpr(Expr.Binary expr) { public Object visitBinaryExpr(Expr.Binary expr) {
Object left = evaluate(expr.left); Object left = evaluate(expr.left);

View file

@ -20,7 +20,7 @@ class Parser {
List<Stmt> statements = new ArrayList<>(); List<Stmt> statements = new ArrayList<>();
while (!isAtEnd()) { while (!isAtEnd()) {
statements.add(statement()); statements.add(declaration());
} }
return statements; return statements;
@ -30,6 +30,18 @@ class Parser {
return equality(); return equality();
} }
private Stmt declaration() {
try {
if (match(VAR))
return varDeclaration();
return statement();
} catch (ParseError error) {
synchronize();
return null;
}
}
private Stmt statement() { private Stmt statement() {
if (match(PRINT)) if (match(PRINT))
return printStatement(); return printStatement();
@ -43,6 +55,18 @@ class Parser {
return new Stmt.Print(value); return new Stmt.Print(value);
} }
private Stmt varDeclaration() {
Token name = consume(IDENTIFIER, "Expect variable name.");
Expr initializer = null;
if (match(EQUAL)) {
initializer = expression();
}
consume(SEMICOLON, "Expect ';' after variable declaration.");
return new Stmt.Var(name, initializer);
}
private Stmt expressionStatement() { private Stmt expressionStatement() {
Expr expr = expression(); Expr expr = expression();
consume(SEMICOLON, "Expect ';' after expression."); consume(SEMICOLON, "Expect ';' after expression.");
@ -119,6 +143,10 @@ class Parser {
return new Expr.Literal(previous().literal); return new Expr.Literal(previous().literal);
} }
if (match(IDENTIFIER)) {
return new Expr.Variable(previous());
}
if (match(LEFT_PAREN)) { if (match(LEFT_PAREN)) {
Expr expr = expression(); Expr expr = expression();
consume(RIGHT_PAREN, "Expect ')' after expression."); consume(RIGHT_PAREN, "Expect ')' after expression.");

View file

@ -14,9 +14,11 @@ public class GenerateAst {
String outputDir = args[0]; String outputDir = args[0];
defineAst(outputDir, "Expr", Arrays.asList("Binary : Expr left, Token operator, Expr right", defineAst(outputDir, "Expr",
"Grouping : Expr expression", "Literal : Object value", "Unary : Token operator, Expr right")); Arrays.asList("Binary : Expr left, Token operator, Expr right", "Grouping : Expr expression",
defineAst(outputDir, "Stmt", Arrays.asList("Expression : Expr expression", "Print : Expr expression")); "Literal : Object value", "Unary : Token operator, Expr right", "Variable : Token name"));
defineAst(outputDir, "Stmt", Arrays.asList("Expression : Expr expression", "Print : Expr expression",
"Var : Token name, Expr initializer"));
} }
private static void defineAst(String outputDir, String baseName, List<String> types) throws IOException { private static void defineAst(String outputDir, String baseName, List<String> types) throws IOException {