Skip to content

Commit

Permalink
🔮 function objects and declarations
Browse files Browse the repository at this point in the history
aym-n committed Jan 6, 2024
1 parent 536038d commit a4dd2ff
Showing 8 changed files with 198 additions and 15 deletions.
1 change: 1 addition & 0 deletions generate_ast/mod.rs
Original file line number Diff line number Diff line change
@@ -35,6 +35,7 @@ pub fn generate_ast(output_dir: &String) -> io::Result<()> {
&vec![
"Block : Vec<Stmt> statements".to_string(),
"Expression : Expr expression".to_string(),
"Function : Token name, Vec<Token> params, Vec<Stmt> body".to_string(),
"If : Expr condition, Box<Stmt> then_branch, Option<Box<Stmt>> else_branch".to_string(),
"Print : Expr expression".to_string(),
"Var : Token name, Option<Expr> initializer".to_string(),
19 changes: 18 additions & 1 deletion src/callable.rs
Original file line number Diff line number Diff line change
@@ -4,6 +4,7 @@ use crate::tokens::Object;
use crate::errors::Error;
use std::rc::Rc;
use core::fmt::Debug;
use core::fmt::Display;

#[derive(Clone)]
pub struct Callable{
@@ -12,7 +13,13 @@ pub struct Callable{

impl Debug for Callable{
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "<func>")
write!(f, "<fn {}>", self.stringify())
}
}

impl Display for Callable{
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.stringify())
}
}

