diff --git a/Readme.org b/Readme.org index 33a8b0d..d14f292 100644 --- a/Readme.org +++ b/Readme.org @@ -31,10 +31,16 @@ https://matrix.to/#/#vomitorium:matrix.sunnypup.io **** TODO circuit *** TODO Defining variables and functions **** TODO Anatomy +**** TODO Naming conventions **** TODO Undefining variables and functions +*** TODO Easy patterns +**** TODO while-let combo +**** TODO main loop application *** TODO Builtin functions *** TODO Documentation -(help function +**** TODO Tests +**** TODO Help function +**** TODO Snippets directory ** Configuration By default Relish will read from ~/.relishrc for configuration, but the default shell will also accept a filename from the RELISH_CFG_FILE environment variable. @@ -133,7 +139,7 @@ Note: this section only tracks the state of incomplete TODO items. Having everyt *** TODO Control Flow **** DONE if form **** DONE let form -**** TODO while form +**** DONE while form **** TODO circuit form *** TODO Clean up tests, simplify, convert some to unit tests, mention tests in Readme as docs *** TODO Document all internal/builtin functions in the rustiest way possible diff --git a/src/config.rs b/src/config.rs index 5b37bdf..f5b900f 100644 --- a/src/config.rs +++ b/src/config.rs @@ -61,7 +61,7 @@ pub fn configure(filename: String, syms: &mut SymTable) -> Result<(), String> { let mut config_document = fs::read_to_string(filename) .unwrap_or_else(|err: io::Error| { - eprintln!("{}", err.to_string()); + eprintln!("{}", err); "".to_string() }) + ")"; diff --git a/src/stl.rs b/src/stl.rs index 040a4c8..f49b93d 100644 --- a/src/stl.rs +++ b/src/stl.rs @@ -80,6 +80,26 @@ pub fn static_stdlib(syms: &mut SymTable) -> Result<(), String> { }, ); + syms.insert( + "while".to_string(), + Symbol { + name: String::from("while"), + args: Args::Infinite, + conditional_branches: true, + value: ValueType::Internal(Rc::new(control::while_callback)), + }, + ); + + syms.insert( + "circuit".to_string(), + Symbol { + name: String::from("circuit"), + args: Args::Infinite, + conditional_branches: true, + value: ValueType::Internal(Rc::new(control::circuit_callback)), + }, + ); + syms.insert( "and".to_string(), Symbol { diff --git a/src/stl/control.rs b/src/stl/control.rs index 23d841e..4d59418 100644 --- a/src/stl/control.rs +++ b/src/stl/control.rs @@ -21,6 +21,7 @@ use crate::sym::{SymTable, Symbol, ValueType, Args}; pub fn if_callback(ast: &Seg, syms: &mut SymTable) -> Result { let cond: bool; + println!("Y: {}", *ast.car); match *ast.car { Ctr::Seg(ref cond_form) => { if let Ctr::Bool(cond_from_eval) = *eval(cond_form, syms)? { @@ -30,6 +31,17 @@ pub fn if_callback(ast: &Seg, syms: &mut SymTable) -> Result { } } + Ctr::Symbol(ref cond_name) => { + if let Ctr::Bool(cond_from_eval) = *syms.call_symbol( + cond_name, + &Seg::new(), false + )? { + cond = cond_from_eval; + } else { + return Err("first argument to if must evaluate to be a boolean".to_string()); + } + } + Ctr::Bool(cond_from_car) => cond = cond_from_car, _ => return Err("first argument to if must evaluate to be a boolean".to_string()), } @@ -167,10 +179,70 @@ pub fn let_callback(ast: &Seg, syms: &mut SymTable) -> Result { Ok((*result).clone()) } -pub fn while_callback(_ast: &Seg, _syms: &mut SymTable) -> Result { - todo!() +/* +(while (cond) evalme evalme evalme evalme ... ) + */ +pub fn while_callback(ast: &Seg, syms: &mut SymTable) -> Result { + let eval_cond: &Seg; + let outer_maybe: Seg; + let eval_bodies_head: &Seg; + let mut unwrap = false; + let mut result: Result, String> = Ok(Box::new(Ctr::None)); + + if let Ctr::Seg(ref cond) = *ast.car { + eval_cond = cond; + } else { + outer_maybe = Seg::from_mono(ast.car.clone()); + eval_cond = &outer_maybe; + unwrap = true; + } + + if let Ctr::Seg(ref eval) = *ast.cdr { + eval_bodies_head = eval; + } else { + return Err("expected N more bodies in while form".to_string()) + } + + loop { + let cond_res = *eval(eval_cond, syms)?; + if unwrap { + if let Ctr::Seg(ref cond_res_inner) = cond_res { + if let Ctr::Bool(ref b) = *cond_res_inner.car { + if !b {break} + } + } else { + panic!() + } + } else if let Ctr::Bool(b) = cond_res { + if !b {break} + } else { + return Err("first body of while form should evaluate to a bool".to_string()) + } + + if !eval_bodies_head.circuit(&mut |body: &Ctr| -> bool { + let outer_scope_seg: Seg; + let eval_arg: &Seg; + if let Ctr::Seg(ref eval_body) = *body { + eval_arg = eval_body; + } else { + outer_scope_seg = Seg::from_mono(Box::new(body.clone())); + eval_arg = &outer_scope_seg; + } + + result = eval(eval_arg, syms); + result.is_ok() + }) { + return Err(result.err().unwrap()); + } + } + + Ok(*(result.unwrap()).clone()) } +/* +(circuit makes_a_bool makes_a_bool makes_a_bool makes_a_bool ... ) +*/ pub fn circuit_callback(_ast: &Seg, _syms: &mut SymTable) -> Result { todo!() } + diff --git a/tests/test_lib_control.rs b/tests/test_lib_control.rs index 2727399..0e130e6 100644 --- a/tests/test_lib_control.rs +++ b/tests/test_lib_control.rs @@ -131,4 +131,86 @@ mod control_lib_tests { assert!(false); } } + + #[test] + fn test_while_basic() { + let switch_dec = "(def switch true)"; + // if prev is true, switch looped once and only once + // else prev will have a problematic type + let while_loop = " + (while switch + (def prev switch) + (toggle switch) + (if switch + (def prev) + ()))"; + let test_check = "prev"; + + let switch_tree = lex(&switch_dec.to_string()).unwrap(); + let while_tree = lex(&while_loop.to_string()).unwrap(); + let check_tree = lex(&test_check.to_string()).unwrap(); + + let mut syms = SymTable::new(); + static_stdlib(&mut syms).unwrap(); + dynamic_stdlib(&mut syms).unwrap(); + + eval(&switch_tree, &mut syms).unwrap(); + eval(&while_tree, &mut syms).unwrap(); + eval(&check_tree, &mut syms).unwrap(); + } + + #[test] + fn test_while_eval_cond() { + let switch_dec = "(def switch true)"; + // if prev is true, switch looped once and only once + // else prev will have a problematic type + let while_loop = " + (while (or switch switch) + (def prev switch) + (toggle switch) + (if switch + (def prev) + ()))"; + let test_check = "prev"; + + let switch_tree = lex(&switch_dec.to_string()).unwrap(); + let while_tree = lex(&while_loop.to_string()).unwrap(); + let check_tree = lex(&test_check.to_string()).unwrap(); + + let mut syms = SymTable::new(); + static_stdlib(&mut syms).unwrap(); + dynamic_stdlib(&mut syms).unwrap(); + + eval(&switch_tree, &mut syms).unwrap(); + eval(&while_tree, &mut syms).unwrap(); + eval(&check_tree, &mut syms).unwrap(); + } + + #[test] + fn test_while_2_iter() { + let additional = "(def sw1 true)"; + let switch_dec = "(def sw2 true)"; + // while should loop twice and define result + let while_loop = " + (while sw1 + (toggle sw2) + (if (and sw1 sw2) + (def sw1 false) + (def result 'yay')))"; + let test_check = "result"; + + let another_tree = lex(&additional.to_string()).unwrap(); + let switch_tree = lex(&switch_dec.to_string()).unwrap(); + let while_tree = lex(&while_loop.to_string()).unwrap(); + let check_tree = lex(&test_check.to_string()).unwrap(); + + let mut syms = SymTable::new(); + static_stdlib(&mut syms).unwrap(); + dynamic_stdlib(&mut syms).unwrap(); + + eval(&another_tree, &mut syms).unwrap(); + eval(&switch_tree, &mut syms).unwrap(); + eval(&while_tree, &mut syms).unwrap(); + eval(&check_tree, &mut syms).unwrap(); + } }