From 6ebe1bc7ae1c056bc294ef9b595a2df48f2d311a Mon Sep 17 00:00:00 2001 From: azur Date: Mon, 19 Dec 2022 14:28:30 +0700 Subject: [PATCH] Fix closure not capturing outer environment --- Cargo.lock | 7 +++++++ Cargo.toml | 1 + examples/a.sial | 21 +++++---------------- examples/sim.sial | 4 +++- lower/Cargo.toml | 7 +++++++ lower/src/lib.rs | 41 +++++++++++++++++++++++++++++++++++++++++ parser/src/lib.rs | 11 +++++++++++ vm/src/exec.rs | 12 +----------- vm/src/model.rs | 34 ++++++++++++++++++++++++++++++---- 9 files changed, 106 insertions(+), 32 deletions(-) create mode 100644 lower/Cargo.toml create mode 100644 lower/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index c49ca3c..847e7b6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -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" diff --git a/Cargo.toml b/Cargo.toml index 1e8b8fc..1ac8f72 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,6 +2,7 @@ members = [ "entry", "parser", + "lower", "compiler", "vm", ] diff --git a/examples/a.sial b/examples/a.sial index 1f643d4..802db1d 100644 --- a/examples/a.sial +++ b/examples/a.sial @@ -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)) \ No newline at end of file + print(add(double(17), succ(34))) +end \ No newline at end of file diff --git a/examples/sim.sial b/examples/sim.sial index c71fd03..f2b4455 100644 --- a/examples/sim.sial +++ b/examples/sim.sial @@ -1,3 +1,5 @@ fun main = do - println((\x -> x + 1)(9)) + 1 + |> \x -> x + 1 + |> \x -> println(x) end \ No newline at end of file diff --git a/lower/Cargo.toml b/lower/Cargo.toml new file mode 100644 index 0000000..5b7c85a --- /dev/null +++ b/lower/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "lower" +version = "0.1.0" +edition = "2021" + +[dependencies] +parser = { path = "../parser" } \ No newline at end of file diff --git a/lower/src/lib.rs b/lower/src/lib.rs new file mode 100644 index 0000000..75510ed --- /dev/null +++ b/lower/src/lib.rs @@ -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 { + 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); + } +} diff --git a/parser/src/lib.rs b/parser/src/lib.rs index fa64b5c..dd47c8b 100644 --- a/parser/src/lib.rs +++ b/parser/src/lib.rs @@ -532,6 +532,17 @@ pub fn parse( (ast, parse_error) } +pub fn parse_expr( + tokens: Vec>, + len: usize, +) -> (Option>, Vec>) { + 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, src: &str) { let report = Report::build(ReportKind::Error, (), e.span().start()); diff --git a/vm/src/exec.rs b/vm/src/exec.rs index 1c3b920..cc2a318 100644 --- a/vm/src/exec.rs +++ b/vm/src/exec.rs @@ -61,20 +61,10 @@ impl Executor { } fn get(&self, name: &str) -> Result { - // 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> { diff --git a/vm/src/model.rs b/vm/src/model.rs index bc730ef..c90ae49 100644 --- a/vm/src/model.rs +++ b/vm/src/model.rs @@ -49,14 +49,39 @@ impl Display for Value { #[derive(Clone, Debug, Eq, PartialEq)] pub struct Env { pub binds: FnvHashMap, + pub parent: Option>, } impl Env { pub fn new() -> Self { Self { binds: FnvHashMap::default(), + parent: None, } } + + pub fn new_with_parent(parent: Rc) -> Self { + Self { + binds: FnvHashMap::default(), + parent: Some(parent), + } + } + + pub fn get(&self, name: &str) -> Option { + // 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) -> Result, 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,