Skip to content

Commit

Permalink
🔫 added superclass methods
Browse files Browse the repository at this point in the history
  • Loading branch information
aym-n committed Jan 10, 2024
1 parent fe4acfa commit 5fe0ea5
Show file tree
Hide file tree
Showing 7 changed files with 152 additions and 33 deletions.
1 change: 1 addition & 0 deletions generate_ast/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ pub fn generate_ast(output_dir: &str) -> io::Result<()> {
"Literal : Option<Object> value",
"Logical : Rc<Expr> left, Token operator, Rc<Expr> right",
"Set : Rc<Expr> object, Token name, Rc<Expr> value",
"Super : Token keyword, Token method",
"This : Token keyword",
"Unary : Token operator, Rc<Expr> right",
"Variable : Token name",
Expand Down
11 changes: 6 additions & 5 deletions main.arc
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
class Foo {
init() {
print this;
class Doughnut {
cook() {
print "Fry until golden brown.";
}
}

var foo = Foo();
print foo.init();
class BostonCream < Doughnut {}

BostonCream().cook();
10 changes: 10 additions & 0 deletions src/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ pub enum Expr {
Literal(Rc<LiteralExpr>),
Logical(Rc<LogicalExpr>),
Set(Rc<SetExpr>),
Super(Rc<SuperExpr>),
This(Rc<ThisExpr>),
Unary(Rc<UnaryExpr>),
Variable(Rc<VariableExpr>),
Expand All @@ -27,6 +28,7 @@ impl PartialEq for Expr {
(Expr::Literal(a), Expr::Literal(b)) => Rc::ptr_eq(a, b),
(Expr::Logical(a), Expr::Logical(b)) => Rc::ptr_eq(a, b),
(Expr::Set(a), Expr::Set(b)) => Rc::ptr_eq(a, b),
(Expr::Super(a), Expr::Super(b)) => Rc::ptr_eq(a, b),
(Expr::This(a), Expr::This(b)) => Rc::ptr_eq(a, b),
(Expr::Unary(a), Expr::Unary(b)) => Rc::ptr_eq(a, b),
(Expr::Variable(a), Expr::Variable(b)) => Rc::ptr_eq(a, b),
Expand All @@ -50,6 +52,7 @@ impl Hash for Expr {
Expr::Literal(a) => { hasher.write_usize(Rc::as_ptr(a) as usize); }
Expr::Logical(a) => { hasher.write_usize(Rc::as_ptr(a) as usize); }
Expr::Set(a) => { hasher.write_usize(Rc::as_ptr(a) as usize); }
Expr::Super(a) => { hasher.write_usize(Rc::as_ptr(a) as usize); }
Expr::This(a) => { hasher.write_usize(Rc::as_ptr(a) as usize); }
Expr::Unary(a) => { hasher.write_usize(Rc::as_ptr(a) as usize); }
Expr::Variable(a) => { hasher.write_usize(Rc::as_ptr(a) as usize); }
Expand All @@ -68,6 +71,7 @@ impl Expr {
Expr::Literal(v) => expr_visitor.visit_literal_expr(wrapper, v),
Expr::Logical(v) => expr_visitor.visit_logical_expr(wrapper, v),
Expr::Set(v) => expr_visitor.visit_set_expr(wrapper, v),
Expr::Super(v) => expr_visitor.visit_super_expr(wrapper, v),
Expr::This(v) => expr_visitor.visit_this_expr(wrapper, v),
Expr::Unary(v) => expr_visitor.visit_unary_expr(wrapper, v),
Expr::Variable(v) => expr_visitor.visit_variable_expr(wrapper, v),
Expand Down Expand Up @@ -117,6 +121,11 @@ pub struct SetExpr {
pub value: Rc<Expr>,
}

pub struct SuperExpr {
pub keyword: Token,
pub method: Token,
}

pub struct ThisExpr {
pub keyword: Token,
}
Expand All @@ -139,6 +148,7 @@ pub trait ExprVisitor<T> {
fn visit_literal_expr(&self, wrapper: Rc<Expr>, expr: &LiteralExpr) -> Result<T, Error>;
fn visit_logical_expr(&self, wrapper: Rc<Expr>, expr: &LogicalExpr) -> Result<T, Error>;
fn visit_set_expr(&self, wrapper: Rc<Expr>, expr: &SetExpr) -> Result<T, Error>;
fn visit_super_expr(&self, wrapper: Rc<Expr>, expr: &SuperExpr) -> Result<T, Error>;
fn visit_this_expr(&self, wrapper: Rc<Expr>, expr: &ThisExpr) -> Result<T, Error>;
fn visit_unary_expr(&self, wrapper: Rc<Expr>, expr: &UnaryExpr) -> Result<T, Error>;
fn visit_variable_expr(&self, wrapper: Rc<Expr>, expr: &VariableExpr) -> Result<T, Error>;
Expand Down
104 changes: 78 additions & 26 deletions src/interpreter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,37 +18,43 @@ pub struct Interpreter {
}

impl StmtVisitor<()> for Interpreter {

fn visit_class_stmt(&self, _: Rc<Stmt>, stmt: &ClassStmt) -> Result<(), Error> {
let superclass = if let Some(superclass_expr) = &stmt.superclass {
let superclass = self.evaluate(superclass_expr.clone())?;

if let Object::Class(c) = superclass {
Some(c)
} else if let Expr::Variable(v) = superclass_expr.deref() {
return Err(Error::runtime_error(
&v.name,
"Superclass must be a class.",
));
return Err(Error::runtime_error(&v.name, "Superclass must be a class."));
} else {
panic!("could not extract variable expr");
}
} else {
None
};

self.environment
.borrow()
.borrow_mut()
.define(stmt.name.lexeme.clone(), Object::Nil);

let enclosing = if let Some(ref s) = superclass {
let mut e = Environment::new_with_enclosing(self.environment.borrow().clone());
e.define("super".to_string(), Object::Class(s.clone()));
Some(self.environment.replace(Rc::new(RefCell::new(e))))
} else {
None
};

let mut methods = HashMap::new();
for method in stmt.methods.deref() {
if let Stmt::Function(func) = method.deref() {
let is_initializer = func.name.lexeme == "init";
let function = Object::Function(Rc::new(
Function::new(func, self.environment.borrow().deref(), is_initializer),
));
let function = Object::Function(Rc::new(Function::new(
func,
self.environment.borrow().deref(),
is_initializer,
)));
methods.insert(func.name.lexeme.clone(), function);
} else {
return Err(Error::runtime_error(
Expand All @@ -58,7 +64,15 @@ impl StmtVisitor<()> for Interpreter {
};
}

let cls = Object::Class(Rc::new(ClassStruct::new(stmt.name.lexeme.clone(), superclass, methods)));
let cls = Object::Class(Rc::new(ClassStruct::new(
stmt.name.lexeme.clone(),
superclass,
methods,
)));

if let Some(previous) = enclosing {
self.environment.replace(previous);
}

self.environment
.borrow()
Expand Down Expand Up @@ -131,11 +145,50 @@ impl StmtVisitor<()> for Interpreter {
}

impl ExprVisitor<Object> for Interpreter {
fn visit_super_expr(&self, wrapper: Rc<Expr>, expr: &SuperExpr) -> Result<Object, Error> {
let distance = *self.locals.borrow().get(&wrapper).unwrap();
let superclass = if let Some(sc) = self
.environment
.borrow()
.borrow()
.get_at(distance, "super")
.ok()
{
if let Object::Class(superclass) = sc {
superclass
} else {
panic!("Unable to extract superclass");
}
} else {
panic!("Unable to extract superclass");
};

let object = self
.environment
.borrow()
.borrow()
.get_at(distance - 1, "this")
.ok()
.unwrap();

if let Some(method) = superclass.find_method(expr.method.lexeme.clone()) {
if let Object::Function(func) = method {
Ok(func.bind(&object))
} else {
panic!("method was not a function");
}
} else {
Err(Error::runtime_error(
&expr.method,
&format!("Undefined property '{}'.", expr.method.lexeme),
))
}
}

fn visit_this_expr(&self, wrapper: Rc<Expr>, expr: &ThisExpr) -> Result<Object, Error> {
self.look_up_variable(&expr.keyword, wrapper)
}

fn visit_set_expr(&self, wrapper: Rc<Expr>, expr: &SetExpr) -> Result<Object, Error> {
let object = self.evaluate(expr.object.clone())?;

Expand Down Expand Up @@ -306,17 +359,17 @@ impl ExprVisitor<Object> for Interpreter {
arguments.push(self.evaluate(argument)?);
}

let (callfunc, cls): (Option<Rc<dyn CallableTrait>>, Option<Rc<ClassStruct>>) =
match callee {
Object::Function(func) => (Some(func), None),
Object::Native(native) => (Some(native.func.clone()), None),
Object::Class(cls) => {
let class = Rc::clone(&cls);
(Some(cls), Some(class))
},
_ => (None, None),
};
let (callfunc, cls): (Option<Rc<dyn CallableTrait>>, Option<Rc<ClassStruct>>) = match callee
{
Object::Function(func) => (Some(func), None),
Object::Native(native) => (Some(native.func.clone()), None),
Object::Class(cls) => {
let class = Rc::clone(&cls);
(Some(cls), Some(class))
}
_ => (None, None),
};

if let Some(callfunc) = callfunc {
if arguments.len() != callfunc.arity() {
return Err(Error::runtime_error(
Expand All @@ -335,7 +388,6 @@ impl ExprVisitor<Object> for Interpreter {
"Can only call functions and classes.",
))
}

}

fn visit_variable_expr(&self, wrapper: Rc<Expr>, expr: &VariableExpr) -> Result<Object, Error> {
Expand All @@ -349,9 +401,9 @@ impl Interpreter {

global.borrow_mut().define(
"clock".to_string(),
Object::Native(Rc::new(Native{
func: Rc::new(NativeClock{}),
}))
Object::Native(Rc::new(Native {
func: Rc::new(NativeClock {}),
})),
);

Interpreter {
Expand Down
12 changes: 12 additions & 0 deletions src/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -485,6 +485,18 @@ impl Parser {
})));
}

if self.match_token(vec![TokenKind::Super]){
let keyword = self.previous();
self.consume(TokenKind::Dot, "Expect . after super")?;
let method = self.consume(TokenKind::Identifier, "Expect Superclass method name.")?;

return Ok(Expr::Super(Rc::new(SuperExpr{
keyword,
method,
})))

}

if self.match_token(vec![TokenKind::This]) {
return Ok(Expr::This(Rc::new(ThisExpr {
keyword: self.previous(),
Expand Down
39 changes: 38 additions & 1 deletion src/resolver.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ enum FunctionType {
enum ClassType {
None,
Class,
SubClass,
}


Expand All @@ -41,12 +42,26 @@ impl<'a> StmtVisitor<()> for Resolver<'a>{
self.define(&stmt.name);

if let Some(superclass) = stmt.superclass.clone() {

self.current_class.replace(ClassType::SubClass);

if let Expr::Variable(v) = &superclass.deref() {
if v.name.lexeme == stmt.name.lexeme {
return Err(Error::runtime_error(&v.name, "A class cannot inherit from itself."));
}
}
self.resolve_expr(superclass);

self.begin_scope();
self.scopes
.borrow()
.last()
.unwrap()
.borrow_mut()
.insert("super".to_string(), true);

self.begin_scope();
self.scopes.borrow().last().unwrap().borrow_mut().insert("super".to_string(), true);
}

self.begin_scope();
Expand All @@ -64,8 +79,12 @@ impl<'a> StmtVisitor<()> for Resolver<'a>{
return Err(Error::runtime_error(&stmt.name, "Class method did not resolve to a function."));
}
}

self.end_scope();

if stmt.superclass.is_some(){
self.end_scope();
}

self.current_class.replace(enclosing_class);

Ok(())
Expand Down Expand Up @@ -132,6 +151,24 @@ impl<'a> StmtVisitor<()> for Resolver<'a>{
}

impl<'a> ExprVisitor<()> for Resolver<'a>{
fn visit_super_expr(&self, wrapper: Rc<Expr>, expr: &SuperExpr) -> Result<(), Error> {
match self.current_class.borrow().deref() {
ClassType::None => {
self.error(&expr.keyword, "Can't use 'super' outside of a class.");
}
ClassType::SubClass => {}
_ => {
self.error(
&expr.keyword,
"Can't use 'super' in a class with no superclass.",
);
}
}

self.resolve_local(wrapper, &expr.keyword);
Ok(())
}

fn visit_this_expr(&self, wrapper: Rc<Expr>, expr: &ThisExpr) -> Result<(), Error> {
if *self.current_class.borrow() == ClassType::None {
self.error(&expr.keyword, "Cannot use 'this' outside of a class.");
Expand Down
8 changes: 7 additions & 1 deletion src/tokens.rs
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,13 @@ impl ClassStruct {
}

pub fn find_method(&self, name: String) -> Option<Object> {
self.methods.get(&name).cloned()
if let Some(method) = self.methods.get(&name){
Some(method.clone())
}else if let Some(superclass) = &self.superclass {
superclass.find_method(name)
}else{
None
}
}
}

Expand Down

0 comments on commit 5fe0ea5

Please sign in to comment.