Fix closure not capturing outer environment

pull/4/head
azur 2022-12-19 14:28:30 +07:00
parent dfc24c5444
commit 6ebe1bc7ae
9 changed files with 106 additions and 32 deletions

7
Cargo.lock generated
View File

@ -103,6 +103,13 @@ version = "0.2.138"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "db6d7e329c562c5dfab7a46a2afabc8b987ab9a4834c9d1ca04dc54c1546cef8"
[[package]]
name = "lower"
version = "0.1.0"
dependencies = [
"parser",
]
[[package]]
name = "once_cell"
version = "1.16.0"

View File

@ -2,6 +2,7 @@
members = [
"entry",
"parser",
"lower",
"compiler",
"vm",
]

View File

@ -1,24 +1,13 @@
fun succ x = do
let one = 1
let y = x + one in y
let y = x + one
in y
end
fun double x = x * 2
fun main = do
let add = \x y -> x + y in
succ(34)
|> \x -> add(34, x)
|> \x -> println(x)
let result = add(34, 35)
println(result)
end
fun main = do
let add = \x y -> x + y in
print(add(34, succ(34)))
end
// succ(34) |> \x -> add (34, x)
// (\x -> add(34, x))(succ(34))
print(add(double(17), succ(34)))
end

View File

@ -1,3 +1,5 @@
fun main = do
println((\x -> x + 1)(9))
1
|> \x -> x + 1
|> \x -> println(x)
end

7
lower/Cargo.toml Normal file
View File

@ -0,0 +1,7 @@
[package]
name = "lower"
version = "0.1.0"
edition = "2021"
[dependencies]
parser = { path = "../parser" }

41
lower/src/lib.rs Normal file
View File

@ -0,0 +1,41 @@
#![allow(clippy::new_without_default)]
use parser::*;
pub struct Lower {}
impl Lower {
pub fn new() -> Self {
Self {}
}
fn fold_pipe(&self, e: Expr) -> Vec<Expr> {
if let Expr::Binary((BinaryOp::Pipe, _), left, right) = e {
vec![Expr::Call(right, vec![*left])]
} else {
unreachable!()
}
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_fold_pipe() {
let s = "1 |> \\x -> x + 1";
println!("{}", s);
let (ts, es) = lex(s.to_owned());
assert!(es.is_empty());
let (ex, es) = parse_expr(ts.unwrap(), s.chars().count());
assert!(es.is_empty());
let ex = ex.unwrap();
let l = Lower::new();
let ex = l.fold_pipe(ex.0);
println!("{:?}", ex);
}
}

View File

@ -532,6 +532,17 @@ pub fn parse(
(ast, parse_error)
}
pub fn parse_expr(
tokens: Vec<Spanned<Token>>,
len: usize,
) -> (Option<Spanned<Expr>>, Vec<Simple<Token>>) {
let (ast, parse_error) = expr_parser()
.then_ignore(end())
.parse_recovery(Stream::from_iter(len..len + 1, tokens.into_iter()));
(ast, parse_error)
}
pub fn report(e: Simple<String>, src: &str) {
let report = Report::build(ReportKind::Error, (), e.span().start());

View File

@ -61,20 +61,10 @@ impl Executor {
}
fn get(&self, name: &str) -> Result<Value, Error> {
// Get from the current environment first
self.env
.borrow()
.binds
.get(name)
.cloned()
// If it doesn't exist then try the outer environment
.or_else(|| {
self.outer_env
.as_ref()
.and_then(|env| env.borrow().binds.get(name).cloned())
.or(None)
})
.ok_or_else(|| self.err(format!("undefined variable {}", name).as_str()))
.ok_or_else(|| self.err(format!("unbound variable: {}", name).as_str()))
}
fn set(&mut self, name: &str, v: Value) -> Result<(), Error> {

View File

@ -49,14 +49,39 @@ impl Display for Value {
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct Env {
pub binds: FnvHashMap<String, Value>,
pub parent: Option<Rc<Self>>,
}
impl Env {
pub fn new() -> Self {
Self {
binds: FnvHashMap::default(),
parent: None,
}
}
pub fn new_with_parent(parent: Rc<Self>) -> Self {
Self {
binds: FnvHashMap::default(),
parent: Some(parent),
}
}
pub fn get(&self, name: &str) -> Option<Value> {
// Get the value from the current environment first
// and then from the parent environment recursively
self.binds
.get(name)
.cloned()
.or_else(|| self.parent.as_ref().and_then(|p| p.get(name)).or(None))
}
pub fn set(&mut self, name: String, value: Value) {
// Set the value in the current environment
// The handling of deciding whether to create a new binding
// is done in the Executor
self.binds.insert(name, value);
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
@ -73,16 +98,17 @@ impl Func {
pub fn run(self, args: Vec<Value>) -> Result<Vec<Value>, Error> {
// Create a new environment for the closure
let mut new_env = Env::new();
let mut closure_env = Env::new();
for (arg, val) in self.args.iter().zip(args) {
new_env.binds.insert(arg.clone(), val);
closure_env.binds.insert(arg.clone(), val);
}
let new_env = Rc::new(RefCell::new(new_env));
// Set the parent to the current environment
closure_env.parent = Some(Rc::new(self.env.borrow().clone()));
// Execute the closure
let mut new_executor = Executor {
stack: Vec::new(),
env: new_env,
env: Rc::new(RefCell::new(closure_env)),
outer_env: Some(Rc::clone(&self.env)),
instrs: self.instrs,
ip: 0,