From 2dfe73de6b315e8634ac2705b74c9d3260f6246a Mon Sep 17 00:00:00 2001 From: Ava Hahn Date: Fri, 17 Mar 2023 12:01:43 -0700 Subject: [PATCH] variables defined using def in a let statement now escape local scope test added as well. --- src/stl/control.rs | 13 +++++++++++++ src/sym.rs | 4 ++++ tests/test_lib_control.rs | 20 ++++++++++++++++++++ 3 files changed, 37 insertions(+) diff --git a/src/stl/control.rs b/src/stl/control.rs index 0b94f30..6a6509c 100644 --- a/src/stl/control.rs +++ b/src/stl/control.rs @@ -111,6 +111,7 @@ Since the call to some-func is the final form, its value is returned."; pub fn let_callback(ast: &Seg, syms: &mut SymTable) -> Result { let mut localsyms = syms.clone(); + let mut locals = vec![]; let locals_form: &Seg; let eval_forms: &Seg; if let Ctr::Seg(ref locals_form_list) = *ast.car { @@ -156,6 +157,7 @@ pub fn let_callback(ast: &Seg, syms: &mut SymTable) -> Result { &Seg::from_mono(Box::new(*var_val_res.unwrap().clone())), None), ); + locals.push(name.clone()); } } else if let Ctr::None = *var_form.car { // nothing to declare @@ -201,6 +203,17 @@ pub fn let_callback(ast: &Seg, syms: &mut SymTable) -> Result { return Err("evaluation failure".to_string()); } + // we need to get any var declared from within the let eval forms + // those need to exist in the outside context + for i in localsyms.iter() { + if !locals.contains(i.0) && !syms.contains_key(i.0) { + syms.insert( + i.0.clone(), + i.1.clone(), + ); + } + } + Ok((*result).clone()) } diff --git a/src/sym.rs b/src/sym.rs index e8bf507..2a17285 100644 --- a/src/sym.rs +++ b/src/sym.rs @@ -79,6 +79,10 @@ impl SymTable { self.0.get(arg) } + pub fn contains_key(&self, arg: &String) -> bool { + self.0.contains_key(arg) + } + pub fn insert(&mut self, k: String, v: Symbol) -> Option { self.0.insert(k, v) } diff --git a/tests/test_lib_control.rs b/tests/test_lib_control.rs index 13e2f9b..56defb5 100644 --- a/tests/test_lib_control.rs +++ b/tests/test_lib_control.rs @@ -65,6 +65,26 @@ mod control_lib_tests { ); } + #[test] + fn test_let_def_escapes_locals() { + let document1 = "(let ( + (temp 'hello') + (temp (concat temp ' ' 'world'))) + (def global '' temp))"; + let document2 = "global"; + let result = "('hello world')"; + let mut syms = SymTable::new(); + static_stdlib(&mut syms).unwrap(); + dynamic_stdlib(&mut syms).unwrap(); + eval(&lex(&document1.to_string()).unwrap(), &mut syms).unwrap(); + assert_eq!( + *eval(&lex(&document2.to_string()).unwrap(), &mut syms) + .unwrap() + .to_string(), + result.to_string(), + ); + } + #[test] fn test_let_multibody_evals() { let document = "(let ((temp '1')) temp (cons () temp '2'))";