implement chapter 12 in rust

This commit is contained in:
Sebastian Hugentobler 2025-05-25 16:04:00 +02:00
parent 283155c38b
commit 621c97102a
Signed by: shu
SSH key fingerprint: SHA256:ppcx6MlixdNZd5EUM1nkHOKoyQYoJwzuQKXM6J/t66M
10 changed files with 366 additions and 17 deletions

58
rust/rox/src/class.rs Normal file
View file

@ -0,0 +1,58 @@
use std::{collections::HashMap, fmt::Display};
use crate::{
callable::{Callable, CallingError},
function::Function,
instance::Instance,
value::Value,
};
#[derive(Debug, Clone, PartialEq)]
pub struct Class {
pub name: String,
pub methods: HashMap<String, Function>,
}
impl Display for Class {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.name)
}
}
impl Class {
pub fn new(name: String, methods: HashMap<String, Function>) -> Self {
Self { name, methods }
}
pub fn find_method(&self, name: &str) -> Option<&Function> {
self.methods.get(name)
}
}
impl Callable for Class {
fn name(&self) -> String {
self.name.clone()
}
fn arity(&self) -> usize {
if let Some(initializer) = self.find_method("init") {
initializer.arity()
} else {
0
}
}
fn call(
&self,
interpreter: &mut crate::interpreter::Interpreter,
args: Vec<crate::value::Value>,
) -> Result<Value, CallingError> {
let class = self.clone();
let instance = Instance::new(class);
if let Some(initializer) = self.find_method("init") {
initializer.bind(instance.clone()).call(interpreter, args)?;
};
Ok(Value::Instance(instance))
}
}

View file

@ -17,6 +17,18 @@ pub enum Expression {
paren: Token, paren: Token,
args: Vec<Expression>, args: Vec<Expression>,
}, },
Get {
object: Box<Expression>,
name: Token,
},
Set {
object: Box<Expression>,
name: Token,
value: Box<Expression>,
},
This {
keyword: Token,
},
Grouping { Grouping {
expression: Box<Expression>, expression: Box<Expression>,
}, },

View file

@ -1,6 +1,7 @@
use crate::{ use crate::{
callable::{Callable, CallingError}, callable::{Callable, CallingError},
environment::Environment, environment::Environment,
instance::Instance,
interpreter::Interpreter, interpreter::Interpreter,
statement::Statement, statement::Statement,
token::Token, token::Token,
@ -13,6 +14,7 @@ use std::{cell::RefCell, fmt::Display, rc::Rc};
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct Function { pub struct Function {
pub name: Token, pub name: Token,
pub is_initializer: bool,
pub params: Vec<Token>, pub params: Vec<Token>,
pub body: Vec<Statement>, pub body: Vec<Statement>,
pub closure: Rc<RefCell<Environment>>, pub closure: Rc<RefCell<Environment>>,
@ -52,10 +54,24 @@ impl Callable for Function {
env.define(param.lexeme.clone(), arg); env.define(param.lexeme.clone(), arg);
} }
let result = interpreter Ok(interpreter
.block(&self.body, env) .block(&self.body, env)
.map_err(|e| CallingError::CallInterpretationFailed(e.to_string()))? .map_err(|e| CallingError::CallInterpretationFailed(e.to_string()))?
.unwrap_or(Value::Nil); .unwrap_or(Value::Nil))
Ok(result) }
}
impl Function {
pub fn bind(&self, instance: Rc<RefCell<Instance>>) -> Self {
let mut env = Environment::with_enclosing(self.closure.clone());
env.define("this".to_string(), Value::Instance(instance));
Self {
name: self.name.clone(),
is_initializer: self.is_initializer,
params: self.params.clone(),
body: self.body.clone(),
closure: Rc::new(RefCell::new(env)),
}
} }
} }

49
rust/rox/src/instance.rs Normal file
View file

