From b0e81b05d08b772534763b03107e7663cd79a932 Mon Sep 17 00:00:00 2001 From: Alex van de Sandt Date: Thu, 18 Jul 2024 16:17:49 -0400 Subject: [PATCH] Handle undefined object fields and non-object derefs --- src/interpreter.rs | 48 ++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 44 insertions(+), 4 deletions(-) diff --git a/src/interpreter.rs b/src/interpreter.rs index fe99fcb..ba09672 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -209,10 +209,13 @@ impl<'a> Interpreter<'a> { callable.call(self, evaluated_args) } Expr::Dereference { object, name } => { - let Some(instance) = self.interpret_expr(object, env)?.as_instance() else { - todo!("deref target isn't class instance") - }; - let value = instance.get(name).expect("field not defined"); + let val = self.interpret_expr(object, env)?; + let instance = val + .as_instance() + .ok_or_else(|| RuntimeError::deref_non_instance(&val, object.span()))?; + let value = instance + .get(name) + .ok_or_else(|| RuntimeError::undefined_field(object.span(), name))?; Ok(value) } } @@ -407,6 +410,20 @@ pub enum RuntimeError { #[label("this is a `{actual_type}`, not an object")] non_instance: SourceSpan, }, + #[error("Only objects have fields")] + DerefNonClassInstance { + actual_type: &'static str, + #[label("this is a `{actual_type}`, not an object")] + non_instance: SourceSpan, + }, + #[error("Field `{field_name}` not defined on object")] + UndefinedField { + field_name: String, + #[label("this object...")] + object: SourceSpan, + #[label("...does not have this field or method defined")] + target: SourceSpan, + }, #[error(transparent)] IoError(#[from] std::io::Error), @@ -457,6 +474,21 @@ impl RuntimeError { non_instance: value_span.into(), } } + + fn deref_non_instance(non_instance: &Value, value_span: Span) -> Self { + Self::DerefNonClassInstance { + actual_type: non_instance.type_str(), + non_instance: value_span.into(), + } + } + + fn undefined_field(object_span: Span, name: &Spanned) -> Self { + Self::UndefinedField { + field_name: name.as_ref().clone(), + object: object_span.into(), + target: name.span().into(), + } + } } #[cfg(test)] @@ -792,6 +824,14 @@ mod test { ); } + #[test] + fn errs_on_non_instance_deref() { + assert_matches!( + interpret_expr_to_err("1.field"), + RuntimeError::DerefNonClassInstance { .. } + ) + } + #[test] fn errs_on_illegal_arithmetic() { for input in [ -- 2.51.2