@@ -25,10 +32,20 @@ impl PartialEq for Callable{

pub trait CallableTrait {
fn call(&self, interpreter: &Interpreter, arguments: &Vec<Object>) -> Result<Object, Error>;
fn arity(&self) -> usize;
fn stringify(&self) -> String;
}

impl CallableTrait for Callable {
fn call(&self, interpreter: &Interpreter, arguments: &Vec<Object>) -> Result<Object, Error> {
self.func.call(interpreter, arguments)
}

fn arity(&self) -> usize {
self.func.arity()
}

fn stringify(&self) -> String {
self.func.stringify()
}
}
52 changes: 52 additions & 0 deletions src/functions.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
use crate::callable::*;
use crate::enviroment::Environment;
use crate::errors::*;
use crate::stmt::*;
use crate::tokens::*;
use std::rc::Rc;

use crate::interpreter::Interpreter;
pub struct Function {
declaration: Rc<Stmt>,
}

impl Function {
pub fn new(declaration: &Rc<Stmt>) -> Self {
Self {
declaration: Rc::clone(declaration),
}
}
}

impl CallableTrait for Function {
fn call(&self, interpreter: &Interpreter, arguments: &Vec<Object>) -> Result<Object, Error> {
if let Stmt::Function(FunctionStmt { name, params, body }) = &*self.declaration {
let mut e = Environment::new_with_enclosing(Rc::clone(&interpreter.globals));
for (param, arg) in params.iter().zip(arguments.iter()) {
e.define(param.lexeme.clone(), arg.clone());
}

interpreter.execute_block(body, e)?;
Ok(Object::Nil)
}else{
Err(Error::new(0, "Function declaration is not a function".to_string()))
}

}

fn arity(&self) -> usize {
if let Stmt::Function(FunctionStmt { name, params, body }) = &*self.declaration {
params.len()
}else{
panic!("Function declaration is not a function")
}
}

fn stringify(&self) -> String {
if let Stmt::Function(FunctionStmt { name, params, body }) = &*self.declaration {
format!("<fn {}>", name.lexeme)
}else{
panic!("Function declaration is not a function")
}
}
}
65 changes: 53 additions & 12 deletions src/interpreter.rs
Original file line number Diff line number Diff line change
@@ -5,12 +5,28 @@ use crate::stmt::*;
use crate::tokens::*;
use std::cell::RefCell;
use std::rc::Rc;

use crate::native_functions::*;
use crate::callable::*;
use crate::functions::*;
pub struct Interpreter {
pub globals: Rc<RefCell<Environment>>,
environment: RefCell<Rc<RefCell<Environment>>>,
}

impl StmtVisitor<()> for Interpreter {

fn visit_function_stmt(&self, stmt: &FunctionStmt) -> Result<(), Error>{
let function = Function::new(&Rc::new(stmt));
self.environment
.borrow()
.borrow_mut()
.define(stmt.name.lexeme.clone(), Object::Function(Callable {
func: Rc::new(function),
}));
Ok(())
}


fn visit_if_stmt(&self, stmt: &IfStmt) -> Result<(), Error> {
if self.is_truthy(self.evaluate(&stmt.condition)?) {
self.execute(&stmt.then_branch)?;
@@ -58,7 +74,7 @@ impl StmtVisitor<()> for Interpreter {
}

impl ExprVisitor<Object> for Interpreter {
fn visit_logical_expr(&self, expr: &LogicalExpr) -> Result<Object, Error>{
fn visit_logical_expr(&self, expr: &LogicalExpr) -> Result<Object, Error> {
let left = self.evaluate(&expr.left)?;

if expr.operator.kind == TokenKind::Or {
@@ -182,28 +198,33 @@ impl ExprVisitor<Object> for Interpreter {
}

fn visit_call_expr(&self, expr: &CallExpr) -> Result<Object, Error> {

let callee = self.evaluate(&expr.callee)?;

let mut arguments = Vec::new();

for argument in &expr.arguments {
arguments.push(self.evaluate(argument)?);
}


if let Object::Function(function) = callee {

let _ = function.func.call(self, &arguments);

if arguments.len() != function.arity() {
return Err(Error::new(
expr.paren.line,
format!(
"Expected {} arguments but got {}",
function.arity(),
arguments.len()
),
));
}
function.func.call(self, &arguments)
} else {
return Err(Error::new(
expr.paren.line,
format!("Can only call functions and classes"),
));
}

Ok(Object::Nil)
}

fn visit_variable_expr(&self, expr: &VariableExpr) -> Result<Object, Error> {
@@ -213,16 +234,26 @@ impl ExprVisitor<Object> for Interpreter {

impl Interpreter {
pub fn new() -> Interpreter {
let global = Rc::new(RefCell::new(Environment::new()));

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

Interpreter {
environment: RefCell::new(Rc::new(RefCell::new(Environment::new()))),
globals: Rc::clone(&global),
environment: RefCell::new(Rc::clone(&global)),
}
}

fn execute(&self, stmt: &Stmt) -> Result<(), Error> {
stmt.accept(self)
}

fn execute_block(&self, statements: &[Stmt], environment: Environment) -> Result<(), Error> {
pub fn execute_block(&self, statements: &[Stmt], environment: Environment) -> Result<(), Error> {
let previous = self.environment.replace(Rc::new(RefCell::new(environment)));

let result = statements
@@ -668,7 +699,12 @@ mod tests {
let _ = interpreter.visit_var_stmt(&stmt);
assert!(result.is_ok());
assert_eq!(
interpreter.environment.borrow().borrow().get(&stmt.name).ok(),
interpreter
.environment
.borrow()
.borrow()
.get(&stmt.name)
.ok(),
Some(Object::Num(1.0))
);
}
@@ -687,7 +723,12 @@ mod tests {
let _ = interpreter.visit_var_stmt(&stmt);
assert!(result.is_ok());
assert_eq!(
interpreter.environment.borrow().borrow().get(&stmt.name).ok(),
interpreter
.environment
.borrow()
.borrow()
.get(&stmt.name)
.ok(),
Some(Object::Nil)
);
}
4 changes: 4 additions & 0 deletions src/main.rs
Original file line number Diff line number Diff line change
@@ -26,6 +26,10 @@ mod enviroment;

mod callable;

mod native_functions;

mod functions;

fn eval(source: &str) -> Result<(), Error> {
let lexer = Lexer::new(source.to_string());
let mut tokens: Vec<Token> = lexer.collect();
25 changes: 25 additions & 0 deletions src/native_functions.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
use crate::callable::*;
use crate::errors::*;
use crate::interpreter::*;
use crate::tokens::*;

use std::time::SystemTime;

pub struct NativeClock {}

impl CallableTrait for NativeClock {
fn call(&self, _terp: &Interpreter, _args: &Vec<Object>) -> Result<Object, Error> {
match SystemTime::now().duration_since(SystemTime::UNIX_EPOCH) {
Ok(n) => Ok(Object::Num(n.as_millis() as f64)),
Err(_) => Err(Error::new(0, "Could not get time".to_string())),
}
}

fn arity(&self) -> usize {
0
}

fn stringify(&self) -> String {
"Native::Clock".to_string()
}
}
32 changes: 30 additions & 2 deletions src/parser.rs
Original file line number Diff line number Diff line change
@@ -26,7 +26,9 @@ impl Parser {
}

fn declaration(&mut self) -> Result<Stmt, Error> {
let result = if self.match_token(vec![TokenKind::Var]) {
let result = if self.match_token(vec![TokenKind::Fn]) {
self.function("function")
} else if self.match_token(vec![TokenKind::Var]) {
self.var_declaration()
} else {
self.statement()
@@ -175,6 +177,32 @@ impl Parser {
Ok(Stmt::Expression(ExpressionStmt { expression: value }))
}

fn function(&mut self, kind: &str) -> Result<Stmt, Error> {
let name = self.consume(TokenKind::Identifier, &format!("Expect {kind} name"))?;

self.consume(TokenKind::LeftParen, &format!("Expect '(' after {kind} name"))?;

let mut params = Vec::new();
if !self.check(TokenKind::RightParen) {
params.push(self.consume(TokenKind::Identifier, "Expect Parameter Name")?);
while self.match_token(vec![TokenKind::Comma]) {
if params.len() >= 255 {
return Err(Error::new(
self.peek().line,
format!("can't have more than 255 parameters"),
));
}
params.push(self.consume(TokenKind::Identifier, "Expect Parameter Name")?);
}
}

self.consume(TokenKind::RightParen, "Expect ')' after parameter")?;
self.consume(TokenKind::LeftBrace, &format!("Expect '{{' after {kind} body"))?;

let body = self.block()?;
Ok(Stmt::Function(FunctionStmt { name, params, body }))
}

fn block(&mut self) -> Result<Vec<Stmt>, Error> {
let mut statements: Vec<Stmt> = Vec::new();

@@ -327,7 +355,7 @@ impl Parser {
Ok(self.call()?)
}

fn finish_call(&mut self, callee : Expr) -> Result<Expr, Error> {
fn finish_call(&mut self, callee: Expr) -> Result<Expr, Error> {
let mut arguments: Vec<Expr> = Vec::new();

if !self.check(TokenKind::RightParen) {
15 changes: 15 additions & 0 deletions src/stmt.rs
Original file line number Diff line number Diff line change
@@ -5,6 +5,7 @@ use crate::tokens::*;
pub enum Stmt {
Block(BlockStmt),
Expression(ExpressionStmt),
Function(FunctionStmt),
If(IfStmt),
Print(PrintStmt),
Var(VarStmt),
@@ -16,6 +17,7 @@ impl Stmt {
match self {
Stmt::Block(v) => v.accept(stmt_visitor),
Stmt::Expression(v) => v.accept(stmt_visitor),
Stmt::Function(v) => v.accept(stmt_visitor),
Stmt::If(v) => v.accept(stmt_visitor),
Stmt::Print(v) => v.accept(stmt_visitor),
Stmt::Var(v) => v.accept(stmt_visitor),
@@ -32,6 +34,12 @@ pub struct ExpressionStmt {
pub expression: Expr,
}

pub struct FunctionStmt {
pub name: Token,
pub params: Vec<Token>,
pub body: Vec<Stmt>,
}

pub struct IfStmt {
pub condition: Expr,
pub then_branch: Box<Stmt>,
@@ -55,6 +63,7 @@ pub struct WhileStmt {
pub trait StmtVisitor<T> {
fn visit_block_stmt(&self, expr: &BlockStmt) -> Result<T , Error>;
fn visit_expression_stmt(&self, expr: &ExpressionStmt) -> Result<T , Error>;
fn visit_function_stmt(&self, expr: &FunctionStmt) -> Result<T , Error>;
fn visit_if_stmt(&self, expr: &IfStmt) -> Result<T , Error>;
fn visit_print_stmt(&self, expr: &PrintStmt) -> Result<T , Error>;
fn visit_var_stmt(&self, expr: &VarStmt) -> Result<T , Error>;
@@ -73,6 +82,12 @@ impl ExpressionStmt {
}
}

impl FunctionStmt {
pub fn accept<T>(&self, visitor: &dyn StmtVisitor<T>) -> Result<T , Error> {
visitor.visit_function_stmt(self)
}
}

impl IfStmt {
pub fn accept<T>(&self, visitor: &dyn StmtVisitor<T>) -> Result<T , Error> {
visitor.visit_if_stmt(self)

0 comments on commit a4dd2ff

Please sign in to comment.