diff --git a/src/ast.rs b/src/ast.rs index 326b00d..8603cd5 100644 --- a/src/ast.rs +++ b/src/ast.rs @@ -12,6 +12,11 @@ pub enum Expr { op: Token, right: Box, }, + Call { + callee: Box, + paren: Token, + args: Vec, + }, Grouping { expression: Box, }, @@ -36,6 +41,7 @@ pub enum Expr { }, } +#[derive(Debug, Clone)] pub enum Stmt { Block { statements: Vec, @@ -44,6 +50,11 @@ pub enum Stmt { Expression { expression: Expr, }, + Function { + name: Token, + params: Vec, + body: Vec, + }, If { condition: Expr, then_branch: Box, @@ -52,6 +63,10 @@ pub enum Stmt { Print { expression: Expr, }, + Return { + keyword: Token, + value: Option, + }, Var { name: Token, initializer: Option, diff --git a/src/callable.rs b/src/callable.rs new file mode 100644 index 0000000..6431a96 --- /dev/null +++ b/src/callable.rs @@ -0,0 +1,105 @@ +use std::{cell::RefCell, fmt::Display, rc::Rc}; + +use crate::{ + ast::Stmt, + environment::Environment, + interpreter::{execute_block, InterpreterEnvironment, InterpreterSignal, RuntimeError}, + token::{LiteralType, Token}, +}; + +pub trait CallableTrait { + fn arity(&self) -> u8; + fn call( + &self, + args: &[LiteralType], + env: &InterpreterEnvironment, + ) -> Result; +} + +#[derive(Debug, Clone)] +pub enum Callable { + Function { + name: Box, + params: Vec, + body: Vec, + closure: Rc>, + }, + NativeFunction(NativeFunction), +} + +impl CallableTrait for Callable { + fn arity(&self) -> u8 { + match self { + Callable::Function { params, .. } => params.len() as u8, + Callable::NativeFunction(native_function) => native_function.arity, + } + } + + fn call( + &self, + args: &[LiteralType], + env: &InterpreterEnvironment, + ) -> Result { + match self { + Callable::Function { + name: _, + params, + body, + closure, + } => { + let environment = Rc::new(RefCell::new(Environment::with_enclosing(closure))); + + for (param, arg) in params.iter().zip(args) { + environment + .borrow_mut() + .define(¶m.lexeme, Some(arg.clone())); + } + + let environment = InterpreterEnvironment { + globals: Rc::clone(&env.globals), + environment, + }; + + match execute_block(body, &environment) { + Err(InterpreterSignal::Return(v)) => Ok(v), + v => v.map(|_| LiteralType::Nil), + } + } + Callable::NativeFunction(native_function) => (native_function.call_impl)(args), + } + } +} + +#[derive(Debug, Clone)] +pub struct NativeFunction { + name: String, + arity: u8, + call_impl: fn(&[LiteralType]) -> Result, +} + +impl NativeFunction { + pub fn new( + name: String, + arity: u8, + call_impl: fn(&[LiteralType]) -> Result, + ) -> Self { + Self { + name, + arity, + call_impl, + } + } +} + +impl Display for Callable { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Callable::Function { name, .. } => { + write!(f, "{}", name.lexeme) + } + Callable::NativeFunction(native_function) => { + write!(f, "{}", native_function.name) + } + } + } +} diff --git a/src/environment.rs b/src/environment.rs index dc4b36d..6311347 100644 --- a/src/environment.rs +++ b/src/environment.rs @@ -1,7 +1,11 @@ use std::{cell::RefCell, collections::HashMap, rc::Rc}; -use crate::token::{LiteralType, Token}; +use crate::{ + callable::Callable, + token::{LiteralType, Token}, +}; +#[derive(Debug, Clone)] pub struct Environment { values: HashMap>, enclosing: Option>>, diff --git a/src/interpreter.rs b/src/interpreter.rs index 3ead726..0778202 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -1,18 +1,30 @@ use core::panic; -use std::{cell::RefCell, rc::Rc}; +use std::{ + cell::RefCell, + rc::Rc, + time::{SystemTime, UNIX_EPOCH}, +}; use crate::{ ast::{Expr, Stmt}, + callable::{Callable, CallableTrait, NativeFunction}, environment::Environment, token::{LiteralType, Token, TokenType}, }; +type InterpreterResult = Result; + #[derive(Debug)] pub struct RuntimeError { pub token: Token, pub message: String, } +pub struct InterpreterEnvironment { + pub globals: Rc>, + pub environment: Rc>, +} + impl RuntimeError { pub fn new(token: &Token, message: &str) -> RuntimeError { RuntimeError { @@ -22,22 +34,29 @@ impl RuntimeError { } } -pub enum InterpreterError { +pub enum InterpreterSignal { RuntimeError(RuntimeError), - BreakSignal, + Break, + Return(LiteralType), } +/* + This two impl blocks are for the ? operator. I'm too lazy to write the wrapping code for the enums and it also looks ugly, + so i just abuse the ? operator lol + Instead of InterpreterError::RuntimeError(RuntimeError {...} ) i can just RuntimeError {...}? to turn it into a InterpreterError +*/ -impl From for InterpreterError { +impl From for InterpreterSignal { fn from(value: RuntimeError) -> Self { Self::RuntimeError(value) } } -impl From for RuntimeError { - fn from(value: InterpreterError) -> Self { +impl From for RuntimeError { + fn from(value: InterpreterSignal) -> Self { match value { - InterpreterError::RuntimeError(runtime_error) => runtime_error, - InterpreterError::BreakSignal => panic!("Not a runtime error"), + InterpreterSignal::RuntimeError(runtime_error) => runtime_error, + InterpreterSignal::Break => panic!("Not a runtime error"), + InterpreterSignal::Return(_) => panic!("Not a runtime error"), } } } @@ -45,9 +64,30 @@ impl From for RuntimeError { pub fn interpret( statements: &Vec, environment: &Rc>, -) -> Result<(), InterpreterError> { +) -> Result<(), InterpreterSignal> { + let clock = |_arg: &[LiteralType]| { + Ok(LiteralType::Number( + SystemTime::now() + .duration_since(UNIX_EPOCH) + .expect("Time went backwards") + .as_secs_f64() + / 1000.0, + )) + }; + + let clock_function = NativeFunction::new("clock".to_string(), 0, clock); + let environment = InterpreterEnvironment { + globals: Rc::clone(environment), + environment: Rc::clone(environment), + }; + environment.globals.borrow_mut().define( + "clock", + Some(LiteralType::Callable(Callable::NativeFunction( + clock_function, + ))), + ); for statement in statements { - execute(statement, environment)?; + execute(statement, &environment)? } Ok(()) @@ -55,23 +95,24 @@ pub fn interpret( fn execute( statement: &Stmt, - environment: &Rc>, -) -> Result<(), InterpreterError> { + environment: &InterpreterEnvironment, +) -> Result<(), InterpreterSignal> { + let curr_environment = &environment.environment; match statement { Stmt::Expression { expression } => { - evaluate(expression, &mut environment.borrow_mut())?; + evaluate(expression, environment)?; } Stmt::Print { expression } => { - let expr = evaluate(expression, &mut environment.borrow_mut())?; + let expr = evaluate(expression, environment)?; println!("{expr}"); } Stmt::Var { name, initializer } => { let value = if let Some(initializer) = initializer { - Some(evaluate(initializer, &mut environment.borrow_mut())?) + Some(evaluate(initializer, environment)?) } else { None }; - environment.borrow_mut().define(&name.lexeme, value); + curr_environment.borrow_mut().define(&name.lexeme, value); } Stmt::Block { statements } => { execute_block(statements, environment)?; @@ -81,39 +122,69 @@ fn execute( then_branch, else_branch, } => { - if is_truthy(&evaluate(condition, &mut environment.borrow_mut())?) { + if is_truthy(&evaluate(condition, environment)?) { execute(then_branch, environment)?; } else if let Some(else_branch) = else_branch { execute(else_branch, environment)?; } } Stmt::While { condition, body } => { - while is_truthy(&evaluate(condition, &mut environment.borrow_mut())?) { + while is_truthy(&evaluate(condition, environment)?) { let result = execute(body, environment); if result.is_err() { break; } } } - Stmt::Break => Err(InterpreterError::BreakSignal)?, + Stmt::Break => Err(InterpreterSignal::Break)?, + Stmt::Function { name, params, body } => { + let function = Callable::Function { + name: Box::new(name.clone()), + body: body.to_vec(), + params: params.to_vec(), + closure: Rc::clone(curr_environment), + }; + environment + .globals + .borrow_mut() + .define(&name.lexeme, Some(LiteralType::Callable(function))); + } + Stmt::Return { value, .. } => { + let value = if let Some(v) = value { + evaluate(v, environment)? + } else { + LiteralType::Nil + }; + + return Err(InterpreterSignal::Return(value)); + } } Ok(()) } -fn execute_block( +pub fn execute_block( statements: &Vec, - environment: &Rc>, -) -> Result<(), InterpreterError> { - let block_enviroment = Rc::new(RefCell::new(Environment::with_enclosing(environment))); + environment: &InterpreterEnvironment, +) -> Result<(), InterpreterSignal> { + let block_enviroment = Rc::new(RefCell::new(Environment::with_enclosing( + &environment.environment, + ))); + // we just move the block_enviroment to a new InterpreterEnvironment and clone the reference to + // globals, bcs outer environments might have the globals reference + let environment = InterpreterEnvironment { + globals: Rc::clone(&environment.globals), + environment: block_enviroment, + }; for stmt in statements { - execute(stmt, &block_enviroment)?; + execute(stmt, &environment)?; } Ok(()) } -fn evaluate(expr: &Expr, environment: &mut Environment) -> Result { +fn evaluate(expr: &Expr, environment: &InterpreterEnvironment) -> InterpreterResult { + let curr_environment = &environment.environment; match expr { Expr::Ternary { first, @@ -129,7 +200,8 @@ fn evaluate(expr: &Expr, environment: &mut Environment) -> Result evaluate(expression, environment), Expr::Literal { value } => Ok(value.clone()), Expr::Unary { op, right } => Ok(unary(&evaluate(right, environment)?, op)), - Expr::Variable { name } => environment + Expr::Variable { name } => curr_environment + .borrow() .get(name) .ok_or_else(|| RuntimeError { token: name.clone(), @@ -141,10 +213,11 @@ fn evaluate(expr: &Expr, environment: &mut Environment) -> Result { let value = evaluate(value, environment)?; - environment + curr_environment + .borrow_mut() .assign(name, value.clone()) .map_err(|_| RuntimeError { token: name.clone(), @@ -166,6 +239,38 @@ fn evaluate(expr: &Expr, environment: &mut Environment) -> Result { + let callee_result = evaluate(callee, environment)?; + + let mut arguments = Vec::new(); + for arg in args { + arguments.push(evaluate(arg, environment)?); + } + + match callee_result { + LiteralType::Callable(function) => { + if arguments.len() as u8 != function.arity() { + Err(RuntimeError { + token: paren.clone(), + message: format!( + "Expected {} arguments but got {}.", + function.arity(), + args.len() + ), + })? + } + Ok(function.call(&arguments, environment)?) + } + _ => Err(RuntimeError { + token: paren.clone(), + message: "Can only call functions and classes".to_string(), + })?, + } + } } } @@ -173,8 +278,8 @@ fn ternary( first: &Expr, second: &Expr, third: &Expr, - environment: &mut Environment, -) -> Result { + environment: &InterpreterEnvironment, +) -> InterpreterResult { let first = evaluate(first, environment)?; if is_truthy(&first) { return evaluate(second, environment); @@ -182,11 +287,7 @@ fn ternary( evaluate(third, environment) } -fn binary( - left: &LiteralType, - right: &LiteralType, - op: &Token, -) -> Result { +fn binary(left: &LiteralType, right: &LiteralType, op: &Token) -> InterpreterResult { use LiteralType::{Bool, Number, String}; use TokenType::{ BangEqual, Comma, EqualEqual, Greater, GreaterEqual, Less, LessEqual, Minus, Plus, Slash, @@ -232,10 +333,14 @@ fn is_truthy(literal: &LiteralType) -> bool { } } -fn is_equal(left: &LiteralType, right: &LiteralType) -> bool { +pub fn is_equal(left: &LiteralType, right: &LiteralType) -> bool { match (left, right) { (LiteralType::Nil, LiteralType::Nil) => true, (LiteralType::Nil, _) => false, - _ => left == right, + // i could've implemeneted PartialEq but it doesn't make sense for every LiteralType + (LiteralType::String(s), LiteralType::String(s2)) => s == s2, + (LiteralType::Number(n1), LiteralType::Number(n2)) => n1 == n2, + (LiteralType::Bool(t1), LiteralType::Bool(t2)) => t1 == t2, + _ => false, } } diff --git a/src/lib.rs b/src/lib.rs index dbd2e9b..c570929 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -13,6 +13,7 @@ use scanner::Scanner; use token::TokenType; mod ast; +mod callable; mod environment; mod interpreter; mod parser; diff --git a/src/parser.rs b/src/parser.rs index 53d383d..d56c021 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -55,7 +55,9 @@ impl Parser<'_> { } fn declaration(&mut self) -> Result { - let stmt = if self.match_token(&[TokenType::Var]) { + let stmt = if self.match_token(&[TokenType::Fun]) { + self.function("function") + } else if self.match_token(&[TokenType::Var]) { self.var_declaration() } else { self.statement() @@ -64,6 +66,42 @@ impl Parser<'_> { stmt.inspect_err(|_| self.synchronize()) } + fn function(&mut self, kind: &str) -> Result { + let name = self.consume(TokenType::Identifier, &format!("Expect {} name.", kind))?; + self.consume( + TokenType::LeftParen, + &format!("Expect '(' after {kind} name."), + )?; + let mut params = Vec::new(); + if !self.check(TokenType::RightParen) { + loop { + if params.len() >= 255 { + return Err(ParseError { + token: self.peek().clone(), + msg: "Can't have more than 255 parameters".to_string(), + }); + } + + params.push(self.consume(TokenType::Identifier, "Expect parameter name.")?); + + if !self.match_token(&[TokenType::Comma]) { + break; + } + } + } + + self.consume(TokenType::RightParen, "Expect ')' after parameters")?; + + self.consume( + TokenType::LeftBrace, + &format!("Expect '{{' before {} body.", kind), + )?; + + let body = self.block()?; + + Ok(Stmt::Function { name, params, body }) + } + fn var_declaration(&mut self) -> Result { let name = self.consume(TokenType::Identifier, "Expect variable name")?; let initializer = if self.match_token(&[TokenType::Equal]) { @@ -90,6 +128,9 @@ impl Parser<'_> { if self.match_token(&[TokenType::Print]) { return self.print_statement(); } + if self.match_token(&[TokenType::Return]) { + return self.return_statement(); + } if self.match_token(&[TokenType::While]) { return self.while_statement(); } @@ -227,6 +268,19 @@ impl Parser<'_> { Ok(Stmt::Print { expression }) } + fn return_statement(&mut self) -> Result { + let keyword = self.previous().clone(); + let value = if !self.check(TokenType::Semicolon) { + Some(self.expression()?) + } else { + None + }; + + self.consume(TokenType::Semicolon, "Expect ';' after return value."); + + Ok(Stmt::Return { keyword, value }) + } + fn expression_statement(&mut self) -> Result { let expression = self.expression()?; self.consume(TokenType::Semicolon, "Expect ';' after expression.")?; @@ -348,7 +402,46 @@ impl Parser<'_> { }); } - self.primary() + self.call() + } + + fn call(&mut self) -> Result { + let mut expr = self.primary()?; + + loop { + if self.match_token(&[TokenType::LeftParen]) { + expr = self.finish_call(expr)?; + } else { + break; + } + } + + Ok(expr) + } + + fn finish_call(&mut self, callee: Expr) -> Result { + let mut args = Vec::new(); + if !self.check(TokenType::RightParen) { + loop { + if args.len() >= 255 { + return Err(ParseError { + token: self.peek().clone(), + msg: "Can't have more than 255 arguments".to_string(), + }); + } + args.push(self.equality()?); + if !self.match_token(&[TokenType::Comma]) { + break; + } + } + } + let paren = self.consume(TokenType::RightParen, "Expect ')' after arguments")?; + + Ok(Expr::Call { + callee: Box::new(callee), + paren, + args, + }) } /* error boundaries: diff --git a/src/printer.rs b/src/printer.rs index 9c9117b..2d394a0 100644 --- a/src/printer.rs +++ b/src/printer.rs @@ -9,6 +9,7 @@ pub fn pretty_print(expr: &Expr) -> String { LiteralType::Number(v) => v.to_string(), LiteralType::Bool(v) => v.to_string(), LiteralType::Nil => "Nil".to_string(), + LiteralType::Callable(_) => todo!(), }, Expr::Unary { op, right } => parenthesize(&op.lexeme, &[right]), Expr::Ternary { @@ -19,6 +20,11 @@ pub fn pretty_print(expr: &Expr) -> String { Expr::Variable { name } => name.lexeme.clone(), Expr::Assign { name, value } => parenthesize(&name.lexeme, &[value]), Expr::Logical { left, op, right } => parenthesize(&op.lexeme, &[left, right]), + Expr::Call { + callee: _, + paren: _, + args: _, + } => todo!(), } } diff --git a/src/scanner.rs b/src/scanner.rs index f8dadff..a511f63 100644 --- a/src/scanner.rs +++ b/src/scanner.rs @@ -273,6 +273,8 @@ fn get_identified_keyword(identifier: &str) -> Option { #[cfg(test)] mod tests { + use crate::interpreter::is_equal; + use super::*; use TokenType::*; @@ -325,7 +327,10 @@ mod tests { let expected = LiteralType::String("salam!".to_string()); - assert_eq!(expected, actual.literal.as_ref().unwrap().clone()); + assert!(is_equal( + &expected, + &actual.literal.as_ref().unwrap().clone(), + )) } #[test] @@ -375,7 +380,10 @@ mod tests { let actual_value = &token.literal; - assert_eq!(expected_value, actual_value.as_ref().unwrap().clone()) + assert!(is_equal( + &expected_value, + &actual_value.as_ref().unwrap().clone() + )) } #[test] @@ -398,6 +406,9 @@ mod tests { let actual_value = &token.literal; - assert_eq!(expected_value, actual_value.as_ref().unwrap().clone()) + assert!(is_equal( + &expected_value, + &actual_value.as_ref().unwrap().clone() + )) } } diff --git a/src/token.rs b/src/token.rs index 696d88f..573423f 100644 --- a/src/token.rs +++ b/src/token.rs @@ -1,5 +1,7 @@ use std::fmt::Display; +use crate::callable::Callable; + #[derive(Debug, PartialEq, Clone, Copy)] pub enum TokenType { LeftParen, @@ -51,12 +53,13 @@ pub enum TokenType { } // i've seen this implementation in the wild -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone)] pub enum LiteralType { String(String), Number(f64), Bool(bool), Nil, + Callable(Callable), } impl LiteralType { @@ -76,6 +79,7 @@ impl Display for LiteralType { LiteralType::Number(v) => write!(f, "{v:.2}"), LiteralType::Bool(v) => write!(f, "{v}"), LiteralType::Nil => write!(f, "nil"), + LiteralType::Callable(c) => write!(f, ""), } } }