Skip to content

Commit

Permalink
Implement closures
Browse files Browse the repository at this point in the history
  • Loading branch information
crazymerlyn committed Dec 24, 2024
1 parent 0860a20 commit f604880
Show file tree
Hide file tree
Showing 6 changed files with 124 additions and 107 deletions.
9 changes: 7 additions & 2 deletions src/ast.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
use crate::errors::Result;
use crate::interpreter::EnvRef;
use crate::scanner::{Token, TokenType};
use std::cell::RefCell;
use std::convert::From;
use std::fmt;
use std::rc::Rc;

#[derive(Debug, Clone, PartialEq)]
pub enum UnaryOperator {
Expand Down Expand Up @@ -166,10 +169,12 @@ pub enum Value {
Number(f64),
String(String),
BuiltinFunc(String, usize, BuitinFunc),
Func(Token, Vec<Token>, Box<Stmt>),
Func(Token, EnvRef, Vec<Token>, Box<Stmt>),
Return(Box<Value>),
}

pub type ValRef = Rc<RefCell<Value>>;

impl fmt::Display for Value {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
Expand All @@ -178,7 +183,7 @@ impl fmt::Display for Value {
Value::Number(n) => write!(f, "{}", n),
Value::String(ref s) => write!(f, "\"{}\"", s),
Value::BuiltinFunc(ref name, _, _) => write!(f, "<built-in function {}>", name),
Value::Func(ref tok, _, _) => write!(f, "<function {}>", tok.lexeme),
Value::Func(ref tok, _, _, _) => write!(f, "<function {}>", tok.lexeme),
Value::Return(ref val) => write!(f, "return {};", val),
}
}
Expand Down
27 changes: 15 additions & 12 deletions src/callable.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
use std::cell::RefCell;
use std::rc::Rc;

use crate::ast::*;
use crate::errors::{ErrorKind, Result};
use crate::interpretable::Interpretable;
use crate::interpreter::Environment;
use crate::interpreter::{EnvRef, Environment};

pub trait Callable {
fn call(&self, env: &mut Environment, args: Vec<Value>) -> Result<Value>;
fn call(&self, env: EnvRef, args: Vec<Value>) -> Result<Value>;
}

impl Callable for Value {
fn call(&self, env: &mut Environment, args: Vec<Value>) -> Result<Value> {
fn call(&self, _env: EnvRef, args: Vec<Value>) -> Result<Value> {
match *self {
Value::BuiltinFunc(_, ref arity, ref func) => {
if *arity != args.len() {
Expand All @@ -21,30 +24,30 @@ impl Callable for Value {
func(args)
}
}
Value::Func(_, ref params, ref block) => {
Value::Func(_, ref closure, ref params, ref block) => {
if params.len() != args.len() {
Err(ErrorKind::EvaluateError(format!(
"Wrong number of arguments: Expected {}, got {}",
params.len(),
args.len()
))
.into())
)))
} else {
let non_globals = env.export_non_globals();
env.push_local_scope();
let funcenv = Rc::new(RefCell::new(Environment::wrap(closure.clone())));
for (param, value) in params.iter().zip(args.into_iter()) {
env.insert(&param.lexeme, value);
funcenv.borrow_mut().insert(&param.lexeme, value);
}
let res = block.interpret(env);
env.import_non_globals(non_globals);
let res = block.interpret(funcenv);

match res {
Ok(Value::Return(x)) => Ok(*x),
_ => res,
}
}
}
_ => Err(ErrorKind::EvaluateError(format!("{} is not a valid function", self)).into()),
_ => Err(ErrorKind::EvaluateError(format!(
"{} is not a valid function",
self
))),
}
}
}
74 changes: 41 additions & 33 deletions src/evaluable.rs
Original file line number Diff line number Diff line change
@@ -1,35 +1,42 @@
use crate::ast::*;
use crate::callable::Callable;
use crate::errors::{ErrorKind, Result};
use crate::interpreter::Environment;
use crate::interpreter::EnvRef;

pub trait Evaluable {
fn evaluate(&self, env: &mut Environment) -> Result<Value>;
fn evaluate(&self, env: EnvRef) -> Result<Value>;
}

impl Evaluable for Expr {
fn evaluate(&self, env: &mut Environment) -> Result<Value> {
fn evaluate(&self, env: EnvRef) -> Result<Value> {
match *self {
Expr::Literal(ref v) => Ok(v.clone()),
Expr::Unary(ref u) => u.evaluate(env),
Expr::Binary(ref b) => b.evaluate(env),
Expr::Logical(ref l) => l.evaluate(env),
Expr::Grouping(ref g) => g.evaluate(env),
Expr::Variable(ref id) => env.get(&id.name.lexeme).cloned().ok_or(
ErrorKind::EvaluateError(format!("Undefined variable: {}", id.name.lexeme)).into(),
),
Expr::Variable(ref id) => env
.borrow()
.get(&id.name.lexeme)
.map(|v| v.borrow().clone())
.ok_or(ErrorKind::EvaluateError(format!(
"Undefined variable: {}",
id.name.lexeme
))),
Expr::Assign(ref id, ref e) => {
let value = e.evaluate(env)?;
env.update(&id.name.lexeme, value).ok_or(
ErrorKind::EvaluateError(format!("Undefined variable: {}", id.name.lexeme))
.into(),
)
let value = e.evaluate(env.clone())?;
env.borrow_mut()
.update(&id.name.lexeme, value)
.ok_or(ErrorKind::EvaluateError(format!(
"Undefined variable: {}",
id.name.lexeme
)))
}
Expr::Call(ref expr, ref args) => {
let func = expr.evaluate(env)?;
let func = expr.evaluate(env.clone())?;
let mut values = vec![];
for arg in args {
values.push(arg.evaluate(env)?)
values.push(arg.evaluate(env.clone())?)
}
func.call(env, values)
}
Expand All @@ -38,21 +45,21 @@ impl Evaluable for Expr {
}

impl Evaluable for UnaryExpr {
fn evaluate(&self, env: &mut Environment) -> Result<Value> {
fn evaluate(&self, env: EnvRef) -> Result<Value> {
match self.op {
UnaryOperator::Bang => Ok(Value::Bool(!self.expr.evaluate(env)?.is_truthy())),
UnaryOperator::Minus => match self.expr.evaluate(env)? {
Value::Number(n) => Ok(Value::Number(-n)),
x => Err(ErrorKind::EvaluateError(format!("Can't negate {}", x)).into()),
x => Err(ErrorKind::EvaluateError(format!("Can't negate {}", x))),
},
}
}
}

impl Evaluable for BinaryExpr {
fn evaluate(&self, env: &mut Environment) -> Result<Value> {
let left = self.left.evaluate(env)?;
let right = self.right.evaluate(env)?;
fn evaluate(&self, env: EnvRef) -> Result<Value> {
let left = self.left.evaluate(env.clone())?;
let right = self.right.evaluate(env.clone())?;

match self.op {
BinaryOperator::Minus
Expand Down Expand Up @@ -83,10 +90,10 @@ impl Evaluable for BinaryExpr {
} else if let Value::String(r) = right {
Ok(Value::String(format!("{}{}", l, r)))
} else {
Err(
ErrorKind::EvaluateError(format!("Can't add {} to a number", right))
.into(),
)
Err(ErrorKind::EvaluateError(format!(
"Can't add {} to a number",
right
)))
}
} else if let Value::String(l) = left {
if let Value::String(r) = right {
Expand All @@ -99,10 +106,10 @@ impl Evaluable for BinaryExpr {
} else if let Value::String(r) = right {
Ok(Value::String(format!("{}{}", left, r)))
} else {
Err(
ErrorKind::EvaluateError(format!("Can't add {} and {}", left, right))
.into(),
)
Err(ErrorKind::EvaluateError(format!(
"Can't add {} and {}",
left, right
)))
}
}
BinaryOperator::EqualEqual => Ok(Value::Bool(left == right)),
Expand All @@ -113,30 +120,31 @@ impl Evaluable for BinaryExpr {
}

impl Evaluable for LogicalExpr {
fn evaluate(&self, env: &mut Environment) -> Result<Value> {
let left = self.left.evaluate(env)?;
fn evaluate(&self, env: EnvRef) -> Result<Value> {
let left = self.left.evaluate(env.clone())?;
if self.op == LogicalOperator::Or {
if left.is_truthy() {
return Ok(left);
}
} else if !left.is_truthy() {
return Ok(left);
}
self.right.evaluate(env)
self.right.evaluate(env.clone())
}
}

fn number(value: &Value) -> Result<f64> {
match *value {
Value::Number(n) => Ok(n),
_ => Err(
ErrorKind::EvaluateError(format!("Expected a number, instead got: {}", value)).into(),
),
_ => Err(ErrorKind::EvaluateError(format!(
"Expected a number, instead got: {}",
value
))),
}
}

impl Evaluable for Grouping {
fn evaluate(&self, env: &mut Environment) -> Result<Value> {
fn evaluate(&self, env: EnvRef) -> Result<Value> {
self.expr.evaluate(env)
}
}
36 changes: 21 additions & 15 deletions src/interpretable.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
use crate::ast::*;
use crate::errors::Result;
use crate::evaluable::Evaluable;
use crate::interpreter::Environment;
use crate::interpreter::{EnvRef, Environment};
use std::cell::RefCell;
use std::rc::Rc;

pub trait Interpretable {
fn interpret(&self, env: &mut Environment) -> Result<Value>;
fn interpret(&self, env: EnvRef) -> Result<Value>;
}

impl Interpretable for Stmt {
fn interpret(&self, env: &mut Environment) -> Result<Value> {
fn interpret(&self, env: EnvRef) -> Result<Value> {
match *self {
Stmt::Expr(ref expr) => expr.evaluate(env),
Stmt::Print(ref expr) => {
Expand All @@ -19,46 +21,50 @@ impl Interpretable for Stmt {
Ok(Value::Nil)
}
Stmt::Decl(ref id, ref expr) => {
let value = expr.evaluate(env)?;
env.insert(&id.name.lexeme, value);
let value = expr.evaluate(env.clone())?;
RefCell::borrow_mut(&env).insert(&id.name.lexeme, value);
Ok(Value::Nil)
}
Stmt::Block(ref stmts) => {
env.push_local_scope();
let new_env = Rc::new(RefCell::new(Environment::wrap(env.clone())));
let mut res = Value::Nil;
for stmt in stmts {
res = stmt.interpret(env)?;
res = stmt.interpret(new_env.clone())?;
if let Value::Return(_) = res {
return Ok(res);
}
}
env.pop_scope();
Ok(res)
}
Stmt::If(ref cond, ref if_stmt, ref else_stmt) => {
let value = cond.evaluate(env)?;
let value = cond.evaluate(env.clone())?;
if value.is_truthy() {
if_stmt.interpret(env)
if_stmt.interpret(env.clone())
} else if let Some(ref else_stmt) = *else_stmt {
else_stmt.interpret(env)
else_stmt.interpret(env.clone())
} else {
Ok(Value::Nil)
}
}
Stmt::While(ref cond, ref stmt) => {
let mut res = Value::Nil;
while cond.evaluate(env)?.is_truthy() {
res = stmt.interpret(env)?;
while cond.evaluate(env.clone())?.is_truthy() {
res = stmt.interpret(env.clone())?;
if let Value::Return(_) = res {
return Ok(res);
}
}
Ok(res)
}
Stmt::Func(ref name, ref params, ref body) => {
env.insert(
RefCell::borrow_mut(&env).insert(
&name.lexeme,
Value::Func(name.to_owned(), params.to_owned(), body.to_owned()),
Value::Func(
name.to_owned(),
env.clone(),
params.to_owned(),
body.to_owned(),
),
);
Ok(Value::Nil)
}
Expand Down
Loading

0 comments on commit f604880

Please sign in to comment.