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"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "db6d7e329c562c5dfab7a46a2afabc8b987ab9a4834c9d1ca04dc54c1546cef8"
|
checksum = "db6d7e329c562c5dfab7a46a2afabc8b987ab9a4834c9d1ca04dc54c1546cef8"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "lower"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"parser",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "once_cell"
|
name = "once_cell"
|
||||||
version = "1.16.0"
|
version = "1.16.0"
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
members = [
|
members = [
|
||||||
"entry",
|
"entry",
|
||||||
"parser",
|
"parser",
|
||||||
|
"lower",
|
||||||
"compiler",
|
"compiler",
|
||||||
"vm",
|
"vm",
|
||||||
]
|
]
|
||||||
|
|
|
@ -1,24 +1,13 @@
|
||||||
fun succ x = do
|
fun succ x = do
|
||||||
let one = 1
|
let one = 1
|
||||||
let y = x + one in y
|
|
||||||
|
let y = x + one
|
||||||
|
in y
|
||||||
end
|
end
|
||||||
|
|
||||||
fun double x = x * 2
|
fun double x = x * 2
|
||||||
|
|
||||||
fun main = do
|
fun main = do
|
||||||
let add = \x y -> x + y in
|
let add = \x y -> x + y in
|
||||||
succ(34)
|
print(add(double(17), succ(34)))
|
||||||
|> \x -> add(34, x)
|
|
||||||
|> \x -> println(x)
|
|
||||||
|
|
||||||
let result = add(34, 35)
|
|
||||||
println(result)
|
|
||||||
end
|
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))
|
|
|
@ -1,3 +1,5 @@
|
||||||
fun main = do
|
fun main = do
|
||||||
println((\x -> x + 1)(9))
|
1
|
||||||
|
|> \x -> x + 1
|
||||||
|
|> \x -> println(x)
|
||||||
end
|
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)
|
(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) {
|
pub fn report(e: Simple<String>, src: &str) {
|
||||||
let report = Report::build(ReportKind::Error, (), e.span().start());
|
let report = Report::build(ReportKind::Error, (), e.span().start());
|
||||||
|
|
||||||
|
|
|
@ -61,20 +61,10 @@ impl Executor {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get(&self, name: &str) -> Result<Value, Error> {
|
fn get(&self, name: &str) -> Result<Value, Error> {
|
||||||
// Get from the current environment first
|
|
||||||
self.env
|
self.env
|
||||||
.borrow()
|
.borrow()
|
||||||
.binds
|
|
||||||
.get(name)
|
.get(name)
|
||||||
.cloned()
|
.ok_or_else(|| self.err(format!("unbound variable: {}", name).as_str()))
|
||||||
// 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()))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set(&mut self, name: &str, v: Value) -> Result<(), Error> {
|
fn set(&mut self, name: &str, v: Value) -> Result<(), Error> {
|
||||||
|
|
|
@ -49,14 +49,39 @@ impl Display for Value {
|
||||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||||
pub struct Env {
|
pub struct Env {
|
||||||
pub binds: FnvHashMap<String, Value>,
|
pub binds: FnvHashMap<String, Value>,
|
||||||
|
pub parent: Option<Rc<Self>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Env {
|
impl Env {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
binds: FnvHashMap::default(),
|
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)]
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||||
|
@ -73,16 +98,17 @@ impl Func {
|
||||||
|
|
||||||
pub fn run(self, args: Vec<Value>) -> Result<Vec<Value>, Error> {
|
pub fn run(self, args: Vec<Value>) -> Result<Vec<Value>, Error> {
|
||||||
// Create a new environment for the closure
|
// 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) {
|
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
|
// Execute the closure
|
||||||
let mut new_executor = Executor {
|
let mut new_executor = Executor {
|
||||||
stack: Vec::new(),
|
stack: Vec::new(),
|
||||||
env: new_env,
|
env: Rc::new(RefCell::new(closure_env)),
|
||||||
outer_env: Some(Rc::clone(&self.env)),
|
outer_env: Some(Rc::clone(&self.env)),
|
||||||
instrs: self.instrs,
|
instrs: self.instrs,
|
||||||
ip: 0,
|
ip: 0,
|
||||||
|
|
Loading…
Reference in a new issue