[Chapter10&Functions] - Adding functions

[Typo GenerateAst] - Fixing typo on package name of the ast generator
This commit is contained in:
Adnan Ioricce 2024-09-30 14:16:36 -03:00
parent b2167e0a0c
commit 178793a13c
6 changed files with 164 additions and 4 deletions

@ -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) {

@ -0,0 +1,8 @@
package com.lox;
import java.util.List;
interface LoxCallable {
int arity();
Object call(Interpreter interpreter, List<Object> arguments);
}

@ -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

@ -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();