chapter 10 in rust

This commit is contained in:
Sebastian Hugentobler 2025-02-13 12:29:31 +01:00
parent a13db8e29c
commit a98d249399
9 changed files with 401 additions and 35 deletions

39
rust/rox/src/callable.rs Normal file
View File

@ -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<Value>) -> Result<Value, CallingError>;
}
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()
}
}

View File

@ -12,6 +12,11 @@ pub enum Expression {
operator: Token,
right: Box<Expression>,
},
Call {
callee: Box<Expression>,
paren: Token,
args: Vec<Expression>,
},
Grouping {
expression: Box<Expression>,
},

61
rust/rox/src/function.rs Normal file
View File

@ -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<Token>,
pub body: Vec<Statement>,
pub closure: Rc<RefCell<Environment>>,
}
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<Value>) -> Result<Value, CallingError> {
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)
}
}

View File

@ -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<RefCell<Environment>>,
environment: Rc<RefCell<Environment>>,
}
/// Default configuration for the interpreter, with builtin native functions.
impl Default for Interpreter {
fn default() -> Self {
let env: Rc<RefCell<Environment>> = 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<Statement>) -> 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<Option<Value>, 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<Statement>,
statements: &[Statement],
environment: Environment,
) -> Result<(), InterpreterError> {
) -> Result<Option<Value>, 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<Value, InterpreterError> {
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<Option<Value>, 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<Option<Value>, 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<Option<Value>, InterpreterError> {
let value = self.evaluate(expression)?;
println!("{value}");
Ok(())
Ok(None)
}
/// Return a value from a statement.
fn return_statement(
&mut self,
value: &Option<Expression>,
) -> Result<Option<Value>, 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<Option<Value>, 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<Option<Value>, InterpreterError> {
while self.evaluate(condition)?.is_truthy() {
self.execute(body)?;
}
Ok(())
Ok(None)
}
/// Assign the value of an expression to a variable.

View File

@ -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;

View File

@ -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<crate::value::Value>,
) -> Result<Value, CallingError> {
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 {})))]
}

View File

@ -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<Statement, ParserError> {
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<Statement, ParserError> {
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<Statement, ParserError> {
let line = self.current_token.line;
let name = self
@ -315,6 +346,43 @@ impl Parser {
Ok(Statement::Expression(expr))
}
fn function(&mut self) -> Result<Statement, ParserError> {
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<Vec<Statement>, 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<Expression, ParserError> {
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<Expression, ParserError> {
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<Expression, ParserError> {
if self.matches(&[False]) {

View File

@ -19,4 +19,13 @@ pub enum Statement {
condition: Expression,
body: Box<Statement>,
},
Function {
name: Token,
params: Vec<Token>,
body: Vec<Statement>,
},
Return {
keyword: Token,
value: Option<Expression>,
},
}

View File

@ -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<Value>),
Callable(Rc<dyn Callable>),
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}"),