@ -0,0 +1,49 @@
use std::{cell::RefCell, collections::HashMap, fmt::Display, rc::Rc};
use thiserror::Error;
use crate::{class::Class, token::Token, value::Value};
#[derive(Error, Debug)]
pub enum InstanceError {
#[error("line {0}: no property named {1}.")]
NoSuchProperty(usize, String),
}
#[derive(Debug, Clone, PartialEq)]
pub struct Instance {
class: Class,
fields: HashMap<String, Value>,
}
impl Display for Instance {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{} instance", self.class.name)
}
}
impl Instance {
pub fn new(class: Class) -> Rc<RefCell<Self>> {
Rc::new(RefCell::new(Self {
class,
fields: HashMap::default(),
}))
}
pub fn get(&self, name: &Token, instance: Rc<RefCell<Self>>) -> Result<Value, InstanceError> {
if let Some(field) = self.fields.get(name.lexeme.as_str()) {
Ok(field.clone())
} else if let Some(method) = self.class.find_method(name.lexeme.as_str()) {
Ok(Value::Callable(Rc::new(method.bind(instance))))
} else {
Err(InstanceError::NoSuchProperty(
name.line,
name.lexeme.clone(),
))
}
}
pub fn set(&mut self, name: &Token, value: Value) {
self.fields.insert(name.lexeme.clone(), value);
}
}

View file

