Classes - Support using members from superclasses

* This includes methods, as per the book, but also getters and setters
This commit is contained in:
Emmanuel BENOîT 2023-01-15 18:44:30 +01:00
parent 9307fde4b3
commit 896b0deef7

View file

@ -106,15 +106,38 @@ impl Callable for ClassRef {
} }
} }
/// Attempt to find some member, identified by a key, inside a class hierarchy. If the member is
/// found, the specified function is exectued with the member as its argument, and its return value
/// is passed back to the caller. If the member is not found, None is returned.
fn with_class_member<F, Rt>(class: &ClassRef, mb_key: &ClassMemberKey, f: F) -> Option<Rt>
where
F: FnOnce(&Function) -> Rt,
{
let mut cls = class.clone();
loop {
if let Some(member) = cls.borrow().members.get(&mb_key) {
return Some(f(member));
}
let nclr = if let Some(sc) = &cls.borrow().superclass {
sc.clone()
} else {
return None;
};
cls = nclr;
}
}
impl PropertyCarrier for ClassRef { impl PropertyCarrier for ClassRef {
fn get(&self, itpr_state: &mut InterpreterState, name: &Token) -> SloxResult<Value> { fn get(&self, itpr_state: &mut InterpreterState, name: &Token) -> SloxResult<Value> {
let class = self.borrow(); let class = self.borrow();
// Check for a property getter and execute it if found. // Check for a property getter and execute it if found.
let mut mb_key = (ClassMemberKind::Getter, true, name.lexeme.clone()); let mut mb_key = (ClassMemberKind::Getter, true, name.lexeme.clone());
if let Some(getter) = class.members.get(&mb_key) { if let Some(value) = with_class_member(self, &mb_key, |getter| {
let bound_method = bind_method(getter, Value::from(self.clone())); let bound_method = bind_method(getter, Value::from(self.clone()));
return bound_method.call(itpr_state, vec![]); return bound_method.call(itpr_state, vec![]);
}) {
return value;
} }
// Check for an actual field. // Check for an actual field.
@ -124,9 +147,11 @@ impl PropertyCarrier for ClassRef {
// Check for a method. // Check for a method.
mb_key.0 = ClassMemberKind::Method; mb_key.0 = ClassMemberKind::Method;
if let Some(method) = class.members.get(&mb_key) { if let Some(method) = with_class_member(self, &mb_key, |method| {
let bound_method = bind_method(method, Value::from(self.clone())); let bound_method = bind_method(method, Value::from(self.clone()));
return Ok(Value::from(bound_method)); return Ok(Value::from(bound_method));
}) {
return method;
} }
Err(SloxError::with_token( Err(SloxError::with_token(
@ -141,12 +166,13 @@ impl PropertyCarrier for ClassRef {
// Check for a property setter. // Check for a property setter.
let mb_key = (ClassMemberKind::Setter, true, name.lexeme.clone()); let mb_key = (ClassMemberKind::Setter, true, name.lexeme.clone());
if let Some(setter) = class.members.get(&mb_key) { let lookup = with_class_member(self, &mb_key, |setter| {
// Bind and execute the property setter
let bound_method = bind_method(setter, Value::from(self.clone())); let bound_method = bind_method(setter, Value::from(self.clone()));
bound_method bound_method
.call(itpr_state, vec![value]) .call(itpr_state, vec![value.clone()])
.expect("failed to execute setter"); .expect("failed to execute setter");
});
if lookup.is_some() {
return; return;
} }
@ -180,9 +206,11 @@ impl PropertyCarrier for InstanceRef {
// Check for a property getter and execute it if found. // Check for a property getter and execute it if found.
let mut mb_key = (ClassMemberKind::Getter, false, name.lexeme.clone()); let mut mb_key = (ClassMemberKind::Getter, false, name.lexeme.clone());
if let Some(getter) = instance.class.borrow().members.get(&mb_key) { if let Some(value) = with_class_member(&instance.class, &mb_key, |getter| {
let bound_method = bind_method(getter, Value::from(self.clone())); let bound_method = bind_method(getter, Value::from(self.clone()));
return bound_method.call(itpr_state, vec![]); return bound_method.call(itpr_state, vec![]);
}) {
return value;
} }
// Check for an actual field. // Check for an actual field.
@ -192,9 +220,11 @@ impl PropertyCarrier for InstanceRef {
// Check for a method. // Check for a method.
mb_key.0 = ClassMemberKind::Method; mb_key.0 = ClassMemberKind::Method;
if let Some(method) = instance.class.borrow().members.get(&mb_key) { if let Some(method) = with_class_member(&instance.class, &mb_key, |method| {
let bound_method = bind_method(method, Value::from(self.clone())); let bound_method = bind_method(method, Value::from(self.clone()));
return Ok(Value::from(bound_method)); return Ok(Value::from(bound_method));
}) {
return method;
} }
Err(SloxError::with_token( Err(SloxError::with_token(
@ -209,12 +239,13 @@ impl PropertyCarrier for InstanceRef {
// Check for a property setter. // Check for a property setter.
let mb_key = (ClassMemberKind::Setter, false, name.lexeme.clone()); let mb_key = (ClassMemberKind::Setter, false, name.lexeme.clone());
if let Some(setter) = instance.class.borrow().members.get(&mb_key) { let lookup = with_class_member(&instance.class, &mb_key, |setter| {
// Bind and execute the property setter
let bound_method = bind_method(setter, Value::from(self.clone())); let bound_method = bind_method(setter, Value::from(self.clone()));
bound_method bound_method
.call(itpr_state, vec![value]) .call(itpr_state, vec![value.clone()])
.expect("failed to execute setter"); .expect("failed to execute setter");
});
if lookup.is_some() {
return; return;
} }