chapter 10 in java
This commit is contained in:
parent
f82919414e
commit
a13db8e29c
@ -116,6 +116,18 @@
|
|||||||
<goals>
|
<goals>
|
||||||
<goal>java</goal>
|
<goal>java</goal>
|
||||||
</goals>
|
</goals>
|
||||||
|
<configuration>
|
||||||
|
<mainClass>ch.vanwa.lox_interpreter.App</mainClass>
|
||||||
|
<arguments>
|
||||||
|
<argument>${script}</argument>
|
||||||
|
</arguments>
|
||||||
|
</configuration>
|
||||||
|
</execution>
|
||||||
|
<execution>
|
||||||
|
<id>jlox-repl</id>
|
||||||
|
<goals>
|
||||||
|
<goal>java</goal>
|
||||||
|
</goals>
|
||||||
<configuration>
|
<configuration>
|
||||||
<mainClass>ch.vanwa.lox_interpreter.App</mainClass>
|
<mainClass>ch.vanwa.lox_interpreter.App</mainClass>
|
||||||
</configuration>
|
</configuration>
|
||||||
|
@ -5,20 +5,14 @@ import java.util.List;
|
|||||||
abstract class Expr {
|
abstract class Expr {
|
||||||
interface Visitor<R> {
|
interface Visitor<R> {
|
||||||
R visitAssignExpr(Assign expr);
|
R visitAssignExpr(Assign expr);
|
||||||
|
|
||||||
R visitBinaryExpr(Binary expr);
|
R visitBinaryExpr(Binary expr);
|
||||||
|
R visitCallExpr(Call expr);
|
||||||
R visitGroupingExpr(Grouping expr);
|
R visitGroupingExpr(Grouping expr);
|
||||||
|
|
||||||
R visitLiteralExpr(Literal expr);
|
R visitLiteralExpr(Literal expr);
|
||||||
|
|
||||||
R visitLogicalExpr(Logical expr);
|
R visitLogicalExpr(Logical expr);
|
||||||
|
|
||||||
R visitUnaryExpr(Unary expr);
|
R visitUnaryExpr(Unary expr);
|
||||||
|
|
||||||
R visitVariableExpr(Variable expr);
|
R visitVariableExpr(Variable expr);
|
||||||
}
|
}
|
||||||
|
|
||||||
static class Assign extends Expr {
|
static class Assign extends Expr {
|
||||||
Assign(Token name, Expr value) {
|
Assign(Token name, Expr value) {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
@ -33,7 +27,6 @@ abstract class Expr {
|
|||||||
final Token name;
|
final Token name;
|
||||||
final Expr value;
|
final Expr value;
|
||||||
}
|
}
|
||||||
|
|
||||||
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;
|
||||||
@ -50,7 +43,22 @@ abstract class Expr {
|
|||||||
final Token operator;
|
final Token operator;
|
||||||
final Expr right;
|
final Expr right;
|
||||||
}
|
}
|
||||||
|
static class Call extends Expr {
|
||||||
|
Call(Expr callee, Token paren, List<Expr> arguments) {
|
||||||
|
this.callee = callee;
|
||||||
|
this.paren = paren;
|
||||||
|
this.arguments = arguments;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
<R> R accept(Visitor<R> visitor) {
|
||||||
|
return visitor.visitCallExpr(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
final Expr callee;
|
||||||
|
final Token paren;
|
||||||
|
final List<Expr> arguments;
|
||||||
|
}
|
||||||
static class Grouping extends Expr {
|
static class Grouping extends Expr {
|
||||||
Grouping(Expr expression) {
|
Grouping(Expr expression) {
|
||||||
this.expression = expression;
|
this.expression = expression;
|
||||||
@ -63,7 +71,6 @@ abstract class Expr {
|
|||||||
|
|
||||||
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;
|
||||||
@ -76,7 +83,6 @@ abstract class Expr {
|
|||||||
|
|
||||||
final Object value;
|
final Object value;
|
||||||
}
|
}
|
||||||
|
|
||||||
static class Logical extends Expr {
|
static class Logical extends Expr {
|
||||||
Logical(Expr left, Token operator, Expr right) {
|
Logical(Expr left, Token operator, Expr right) {
|
||||||
this.left = left;
|
this.left = left;
|
||||||
@ -93,7 +99,6 @@ abstract class Expr {
|
|||||||
final Token operator;
|
final Token operator;
|
||||||
final Expr right;
|
final Expr right;
|
||||||
}
|
}
|
||||||
|
|
||||||
static class Unary extends Expr {
|
static class Unary extends Expr {
|
||||||
Unary(Token operator, Expr right) {
|
Unary(Token operator, Expr right) {
|
||||||
this.operator = operator;
|
this.operator = operator;
|
||||||
@ -108,7 +113,6 @@ abstract class Expr {
|
|||||||
final Token operator;
|
final Token operator;
|
||||||
final Expr right;
|
final Expr right;
|
||||||
}
|
}
|
||||||
|
|
||||||
static class Variable extends Expr {
|
static class Variable extends Expr {
|
||||||
Variable(Token name) {
|
Variable(Token name) {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
|
@ -1,13 +1,34 @@
|
|||||||
package ch.vanwa.lox_interpreter;
|
package ch.vanwa.lox_interpreter;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Evaluate Lox expressions.
|
* Evaluate Lox expressions.
|
||||||
*/
|
*/
|
||||||
class Interpreter implements Expr.Visitor<Object>, Stmt.Visitor<Void> {
|
class Interpreter implements Expr.Visitor<Object>, Stmt.Visitor<Void> {
|
||||||
|
final Environment globals = new Environment();
|
||||||
|
private Environment environment = globals;
|
||||||
|
|
||||||
private Environment environment = new Environment();
|
Interpreter() {
|
||||||
|
globals.define("clock", new LoxCallable() {
|
||||||
|
@Override
|
||||||
|
public int arity() {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object call(Interpreter interpreter,
|
||||||
|
List<Object> arguments) {
|
||||||
|
return (double) System.currentTimeMillis() / 1000.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "<native fn>";
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
void interpret(List<Stmt> statements) {
|
void interpret(List<Stmt> statements) {
|
||||||
try {
|
try {
|
||||||
@ -82,6 +103,13 @@ class Interpreter implements Expr.Visitor<Object>, Stmt.Visitor<Void> {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Void visitFunctionStmt(Stmt.Function stmt) {
|
||||||
|
LoxFunction function = new LoxFunction(stmt, environment);
|
||||||
|
environment.define(stmt.name.lexeme(), function);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Void visitIfStmt(Stmt.If stmt) {
|
public Void visitIfStmt(Stmt.If stmt) {
|
||||||
if (isTruthy(evaluate(stmt.condition))) {
|
if (isTruthy(evaluate(stmt.condition))) {
|
||||||
@ -99,6 +127,15 @@ class Interpreter implements Expr.Visitor<Object>, Stmt.Visitor<Void> {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Void visitReturnStmt(Stmt.Return stmt) {
|
||||||
|
Object value = null;
|
||||||
|
if (stmt.value != null)
|
||||||
|
value = evaluate(stmt.value);
|
||||||
|
|
||||||
|
throw new Return(value);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Void visitVarStmt(Stmt.Var stmt) {
|
public Void visitVarStmt(Stmt.Var stmt) {
|
||||||
Object value = null;
|
Object value = null;
|
||||||
@ -172,6 +209,30 @@ class Interpreter implements Expr.Visitor<Object>, Stmt.Visitor<Void> {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object visitCallExpr(Expr.Call expr) {
|
||||||
|
Object callee = evaluate(expr.callee);
|
||||||
|
|
||||||
|
List<Object> arguments = new ArrayList<>();
|
||||||
|
for (Expr argument : expr.arguments) {
|
||||||
|
arguments.add(evaluate(argument));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(callee instanceof LoxCallable)) {
|
||||||
|
throw new RuntimeError(expr.paren,
|
||||||
|
"Can only call functions and classes.");
|
||||||
|
}
|
||||||
|
|
||||||
|
LoxCallable function = (LoxCallable) callee;
|
||||||
|
if (arguments.size() != function.arity()) {
|
||||||
|
throw new RuntimeError(expr.paren, "Expected " +
|
||||||
|
function.arity() + " arguments but got " +
|
||||||
|
arguments.size() + ".");
|
||||||
|
}
|
||||||
|
|
||||||
|
return function.call(this, arguments);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object visitUnaryExpr(Expr.Unary expr) {
|
public Object visitUnaryExpr(Expr.Unary expr) {
|
||||||
Object right = evaluate(expr.right);
|
Object right = evaluate(expr.right);
|
||||||
|
@ -0,0 +1,9 @@
|
|||||||
|
package ch.vanwa.lox_interpreter;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
interface LoxCallable {
|
||||||
|
int arity();
|
||||||
|
|
||||||
|
Object call(Interpreter interpreter, List<Object> arguments);
|
||||||
|
}
|
@ -0,0 +1,41 @@
|
|||||||
|
package ch.vanwa.lox_interpreter;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
class LoxFunction implements LoxCallable {
|
||||||
|
private final Stmt.Function declaration;
|
||||||
|
private final Environment closure;
|
||||||
|
|
||||||
|
LoxFunction(Stmt.Function declaration, Environment closure) {
|
||||||
|
this.closure = closure;
|
||||||
|
this.declaration = declaration;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "<fn " + declaration.name.lexeme() + ">";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int arity() {
|
||||||
|
return declaration.params.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object call(Interpreter interpreter,
|
||||||
|
List<Object> arguments) {
|
||||||
|
Environment environment = new Environment(closure);
|
||||||
|
for (int i = 0; i < declaration.params.size(); i++) {
|
||||||
|
environment.define(declaration.params.get(i).lexeme(),
|
||||||
|
arguments.get(i));
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
interpreter.executeBlock(declaration.body, environment);
|
||||||
|
} catch (Return returnValue) {
|
||||||
|
return returnValue.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
@ -58,6 +58,9 @@ class Parser {
|
|||||||
|
|
||||||
private Stmt declaration() {
|
private Stmt declaration() {
|
||||||
try {
|
try {
|
||||||
|
if (match(FUN)) {
|
||||||
|
return function("function");
|
||||||
|
}
|
||||||
if (match(VAR)) {
|
if (match(VAR)) {
|
||||||
return varDeclaration();
|
return varDeclaration();
|
||||||
}
|
}
|
||||||
@ -79,6 +82,9 @@ class Parser {
|
|||||||
if (match(PRINT)) {
|
if (match(PRINT)) {
|
||||||
return printStatement();
|
return printStatement();
|
||||||
}
|
}
|
||||||
|
if (match(RETURN)) {
|
||||||
|
return returnStatement();
|
||||||
|
}
|
||||||
if (match(WHILE)) {
|
if (match(WHILE)) {
|
||||||
return whileStatement();
|
return whileStatement();
|
||||||
}
|
}
|
||||||
@ -166,6 +172,17 @@ class Parser {
|
|||||||
return new Stmt.Var(name, initializer);
|
return new Stmt.Var(name, initializer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Stmt returnStatement() {
|
||||||
|
Token keyword = previous();
|
||||||
|
Expr value = null;
|
||||||
|
if (!check(SEMICOLON)) {
|
||||||
|
value = expression();
|
||||||
|
}
|
||||||
|
|
||||||
|
consume(SEMICOLON, "Expect ';' after return value.");
|
||||||
|
return new Stmt.Return(keyword, value);
|
||||||
|
}
|
||||||
|
|
||||||
private Stmt whileStatement() {
|
private Stmt whileStatement() {
|
||||||
consume(LEFT_PAREN, "Expect '(' after 'while'.");
|
consume(LEFT_PAREN, "Expect '(' after 'while'.");
|
||||||
Expr condition = expression();
|
Expr condition = expression();
|
||||||
@ -181,6 +198,27 @@ class Parser {
|
|||||||
return new Stmt.Expression(expr);
|
return new Stmt.Expression(expr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Stmt.Function function(String kind) {
|
||||||
|
Token name = consume(IDENTIFIER, "Expect " + kind + " name.");
|
||||||
|
consume(LEFT_PAREN, "Expect '(' after " + kind + " name.");
|
||||||
|
List<Token> parameters = new ArrayList<>();
|
||||||
|
if (!check(RIGHT_PAREN)) {
|
||||||
|
do {
|
||||||
|
if (parameters.size() >= 255) {
|
||||||
|
error(peek(), "Can't have more than 255 parameters.");
|
||||||
|
}
|
||||||
|
|
||||||
|
parameters.add(
|
||||||
|
consume(IDENTIFIER, "Expect parameter name."));
|
||||||
|
} while (match(COMMA));
|
||||||
|
}
|
||||||
|
consume(RIGHT_PAREN, "Expect ')' after parameters.");
|
||||||
|
|
||||||
|
consume(LEFT_BRACE, "Expect '{' before " + kind + " body.");
|
||||||
|
List<Stmt> body = block();
|
||||||
|
return new Stmt.Function(name, parameters, body);
|
||||||
|
}
|
||||||
|
|
||||||
private List<Stmt> block() {
|
private List<Stmt> block() {
|
||||||
List<Stmt> statements = new ArrayList<>();
|
List<Stmt> statements = new ArrayList<>();
|
||||||
|
|
||||||
@ -289,7 +327,38 @@ class Parser {
|
|||||||
return new Expr.Unary(operator, right);
|
return new Expr.Unary(operator, right);
|
||||||
}
|
}
|
||||||
|
|
||||||
return primary();
|
return call();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Expr call() {
|
||||||
|
Expr expr = primary();
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
if (match(LEFT_PAREN)) {
|
||||||
|
expr = finishCall(expr);
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return expr;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Expr finishCall(Expr callee) {
|
||||||
|
List<Expr> arguments = new ArrayList<>();
|
||||||
|
if (!check(RIGHT_PAREN)) {
|
||||||
|
do {
|
||||||
|
if (arguments.size() >= 255) {
|
||||||
|
error(peek(), "Can't have more than 255 arguments.");
|
||||||
|
}
|
||||||
|
arguments.add(expression());
|
||||||
|
} while (match(COMMA));
|
||||||
|
}
|
||||||
|
|
||||||
|
Token paren = consume(RIGHT_PAREN,
|
||||||
|
"Expect ')' after arguments.");
|
||||||
|
|
||||||
|
return new Expr.Call(callee, paren, arguments);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Expr primary() {
|
private Expr primary() {
|
||||||
|
@ -0,0 +1,10 @@
|
|||||||
|
package ch.vanwa.lox_interpreter;
|
||||||
|
|
||||||
|
class Return extends RuntimeException {
|
||||||
|
final Object value;
|
||||||
|
|
||||||
|
Return(Object value) {
|
||||||
|
super(null, null, false, false);
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
}
|
@ -5,18 +5,14 @@ import java.util.List;
|
|||||||
abstract class Stmt {
|
abstract class Stmt {
|
||||||
interface Visitor<R> {
|
interface Visitor<R> {
|
||||||
R visitBlockStmt(Block stmt);
|
R visitBlockStmt(Block stmt);
|
||||||
|
|
||||||
R visitExpressionStmt(Expression stmt);
|
R visitExpressionStmt(Expression stmt);
|
||||||
|
R visitFunctionStmt(Function stmt);
|
||||||
R visitIfStmt(If stmt);
|
R visitIfStmt(If stmt);
|
||||||
|
|
||||||
R visitPrintStmt(Print stmt);
|
R visitPrintStmt(Print stmt);
|
||||||
|
R visitReturnStmt(Return stmt);
|
||||||
R visitVarStmt(Var stmt);
|
R visitVarStmt(Var stmt);
|
||||||
|
|
||||||
R visitWhileStmt(While stmt);
|
R visitWhileStmt(While stmt);
|
||||||
}
|
}
|
||||||
|
|
||||||
static class Block extends Stmt {
|
static class Block extends Stmt {
|
||||||
Block(List<Stmt> statements) {
|
Block(List<Stmt> statements) {
|
||||||
this.statements = statements;
|
this.statements = statements;
|
||||||
@ -29,7 +25,6 @@ abstract class Stmt {
|
|||||||
|
|
||||||
final List<Stmt> statements;
|
final List<Stmt> statements;
|
||||||
}
|
}
|
||||||
|
|
||||||
static class Expression extends Stmt {
|
static class Expression extends Stmt {
|
||||||
Expression(Expr expression) {
|
Expression(Expr expression) {
|
||||||
this.expression = expression;
|
this.expression = expression;
|
||||||
@ -42,7 +37,22 @@ abstract class Stmt {
|
|||||||
|
|
||||||
final Expr expression;
|
final Expr expression;
|
||||||
}
|
}
|
||||||
|
static class Function extends Stmt {
|
||||||
|
Function(Token name, List<Token> params, List<Stmt> body) {
|
||||||
|
this.name = name;
|
||||||
|
this.params = params;
|
||||||
|
this.body = body;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
<R> R accept(Visitor<R> visitor) {
|
||||||
|
return visitor.visitFunctionStmt(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
final Token name;
|
||||||
|
final List<Token> params;
|
||||||
|
final List<Stmt> body;
|
||||||
|
}
|
||||||
static class If extends Stmt {
|
static class If extends Stmt {
|
||||||
If(Expr condition, Stmt thenBranch, Stmt elseBranch) {
|
If(Expr condition, Stmt thenBranch, Stmt elseBranch) {
|
||||||
this.condition = condition;
|
this.condition = condition;
|
||||||
@ -59,7 +69,6 @@ abstract class Stmt {
|
|||||||
final Stmt thenBranch;
|
final Stmt thenBranch;
|
||||||
final Stmt elseBranch;
|
final Stmt elseBranch;
|
||||||
}
|
}
|
||||||
|
|
||||||
static class Print extends Stmt {
|
static class Print extends Stmt {
|
||||||
Print(Expr expression) {
|
Print(Expr expression) {
|
||||||
this.expression = expression;
|
this.expression = expression;
|
||||||
@ -72,7 +81,20 @@ abstract class Stmt {
|
|||||||
|
|
||||||
final Expr expression;
|
final Expr expression;
|
||||||
}
|
}
|
||||||
|
static class Return extends Stmt {
|
||||||
|
Return(Token keyword, Expr value) {
|
||||||
|
this.keyword = keyword;
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
<R> R accept(Visitor<R> visitor) {
|
||||||
|
return visitor.visitReturnStmt(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
final Token keyword;
|
||||||
|
final Expr value;
|
||||||
|
}
|
||||||
static class Var extends Stmt {
|
static class Var extends Stmt {
|
||||||
Var(Token name, Expr initializer) {
|
Var(Token name, Expr initializer) {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
@ -87,7 +109,6 @@ abstract class Stmt {
|
|||||||
final Token name;
|
final Token name;
|
||||||
final Expr initializer;
|
final Expr initializer;
|
||||||
}
|
}
|
||||||
|
|
||||||
static class While extends Stmt {
|
static class While extends Stmt {
|
||||||
While(Expr condition, Stmt body) {
|
While(Expr condition, Stmt body) {
|
||||||
this.condition = condition;
|
this.condition = condition;
|
||||||
|
@ -16,6 +16,7 @@ public class GenerateAst {
|
|||||||
defineAst(outputDir, "Expr", Arrays.asList(
|
defineAst(outputDir, "Expr", Arrays.asList(
|
||||||
"Assign : Token name, Expr value",
|
"Assign : Token name, Expr value",
|
||||||
"Binary : Expr left, Token operator, Expr right",
|
"Binary : Expr left, Token operator, Expr right",
|
||||||
|
"Call : Expr callee, Token paren, List<Expr> arguments",
|
||||||
"Grouping : Expr expression",
|
"Grouping : Expr expression",
|
||||||
"Literal : Object value",
|
"Literal : Object value",
|
||||||
"Logical : Expr left, Token operator, Expr right",
|
"Logical : Expr left, Token operator, Expr right",
|
||||||
@ -25,9 +26,12 @@ public class GenerateAst {
|
|||||||
defineAst(outputDir, "Stmt", Arrays.asList(
|
defineAst(outputDir, "Stmt", Arrays.asList(
|
||||||
"Block : List<Stmt> statements",
|
"Block : List<Stmt> statements",
|
||||||
"Expression : Expr expression",
|
"Expression : Expr expression",
|
||||||
|
"Function : Token name, List<Token> params," +
|
||||||
|
" List<Stmt> body",
|
||||||
"If : Expr condition, Stmt thenBranch," +
|
"If : Expr condition, Stmt thenBranch," +
|
||||||
" Stmt elseBranch",
|
" Stmt elseBranch",
|
||||||
"Print : Expr expression",
|
"Print : Expr expression",
|
||||||
|
"Return : Token keyword, Expr value",
|
||||||
"Var : Token name, Expr initializer",
|
"Var : Token name, Expr initializer",
|
||||||
"While : Expr condition, Stmt body"));
|
"While : Expr condition, Stmt body"));
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user