[Chapter10&Functions] - Adding functions
[Typo GenerateAst] - Fixing typo on package name of the ast generator
This commit is contained in:
parent
b2167e0a0c
commit
178793a13c
@ -1,9 +1,26 @@
|
||||
package com.lox;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
class Interpreter implements Expr.Visitor<Object>,Stmt.Visitor<Void> {
|
||||
private Environment environment = new Environment();
|
||||
final Environment globals = new Environment();
|
||||
private Environment environment = globals;
|
||||
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>"; }
|
||||
});
|
||||
}
|
||||
@Override
|
||||
public Object visitLiteralExpr(Expr.Literal expr){
|
||||
return expr.value;
|
||||
@ -99,11 +116,33 @@ class Interpreter implements Expr.Visitor<Object>,Stmt.Visitor<Void> {
|
||||
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
|
||||
public Void visitExpressionStmt(Stmt.Expression stmt) {
|
||||
evaluate(stmt.expression);
|
||||
return null;
|
||||
}
|
||||
@Override
|
||||
public Void visitFunctionStmt(Stmt.Function stmt) {
|
||||
LoxFunction function = new LoxFunction(stmt,environment);
|
||||
environment.define(stmt.name.lexeme, function);
|
||||
return null;
|
||||
}
|
||||
@Override
|
||||
public Void visitIfStmt(Stmt.If stmt){
|
||||
if(isTruthy(evaluate(stmt.condition))){
|
||||
execute(stmt.thenBranch);
|
||||
@ -120,6 +159,13 @@ class Interpreter implements Expr.Visitor<Object>,Stmt.Visitor<Void> {
|
||||
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
|
||||
public Void visitVarStmt(Stmt.Var stmt) {
|
||||
Object value = null;
|
||||
if (stmt.initializer != null) {
|
||||
|
8
src/com/lox/LoxCallable.java
Normal file
8
src/com/lox/LoxCallable.java
Normal file
@ -0,0 +1,8 @@
|
||||
package com.lox;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
interface LoxCallable {
|
||||
int arity();
|
||||
Object call(Interpreter interpreter, List<Object> arguments);
|
||||
}
|
35
src/com/lox/LoxFunction.java
Normal file
35
src/com/lox/LoxFunction.java
Normal file
@ -0,0 +1,35 @@
|
||||
package com.lox;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
@ -23,6 +23,7 @@ class Parser {
|
||||
}
|
||||
private Stmt declaration() {
|
||||
try {
|
||||
if (match(FUN)) return function("function");
|
||||
if (match(VAR)) return varDeclaration();
|
||||
return statement();
|
||||
} catch (ParseError error) {
|
||||
@ -38,6 +39,7 @@ class Parser {
|
||||
if (match(FOR)) return forStatement();
|
||||
if (match(IF)) return ifStatement();
|
||||
if (match(PRINT)) return printStatement();
|
||||
if (match(RETURN)) return returnStatement();
|
||||
if (match(WHILE)) return whileStatement();
|
||||
if (match(LEFT_BRACE)) return new Stmt.Block(block());
|
||||
return expressionStatement();
|
||||
@ -98,6 +100,16 @@ class Parser {
|
||||
consume(SEMICOLON, "Expect ';' after value.");
|
||||
return new Stmt.Print(value);
|
||||
}
|
||||
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 varDeclaration() {
|
||||
Token name = consume(IDENTIFIER, "Expect variable name.");
|
||||
|
||||
@ -121,6 +133,26 @@ class Parser {
|
||||
consume(SEMICOLON, "Expect ';' after expression.");
|
||||
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() {
|
||||
List<Stmt> statements = new ArrayList<>();
|
||||
|
||||
@ -222,9 +254,34 @@ class Parser {
|
||||
return new Expr.Unary(operator, right);
|
||||
}
|
||||
|
||||
return primary();
|
||||
return call();
|
||||
}
|
||||
|
||||
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(calle,paren,arguments);
|
||||
}
|
||||
private Expr call(){
|
||||
Expr expr = primary();
|
||||
while(true){
|
||||
if(match(LEFT_PAREN)){
|
||||
expr = finishCall(expr);
|
||||
}
|
||||
else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return expr;
|
||||
}
|
||||
private Expr primary() {
|
||||
if (match(FALSE)) return new Expr.Literal(false);
|
||||
if (match(TRUE)) return new Expr.Literal(true);
|
||||
|
10
src/com/lox/Return.java
Normal file
10
src/com/lox/Return.java
Normal file
@ -0,0 +1,10 @@
|
||||
package com.lox;
|
||||
|
||||
class Return extends RuntimeException {
|
||||
final Object value;
|
||||
|
||||
Return(Object value) {
|
||||
super(null, null, false, false);
|
||||
this.value = value;
|
||||
}
|
||||
}
|
@ -15,6 +15,7 @@ public class GenerateAst {
|
||||
defineAst(outputDir, "Expr", Arrays.asList(
|
||||
"Assign : Token name, Expr value",
|
||||
"Binary : Expr left, Token operator, Expr right",
|
||||
"Call : Expr callee, Token paren, List<Expr> arguments",
|
||||
"Grouping : Expr expression",
|
||||
"Literal : Object value",
|
||||
"Logical : Expr left, Token operator, Expr right",
|
||||
@ -26,7 +27,10 @@ public class GenerateAst {
|
||||
"If : Expr condition, Stmt thenBranch," +
|
||||
" Stmt elseBranch",
|
||||
"Expression : Expr expression",
|
||||
"Function : Token name, List<Token> params," +
|
||||
" List<Stmt> body",
|
||||
"Print : Expr expression",
|
||||
"Return : Token keyword, Expr value",
|
||||
"Var : Token name, Expr initializer",
|
||||
"While : Expr condition, Stmt body"
|
||||
));
|
||||
@ -37,7 +41,7 @@ public class GenerateAst {
|
||||
String path = outputDir + "/" + baseName + ".java";
|
||||
PrintWriter writer = new PrintWriter(path, "UTF-8");
|
||||
|
||||
writer.println("package com.craftinginterpreters.lox;");
|
||||
writer.println("package com.lox;");
|
||||
writer.println();
|
||||
writer.println("import java.util.List;");
|
||||
writer.println();
|
||||
|
Loading…
Reference in New Issue
Block a user