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,
|
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>,
|
||||||
},
|
},
|
||||||
|
|
|
@ -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
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::{
|
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(),
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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 })
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -28,4 +28,8 @@ pub enum Statement {
|
||||||
keyword: Token,
|
keyword: Token,
|
||||||
value: Option<Expression>,
|
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 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"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue