diff --git a/rust/rox/src/callable.rs b/rust/rox/src/callable.rs new file mode 100644 index 0000000..2d3898f --- /dev/null +++ b/rust/rox/src/callable.rs @@ -0,0 +1,39 @@ +use crate::{interpreter::Interpreter, value::Value}; +use core::fmt::Debug; +use std::fmt::Display; +use thiserror::Error; + +#[derive(Error, Debug)] +pub enum CallingError { + #[error("Failed calling: {0}.")] + CallFailed(String), + #[error("Expected {0} arguments but got {1}.")] + ArgumentMismatch(usize, usize), + #[error("Failed calling: {0}.")] + CallInterpretationFailed(String), +} + +/// Implementations can be called. +pub trait Callable { + fn name(&self) -> String; + fn arity(&self) -> usize; + fn call(&self, interpreter: &mut Interpreter, args: Vec) -> Result; +} + +impl Debug for dyn Callable { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self) + } +} + +impl Display for dyn Callable { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.name()) + } +} + +impl PartialEq for dyn Callable { + fn eq(&self, other: &Self) -> bool { + self.name() == other.name() && self.arity() == other.arity() + } +} diff --git a/rust/rox/src/expression.rs b/rust/rox/src/expression.rs index 0600328..defaba6 100644 --- a/rust/rox/src/expression.rs +++ b/rust/rox/src/expression.rs @@ -12,6 +12,11 @@ pub enum Expression { operator: Token, right: Box, }, + Call { + callee: Box, + paren: Token, + args: Vec, + }, Grouping { expression: Box, }, diff --git a/rust/rox/src/function.rs b/rust/rox/src/function.rs new file mode 100644 index 0000000..47e92fe --- /dev/null +++ b/rust/rox/src/function.rs @@ -0,0 +1,61 @@ +use crate::{ + callable::{Callable, CallingError}, + environment::Environment, + interpreter::Interpreter, + statement::Statement, + token::Token, + value::Value, +}; +use core::fmt::Debug; +use std::{cell::RefCell, fmt::Display, rc::Rc}; + +/// A lox function. +#[derive(Debug, Clone)] +pub struct Function { + pub name: Token, + pub params: Vec, + pub body: Vec, + pub closure: Rc>, +} + +impl Display for Function { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.name.lexeme) + } +} + +impl PartialEq for Function { + fn eq(&self, other: &Self) -> bool { + self.name == other.name && self.params == other.params && self.body == other.body + } +} + +impl Callable for Function { + fn name(&self) -> String { + self.name.lexeme.clone() + } + + fn arity(&self) -> usize { + self.params.len() + } + + fn call(&self, interpreter: &mut Interpreter, args: Vec) -> Result { + let mut env = Environment::with_enclosing(self.closure.clone()); + for (i, param) in self.params.iter().enumerate() { + let arg = args + .get(i) + .ok_or(CallingError::ArgumentMismatch( + self.params.len(), + args.len(), + ))? + .clone(); + env.define(param.lexeme.clone(), arg); + } + + let result = interpreter + .block(&self.body, env) + .map_err(|e| CallingError::CallInterpretationFailed(e.to_string()))? + .unwrap_or(Value::Nil); + Ok(result) + } +} diff --git a/rust/rox/src/interpreter.rs b/rust/rox/src/interpreter.rs index 1438d81..239d043 100644 --- a/rust/rox/src/interpreter.rs +++ b/rust/rox/src/interpreter.rs @@ -4,8 +4,11 @@ use thiserror::Error; use tracing::error; use crate::{ + callable::CallingError, environment::{Environment, EnvironmentError}, expression::Expression, + function::Function, + native_functions, statement::Statement, token::{Literal, Token, TokenType}, value::Value, @@ -25,14 +28,39 @@ pub enum InterpreterError { BinaryExpressionNeedsNumberOrString(usize), #[error("{0}")] UndefinedVariable(EnvironmentError), + #[error("[line {0}] {1} is not callable.")] + NotACallable(usize, Value), + #[error("[line {0}] {1}.")] + FailedToCall(usize, CallingError), } /// Interpreter for the Lox language. -#[derive(Default, Debug)] +#[derive(Debug)] pub struct Interpreter { + pub globals: Rc>, environment: Rc>, } +/// Default configuration for the interpreter, with builtin native functions. +impl Default for Interpreter { + fn default() -> Self { + let env: Rc> = Default::default(); + + { + // add all native functions to global environment + let mut env = env.borrow_mut(); + for (name, fun) in native_functions::all() { + env.define(name, fun); + } + } + + Self { + globals: env.clone(), + environment: env.clone(), + } + } +} + impl Interpreter { /// Try to evaluate an expression and return its result. pub fn run(&mut self, statements: Vec) -> Result<(), InterpreterError> { @@ -47,18 +75,16 @@ impl Interpreter { } ///Execute a statement. - fn execute(&mut self, statement: &Statement) -> Result<(), InterpreterError> { + fn execute(&mut self, statement: &Statement) -> Result, InterpreterError> { match statement { Statement::Block(statements) => { let sub_env = Environment::with_enclosing(self.environment.clone()); - self.block(statements, sub_env)? - } - Statement::Print(expression) => self.print_statement(expression)?, - Statement::Expression(expression) => { - self.evaluate(expression)?; + self.block(statements, sub_env) } + Statement::Print(expression) => self.print_statement(expression), + Statement::Expression(expression) => Ok(Some(self.evaluate(expression)?)), Statement::Var { name, initializer } => { - self.var_statement(name, initializer.as_ref().as_ref())? + self.var_statement(name, initializer.as_ref().as_ref()) } Statement::If { condition, @@ -66,33 +92,43 @@ impl Interpreter { else_branch, } => { let else_branch = else_branch.as_ref().map(|x| *x.clone()); - self.if_statement(condition, then_branch, else_branch.as_ref())? + self.if_statement(condition, then_branch, else_branch.as_ref()) } - Statement::While { condition, body } => self.while_statement(condition, body)?, - }; - - Ok(()) + Statement::While { condition, body } => self.while_statement(condition, body), + Statement::Function { name, params, body } => { + self.function_statement(name, params, body) + } + Statement::Return { keyword: _, value } => self.return_statement(value), + } } /// Execute all statements within a block, using a new environment (with the old one as the - /// enclosing one). - fn block( + /// enclosing one). Immediately return the value if a Return Value is encountered after + /// execution of a statement. + pub fn block( &mut self, - statements: &Vec, + statements: &[Statement], environment: Environment, - ) -> Result<(), InterpreterError> { + ) -> Result, InterpreterError> { let prev_env = self.environment.clone(); self.environment = Rc::new(RefCell::new(environment)); for stmt in statements { - if let Err(e) = self.execute(stmt) { - error!("{e}"); + let execution = self.execute(stmt); + match execution { + Ok(x) => { + if let Some(Value::Return(x)) = x { + self.environment = prev_env.clone(); + return Ok(Some(*x)); + } + } + Err(e) => error!("{e}"), } } self.environment = prev_env.clone(); - Ok(()) + Ok(None) } /// Evaluate an expression and return its value. @@ -116,31 +152,95 @@ impl Interpreter { operator, right, } => self.logical_expression(left, operator, right), + Expression::Call { + callee, + paren, + args, + } => self.call(callee, paren, args), } } + /// Call a callable if it is one (meaning it starts with a LeftParen after an identifier), + /// otherwise evaluate the expression. + fn call( + &mut self, + callee: &Expression, + paren: &Token, + arg_expressions: &[Expression], + ) -> Result { + let callee = self.evaluate(callee)?; + let mut args = Vec::new(); + + for arg in arg_expressions { + args.push(self.evaluate(arg)?); + } + + if let Value::Callable(f) = callee { + f.call(self, args) + .map_err(|e| InterpreterError::FailedToCall(paren.line, e)) + } else { + Err(InterpreterError::NotACallable(paren.line, callee)) + } + } + + /// Define a new function. + fn function_statement( + &mut self, + name: &Token, + params: &[Token], + body: &[Statement], + ) -> Result, InterpreterError> { + let fn_name = name.lexeme.clone(); + let fun = Function { + name: name.clone(), + params: params.to_vec(), + body: body.to_vec(), + closure: self.environment.clone(), + }; + + self.environment + .borrow_mut() + .define(fn_name, Value::Callable(Rc::new(fun))); + + Ok(None) + } + /// If the condition evaluates to truthy, execute the then branch, otherwise the else branch. fn if_statement( &mut self, condition: &Expression, then_branch: &Statement, else_branch: Option<&Statement>, - ) -> Result<(), InterpreterError> { + ) -> Result, InterpreterError> { if self.evaluate(condition)?.is_truthy() { self.execute(then_branch) } else if let Some(else_branch) = else_branch { self.execute(else_branch) } else { - Ok(()) + Ok(None) } } /// Evaluate an expression and print its value to stdout. - fn print_statement(&mut self, expression: &Expression) -> Result<(), InterpreterError> { + fn print_statement( + &mut self, + expression: &Expression, + ) -> Result, InterpreterError> { let value = self.evaluate(expression)?; println!("{value}"); - Ok(()) + Ok(None) + } + + /// Return a value from a statement. + fn return_statement( + &mut self, + value: &Option, + ) -> Result, InterpreterError> { + Ok(match value { + Some(x) => Some(Value::Return(Box::new(self.evaluate(x)?))), + None => Some(Value::Return(Box::new(Value::Nil))), + }) } /// Initialize a variable with an initializer expression or nil. @@ -148,7 +248,7 @@ impl Interpreter { &mut self, name: &Token, initializer: Option<&Expression>, - ) -> Result<(), InterpreterError> { + ) -> Result, InterpreterError> { let value = if let Some(initializer) = initializer { self.evaluate(initializer) } else { @@ -159,7 +259,7 @@ impl Interpreter { .borrow_mut() .define(name.lexeme.clone(), value); - Ok(()) + Ok(None) } /// Execute the body as long as the condition evaluates to true. @@ -167,12 +267,12 @@ impl Interpreter { &mut self, condition: &Expression, body: &Statement, - ) -> Result<(), InterpreterError> { + ) -> Result, InterpreterError> { while self.evaluate(condition)?.is_truthy() { self.execute(body)?; } - Ok(()) + Ok(None) } /// Assign the value of an expression to a variable. diff --git a/rust/rox/src/lib.rs b/rust/rox/src/lib.rs index dd37957..81757f9 100644 --- a/rust/rox/src/lib.rs +++ b/rust/rox/src/lib.rs @@ -10,11 +10,14 @@ use std::{ use interpreter::Interpreter; use tracing::error; +pub mod callable; pub mod cli; pub mod environment; pub mod expression; +pub mod function; pub mod interpreter; pub mod keywords; +pub mod native_functions; pub mod parser; pub mod scanner; pub mod statement; diff --git a/rust/rox/src/native_functions.rs b/rust/rox/src/native_functions.rs new file mode 100644 index 0000000..1268b41 --- /dev/null +++ b/rust/rox/src/native_functions.rs @@ -0,0 +1,39 @@ +use std::{ + rc::Rc, + time::{SystemTime, UNIX_EPOCH}, +}; + +use crate::{ + callable::{Callable, CallingError}, + interpreter::Interpreter, + value::Value, +}; + +struct Clock; + +impl Callable for Clock { + fn name(&self) -> String { + "clock".into() + } + + fn arity(&self) -> usize { + 0 + } + + fn call( + &self, + _interpreter: &mut Interpreter, + _args: Vec, + ) -> Result { + let start = SystemTime::now(); + let since_the_epoch = start + .duration_since(UNIX_EPOCH) + .map_err(|e| CallingError::CallFailed(e.to_string()))?; + + Ok(Value::Number(since_the_epoch.as_secs_f64())) + } +} + +pub fn all() -> Vec<(String, Value)> { + vec![("clock".into(), Value::Callable(Rc::new(Clock {})))] +} diff --git a/rust/rox/src/parser.rs b/rust/rox/src/parser.rs index faab052..1d0892c 100644 --- a/rust/rox/src/parser.rs +++ b/rust/rox/src/parser.rs @@ -1,6 +1,3 @@ -use thiserror::Error; -use tracing::error; - use crate::{ expression::Expression, statement::Statement, @@ -9,6 +6,8 @@ use crate::{ TokenType::{self, *}, }, }; +use thiserror::Error; +use tracing::error; #[derive(Error, Debug)] pub enum ParserError { @@ -44,6 +43,20 @@ pub enum ParserError { SemicolonAfterLoopConditionExpected(usize), #[error("[line {0}] expected ')' after for clauses.")] RightParenAfterForClausesExpected(usize), + #[error("[line {0}] expected ')' after arguments.")] + RightParenAfterArgumentsExpected(usize), + #[error("[line {0}] expected function name.")] + FunctionNameExpected(usize), + #[error("[line {0}] expected '(' after function name.")] + LeftParenAfterFunctionNameExpected(usize), + #[error("[line {0}] expected ')' after parameters.")] + RightParenAfterParamsExpected(usize), + #[error("[line {0}] expected parameter name.")] + ParamNameExpected(usize), + #[error("[line {0}] expected '{{' before function body.")] + LeftBraceBeforeFunctionBodyExpected(usize), + #[error("[line {0}] expected ';' after return value.")] + SemicolonAfterReturnExpected(usize), } /// Parse the Lox language. @@ -161,7 +174,9 @@ impl Parser { } fn declaration(&mut self) -> Result { - if self.matches(&[Var]) { + if self.matches(&[Fun]) { + self.function() + } else if self.matches(&[Var]) { self.var_declaration() } else { self.statement() @@ -175,6 +190,8 @@ impl Parser { self.if_statement() } else if self.matches(&[Print]) { self.print_statement() + } else if self.matches(&[Return]) { + self.return_statement() } else if self.matches(&[While]) { self.while_statement() } else if self.matches(&[LeftBrace]) { @@ -266,6 +283,20 @@ impl Parser { Ok(Statement::Print(value)) } + fn return_statement(&mut self) -> Result { + let keyword = self.previous()?.clone(); + let value = if self.check(&Semicolon) { + None + } else { + Some(self.expression()?) + }; + + self.consume(&Semicolon) + .ok_or(ParserError::SemicolonAfterReturnExpected(keyword.line))?; + + Ok(Statement::Return { keyword, value }) + } + fn var_declaration(&mut self) -> Result { let line = self.current_token.line; let name = self @@ -315,6 +346,43 @@ impl Parser { Ok(Statement::Expression(expr)) } + fn function(&mut self) -> Result { + let line = self.current_token.line; + let name = self + .consume(&Identifier) + .ok_or(ParserError::FunctionNameExpected(line))? + .clone(); + + self.consume(&LeftParen) + .ok_or(ParserError::LeftParenAfterFunctionNameExpected(line))?; + + let mut params = Vec::new(); + if !self.check(&RightParen) { + let param = self + .consume(&Identifier) + .ok_or(ParserError::ParamNameExpected(line))? + .clone(); + params.push(param); + + while self.matches(&[Comma]) { + let param = self + .consume(&Identifier) + .ok_or(ParserError::ParamNameExpected(line))? + .clone(); + params.push(param); + } + } + + self.consume(&RightParen) + .ok_or(ParserError::RightParenAfterParamsExpected(line))?; + self.consume(&LeftBrace) + .ok_or(ParserError::LeftBraceBeforeFunctionBodyExpected(line))?; + + let body = self.block()?; + + Ok(Statement::Function { name, params, body }) + } + fn block(&mut self) -> Result, ParserError> { let mut statements = Vec::new(); @@ -412,10 +480,48 @@ impl Parser { right: Box::new(right), }) } else { - self.primary() + self.call() } } + fn call(&mut self) -> Result { + let mut expr = self.primary()?; + + loop { + if self.matches(&[LeftParen]) { + expr = self.finish_call(expr)?; + } else { + break; + } + } + + Ok(expr) + } + + fn finish_call(&mut self, callee: Expression) -> Result { + let mut args = Vec::new(); + + if !self.check(&RightParen) { + args.push(self.expression()?); + + while self.matches(&[Comma]) { + args.push(self.expression()?); + } + } + + let line = self.current_token.line; + let paren = self + .consume(&RightParen) + .ok_or(ParserError::RightParenAfterArgumentsExpected(line))? + .clone(); + + Ok(Expression::Call { + callee: Box::new(callee), + paren, + args, + }) + } + /// primary -> NUMBER | STRING | "true" | "false" | "nil" | "(" expression ")" ; fn primary(&mut self) -> Result { if self.matches(&[False]) { diff --git a/rust/rox/src/statement.rs b/rust/rox/src/statement.rs index 1ade330..c75b3c4 100644 --- a/rust/rox/src/statement.rs +++ b/rust/rox/src/statement.rs @@ -19,4 +19,13 @@ pub enum Statement { condition: Expression, body: Box, }, + Function { + name: Token, + params: Vec, + body: Vec, + }, + Return { + keyword: Token, + value: Option, + }, } diff --git a/rust/rox/src/value.rs b/rust/rox/src/value.rs index 5efcc64..b272014 100644 --- a/rust/rox/src/value.rs +++ b/rust/rox/src/value.rs @@ -1,10 +1,12 @@ -use std::fmt::Display; +use std::{fmt::Display, rc::Rc}; -use crate::token::Literal; +use crate::{callable::Callable, token::Literal}; /// Concrete value in the interpreter. #[derive(Clone, Debug, PartialEq)] pub enum Value { + Return(Box), + Callable(Rc), String(String), Number(f64), Boolean(bool), @@ -14,6 +16,8 @@ pub enum Value { impl Display for Value { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { + Value::Return(x) => write!(f, "{x}"), + Value::Callable(x) => write!(f, "{x}"), Value::String(x) => write!(f, "{x}"), Value::Number(x) => write!(f, "{x}"), Value::Boolean(x) => write!(f, "{x}"),