@ -6,9 +6,10 @@ use tracing::error;
use crate::{ use crate::{
callable::CallingError, callable::CallingError,
class::Class,
environment::{Environment, EnvironmentError}, environment::{Environment, EnvironmentError},
expression::Expression, expression::Expression,
function::Function, function::{self, Function},
native_functions, native_functions,
statement::Statement, statement::Statement,
token::{Literal, Token, TokenType}, token::{Literal, Token, TokenType},
@ -33,6 +34,10 @@ pub enum InterpreterError {
NotACallable(usize, Value), NotACallable(usize, Value),
#[error("line {0}: {1}.")] #[error("line {0}: {1}.")]
FailedToCall(usize, CallingError), FailedToCall(usize, CallingError),
#[error("line {0}: only instances have fields.")]
OnlyInstancesHaveFields(usize),
#[error("{0}")]
InstanceError(#[from] crate::instance::InstanceError),
} }
/// Interpreter for the Lox language. /// Interpreter for the Lox language.
@ -106,6 +111,7 @@ impl Interpreter {
self.function_statement(name, params, body) self.function_statement(name, params, body)
} }
Statement::Return { keyword: _, value } => self.return_statement(value), Statement::Return { keyword: _, value } => self.return_statement(value),
Statement::Class { name, methods } => self.class(name, methods),
} }
} }
@ -138,6 +144,39 @@ impl Interpreter {
Ok(None) Ok(None)
} }
fn class(
&mut self,
name: &Token,
methods: &[Statement],
) -> Result<Option<Value>, InterpreterError> {
self.environment
.borrow_mut()
.define(name.lexeme.clone(), Value::Nil);
let mut class_methods: HashMap<String, Function> = HashMap::new();
for method in methods {
if let Statement::Function { name, params, body } = method {
let function = function::Function {
name: name.clone(),
is_initializer: name.lexeme == "init",
params: params.clone(),
body: body.clone(),
closure: self.environment.clone(),
};
class_methods.insert(name.lexeme.clone(), function);
}
}
let class = Class::new(name.lexeme.clone(), class_methods);
self.environment
.borrow_mut()
.assign(name, Value::Callable(Rc::new(class)))
.map_err(InterpreterError::UndefinedVariable)?;
Ok(None)
}
/// Evaluate an expression and return its value. /// Evaluate an expression and return its value.
fn evaluate(&mut self, expression: &Expression) -> Result<Value, InterpreterError> { fn evaluate(&mut self, expression: &Expression) -> Result<Value, InterpreterError> {
match expression { match expression {
@ -164,6 +203,13 @@ impl Interpreter {
paren, paren,
args, args,
} => self.call(callee, paren, args), } => self.call(callee, paren, args),
Expression::Get { object, name } => self.get(object, name),
Expression::Set {
object,
name,
value,
} => self.set(object, name, value),
Expression::This { keyword } => self.lookup_var(keyword, expression),
} }
} }
@ -190,6 +236,29 @@ impl Interpreter {
} }
} }
fn get(&mut self, object: &Expression, name: &Token) -> Result<Value, InterpreterError> {
match self.evaluate(object)? {
Value::Instance(instance) => Ok(instance.borrow().get(name, instance.clone())?),
_ => Err(InterpreterError::OnlyInstancesHaveFields(name.line)),
}
}
fn set(
&mut self,
object: &Expression,
name: &Token,
value: &Expression,
) -> Result<Value, InterpreterError> {
match self.evaluate(object)? {
Value::Instance(instance) => {
let value = self.evaluate(value)?;
instance.borrow_mut().set(name, value.clone());
Ok(value)
}
_ => Err(InterpreterError::OnlyInstancesHaveFields(name.line)),
}
}
/// Define a new function. /// Define a new function.
fn function_statement( fn function_statement(
&mut self, &mut self,
@ -200,6 +269,7 @@ impl Interpreter {
let fn_name = name.lexeme.clone(); let fn_name = name.lexeme.clone();
let fun = Function { let fun = Function {
name: name.clone(), name: name.clone(),
is_initializer: false,
params: params.to_vec(), params: params.to_vec(),
body: body.to_vec(), body: body.to_vec(),
closure: self.environment.clone(), closure: self.environment.clone(),

View file

@ -14,10 +14,12 @@ use thiserror::Error;
use tracing::error; use tracing::error;
pub mod callable; pub mod callable;
pub mod class;
pub mod cli; pub mod cli;
pub mod environment; pub mod environment;
pub mod expression; pub mod expression;
pub mod function; pub mod function;
pub mod instance;
pub mod interpreter; pub mod interpreter;
pub mod keywords; pub mod keywords;
pub mod native_functions; pub mod native_functions;

View file

@ -25,6 +25,8 @@ pub enum ParserError {
SemicolonAfterValueExpected(usize), SemicolonAfterValueExpected(usize),
#[error("line {0}: expected ';' after expression.")] #[error("line {0}: expected ';' after expression.")]
SemicolonAfterExpressionExpected(usize), SemicolonAfterExpressionExpected(usize),
#[error("line {0}: expected class name.")]
ClassNameExpected(usize),
#[error("line {0}: expected variable name.")] #[error("line {0}: expected variable name.")]
VariableNameExpected(usize), VariableNameExpected(usize),
#[error("line {0}: invalid assignment target.")] #[error("line {0}: invalid assignment target.")]
@ -57,6 +59,12 @@ pub enum ParserError {
LeftBraceBeforeFunctionBodyExpected(usize), LeftBraceBeforeFunctionBodyExpected(usize),
#[error("line {0}: expected ';' after return value.")] #[error("line {0}: expected ';' after return value.")]
SemicolonAfterReturnExpected(usize), SemicolonAfterReturnExpected(usize),
#[error("line {0}: expected '{{' before class body.")]
LeftBraceBeforeClassExpected(usize),
#[error("line {0}: expected '}}' after class body.")]
RightBraceAfterClassExpected(usize),
#[error("line {0}: expected property name after '.'.")]
PropertyNameAfterDotExpected(usize),
} }
/// Parse the Lox language. /// Parse the Lox language.
@ -174,7 +182,9 @@ impl Parser {
} }
fn declaration(&mut self) -> Result<Statement, ParserError> { fn declaration(&mut self) -> Result<Statement, ParserError> {
if self.matches(&[Fun]) { if self.matches(&[Class]) {
self.class_declaration()
} else if self.matches(&[Fun]) {
self.function() self.function()
} else if self.matches(&[Var]) { } else if self.matches(&[Var]) {
self.var_declaration() self.var_declaration()
@ -297,6 +307,28 @@ impl Parser {
Ok(Statement::Return { keyword, value }) Ok(Statement::Return { keyword, value })
} }
fn class_declaration(&mut self) -> Result<Statement, ParserError> {
let line = self.current_token.line;
let name = self
.consume(&Identifier)
.ok_or(ParserError::ClassNameExpected(line))?
.clone();
self.consume(&LeftBrace)
.ok_or(ParserError::LeftBraceBeforeClassExpected(line))?;
let mut methods = Vec::new();
while !self.check(&RightBrace) && !self.is_at_end() {
let method = self.function()?;
methods.push(method);
}
self.consume(&RightBrace)
.ok_or(ParserError::RightBraceAfterClassExpected(line))?;
Ok(Statement::Class { name, methods })
}
fn var_declaration(&mut self) -> Result<Statement, ParserError> { fn var_declaration(&mut self) -> Result<Statement, ParserError> {
let line = self.current_token.line; let line = self.current_token.line;
let name = self let name = self
@ -409,6 +441,12 @@ impl Parser {
name, name,
value: Box::new(value), value: Box::new(value),
}) })
} else if let Expression::Get { object, name } = expr {
Ok(Expression::Set {
object,
name,
value: Box::new(value),
})
} else { } else {
Err(ParserError::InvalidAssignmentTarget(equals.line)) Err(ParserError::InvalidAssignmentTarget(equals.line))
} }
@ -490,6 +528,17 @@ impl Parser {
loop { loop {
if self.matches(&[LeftParen]) { if self.matches(&[LeftParen]) {
expr = self.finish_call(expr)?; expr = self.finish_call(expr)?;
} else if self.matches(&[Dot]) {
let line = self.current_token.line;
let name = self
.consume(&Identifier)
.ok_or(ParserError::PropertyNameAfterDotExpected(line))?
.clone();
expr = Expression::Get {
object: Box::new(expr),
name,
}
} else { } else {
break; break;
} }
@ -536,6 +585,10 @@ impl Parser {
Ok(Expression::Literal { Ok(Expression::Literal {
value: token::Literal::Nil, value: token::Literal::Nil,
}) })
} else if self.matches(&[This]) {
Ok(Expression::This {
keyword: self.previous()?.clone(),
})
} else if self.matches(&[Identifier]) { } else if self.matches(&[Identifier]) {
let prev = self.previous()?.clone(); let prev = self.previous()?.clone();
Ok(Expression::Variable { name: prev }) Ok(Expression::Variable { name: prev })

View file

@ -10,12 +10,24 @@ pub enum ResolverError {
NameRedefinition(usize), NameRedefinition(usize),
#[error("line {0}: can not return from top-level code.")] #[error("line {0}: can not return from top-level code.")]
TopLevelReturn(usize), TopLevelReturn(usize),
#[error("line {0}: can not use 'this' outside of a class.")]
ThisOutsideClass(usize),
#[error("line {0}: can not return a value from an initializer.")]
ReturnFromInitializer(usize),
} }
#[derive(Debug, Clone, Copy, PartialEq, Eq)] #[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum FunctionType { enum FunctionType {
None, None,
Function, Function,
Initializer,
Method,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum ClassType {
None,
Class,
} }
#[derive(Debug)] #[derive(Debug)]
@ -23,6 +35,7 @@ pub struct Resolver<'a> {
scopes: Vec<HashMap<String, bool>>, scopes: Vec<HashMap<String, bool>>,
interpreter: &'a mut Interpreter, interpreter: &'a mut Interpreter,
current_fun: FunctionType, current_fun: FunctionType,
current_class: ClassType,
} }
impl<'a> Resolver<'a> { impl<'a> Resolver<'a> {
@ -31,6 +44,7 @@ impl<'a> Resolver<'a> {
scopes: Vec::default(), scopes: Vec::default(),
interpreter, interpreter,
current_fun: FunctionType::None, current_fun: FunctionType::None,
current_class: ClassType::None,
} }
} }
@ -60,16 +74,8 @@ impl<'a> Resolver<'a> {
Statement::Function { name, params, body } => { Statement::Function { name, params, body } => {
self.resolve_function_stmt(name, params, body) self.resolve_function_stmt(name, params, body)
} }
Statement::Return { keyword, value } => { Statement::Return { keyword, value } => self.resolve_return_stmt(keyword, value),
if self.current_fun == FunctionType::None { Statement::Class { name, methods } => self.resolve_class_stmt(name, methods),
return Err(ResolverError::TopLevelReturn(keyword.line));
}
if let Some(value) = value {
self.resolve_expression(value)
} else {
Ok(())
}
}
} }
} }
@ -95,6 +101,19 @@ impl<'a> Resolver<'a> {
} => self.resolve_binary_expr(left, right), } => self.resolve_binary_expr(left, right),
Expression::Unary { operator: _, right } => self.resolve_expression(right), Expression::Unary { operator: _, right } => self.resolve_expression(right),
Expression::Variable { name } => self.resolve_var_expr(expr, name), Expression::Variable { name } => self.resolve_var_expr(expr, name),
Expression::Get { object, name: _ } => self.resolve_expression(object),
Expression::Set {
object,
name: _,
value,
} => self.resolve_set_expression(object, value),
Expression::This { keyword } => {
if self.current_class == ClassType::None {
return Err(ResolverError::ThisOutsideClass(keyword.line));
}
self.resolve_local(expr, keyword)
}
} }
} }
@ -132,6 +151,59 @@ impl<'a> Resolver<'a> {
Ok(()) Ok(())
} }
fn resolve_class_stmt(
&mut self,
name: &Token,
methods: &[Statement],
) -> Result<(), ResolverError> {
let enclosing_class = self.current_class;
self.current_class = ClassType::Class;
self.declare(name)?;
self.define(name);
self.begin_scope();
if let Some(scope) = self.scopes.last_mut() {
scope.insert("this".to_string(), true);
}
for method in methods {
if let Statement::Function { name, params, body } = method {
let declaration = if name.lexeme == "init" {
FunctionType::Initializer
} else {
FunctionType::Method
};
self.resolve_function(params, body, declaration)?;
}
}
self.end_scope();
self.current_class = enclosing_class;
Ok(())
}
fn resolve_return_stmt(
&mut self,
keyword: &Token,
value: &Option<Expression>,
) -> Result<(), ResolverError> {
if self.current_fun == FunctionType::None {
return Err(ResolverError::TopLevelReturn(keyword.line));
}
if let Some(value) = value {
if self.current_fun == FunctionType::Initializer {
return Err(ResolverError::ReturnFromInitializer(keyword.line));
}
self.resolve_expression(value)
} else {
Ok(())
}
}
fn resolve_var( fn resolve_var(
&mut self, &mut self,
name: &Token, name: &Token,
@ -205,6 +277,17 @@ impl<'a> Resolver<'a> {
Ok(()) Ok(())
} }
fn resolve_set_expression(
&mut self,
object: &Expression,
value: &Expression,
) -> Result<(), ResolverError> {
self.resolve_expression(value)?;
self.resolve_expression(object)?;
Ok(())
}
fn resolve_if_stmt( fn resolve_if_stmt(
&mut self, &mut self,
condition: &Expression, condition: &Expression,

View file

@ -28,4 +28,8 @@ pub enum Statement {
keyword: Token, keyword: Token,
value: Option<Expression>, value: Option<Expression>,
}, },
Class {
name: Token,
methods: Vec<Statement>,
},
} }

