errorhandling & inheritance
This commit is contained in:
parent
67dd129c5e
commit
fff1b21e9b
@ -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>>> {
|
||||
|
||||
@ -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(()));
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user