implement chapter 12 in rust
This commit is contained in:
parent
283155c38b
commit
621c97102a
10 changed files with 366 additions and 17 deletions
58
rust/rox/src/class.rs
Normal file
58
rust/rox/src/class.rs
Normal 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))
|
||||
}
|
||||
}
|
|
@ -17,6 +17,18 @@ pub enum Expression {
|
|||
paren: Token,
|
||||
args: Vec<Expression>,
|
||||
},
|
||||
Get {
|
||||
object: Box<Expression>,
|
||||
name: Token,
|
||||
},
|
||||
Set {
|
||||
object: Box<Expression>,
|
||||
name: Token,
|
||||
value: Box<Expression>,
|
||||
},
|
||||
This {
|
||||
keyword: Token,
|
||||
},
|
||||
Grouping {
|
||||
expression: Box<Expression>,
|
||||
},
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
use crate::{
|
||||
callable::{Callable, CallingError},
|
||||
environment::Environment,
|
||||
instance::Instance,
|
||||
interpreter::Interpreter,
|
||||
statement::Statement,
|
||||
token::Token,
|
||||
|
@ -13,6 +14,7 @@ use std::{cell::RefCell, fmt::Display, rc::Rc};
|
|||
#[derive(Debug, Clone)]
|
||||
pub struct Function {
|
||||
pub name: Token,
|
||||
pub is_initializer: bool,
|
||||
pub params: Vec<Token>,
|
||||
pub body: Vec<Statement>,
|
||||
pub closure: Rc<RefCell<Environment>>,
|
||||
|
@ -52,10 +54,24 @@ impl Callable for Function {
|
|||
env.define(param.lexeme.clone(), arg);
|
||||
}
|
||||
|
||||
let result = interpreter
|
||||
Ok(interpreter
|
||||
.block(&self.body, env)
|
||||
.map_err(|e| CallingError::CallInterpretationFailed(e.to_string()))?
|
||||
.unwrap_or(Value::Nil);
|
||||
Ok(result)
|
||||
.unwrap_or(Value::Nil))
|
||||
}
|
||||
}
|
||||
|
||||
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
49
rust/rox/src/instance.rs
Normal 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);
|
||||
}
|
||||
}
|
|
@ -6,9 +6,10 @@ use tracing::error;
|
|||
|
||||
use crate::{
|
||||
callable::CallingError,
|
||||
class::Class,
|
||||
environment::{Environment, EnvironmentError},
|
||||
expression::Expression,
|
||||
function::Function,
|
||||
function::{self, Function},
|
||||
native_functions,
|
||||
statement::Statement,
|
||||
token::{Literal, Token, TokenType},
|
||||
|
@ -33,6 +34,10 @@ pub enum InterpreterError {
|
|||
NotACallable(usize, Value),
|
||||
#[error("line {0}: {1}.")]
|
||||
FailedToCall(usize, CallingError),
|
||||
#[error("line {0}: only instances have fields.")]
|
||||
OnlyInstancesHaveFields(usize),
|
||||
#[error("{0}")]
|
||||
InstanceError(#[from] crate::instance::InstanceError),
|
||||
}
|
||||
|
||||
/// Interpreter for the Lox language.
|
||||
|
@ -106,6 +111,7 @@ impl Interpreter {
|
|||
self.function_statement(name, params, body)
|
||||
}
|
||||
Statement::Return { keyword: _, value } => self.return_statement(value),
|
||||
Statement::Class { name, methods } => self.class(name, methods),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -138,6 +144,39 @@ impl Interpreter {
|
|||
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.
|
||||
fn evaluate(&mut self, expression: &Expression) -> Result<Value, InterpreterError> {
|
||||
match expression {
|
||||
|
@ -164,6 +203,13 @@ impl Interpreter {
|
|||
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.
|
||||
fn function_statement(
|
||||
&mut self,
|
||||
|
@ -200,6 +269,7 @@ impl Interpreter {
|
|||
let fn_name = name.lexeme.clone();
|
||||
let fun = Function {
|
||||
name: name.clone(),
|
||||
is_initializer: false,
|
||||
params: params.to_vec(),
|
||||
body: body.to_vec(),
|
||||
closure: self.environment.clone(),
|
||||
|
|
|
@ -14,10 +14,12 @@ use thiserror::Error;
|
|||
use tracing::error;
|
||||
|
||||
pub mod callable;
|
||||
pub mod class;
|
||||
pub mod cli;
|
||||
pub mod environment;
|
||||
pub mod expression;
|
||||
pub mod function;
|
||||
pub mod instance;
|
||||
pub mod interpreter;
|
||||
pub mod keywords;
|
||||
pub mod native_functions;
|
||||
|
|
|
@ -25,6 +25,8 @@ pub enum ParserError {
|
|||
SemicolonAfterValueExpected(usize),
|
||||
#[error("line {0}: expected ';' after expression.")]
|
||||
SemicolonAfterExpressionExpected(usize),
|
||||
#[error("line {0}: expected class name.")]
|
||||
ClassNameExpected(usize),
|
||||
#[error("line {0}: expected variable name.")]
|
||||
VariableNameExpected(usize),
|
||||
#[error("line {0}: invalid assignment target.")]
|
||||
|
@ -57,6 +59,12 @@ pub enum ParserError {
|
|||
LeftBraceBeforeFunctionBodyExpected(usize),
|
||||
#[error("line {0}: expected ';' after return value.")]
|
||||
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.
|
||||
|
@ -174,7 +182,9 @@ impl Parser {
|
|||
}
|
||||
|
||||
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()
|
||||
} else if self.matches(&[Var]) {
|
||||
self.var_declaration()
|
||||
|
@ -297,6 +307,28 @@ impl Parser {
|
|||
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> {
|
||||
let line = self.current_token.line;
|
||||
let name = self
|
||||
|
@ -409,6 +441,12 @@ impl Parser {
|
|||
name,
|
||||
value: Box::new(value),
|
||||
})
|
||||
} else if let Expression::Get { object, name } = expr {
|
||||
Ok(Expression::Set {
|
||||
object,
|
||||
name,
|
||||
value: Box::new(value),
|
||||
})
|
||||
} else {
|
||||
Err(ParserError::InvalidAssignmentTarget(equals.line))
|
||||
}
|
||||
|
@ -490,6 +528,17 @@ impl Parser {
|
|||
loop {
|
||||
if self.matches(&[LeftParen]) {
|
||||
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 {
|
||||
break;
|
||||
}
|
||||
|
@ -536,6 +585,10 @@ impl Parser {
|
|||
Ok(Expression::Literal {
|
||||
value: token::Literal::Nil,
|
||||
})
|
||||
} else if self.matches(&[This]) {
|
||||
Ok(Expression::This {
|
||||
keyword: self.previous()?.clone(),
|
||||
})
|
||||
} else if self.matches(&[Identifier]) {
|
||||
let prev = self.previous()?.clone();
|
||||
Ok(Expression::Variable { name: prev })
|
||||
|
|
|
@ -10,12 +10,24 @@ pub enum ResolverError {
|
|||
NameRedefinition(usize),
|
||||
#[error("line {0}: can not return from top-level code.")]
|
||||
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)]
|
||||
enum FunctionType {
|
||||
None,
|
||||
Function,
|
||||
Initializer,
|
||||
Method,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
enum ClassType {
|
||||
None,
|
||||
Class,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
|
@ -23,6 +35,7 @@ pub struct Resolver<'a> {
|
|||
scopes: Vec<HashMap<String, bool>>,
|
||||
interpreter: &'a mut Interpreter,
|
||||
current_fun: FunctionType,
|
||||
current_class: ClassType,
|
||||
}
|
||||
|
||||
impl<'a> Resolver<'a> {
|
||||
|
@ -31,6 +44,7 @@ impl<'a> Resolver<'a> {
|
|||
scopes: Vec::default(),
|
||||
interpreter,
|
||||
current_fun: FunctionType::None,
|
||||
current_class: ClassType::None,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -60,16 +74,8 @@ impl<'a> Resolver<'a> {
|
|||
Statement::Function { name, params, body } => {
|
||||
self.resolve_function_stmt(name, params, body)
|
||||
}
|
||||
Statement::Return { keyword, value } => {
|
||||
if self.current_fun == FunctionType::None {
|
||||
return Err(ResolverError::TopLevelReturn(keyword.line));
|
||||
}
|
||||
if let Some(value) = value {
|
||||
self.resolve_expression(value)
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
Statement::Return { keyword, value } => self.resolve_return_stmt(keyword, value),
|
||||
Statement::Class { name, methods } => self.resolve_class_stmt(name, methods),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -95,6 +101,19 @@ impl<'a> Resolver<'a> {
|
|||
} => self.resolve_binary_expr(left, right),
|
||||
Expression::Unary { operator: _, right } => self.resolve_expression(right),
|
||||
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(())
|
||||
}
|
||||
|
||||
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(
|
||||
&mut self,
|
||||
name: &Token,
|
||||
|
@ -205,6 +277,17 @@ impl<'a> Resolver<'a> {
|
|||
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(
|
||||
&mut self,
|
||||
condition: &Expression,
|
||||
|
|
|
@ -28,4 +28,8 @@ pub enum Statement {
|
|||
keyword: Token,
|
||||
value: Option<Expression>,
|
||||
},
|
||||
Class {
|
||||
name: Token,
|
||||
methods: Vec<Statement>,
|
||||
},
|
||||
}
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
use std::{fmt::Display, rc::Rc};
|
||||
use std::{cell::RefCell, fmt::Display, rc::Rc};
|
||||
|
||||
use ordered_float::OrderedFloat;
|
||||
|
||||
use crate::{callable::Callable, token::Literal};
|
||||
use crate::{callable::Callable, instance, token::Literal};
|
||||
|
||||
/// Concrete value in the interpreter.
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
|
@ -12,6 +12,7 @@ pub enum Value {
|
|||
String(String),
|
||||
Number(OrderedFloat<f64>),
|
||||
Boolean(bool),
|
||||
Instance(Rc<RefCell<instance::Instance>>),
|
||||
Nil,
|
||||
}
|
||||
|
||||
|
@ -23,6 +24,7 @@ impl Display for Value {
|
|||
Value::String(x) => write!(f, "{x}"),
|
||||
Value::Number(x) => write!(f, "{x}"),
|
||||
Value::Boolean(x) => write!(f, "{x}"),
|
||||
Value::Instance(x) => write!(f, "{}", x.borrow()),
|
||||
Value::Nil => write!(f, "nil"),
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue