diff --git a/Readme.org b/Readme.org index d13758d..8d33218 100644 --- a/Readme.org +++ b/Readme.org @@ -160,7 +160,7 @@ Will need a concatenate function for tables **** TODO xor **** DONE no **** TODO eq? -**** TODO toggle +**** DONE toggle *** TODO string operations **** TODO typecast (string) **** TODO contains diff --git a/src/stl.rs b/src/stl.rs index ee9dba0..1f534dd 100644 --- a/src/stl.rs +++ b/src/stl.rs @@ -231,7 +231,7 @@ fn _store_callback(ast: &Seg, syms: &mut SymTable, env_cfg: bool) -> Result Result { let mut type_error = false; @@ -71,6 +71,27 @@ pub fn bool_iseq_callback(_ast: &Seg, _syms: &mut SymTable) -> Result Result { - todo!() +pub fn bool_toggle_callback(ast: &Seg, syms: &mut SymTable) -> Result { + let var_name: String; + if let Ctr::Symbol(ref s) = *ast.car { + var_name = s.clone(); + } else { + return Err("argument to toggle should be a symbol".to_string()) + } + + let mut sym = syms.remove(&var_name).expect(&format!("symbol {var_name} is not defined")); + if let ValueType::VarForm(ref var) = sym.value { + if let Ctr::Bool(ref b) = **var { + sym.value = ValueType::VarForm(Box::new(Ctr::Bool(!b))); + } else { + syms.insert(var_name, sym); + return Err("can only toggle a boolean".to_string()) + } + } else { + syms.insert(var_name, sym); + return Err("cannot toggle a function".to_string()) + } + + syms.insert(var_name, sym); + Ok(Ctr::None) } diff --git a/src/stl/control.rs b/src/stl/control.rs index f6bb064..23d841e 100644 --- a/src/stl/control.rs +++ b/src/stl/control.rs @@ -116,9 +116,6 @@ pub fn let_callback(ast: &Seg, syms: &mut SymTable) -> Result { return false } - let temp = var_val_res.clone().unwrap(); - println!("dbg: {}", temp); - localsyms.insert(name.clone(), Symbol { name: name.clone(), args: Args::None, diff --git a/tests/test_lib_bools.rs b/tests/test_lib_bools.rs index 0bc9af0..ce64efd 100644 --- a/tests/test_lib_bools.rs +++ b/tests/test_lib_bools.rs @@ -141,4 +141,101 @@ mod bool_lib_tests { assert!(false); } } + + #[test] + fn test_toggle_a_bool() { + let document = "(def tester true)"; + let change = "(toggle tester)"; + let check = "(tester)"; + + let mut syms = SymTable::new(); + static_stdlib(&mut syms).unwrap(); + dynamic_stdlib(&mut syms).unwrap(); + + let doc_tree = lex(&document.to_string()).unwrap(); + let change_tree = lex(&change.to_string()).unwrap(); + let check_tree = lex(&check.to_string()).unwrap(); + + eval(&doc_tree, &mut syms).unwrap(); + eval(&change_tree, &mut syms).unwrap(); + + if let Ctr::Seg(ref s) = *eval(&check_tree, &mut syms).unwrap() { + if let Ctr::Bool(ref b) = *s.car{ + assert_eq!(false, *b) + } else { + panic!() + } + } else { + panic!() + } + + eval(&change_tree, &mut syms).unwrap(); + if let Ctr::Seg(ref s) = *eval(&check_tree, &mut syms).unwrap() { + if let Ctr::Bool(ref b) = *s.car{ + assert_eq!(true, *b) + } else { + panic!() + } + } else { + panic!() + } + } + + #[test] + fn test_toggle_errors_dont_lose_vars() { + let document = "(def tester 'oops')"; + let change = "(toggle tester)"; + let check = "(tester)"; + + let mut syms = SymTable::new(); + static_stdlib(&mut syms).unwrap(); + dynamic_stdlib(&mut syms).unwrap(); + + let doc_tree = lex(&document.to_string()).unwrap(); + let change_tree = lex(&change.to_string()).unwrap(); + let check_tree = lex(&check.to_string()).unwrap(); + + eval(&doc_tree, &mut syms).unwrap(); + if let Err(s) = eval(&change_tree, &mut syms) { + assert_eq!(s, "error in call to toggle: can only toggle a boolean".to_string()); + let intermediate = *eval(&check_tree, &mut syms).unwrap(); + if let Ctr::Seg(ref s) = intermediate { + assert_eq!(s.to_string(), "('oops')".to_string()); + } else { + eprintln!("did not expect: {}", intermediate); + panic!() + } + } else { + eprintln!("shouldn't have succeeded!"); + panic!() + } + } + + #[test] + fn test_toggle_errors_dont_lose_funcs() { + let document = "(def tester (oops) oops)"; + let change = "(toggle tester)"; + let check = "(tester '1')"; + + let mut syms = SymTable::new(); + static_stdlib(&mut syms).unwrap(); + dynamic_stdlib(&mut syms).unwrap(); + + let doc_tree = lex(&document.to_string()).unwrap(); + let change_tree = lex(&change.to_string()).unwrap(); + let check_tree = lex(&check.to_string()).unwrap(); + + eval(&doc_tree, &mut syms).unwrap(); + if let Err(s) = eval(&change_tree, &mut syms) { + assert_eq!(s, "error in call to toggle: cannot toggle a function".to_string()); + if let Ctr::String(ref s) = *eval(&check_tree, &mut syms).unwrap() { + assert_eq!(*s, "1".to_string()); + } else { + panic!() + } + } else { + eprintln!("shouldn't have succeeded!"); + panic!() + } + } } diff --git a/tests/test_vars.rs b/tests/test_vars.rs index 062ce35..eb5a800 100644 --- a/tests/test_vars.rs +++ b/tests/test_vars.rs @@ -40,6 +40,43 @@ mod var_lib_tests { } } + #[test] + fn test_func_def_and_lookup() { + let doc1 = "(def test (hello) hello)"; + let doc2 = "(test '1')"; + let result = "1"; + + let mut syms = SymTable::new(); + static_stdlib(&mut syms).unwrap(); + dynamic_stdlib(&mut syms).unwrap(); + + if let Ok(tree) = lex(&doc1.to_string()) { + let eval_result = *eval(&tree, &mut syms).unwrap(); + if let Ctr::None = eval_result { + // pass + } else { + eprintln!("bad: {eval_result}"); + assert!(false); + } + } else { + eprintln!("couldn't lex doc1"); + assert!(false); + } + + if let Ok(tree) = lex(&doc2.to_string()) { + let eval_result = *eval(&tree, &mut syms).unwrap(); + if let Ctr::String(ref i) = eval_result { + assert_eq!(i.to_string(), result); + } else { + eprintln!("bad: {eval_result}"); + assert!(false); + } + } else { + eprintln!("couldn't lex doc2"); + assert!(false); + } + } + #[test] fn test_variable_def_redef_and_lookup() { let doc1 = "(def test 1)";