errorhandling & inheritance
Some checks are pending
Interp Tester / Check (push) Waiting to run
Interp Tester / Test Suite (push) Waiting to run
Interp Tester / Rustfmt (push) Waiting to run
Interp Tester / Clippy (push) Waiting to run
Interp Tester / build (push) Waiting to run

This commit is contained in:
hendrik 2025-06-28 11:39:25 +02:00
parent 67dd129c5e
commit fff1b21e9b
3 changed files with 415 additions and 14 deletions

View File

@ -278,23 +278,42 @@ impl Interpreter {
let ExprEnum::Get(g) = call else {
panic!("only call with get");
};
let ExprEnum::Variable(inst_name) = g.object() else {
panic!("expected name to be of type variable")
};
let resolved = self.lookup_variable(inst_name.name(), call).unwrap();
let resolved = self
.visit_expr(g.object())
.expect("expected object in get call");
let EvaluatedToken::Object(obj) = resolved else {
panic!("expected object");
};
let obj_class = obj.borrow().class();
let res = obj_class
.find_method(&g.name().lexeme())
.expect("method not found");
let res = res.bind_ref(obj.clone());
//.map(|f| f as Rc<dyn Callable>);
let call_res = res.call(Vec::new(), self).expect("call failed");
/*
fn resolve_callable(
obj: &Rc<RefCell<LoxInstance>>,
callable: Rc<Box<dyn Callable>>,
inter: &mut Interpreter,
) -> Option<Rc<Box<dyn Callable>>> {
if callable.arity() == 0 {
let call_res = callable.call(Vec::new(), inter).expect("call failed");
match call_res {
EvaluatedToken::Callable(c) => {
c.bind_ref(obj.clone());
return resolve_callable(obj, c, inter);
}
_ => {
return Some(callable);
}
}
}
Some(callable)
}
*/
let callable = obj
.borrow()
.class()
.find_method(&g.name().lexeme())?
.bind_ref(obj.clone());
let call_res = callable.call(Vec::new(), self).expect("call failed");
match call_res {
EvaluatedToken::Callable(c) => {
c.bind_ref(obj.clone());
@ -307,8 +326,8 @@ impl Interpreter {
panic!("unexpected return type");
}
}
println!("lookup call get: {:#?}", res);
Some(Rc::new(res))
println!("lookup call get: {:#?}", callable);
Some(Rc::new(callable))
}
fn lookup_func(&mut self, call: &ExprEnum) -> Option<Rc<Box<dyn Callable>>> {

View File

@ -226,3 +226,385 @@ baz.inbaz();
assert_eq!(result, Ok(()));
}
}
#[cfg(test)]
mod overriding_methods_test {
use interpreter_starter_rust::{output::config::create_output_capurer, runner::interpret};
#[test]
fn test1() {
let capturer = create_output_capurer();
let input = r#"
class A {
method() {
print "A method";
}
}
class B < A {
method() {
print "B method";
}
}
var b = B();
b.method();
"#;
let expected_result = String::from("B method\n");
let expected_stderr = String::new();
let result = interpret(input);
assert_eq!(capturer.get_stderr_content(), expected_stderr);
assert_eq!(capturer.get_stdout_content(), expected_result);
assert_eq!(result, Ok(()));
}
#[test]
fn test2() {
let capturer = create_output_capurer();
let input = r#"
class Base {
init(a) {
this.a = a;
}
}
class Derived < Base {
init(a, b) {
this.a = a;
this.b = b;
}
}
var derived = Derived(53, 20);
print derived.a;
print derived.b;
"#;
let expected_result = String::from("53\n20\n");
let expected_stderr = String::new();
let result = interpret(input);
assert_eq!(capturer.get_stderr_content(), expected_stderr);
assert_eq!(capturer.get_stdout_content(), expected_result);
assert_eq!(result, Ok(()));
}
#[test]
fn test3() {
let capturer = create_output_capurer();
let input = r#"
class Base {
init(a) {
this.a = a;
}
cook() {
return "Base cooking " + this.a;
}
}
class Derived < Base {
init(a, b) {
this.a = a;
this.b = b;
}
cook() {
return "Derived cooking " + this.b + " with " + this.a + " and " + this.b;
}
makeFood() {
return this.cook();
}
}
var derived = Derived("onions", "shallots");
print derived.a;
print derived.b;
print Base("ingredient").cook();
print derived.cook();
"#;
let expected_result = String::from(
"onions\nshallots\nBase cooking ingredient\nDerived cooking shallots with onions and shallots\n",
);
let expected_stderr = String::new();
let result = interpret(input);
assert_eq!(capturer.get_stderr_content(), expected_stderr);
assert_eq!(capturer.get_stdout_content(), expected_result);
assert_eq!(result, Ok(()));
}
#[test]
fn test4() {
let capturer = create_output_capurer();
let input = r#"
class Animal {
speak() {
return "Animal speaks";
}
makeSound() {
return "Generic sound";
}
communicate() {
return this.speak() + " : " + this.makeSound();
}
}
class Dog < Animal {
speak() {
return "Dog speaks";
}
makeSound() {
return "Woof";
}
}
class Puppy < Dog {
speak() {
return "Puppy speaks";
}
}
var animal = Animal();
var dog = Dog();
var puppy = Puppy();
print animal.communicate();
print dog.communicate();
print puppy.communicate();
"#;
let expected_result =
String::from("Animal speaks : Generic sound\nDog speaks : Woof\nPuppy speaks : Woof\n");
let expected_stderr = String::new();
let result = interpret(input);
assert_eq!(capturer.get_stderr_content(), expected_stderr);
assert_eq!(capturer.get_stdout_content(), expected_result);
assert_eq!(result, Ok(()));
}
}
#[cfg(test)]
mod ab0 {
use interpreter_starter_rust::{output::config::create_output_capurer, runner::interpret};
#[test]
fn test1() {
let capturer = create_output_capurer();
let input = r#"
class Foo < Foo {} // expect compile error
"#;
let expected_stderr = String::from("[line 2] Error: A class can't inherit from itself.\n");
let expected_result = String::new();
let result = interpret(input);
assert_eq!(capturer.get_stderr_content(), expected_stderr);
assert_eq!(capturer.get_stdout_content(), expected_result);
assert_eq!(result, Err(65)); // Expecting a compilation error
}
#[test]
fn test2() {
let capturer = create_output_capurer();
let input = r#"
fun A() {}
class B < A {} // expect runtime error
print A();
print B();
"#;
let expected_stderr = String::from("[line 4] Error: Superclass must be a class.\n");
let expected_result = String::new();
let result = interpret(input);
assert_eq!(capturer.get_stderr_content(), expected_stderr);
assert_eq!(capturer.get_stdout_content(), expected_result);
assert_eq!(result, Err(70)); // Expecting a runtime error
}
#[test]
fn test3() {
let capturer = create_output_capurer();
let input = r#"
var A = "class";
class B < A {} // expect runtime error
print B();
"#;
let expected_stderr = String::from("[line 4] Error: Superclass must be a class.\n");
let expected_result = String::new();
let result = interpret(input);
assert_eq!(capturer.get_stderr_content(), expected_stderr);
assert_eq!(capturer.get_stdout_content(), expected_result);
assert_eq!(result, Err(70)); // Expecting a runtime error
}
#[test]
fn test4() {
let capturer = create_output_capurer();
let input = r#"
class A {
method() {
print "A";
}
}
class B < A {}
class C < B {}
class D < A {}
B = "not a class";
class E < B {} // expect runtime error
"#;
let expected_stderr = String::from("[line 13] Error: Superclass must be a class.\n");
let expected_result = String::new();
let result = interpret(input);
assert_eq!(capturer.get_stderr_content(), expected_stderr);
assert_eq!(capturer.get_stdout_content(), expected_result);
assert_eq!(result, Err(70)); // Expecting a runtime error
}
}
#[cfg(test)]
mod qi0 {
use interpreter_starter_rust::{output::config::create_output_capurer, runner::interpret};
#[test]
fn test1() {
let capturer = create_output_capurer();
let input = r#"
class Doughnut {
cook() {
print "Fry until golden brown.";
}
}
class BostonCream < Doughnut {
cook() {
super.cook();
}
}
BostonCream().cook();
"#;
let expected_result = String::from("Fry until golden brown.\n");
let expected_stderr = String::new();
let result = interpret(input);
assert_eq!(capturer.get_stderr_content(), expected_stderr);
assert_eq!(capturer.get_stdout_content(), expected_result);
assert_eq!(result, Ok(()));
}
#[test]
fn test2() {
let capturer = create_output_capurer();
let input = r#"
class A {
say() {
print "A";
}
}
class B < A {
test() {
super.say();
}
say() {
print "B";
}
}
class C < B {
say() {
print "C";
}
}
C().say();
C().test(); // expect: A
"#;
let expected_result = String::from("C\nA\n");
let expected_stderr = String::new();
let result = interpret(input);
assert_eq!(capturer.get_stderr_content(), expected_stderr);
assert_eq!(capturer.get_stdout_content(), expected_result);
assert_eq!(result, Ok(()));
}
#[test]
fn test3() {
let capturer = create_output_capurer();
let input = r#"
class A {
say() {
print "A";
}
}
class B < A {
getClosure() {
fun closure() {
super.say();
}
return closure;
}
say() {
print "B";
}
}
class C < B {
say() {
print "C";
}
}
C().getClosure()(); // expect: A
"#;
let expected_result = String::from("A\n");
let expected_stderr = String::new();
let result = interpret(input);
assert_eq!(capturer.get_stderr_content(), expected_stderr);
assert_eq!(capturer.get_stdout_content(), expected_result);
assert_eq!(result, Ok(()));
}
#[test]
fn test4() {
let capturer = create_output_capurer();
let input = r#"
class Base {
method() {
print "Base.method()";
}
}
// Parent inherits method from Base
class Parent < Base {
method() {
super.method();
}
}
// Child inherits method from Parent
class Child < Parent {
method() {
super.method();
}
}
var parent = Parent();
parent.method(); // expect: Base.method()
var child = Child();
child.method(); // expect: Base.method()
"#;
let expected_stderr = String::new();
let expected_result = String::from("Base.method()\nBase.method()\n");
let result = interpret(input);
assert_eq!(capturer.get_stderr_content(), expected_stderr);
assert_eq!(capturer.get_stdout_content(), expected_result);
assert_eq!(result, Ok(()));
}
}

0
tmp Normal file
View File