diff --git a/src/interpreter/callable.rs b/src/interpreter/callable.rs index bb00afc..62853e0 100644 --- a/src/interpreter/callable.rs +++ b/src/interpreter/callable.rs @@ -1,18 +1,15 @@ -use std::{cell::RefCell, fmt::Debug, rc::Rc}; +use std::fmt::Debug; use crate::errors::SloxResult; use super::{InterpreterState, Value}; /// A callable is some object that supports being called. -pub trait Callable: Debug + ToString { +pub trait Callable: Debug { /// Return the amount of arguments supported by the callable. fn arity(&self) -> usize; /// Run the callable in the execution environment with the specified /// arguments. - fn call(&self, callee: &Value, environment: &mut InterpreterState, arguments: Vec) -> SloxResult; + fn call(&self, itpr_state: &mut InterpreterState, arguments: Vec) -> SloxResult; } - -/// A reference to a callable. -pub type CallableRef = Rc>; diff --git a/src/interpreter/class.rs b/src/interpreter/class.rs index d83b7e1..f551255 100644 --- a/src/interpreter/class.rs +++ b/src/interpreter/class.rs @@ -1,9 +1,28 @@ +use std::{cell::RefCell, fmt::Display, rc::Rc}; + +use crate::errors::SloxResult; + +use super::{Callable, InterpreterState, Value}; + /// A Lox class. #[derive(Debug, Clone)] pub struct Class { name: String, } +/// Classes are mostly used through references. +pub type ClassRef = Rc>; + +/// An instance of a Lox class +#[derive(Debug, Clone)] +pub struct Instance { + class: Rc>, +} + +/* -------------------- * + * Class implementation * + * -------------------- */ + impl Class { /// Create a new class, specifying its name. pub fn new(name: String) -> Self { @@ -11,8 +30,40 @@ impl Class { } } -impl ToString for Class { - fn to_string(&self) -> String { - self.name.clone() +impl Display for Class { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_str("") + } +} + +impl Callable for ClassRef { + fn arity(&self) -> usize { + return 0; + } + + fn call(&self, _itpr_state: &mut InterpreterState, _arguments: Vec) -> SloxResult { + let instance = Instance::new(self.clone()); + Ok(Value::from(instance)) + } +} + +/* ----------------------- * + * Instance implementation * + * ----------------------- */ + +impl Instance { + fn new(class: ClassRef) -> Self { + Self { class } + } +} + +impl Display for Instance { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_fmt(format_args!( + "", + self.class.borrow().to_string() + )) } } diff --git a/src/interpreter/environment.rs b/src/interpreter/environment.rs index 02ff2f0..c8dbce2 100644 --- a/src/interpreter/environment.rs +++ b/src/interpreter/environment.rs @@ -5,7 +5,7 @@ use crate::{ tokens::Token, }; -use super::{native_fn, CallableRef, Value}; +use super::{Value, native_fn::{self, NativeFunction}}; /// A mutable reference to an environment. pub(super) type EnvironmentRef = Rc>; @@ -27,7 +27,7 @@ impl Default for Environment { enclosing: None, values: HashMap::new(), }; - env.add_default_fun("clock", native_fn::clock()); + env.add_default_fun(native_fn::clock()); env } } @@ -42,9 +42,10 @@ impl Environment { } /// Add a default function to the environment. - fn add_default_fun(&mut self, name: &str, fun: CallableRef) { - let value = Some(Value::Callable(fun)); - self.values.insert(name.to_owned(), value); + fn add_default_fun(&mut self, fun: NativeFunction) { + let name = fun.name().to_owned(); + let value = Some(Value::from(fun)); + self.values.insert(name, value); } /// Define a new variable. diff --git a/src/interpreter/functions.rs b/src/interpreter/functions.rs index d45c654..cac76bd 100644 --- a/src/interpreter/functions.rs +++ b/src/interpreter/functions.rs @@ -1,4 +1,4 @@ -use std::{cell::RefCell, rc::Rc}; +use std::fmt::Display; use itertools::izip; @@ -9,8 +9,8 @@ use super::{ use crate::{ast, errors::SloxResult, tokens::Token}; /// A function implemented in the Lox-ish language. -#[derive(Debug)] -pub(super) struct Function { +#[derive(Debug, Clone)] +pub struct Function { name: Option, params: Vec, body: Vec, @@ -23,14 +23,13 @@ impl Function { params: &[Token], body: &[ast::StmtNode], environment: EnvironmentRef, - ) -> Rc> { - let fun = Self { + ) -> Self { + Self { name: name.cloned(), params: params.to_owned(), body: body.to_owned(), env: environment, - }; - Rc::new(RefCell::new(fun)) + } } } @@ -39,17 +38,12 @@ impl Callable for Function { self.params.len() } - fn call( - &self, - _callee: &Value, - es: &mut InterpreterState, - arguments: Vec, - ) -> SloxResult { + fn call(&self, itpr_state: &mut InterpreterState, arguments: Vec) -> SloxResult { assert_eq!(arguments.len(), self.arity()); let param_env = InterpreterState { environment: Environment::create_child(&self.env), - globals: es.globals.clone(), - locals: es.locals, + globals: itpr_state.globals.clone(), + locals: itpr_state.locals, }; for (arg, value) in izip!(self.params.iter(), arguments.into_iter()) { param_env @@ -72,11 +66,15 @@ impl Callable for Function { } } -impl ToString for Function { - fn to_string(&self) -> String { +impl Display for Function { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match &self.name { - None => "".to_owned(), - Some(token) => format!("", token.lexeme), + None => f.write_str(""), + Some(token) => { + f.write_str("") + } } } } diff --git a/src/interpreter/interpretable.rs b/src/interpreter/interpretable.rs index c938077..d69f969 100644 --- a/src/interpreter/interpretable.rs +++ b/src/interpreter/interpretable.rs @@ -200,7 +200,7 @@ impl StmtNode { let class = Class::new(decl.name.lexeme.clone()); es.environment .borrow_mut() - .assign(&decl.name, Value::Class(class))?; + .assign(&decl.name, class.into())?; Ok(InterpreterFlowControl::default()) } @@ -214,7 +214,7 @@ impl StmtNode { ); es.environment .borrow_mut() - .define(&decl.name, Some(Value::Callable(fun)))?; + .define(&decl.name, Some(fun.into()))?; Ok(InterpreterFlowControl::default()) } @@ -338,10 +338,8 @@ impl Interpretable for ExprNode { arguments, } => self.on_call(es, callee, right_paren, arguments), ExprNode::Lambda { params, body } => { - Ok( - Value::Callable(Function::new(None, params, body, es.environment.clone())) - .into(), - ) + let lambda = Function::new(None, params, body, es.environment.clone()); + Ok(Value::from(lambda).into()) } } } @@ -492,23 +490,23 @@ impl ExprNode { } v }; - if let Value::Callable(callable_ref) = &callee { - let callable = callable_ref.borrow(); - if callable.arity() != arg_values.len() { - Err(SloxError::with_token( - ErrorKind::Runtime, - right_paren, - format!( - "expected {} arguments, found {}", - arg_values.len(), - callable.arity() - ), - )) - } else { - Ok(callable.call(&callee, es, arg_values)?.into()) - } - } else { - error(right_paren, "can only call functions and classes") - } + callee.with_callable( + |callable| { + if callable.arity() != arg_values.len() { + Err(SloxError::with_token( + ErrorKind::Runtime, + right_paren, + format!( + "expected {} arguments, found {}", + arg_values.len(), + callable.arity() + ), + )) + } else { + Ok(callable.call(es, arg_values)?.into()) + } + }, + || error(right_paren, "expression result is not callable") + ) } } diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index 9483801..e3e7c87 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -6,7 +6,7 @@ mod interpretable; mod native_fn; mod value; -pub(self) use callable::{Callable, CallableRef}; +pub(self) use callable::Callable; pub(self) use environment::*; pub use interpretable::*; pub use value::*; diff --git a/src/interpreter/native_fn.rs b/src/interpreter/native_fn.rs index b868bd1..c29ad2d 100644 --- a/src/interpreter/native_fn.rs +++ b/src/interpreter/native_fn.rs @@ -1,45 +1,94 @@ use std::{ - cell::RefCell, - rc::Rc, + fmt::{Debug, Display}, time::{SystemTime, UNIX_EPOCH}, }; use crate::errors::SloxResult; -use super::{Callable, CallableRef, InterpreterState, Value}; +use super::{Callable, InterpreterState, Value}; + +/* ----------------------- * + * Native function support * + * ----------------------- */ + +/// A function pointer to the implementation of some native function. +type NativeFunctionHandler = fn(&mut InterpreterState, Vec) -> SloxResult; + +/// A native function. +#[derive(Clone)] +pub struct NativeFunction { + /// Name of the function. + name: String, + /// Arity of the function. + arity: usize, + /// Rust function that actually implements the function. + handler: NativeFunctionHandler, +} + +impl NativeFunction { + /// Initialize a native function's record. + fn new(name: &str, arity: usize, handler: NativeFunctionHandler) -> Self { + Self { + name: name.to_owned(), + arity, + handler, + } + } + + /// Access the native function's name + pub fn name(&self) -> &str { + &self.name + } +} + +impl PartialEq for NativeFunction { + fn eq(&self, other: &Self) -> bool { + self.name == other.name + } +} + +impl Display for NativeFunction { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_str("") + } +} + +impl Debug for NativeFunction { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_fmt(format_args!( + "NativeFunction {{ name: {:?}, arity: {} }}", + self.name, self.arity + )) + } +} + +impl Callable for NativeFunction { + fn arity(&self) -> usize { + self.arity + } + + fn call(&self, itpr_state: &mut InterpreterState, arguments: Vec) -> SloxResult { + (self.handler)(itpr_state, arguments) + } +} /* ----------- * * clock() * * ----------- */ -#[derive(Debug)] -struct Clock; - -impl Callable for Clock { - fn arity(&self) -> usize { - 0 - } - - fn call( - &self, - _callee: &Value, - _environment: &mut InterpreterState, - _arguments: Vec, - ) -> SloxResult { - let now = SystemTime::now(); - let since_epoch = now - .duration_since(UNIX_EPOCH) - .expect("looks like it's 2038 already"); - Ok(Value::Number((since_epoch.as_millis() as f64) / 1000.0)) - } +fn _clock_implementation( + _environment: &mut InterpreterState, + _arguments: Vec, +) -> SloxResult { + let now = SystemTime::now(); + let since_epoch = now + .duration_since(UNIX_EPOCH) + .expect("looks like it's 2038 already"); + Ok(Value::Number((since_epoch.as_millis() as f64) / 1000.0)) } -impl ToString for Clock { - fn to_string(&self) -> String { - "".to_owned() - } -} - -pub(super) fn clock() -> CallableRef { - Rc::new(RefCell::new(Clock {})) +pub(super) fn clock() -> NativeFunction { + NativeFunction::new("clock", 0, _clock_implementation) } diff --git a/src/interpreter/value.rs b/src/interpreter/value.rs index 47f28f5..0680d1a 100644 --- a/src/interpreter/value.rs +++ b/src/interpreter/value.rs @@ -1,6 +1,11 @@ use std::{cell::RefCell, fmt::Display, rc::Rc}; -use super::{Callable, class::Class}; +use super::{ + class::{Class, ClassRef, Instance}, + functions::Function, + native_fn::NativeFunction, + Callable, +}; /// A value being handled by the interpreter. #[derive(Debug, Clone)] @@ -9,8 +14,50 @@ pub enum Value { Boolean(bool), String(String), Number(f64), - Callable(Rc>), - Class(Class), + Object(Rc>), +} + +#[derive(Debug, Clone)] +pub enum Object { + NativeFunction(NativeFunction), + LoxFunction(Function), + Class(ClassRef), + Instance(Instance), +} + +/* -------------------- * + * Value implementation * + * -------------------- */ + +impl Value { + /// Check whether a value is truthy or not. + pub fn is_truthy(&self) -> bool { + match self { + Self::Nil => false, + Self::Boolean(b) => *b, + _ => true, + } + } + + /// Run some code using the callable object wrapped inside a value. + /// If the value is not callable, an error function will be called + /// instead. + pub fn with_callable(&self, fok: Fok, ferr: Ferr) -> Rt + where + Fok: FnOnce(&dyn Callable) -> Rt, + Ferr: FnOnce() -> Rt, + { + let obj = match self { + Value::Object(obj_ref) => obj_ref.borrow(), + _ => return ferr(), + }; + match &*obj { + Object::NativeFunction(func) => fok(func), + Object::LoxFunction(func) => fok(func), + Object::Class(class) => fok(class), + Object::Instance(_) => ferr(), + } + } } impl PartialEq for Value { @@ -32,19 +79,52 @@ impl Display for Value { Value::Boolean(b) => b.fmt(f), Value::String(s) => s.fmt(f), Value::Number(n) => n.fmt(f), - Value::Callable(c) => f.write_str(&c.borrow().to_string()), - Value::Class(c) => f.write_str(&c.to_string()), + Value::Object(obj) => obj.borrow().fmt(f), } } } -impl Value { - /// Check whether a value is truthy or not. - pub fn is_truthy(&self) -> bool { +impl From for Value { + fn from(value: NativeFunction) -> Self { + Value::Object(Rc::new(RefCell::new(Object::NativeFunction(value)))) + } +} + +impl From for Value { + fn from(value: Function) -> Self { + Value::Object(Rc::new(RefCell::new(Object::LoxFunction(value)))) + } +} + +impl From for Value { + fn from(value: Class) -> Self { + Value::from(Rc::new(RefCell::new(value))) + } +} + +impl From for Value { + fn from(value: ClassRef) -> Self { + Value::Object(Rc::new(RefCell::new(Object::Class(value)))) + } +} + +impl From for Value { + fn from(value: Instance) -> Self { + Value::Object(Rc::new(RefCell::new(Object::Instance(value)))) + } +} + +/* --------------------- * + * Object implementation * + * --------------------- */ + +impl Display for Object { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { - Self::Nil => false, - Self::Boolean(b) => *b, - _ => true, + Object::NativeFunction(func) => func.fmt(f), + Object::LoxFunction(func) => func.fmt(f), + Object::Class(cls) => cls.borrow().fmt(f), + Object::Instance(inst) => inst.fmt(f), } } }