mirror of
https://github.com/azur1s/bobbylisp.git
synced 2024-10-16 02:37:40 -05:00
Fix closure not capturing outer environment
This commit is contained in:
parent
dfc24c5444
commit
6ebe1bc7ae
7
Cargo.lock
generated
7
Cargo.lock
generated
|
@ -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"
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
members = [
|
||||
"entry",
|
||||
"parser",
|
||||
"lower",
|
||||
"compiler",
|
||||
"vm",
|
||||
]
|
||||
|
|
|
@ -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
|
|
@ -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
7
lower/Cargo.toml
Normal 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
41
lower/src/lib.rs
Normal 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);
|
||||
}
|
||||
}
|
|
@ -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());
|
||||
|
||||
|
|
|
@ -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> {
|
||||
|
|
|
@ -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,
|
||||
|
|
Loading…
Reference in a new issue