crafting-interpreters/src/function.rs

79 lines
2.3 KiB
Rust

use crate::{
callable::{Callable, CallingError},
environment::Environment,
instance::Instance,
interpreter::Interpreter,
statement::Statement,
token::Token,
value::Value,
};
use core::fmt::Debug;
use std::{cell::RefCell, fmt::Display, rc::Rc};
/// A Lox function with name, parameters, body, and closure environment.
#[derive(Debug, Clone)]
pub struct Function {
pub name: Token,
pub is_initializer: bool,
pub params: Vec<Token>,
pub body: Vec<Statement>,
pub closure: Rc<RefCell<Environment>>,
}
impl Display for Function {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.name.lexeme)
}
}
impl PartialEq for Function {
fn eq(&self, other: &Self) -> bool {
self.name == other.name && self.params == other.params && self.body == other.body
}
}
impl Callable for Function {
fn name(&self) -> String {
self.name.lexeme.clone()
}
fn arity(&self) -> usize {
self.params.len()
}
fn call(&self, interpreter: &mut Interpreter, args: Vec<Value>) -> Result<Value, CallingError> {
let mut env = Environment::with_enclosing(self.closure.clone());
for (i, param) in self.params.iter().enumerate() {
let arg = args
.get(i)
.ok_or(CallingError::ArgumentMismatch(
self.params.len(),
args.len(),
))?
.clone();
env.define(param.lexeme.clone(), arg);
}
Ok(interpreter
.block(&self.body, env)
.map_err(|e| CallingError::CallInterpretationFailed(e.to_string()))?
.unwrap_or(Value::Nil))
}
}
impl Function {
/// Bind a method to an instance, creating a new function with "this" defined in its environment.
/// This allows methods to access the instance they belong to.
pub fn bind(&self, instance: Rc<RefCell<Instance>>) -> Self {
let mut env = Environment::with_enclosing(self.closure.clone());
env.define("this".to_string(), Value::Instance(instance));
Self {
name: self.name.clone(),
is_initializer: self.is_initializer,
params: self.params.clone(),
body: self.body.clone(),
closure: Rc::new(RefCell::new(env)),
}
}
}