Skip to content

Commit

Permalink
🤖 added classes and methods
Browse files Browse the repository at this point in the history
  • Loading branch information
aym-n committed Jan 10, 2024
1 parent 2df8e18 commit 68fce9a
Show file tree
Hide file tree
Showing 10 changed files with 258 additions and 30 deletions.
3 changes: 3 additions & 0 deletions generate_ast/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,11 @@ pub fn generate_ast(output_dir: &str) -> io::Result<()> {
"Assign : Token name, Rc<Expr> value",
"Binary : Rc<Expr> left, Token operator, Rc<Expr> right",
"Call : Rc<Expr> callee, Token paren, Vec<Rc<Expr>> arguments",
"Get : Rc<Expr> object, Token name",
"Grouping : Rc<Expr> expression",
"Literal : Option<Object> value",
"Logical : Rc<Expr> left, Token operator, Rc<Expr> right",
"Set : Rc<Expr> object, Token name, Rc<Expr> value",
"Unary : Token operator, Rc<Expr> right",
"Variable : Token name",
],
Expand All @@ -30,6 +32,7 @@ pub fn generate_ast(output_dir: &str) -> io::Result<()> {
&["errors", "expr", "tokens", "rc"],
&[
"Block : Rc<Vec<Rc<Stmt>>> statements",
"Class : Token name, Rc<Vec<Rc<Stmt>>> methods",
"Expression : Rc<Expr> expression",
"Function : Token name, Rc<Vec<Token>> params, Rc<Vec<Rc<Stmt>>> body",
"If : Rc<Expr> condition, Rc<Stmt> then_branch, Option<Rc<Stmt>> else_branch",
Expand Down
23 changes: 5 additions & 18 deletions main.arc
Original file line number Diff line number Diff line change
@@ -1,20 +1,7 @@

var a = "global a";
var b = "global b";
var c = "global c";
{
var a = "outer a";
var b = "outer b";
{
var a = "inner a";
print a;
print b;
print c;
class Bacon {
eat() {
print "Crunch crunch crunch!";
}
print a;
print b;
print c;
}
print a;
print b;
print c;

Bacon().eat();
21 changes: 21 additions & 0 deletions src/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,11 @@ pub enum Expr {
Assign(Rc<AssignExpr>),
Binary(Rc<BinaryExpr>),
Call(Rc<CallExpr>),
Get(Rc<GetExpr>),
Grouping(Rc<GroupingExpr>),
Literal(Rc<LiteralExpr>),
Logical(Rc<LogicalExpr>),
Set(Rc<SetExpr>),
Unary(Rc<UnaryExpr>),
Variable(Rc<VariableExpr>),
}
Expand All @@ -19,9 +21,11 @@ impl PartialEq for Expr {
(Expr::Assign(a), Expr::Assign(b)) => Rc::ptr_eq(a, b),
(Expr::Binary(a), Expr::Binary(b)) => Rc::ptr_eq(a, b),
(Expr::Call(a), Expr::Call(b)) => Rc::ptr_eq(a, b),
(Expr::Get(a), Expr::Get(b)) => Rc::ptr_eq(a, b),
(Expr::Grouping(a), Expr::Grouping(b)) => Rc::ptr_eq(a, b),
(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::Unary(a), Expr::Unary(b)) => Rc::ptr_eq(a, b),
(Expr::Variable(a), Expr::Variable(b)) => Rc::ptr_eq(a, b),
_ => false,
Expand All @@ -39,9 +43,11 @@ impl Hash for Expr {
Expr::Assign(a) => { hasher.write_usize(Rc::as_ptr(a) as usize); }
Expr::Binary(a) => { hasher.write_usize(Rc::as_ptr(a) as usize); }
Expr::Call(a) => { hasher.write_usize(Rc::as_ptr(a) as usize); }
Expr::Get(a) => { hasher.write_usize(Rc::as_ptr(a) as usize); }
Expr::Grouping(a) => { hasher.write_usize(Rc::as_ptr(a) as usize); }
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::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 @@ -54,9 +60,11 @@ impl Expr {
Expr::Assign(v) => expr_visitor.visit_assign_expr(wrapper, v),
Expr::Binary(v) => expr_visitor.visit_binary_expr(wrapper, v),
Expr::Call(v) => expr_visitor.visit_call_expr(wrapper, v),
Expr::Get(v) => expr_visitor.visit_get_expr(wrapper, v),
Expr::Grouping(v) => expr_visitor.visit_grouping_expr(wrapper, v),
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::Unary(v) => expr_visitor.visit_unary_expr(wrapper, v),
Expr::Variable(v) => expr_visitor.visit_variable_expr(wrapper, v),
}
Expand All @@ -80,6 +88,11 @@ pub struct CallExpr {
pub arguments: Vec<Rc<Expr>>,
}

pub struct GetExpr {
pub object: Rc<Expr>,
pub name: Token,
}

pub struct GroupingExpr {
pub expression: Rc<Expr>,
}
Expand All @@ -94,6 +107,12 @@ pub struct LogicalExpr {
pub right: Rc<Expr>,
}

pub struct SetExpr {
pub object: Rc<Expr>,
pub name: Token,
pub value: Rc<Expr>,
}

pub struct UnaryExpr {
pub operator: Token,
pub right: Rc<Expr>,
Expand All @@ -107,9 +126,11 @@ pub trait ExprVisitor<T> {
fn visit_assign_expr(&self, wrapper: Rc<Expr>, expr: &AssignExpr) -> Result<T, Error>;
fn visit_binary_expr(&self, wrapper: Rc<Expr>, expr: &BinaryExpr) -> Result<T, Error>;
fn visit_call_expr(&self, wrapper: Rc<Expr>, expr: &CallExpr) -> Result<T, Error>;
fn visit_get_expr(&self, wrapper: Rc<Expr>, expr: &GetExpr) -> Result<T, Error>;
fn visit_grouping_expr(&self, wrapper: Rc<Expr>, expr: &GroupingExpr) -> Result<T, Error>;
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_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
38 changes: 38 additions & 0 deletions src/instance.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
use crate::errors::*;
use crate::tokens::*;
use std::cell::RefCell;
use std::collections::hash_map::Entry;
use std::collections::HashMap;
use std::rc::Rc;

#[derive(Debug, PartialEq, Clone)]
pub struct InstanceStruct {
pub class: Rc<ClassStruct>,
fields: RefCell<HashMap<String, Object>>,
}

impl InstanceStruct {
pub fn new(class: Rc<ClassStruct>) -> Self {
InstanceStruct {
class: Rc::clone(&class),
fields: RefCell::new(HashMap::new()),
}
}

pub fn get(&self, name: &Token) -> Result<Object, Error> {
if let Entry::Occupied(entry) = self.fields.borrow_mut().entry(name.lexeme.clone()) {
Ok(entry.get().clone())
} else if let Some(method) = self.class.find_method(name.lexeme.clone()) {
Ok(method)
} else {
Err(Error::runtime_error(
name,
&format!("Undefined property '{}'.", name.lexeme),
))
}
}

pub fn set(&self, name: &Token, value: Object) {
self.fields.borrow_mut().insert(name.lexeme.clone(), value);
}
}
79 changes: 69 additions & 10 deletions src/interpreter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,36 @@ pub struct Interpreter {
}

impl StmtVisitor<()> for Interpreter {
fn visit_class_stmt(&self, _: Rc<Stmt>, stmt: &ClassStmt) -> Result<(), Error> {
self.environment
.borrow()
.borrow_mut()
.define(stmt.name.lexeme.clone(), Object::Nil);

let mut methods = HashMap::new();
for method in stmt.methods.deref() {
if let Stmt::Function(func) = method.deref() {
let function = Object::Function(Callable {
func: Rc::new(Function::new(func, self.environment.borrow().deref())),
});
methods.insert(func.name.lexeme.clone(), function);
} else {
return Err(Error::runtime_error(
&stmt.name,
"Class method did not resolve to a function.",
));
};
}

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

self.environment
.borrow()
.borrow_mut()
.assign(&stmt.name, cls.clone())?;
Ok(())
}

fn visit_return_stmt(&self, _: Rc<Stmt>, stmt: &ReturnStmt) -> Result<(), Error> {
if let Some(value) = stmt.value.clone() {
let value = self.evaluate(value)?;
Expand Down Expand Up @@ -56,11 +86,7 @@ impl StmtVisitor<()> for Interpreter {
Ok(())
}

fn visit_expression_stmt(
&self,
_: Rc<Stmt>,
stmt: &ExpressionStmt,
) -> Result<(), Error> {
fn visit_expression_stmt(&self, _: Rc<Stmt>, stmt: &ExpressionStmt) -> Result<(), Error> {
self.evaluate(stmt.expression.clone())?;
Ok(())
}
Expand Down Expand Up @@ -88,6 +114,31 @@ impl StmtVisitor<()> for Interpreter {
}

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

if let Object::Instance(inst) = object {
let value = self.evaluate(expr.value.clone())?;
inst.set(&expr.name, value.clone());
return Ok(value);
}

Err(Error::runtime_error(
&expr.name,
"Only instances have fields.",
))
}

fn visit_get_expr(&self, wrapper: Rc<Expr>, expr: &GetExpr) -> Result<Object, Error> {
let object = self.evaluate(expr.object.clone())?;
if let Object::Instance(inst) = object {
return inst.get(&expr.name);
}
Err(Error::runtime_error(
&expr.name,
"Only instances have properties.",
))
}
fn visit_logical_expr(&self, _: Rc<Expr>, expr: &LogicalExpr) -> Result<Object, Error> {
let left = self.evaluate(expr.left.clone())?;

Expand Down Expand Up @@ -124,11 +175,7 @@ impl ExprVisitor<Object> for Interpreter {
Ok(expr.value.clone().unwrap())
}

fn visit_grouping_expr(
&self,
_: Rc<Expr>,
expr: &GroupingExpr,
) -> Result<Object, Error> {
fn visit_grouping_expr(&self, _: Rc<Expr>, expr: &GroupingExpr) -> Result<Object, Error> {
Ok(self.evaluate(expr.expression.clone())?)
}

Expand Down Expand Up @@ -247,6 +294,18 @@ impl ExprVisitor<Object> for Interpreter {
));
}
function.func.call(self, &arguments)
} else if let Object::Class(cls) = callee {
if arguments.len() != cls.arity() {
return Err(Error::runtime_error(
&expr.paren,
&format!(
"Expected {} arguments but got {}",
cls.arity(),
arguments.len()
),
));
}
cls.instantiate(self, arguments, Rc::clone(&cls))
} else {
return Err(Error::runtime_error(
&expr.paren,
Expand Down
2 changes: 2 additions & 0 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ use resolver::*;

use std::rc::Rc;

mod instance;

fn eval(source: &str) -> Result<(), Error> {
let lexer = Lexer::new(source.to_string());
let mut tokens: Vec<Token> = lexer.collect();
Expand Down
36 changes: 34 additions & 2 deletions src/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,9 @@ impl Parser {
}

fn declaration(&mut self) -> Result<Rc<Stmt>, Error> {
let result = if self.match_token(vec![TokenKind::Fn]) {
let result = if self.match_token(vec![TokenKind::Class]){
self.class_declaration()
}else if self.match_token(vec![TokenKind::Fn]) {
self.function("function")
} else if self.match_token(vec![TokenKind::Var]) {
self.var_declaration()
Expand All @@ -42,6 +44,23 @@ impl Parser {
result
}

fn class_declaration(&mut self) -> Result<Rc<Stmt>, Error> {
let name = self.consume(TokenKind::Identifier, "Expect class name.")?;

let mut methods = Vec::new();

self.consume(TokenKind::LeftBrace, "Expect '{' before class body.")?;
while !self.check(TokenKind::RightBrace) && !self.is_at_end() {
methods.push(self.function("method")?);
}
self.consume(TokenKind::RightBrace, "Expect '}' after class body.")?;

Ok(Rc::new(Stmt::Class(Rc::new(ClassStmt {
name,
methods: Rc::new(methods),
}))))
}

fn statement(&mut self) -> Result<Rc<Stmt>, Error> {
if self.match_token(vec![TokenKind::For]) {
return self.for_statement();
Expand Down Expand Up @@ -253,6 +272,13 @@ impl Parser {
value: Rc::new(value),
})));
}
Expr::Get(g) => {
return Ok(Expr::Set(Rc::new(SetExpr {
object: Rc::clone(&g.object),
name: g.name.clone(),
value: Rc::new(value),
})));
}
_ => {
return Err(Error::parse_error(
&equals,
Expand Down Expand Up @@ -412,7 +438,13 @@ impl Parser {
loop {
if self.match_token(vec![TokenKind::LeftParen]) {
expr = self.finish_call(expr)?;
} else {
}else if self.match_token(vec![TokenKind::Dot]) {
let name = self.consume(TokenKind::Identifier, "Expect property name after '.'.")?;
expr = Expr::Get(Rc::new(GetExpr {
object: Rc::new(expr),
name,
}));
}else {
break;
}
}
Expand Down
Loading

0 comments on commit 68fce9a

Please sign in to comment.