View file

@ -1,8 +1,8 @@
use std::{fmt::Display, rc::Rc}; use std::{cell::RefCell, fmt::Display, rc::Rc};
use ordered_float::OrderedFloat; use ordered_float::OrderedFloat;
use crate::{callable::Callable, token::Literal}; use crate::{callable::Callable, instance, token::Literal};
/// Concrete value in the interpreter. /// Concrete value in the interpreter.
#[derive(Clone, Debug, PartialEq)] #[derive(Clone, Debug, PartialEq)]
@ -12,6 +12,7 @@ pub enum Value {
String(String), String(String),
Number(OrderedFloat<f64>), Number(OrderedFloat<f64>),
Boolean(bool), Boolean(bool),
Instance(Rc<RefCell<instance::Instance>>),
Nil, Nil,
} }
@ -23,6 +24,7 @@ impl Display for Value {
Value::String(x) => write!(f, "{x}"), Value::String(x) => write!(f, "{x}"),
Value::Number(x) => write!(f, "{x}"), Value::Number(x) => write!(f, "{x}"),
Value::Boolean(x) => write!(f, "{x}"), Value::Boolean(x) => write!(f, "{x}"),
Value::Instance(x) => write!(f, "{}", x.borrow()),
Value::Nil => write!(f, "nil"), Value::Nil => write!(f, "nil"),
} }
} }