However, in the interpreter code, a function call will only create one environment.
@Override
public Object call(Interpreter interpreter, List<Object> arguments) {
Environment environment = new Environment(closure); // Create new environment.
for (int i = 0; i < declaration.params.size(); i++) {
environment.define(declaration.params.get(i).lexeme,
arguments.get(i));
}
interpreter.executeBlock(declaration.body, environment); // Just use current environment.
return null;
}
So, when we calculate the distance of variable bindings in the enclosure and then try to get the value from the environment, it may cause an error.
A simple solution is to iterate over the function body directly in the resolver:
private void resolveFunction(Stmt.Function function) {
beginScope(); // Create new scope.
for (Token param : function.params) {
declare(param);
define(param);
}
// resolve(function.body); // Call visitBlockStmt create new nest scope.
for (Stmt stmt : function.body.statements) {
resolve(stmt);
}
endScope();
}
In the chapter "Resolving and Binding", I found that a function will create two scopes: one for the arguments, and another for the function body.
... -> scope{ function.params } -> scope{ function.body } -> ...
However, in the interpreter code, a function call will only create one environment.
So, when we calculate the distance of variable bindings in the enclosure and then try to get the value from the environment, it may cause an error.
A simple solution is to iterate over the function body directly in the resolver: