Rewrote value storage and implemented class instantiation
* Probably not the last time. * Object-like things (functions, classes, etc...) are stored as ref-counted cells. * Separate data structure for native functions. * with_callable() method on value to run specific code on objects that are callable * Instance type added. * Instance construction implemented
This commit is contained in:
parent
85e9c7db38
commit
d8dba3ac5f
8 changed files with 274 additions and 100 deletions
|
@ -1,18 +1,15 @@
|
||||||
use std::{cell::RefCell, fmt::Debug, rc::Rc};
|
use std::fmt::Debug;
|
||||||
|
|
||||||
use crate::errors::SloxResult;
|
use crate::errors::SloxResult;
|
||||||
|
|
||||||
use super::{InterpreterState, Value};
|
use super::{InterpreterState, Value};
|
||||||
|
|
||||||
/// A callable is some object that supports being called.
|
/// 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.
|
/// Return the amount of arguments supported by the callable.
|
||||||
fn arity(&self) -> usize;
|
fn arity(&self) -> usize;
|
||||||
|
|
||||||
/// Run the callable in the execution environment with the specified
|
/// Run the callable in the execution environment with the specified
|
||||||
/// arguments.
|
/// arguments.
|
||||||
fn call(&self, callee: &Value, environment: &mut InterpreterState, arguments: Vec<Value>) -> SloxResult<Value>;
|
fn call(&self, itpr_state: &mut InterpreterState, arguments: Vec<Value>) -> SloxResult<Value>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A reference to a callable.
|
|
||||||
pub type CallableRef = Rc<RefCell<dyn Callable>>;
|
|
||||||
|
|
|
@ -1,9 +1,28 @@
|
||||||
|
use std::{cell::RefCell, fmt::Display, rc::Rc};
|
||||||
|
|
||||||
|
use crate::errors::SloxResult;
|
||||||
|
|
||||||
|
use super::{Callable, InterpreterState, Value};
|
||||||
|
|
||||||
/// A Lox class.
|
/// A Lox class.
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct Class {
|
pub struct Class {
|
||||||
name: String,
|
name: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Classes are mostly used through references.
|
||||||
|
pub type ClassRef = Rc<RefCell<Class>>;
|
||||||
|
|
||||||
|
/// An instance of a Lox class
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct Instance {
|
||||||
|
class: Rc<RefCell<Class>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------- *
|
||||||
|
* Class implementation *
|
||||||
|
* -------------------- */
|
||||||
|
|
||||||
impl Class {
|
impl Class {
|
||||||
/// Create a new class, specifying its name.
|
/// Create a new class, specifying its name.
|
||||||
pub fn new(name: String) -> Self {
|
pub fn new(name: String) -> Self {
|
||||||
|
@ -11,8 +30,40 @@ impl Class {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ToString for Class {
|
impl Display for Class {
|
||||||
fn to_string(&self) -> String {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
self.name.clone()
|
f.write_str("<class ")?;
|
||||||
|
f.write_str(&self.name)?;
|
||||||
|
f.write_str(">")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Callable for ClassRef {
|
||||||
|
fn arity(&self) -> usize {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn call(&self, _itpr_state: &mut InterpreterState, _arguments: Vec<Value>) -> SloxResult<Value> {
|
||||||
|
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!(
|
||||||
|
"<Instance of {}>",
|
||||||
|
self.class.borrow().to_string()
|
||||||
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,7 @@ use crate::{
|
||||||
tokens::Token,
|
tokens::Token,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::{native_fn, CallableRef, Value};
|
use super::{Value, native_fn::{self, NativeFunction}};
|
||||||
|
|
||||||
/// A mutable reference to an environment.
|
/// A mutable reference to an environment.
|
||||||
pub(super) type EnvironmentRef = Rc<RefCell<Environment>>;
|
pub(super) type EnvironmentRef = Rc<RefCell<Environment>>;
|
||||||
|
@ -27,7 +27,7 @@ impl Default for Environment {
|
||||||
enclosing: None,
|
enclosing: None,
|
||||||
values: HashMap::new(),
|
values: HashMap::new(),
|
||||||
};
|
};
|
||||||
env.add_default_fun("clock", native_fn::clock());
|
env.add_default_fun(native_fn::clock());
|
||||||
env
|
env
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -42,9 +42,10 @@ impl Environment {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add a default function to the environment.
|
/// Add a default function to the environment.
|
||||||
fn add_default_fun(&mut self, name: &str, fun: CallableRef) {
|
fn add_default_fun(&mut self, fun: NativeFunction) {
|
||||||
let value = Some(Value::Callable(fun));
|
let name = fun.name().to_owned();
|
||||||
self.values.insert(name.to_owned(), value);
|
let value = Some(Value::from(fun));
|
||||||
|
self.values.insert(name, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Define a new variable.
|
/// Define a new variable.
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use std::{cell::RefCell, rc::Rc};
|
use std::fmt::Display;
|
||||||
|
|
||||||
use itertools::izip;
|
use itertools::izip;
|
||||||
|
|
||||||
|
@ -9,8 +9,8 @@ use super::{
|
||||||
use crate::{ast, errors::SloxResult, tokens::Token};
|
use crate::{ast, errors::SloxResult, tokens::Token};
|
||||||
|
|
||||||
/// A function implemented in the Lox-ish language.
|
/// A function implemented in the Lox-ish language.
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Clone)]
|
||||||
pub(super) struct Function {
|
pub struct Function {
|
||||||
name: Option<Token>,
|
name: Option<Token>,
|
||||||
params: Vec<Token>,
|
params: Vec<Token>,
|
||||||
body: Vec<ast::StmtNode>,
|
body: Vec<ast::StmtNode>,
|
||||||
|
@ -23,14 +23,13 @@ impl Function {
|
||||||
params: &[Token],
|
params: &[Token],
|
||||||
body: &[ast::StmtNode],
|
body: &[ast::StmtNode],
|
||||||
environment: EnvironmentRef,
|
environment: EnvironmentRef,
|
||||||
) -> Rc<RefCell<Self>> {
|
) -> Self {
|
||||||
let fun = Self {
|
Self {
|
||||||
name: name.cloned(),
|
name: name.cloned(),
|
||||||
params: params.to_owned(),
|
params: params.to_owned(),
|
||||||
body: body.to_owned(),
|
body: body.to_owned(),
|
||||||
env: environment,
|
env: environment,
|
||||||
};
|
}
|
||||||
Rc::new(RefCell::new(fun))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -39,17 +38,12 @@ impl Callable for Function {
|
||||||
self.params.len()
|
self.params.len()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn call(
|
fn call(&self, itpr_state: &mut InterpreterState, arguments: Vec<Value>) -> SloxResult<Value> {
|
||||||
&self,
|
|
||||||
_callee: &Value,
|
|
||||||
es: &mut InterpreterState,
|
|
||||||
arguments: Vec<Value>,
|
|
||||||
) -> SloxResult<Value> {
|
|
||||||
assert_eq!(arguments.len(), self.arity());
|
assert_eq!(arguments.len(), self.arity());
|
||||||
let param_env = InterpreterState {
|
let param_env = InterpreterState {
|
||||||
environment: Environment::create_child(&self.env),
|
environment: Environment::create_child(&self.env),
|
||||||
globals: es.globals.clone(),
|
globals: itpr_state.globals.clone(),
|
||||||
locals: es.locals,
|
locals: itpr_state.locals,
|
||||||
};
|
};
|
||||||
for (arg, value) in izip!(self.params.iter(), arguments.into_iter()) {
|
for (arg, value) in izip!(self.params.iter(), arguments.into_iter()) {
|
||||||
param_env
|
param_env
|
||||||
|
@ -72,11 +66,15 @@ impl Callable for Function {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ToString for Function {
|
impl Display for Function {
|
||||||
fn to_string(&self) -> String {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
match &self.name {
|
match &self.name {
|
||||||
None => "<lambda>".to_owned(),
|
None => f.write_str("<lambda>"),
|
||||||
Some(token) => format!("<fun {}>", token.lexeme),
|
Some(token) => {
|
||||||
|
f.write_str("<fun ")?;
|
||||||
|
f.write_str(&token.lexeme)?;
|
||||||
|
f.write_str(">")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -200,7 +200,7 @@ impl StmtNode {
|
||||||
let class = Class::new(decl.name.lexeme.clone());
|
let class = Class::new(decl.name.lexeme.clone());
|
||||||
es.environment
|
es.environment
|
||||||
.borrow_mut()
|
.borrow_mut()
|
||||||
.assign(&decl.name, Value::Class(class))?;
|
.assign(&decl.name, class.into())?;
|
||||||
Ok(InterpreterFlowControl::default())
|
Ok(InterpreterFlowControl::default())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -214,7 +214,7 @@ impl StmtNode {
|
||||||
);
|
);
|
||||||
es.environment
|
es.environment
|
||||||
.borrow_mut()
|
.borrow_mut()
|
||||||
.define(&decl.name, Some(Value::Callable(fun)))?;
|
.define(&decl.name, Some(fun.into()))?;
|
||||||
Ok(InterpreterFlowControl::default())
|
Ok(InterpreterFlowControl::default())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -338,10 +338,8 @@ impl Interpretable for ExprNode {
|
||||||
arguments,
|
arguments,
|
||||||
} => self.on_call(es, callee, right_paren, arguments),
|
} => self.on_call(es, callee, right_paren, arguments),
|
||||||
ExprNode::Lambda { params, body } => {
|
ExprNode::Lambda { params, body } => {
|
||||||
Ok(
|
let lambda = Function::new(None, params, body, es.environment.clone());
|
||||||
Value::Callable(Function::new(None, params, body, es.environment.clone()))
|
Ok(Value::from(lambda).into())
|
||||||
.into(),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -492,8 +490,8 @@ impl ExprNode {
|
||||||
}
|
}
|
||||||
v
|
v
|
||||||
};
|
};
|
||||||
if let Value::Callable(callable_ref) = &callee {
|
callee.with_callable(
|
||||||
let callable = callable_ref.borrow();
|
|callable| {
|
||||||
if callable.arity() != arg_values.len() {
|
if callable.arity() != arg_values.len() {
|
||||||
Err(SloxError::with_token(
|
Err(SloxError::with_token(
|
||||||
ErrorKind::Runtime,
|
ErrorKind::Runtime,
|
||||||
|
@ -505,10 +503,10 @@ impl ExprNode {
|
||||||
),
|
),
|
||||||
))
|
))
|
||||||
} else {
|
} else {
|
||||||
Ok(callable.call(&callee, es, arg_values)?.into())
|
Ok(callable.call(es, arg_values)?.into())
|
||||||
}
|
|
||||||
} else {
|
|
||||||
error(right_paren, "can only call functions and classes")
|
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
|| error(right_paren, "expression result is not callable")
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,7 @@ mod interpretable;
|
||||||
mod native_fn;
|
mod native_fn;
|
||||||
mod value;
|
mod value;
|
||||||
|
|
||||||
pub(self) use callable::{Callable, CallableRef};
|
pub(self) use callable::Callable;
|
||||||
pub(self) use environment::*;
|
pub(self) use environment::*;
|
||||||
pub use interpretable::*;
|
pub use interpretable::*;
|
||||||
pub use value::*;
|
pub use value::*;
|
||||||
|
|
|
@ -1,28 +1,84 @@
|
||||||
use std::{
|
use std::{
|
||||||
cell::RefCell,
|
fmt::{Debug, Display},
|
||||||
rc::Rc,
|
|
||||||
time::{SystemTime, UNIX_EPOCH},
|
time::{SystemTime, UNIX_EPOCH},
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::errors::SloxResult;
|
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<Value>) -> SloxResult<Value>;
|
||||||
|
|
||||||
|
/// 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("<native function ")?;
|
||||||
|
f.write_str(&self.name)?;
|
||||||
|
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<Value>) -> SloxResult<Value> {
|
||||||
|
(self.handler)(itpr_state, arguments)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* ----------- *
|
/* ----------- *
|
||||||
* clock() *
|
* clock() *
|
||||||
* ----------- */
|
* ----------- */
|
||||||
|
|
||||||
#[derive(Debug)]
|
fn _clock_implementation(
|
||||||
struct Clock;
|
|
||||||
|
|
||||||
impl Callable for Clock {
|
|
||||||
fn arity(&self) -> usize {
|
|
||||||
0
|
|
||||||
}
|
|
||||||
|
|
||||||
fn call(
|
|
||||||
&self,
|
|
||||||
_callee: &Value,
|
|
||||||
_environment: &mut InterpreterState,
|
_environment: &mut InterpreterState,
|
||||||
_arguments: Vec<Value>,
|
_arguments: Vec<Value>,
|
||||||
) -> SloxResult<Value> {
|
) -> SloxResult<Value> {
|
||||||
|
@ -32,14 +88,7 @@ impl Callable for Clock {
|
||||||
.expect("looks like it's 2038 already");
|
.expect("looks like it's 2038 already");
|
||||||
Ok(Value::Number((since_epoch.as_millis() as f64) / 1000.0))
|
Ok(Value::Number((since_epoch.as_millis() as f64) / 1000.0))
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl ToString for Clock {
|
pub(super) fn clock() -> NativeFunction {
|
||||||
fn to_string(&self) -> String {
|
NativeFunction::new("clock", 0, _clock_implementation)
|
||||||
"<native function clock()>".to_owned()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(super) fn clock() -> CallableRef {
|
|
||||||
Rc::new(RefCell::new(Clock {}))
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,11 @@
|
||||||
use std::{cell::RefCell, fmt::Display, rc::Rc};
|
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.
|
/// A value being handled by the interpreter.
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
|
@ -9,8 +14,50 @@ pub enum Value {
|
||||||
Boolean(bool),
|
Boolean(bool),
|
||||||
String(String),
|
String(String),
|
||||||
Number(f64),
|
Number(f64),
|
||||||
Callable(Rc<RefCell<dyn Callable>>),
|
Object(Rc<RefCell<Object>>),
|
||||||
Class(Class),
|
}
|
||||||
|
|
||||||
|
#[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<Fok, Ferr, Rt>(&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 {
|
impl PartialEq for Value {
|
||||||
|
@ -32,19 +79,52 @@ impl Display for Value {
|
||||||
Value::Boolean(b) => b.fmt(f),
|
Value::Boolean(b) => b.fmt(f),
|
||||||
Value::String(s) => s.fmt(f),
|
Value::String(s) => s.fmt(f),
|
||||||
Value::Number(n) => n.fmt(f),
|
Value::Number(n) => n.fmt(f),
|
||||||
Value::Callable(c) => f.write_str(&c.borrow().to_string()),
|
Value::Object(obj) => obj.borrow().fmt(f),
|
||||||
Value::Class(c) => f.write_str(&c.to_string()),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Value {
|
impl From<NativeFunction> for Value {
|
||||||
/// Check whether a value is truthy or not.
|
fn from(value: NativeFunction) -> Self {
|
||||||
pub fn is_truthy(&self) -> bool {
|
Value::Object(Rc::new(RefCell::new(Object::NativeFunction(value))))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Function> for Value {
|
||||||
|
fn from(value: Function) -> Self {
|
||||||
|
Value::Object(Rc::new(RefCell::new(Object::LoxFunction(value))))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Class> for Value {
|
||||||
|
fn from(value: Class) -> Self {
|
||||||
|
Value::from(Rc::new(RefCell::new(value)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<ClassRef> for Value {
|
||||||
|
fn from(value: ClassRef) -> Self {
|
||||||
|
Value::Object(Rc::new(RefCell::new(Object::Class(value))))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Instance> 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 {
|
match self {
|
||||||
Self::Nil => false,
|
Object::NativeFunction(func) => func.fmt(f),
|
||||||
Self::Boolean(b) => *b,
|
Object::LoxFunction(func) => func.fmt(f),
|
||||||
_ => true,
|
Object::Class(cls) => cls.borrow().fmt(f),
|
||||||
|
Object::Instance(inst) => inst.fmt(f),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue