diff --git a/main.arc b/main.arc index 2d7df01..40c12f3 100644 --- a/main.arc +++ b/main.arc @@ -1,7 +1,9 @@ -class Bacon { - eat() { - print "Crunch crunch crunch!"; +class Foo { + init() { + print this; + return 10; } } -Bacon().eat(); \ No newline at end of file +var foo = Foo(); +print foo.init(); \ No newline at end of file diff --git a/src/errors.rs b/src/errors.rs index fba6eee..8d441b4 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -1,5 +1,6 @@ use crate::tokens::*; +#[derive(Debug, PartialEq, Clone)] pub enum Error { ParseError { token: Token, message: String }, RuntimeError { token: Token, message: String }, diff --git a/src/functions.rs b/src/functions.rs index f4bdfa5..cdcbc76 100644 --- a/src/functions.rs +++ b/src/functions.rs @@ -12,6 +12,7 @@ use crate::interpreter::Interpreter; pub struct Function { name : Token, params : Rc>, + is_initializer: bool, body : Rc>>, closure: Rc>, } @@ -26,6 +27,7 @@ impl Clone for Function { fn clone(&self) -> Self { Self { name: self.name.clone(), + is_initializer: self.is_initializer, params: Rc::clone(&self.params), body: Rc::clone(&self.body), closure: Rc::clone(&self.closure), @@ -40,12 +42,13 @@ impl PartialEq for Function { } impl Function { - pub fn new(declaration: &FunctionStmt, closure: &Rc>) -> Self { + pub fn new(declaration: &FunctionStmt, closure: &Rc>, is_initializer: bool) -> Self { Self { name: declaration.name.clone(), params: Rc::clone(&declaration.params), body: Rc::clone(&declaration.body), closure: Rc::clone(&closure), + is_initializer: is_initializer, } } @@ -54,6 +57,7 @@ impl Function { e.borrow_mut().define("this".to_string(), instance.clone()); Object::Function(Rc::new(Self { name: self.name.clone(), + is_initializer: self.is_initializer, params: Rc::clone(&self.params), body: Rc::clone(&self.body), closure: Rc::new(e), @@ -71,10 +75,19 @@ impl CallableTrait for Function { } match interpreter.execute_block(&self.body, e) { - Ok(_) => Ok(Object::Nil), + Ok(_) => { + if self.is_initializer { + return Ok(self.closure.borrow().get_at(0, "this").unwrap()); + } + Ok(Object::Nil) + } Err(e) => { match e { - Error::Return { value } => Ok(value), + Error::Return { value } => if self.is_initializer { + Ok(self.closure.borrow().get_at(0, "this").unwrap()) + } else { + Ok(value) + } _ => Err(e), } } diff --git a/src/interpreter.rs b/src/interpreter.rs index d8927ef..04cc7ab 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -28,8 +28,9 @@ impl StmtVisitor<()> for Interpreter { 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()), + Function::new(func, self.environment.borrow().deref(), is_initializer), )); methods.insert(func.name.lexeme.clone(), function); } else { @@ -58,7 +59,7 @@ impl StmtVisitor<()> for Interpreter { } } fn visit_function_stmt(&self, _: Rc, stmt: &FunctionStmt) -> Result<(), Error> { - let function = Function::new(stmt, self.environment.borrow().deref()); + let function = Function::new(stmt, self.environment.borrow().deref(), false); self.environment.borrow().borrow_mut().define( stmt.name.lexeme.clone(), Object::Function(Rc::new(function)), diff --git a/src/resolver.rs b/src/resolver.rs index 8cdc7a3..60a2ecb 100644 --- a/src/resolver.rs +++ b/src/resolver.rs @@ -14,17 +14,29 @@ pub struct Resolver<'a>{ scopes: RefCell>>>, had_error: RefCell, current_function: RefCell, + current_class: RefCell, } #[derive(PartialEq)] enum FunctionType { None, Function, + Initializer, Method, } +#[derive(PartialEq)] +enum ClassType { + None, + Class, +} + + impl<'a> StmtVisitor<()> for Resolver<'a>{ fn visit_class_stmt(&self , _: Rc, stmt: &ClassStmt) -> Result<(), Error> { + + let enclosing_class = self.current_class.replace(ClassType::Class); + self.declare(&stmt.name); self.define(&stmt.name); @@ -32,8 +44,12 @@ impl<'a> StmtVisitor<()> for Resolver<'a>{ self.scopes.borrow().last().unwrap().borrow_mut().insert("this".to_string(), true); for method in stmt.methods.deref() { - let declaration = FunctionType::Method; if let Stmt::Function(method) = method.deref() { + let declaration = if method.name.lexeme == "init" { + FunctionType::Initializer + }else{ + FunctionType::Method + }; self.resolve_function(method, declaration); }else{ return Err(Error::runtime_error(&stmt.name, "Class method did not resolve to a function.")); @@ -41,6 +57,7 @@ impl<'a> StmtVisitor<()> for Resolver<'a>{ } self.end_scope(); + self.current_class.replace(enclosing_class); Ok(()) } @@ -51,6 +68,9 @@ impl<'a> StmtVisitor<()> for Resolver<'a>{ } if let Some(value) = stmt.value.clone() { + if *self.current_function.borrow() == FunctionType::Initializer { + return Err(Error::runtime_error(&stmt.keyword, "Cannot return a value from an initializer.")); + } self.resolve_expr(value); } @@ -104,6 +124,10 @@ impl<'a> StmtVisitor<()> for Resolver<'a>{ impl<'a> ExprVisitor<()> for Resolver<'a>{ fn visit_this_expr(&self, wrapper: Rc, expr: &ThisExpr) -> Result<(), Error> { + if *self.current_class.borrow() == ClassType::None { + self.error(&expr.keyword, "Cannot use 'this' outside of a class."); + return Ok(()); + } self.resolve_local(wrapper, &expr.keyword); Ok(()) } @@ -173,6 +197,7 @@ impl<'a> Resolver<'a> { scopes: RefCell::new(Vec::new()), had_error: RefCell::new(false), current_function: RefCell::new(FunctionType::None), + current_class: RefCell::new(ClassType::None), } } pub fn resolve(&self, statement: &Rc>>){ diff --git a/src/tokens.rs b/src/tokens.rs index ac45746..299a1f6 100644 --- a/src/tokens.rs +++ b/src/tokens.rs @@ -113,8 +113,14 @@ impl ClassStruct { } } - pub fn instantiate(&self, _interpreter: &Interpreter, _arguments: Vec, cls: Rc) -> Result { - Ok(Object::Instance(Rc::new(InstanceStruct::new(cls)))) + pub fn instantiate(&self, interpreter: &Interpreter, arguments: Vec, cls: Rc) -> Result { + let instance = Object::Instance(Rc::new(InstanceStruct::new(cls))); + if let Some(Object::Function(initializer)) = self.find_method("init".to_string()){ + if let Object::Function(initializer) = initializer.bind(&instance) { + initializer.call(interpreter, &arguments)?; + } + } + Ok(instance) } pub fn find_method(&self, name: String) -> Option { @@ -128,6 +134,9 @@ impl CallableTrait for ClassStruct { } fn arity(&self) -> usize { + if let Some(Object::Function(initializer)) = self.find_method("init".to_string()){ + return initializer.arity(); + } 0 }