Skip to content

Commit

Permalink
Fix named let
Browse files Browse the repository at this point in the history
  • Loading branch information
maplant committed Dec 27, 2024
1 parent 7e4636a commit 9e0eba7
Show file tree
Hide file tree
Showing 3 changed files with 60 additions and 7 deletions.
37 changes: 31 additions & 6 deletions src/ast/parse.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use crate::{
continuation::Continuation,
env::Env,
env::{Env, VarRef},
error::RuntimeError,
expand::{SyntaxRule, Transformer},
gc::Gc,
Expand Down Expand Up @@ -579,8 +579,16 @@ async fn parse_let(
}
}

let new_env = env.push_lexical_contour(Gc::new(new_contour));
let body = Body::parse(body, &new_env, cont, span).await?;
let new_env = Gc::new(new_contour);
let new_exp_env = env.push_lexical_contour(new_env.clone());

if let Some(name) = name {
new_env
.write()
.def_local_var(name, Gc::new(Value::Undefined));
}

let mut ast_body = Body::parse(body, &new_exp_env, cont, span).await?;

let mut bindings: Vec<_> = compiled_bindings
.into_iter()
Expand All @@ -589,17 +597,34 @@ async fn parse_let(

// If this is a named let, add a binding for a procedure with the same
// body and args of the formals.
// This code is really bad, but I wanted to get this working
if let Some(name) = name {
let mut new_new_env = new_env.new_lexical_contour();
for (binding, _) in &bindings {
new_new_env.def_local_var(binding, Gc::new(Value::Undefined));
}
let new_new_exp_env = new_exp_env.push_lexical_contour(Gc::new(new_new_env));
let lambda = Lambda {
args: Formals::FixedArgs(bindings.iter().map(|(ident, _)| ident.clone()).collect()),
body: body.clone(),
body: Body::parse(body, &new_new_exp_env, cont, span).await?,
};
ast_body = Body {
forms: ArcSlice::from(
vec![AstNode::Expression(Expression::Set(Set {
var: Ref::Regular(VarRef::default().offset(bindings.len())),
val: Arc::new(Expression::Lambda(lambda)),
}))]
.into_iter()
.chain(ast_body.forms.iter().map(|(x, _)| x).cloned())
.collect::<Vec<_>>(),
),
};
bindings.push((name.clone(), Expression::Lambda(lambda)));
bindings.push((name.clone(), Expression::Undefined));
}

Ok(Let {
bindings: Arc::from(bindings),
body,
body: ast_body,
})
}

Expand Down
7 changes: 6 additions & 1 deletion src/env.rs
Original file line number Diff line number Diff line change
Expand Up @@ -214,7 +214,7 @@ pub enum EvalError<'e> {

/// Reference to a variable that is accessible via the current environment. Could be
/// local or non-local depending on the depth field.
#[derive(Debug, Copy, Clone, Trace)]
#[derive(Debug, Copy, Clone, Trace, Default)]
pub struct VarRef {
/// Number of up environments we need to traverse in order to reach the defining
/// scope of the variable. Variables with a depth of 0 are local.
Expand All @@ -238,6 +238,11 @@ impl VarRef {
}
}

pub fn offset(mut self, offset: usize) -> Self {
self.offset += offset;
self
}

pub fn fetch(&self, env: &Gc<Env>) -> Gc<Value> {
env.read().fetch_var(*self)
}
Expand Down
23 changes: 23 additions & 0 deletions tests/r6rs.scm
Original file line number Diff line number Diff line change
Expand Up @@ -201,3 +201,26 @@
;; ((x y) (values a b)))
;; (list a b x y)))
;; (x y x y))

(assert-eq (let loop ((n 1))
(if (> n 10)
'()
(cons n (loop (+ n 1)))))
'(1 2 3 4 5 6 7 8 9 10))

(define-syntax loop
(lambda (x)
(syntax-case x ()
[(k e ...)
(with-syntax
([break (datum->syntax #'k 'break)])
#'(call-with-current-continuation
(lambda (break)
(let f () e ... (f)))))])))

(assert-eq (let ((n 3) (ls '()))
(loop
(if (= n 0) (break ls))
(set! ls (cons 'a ls))
(set! n (- n 1))))
'(a a a))

0 comments on commit 9e0eba7

Please sign in to comment.