From 4b587f11ababa9c222b6884b45bd9b3578ca9912 Mon Sep 17 00:00:00 2001 From: Ava Hahn Date: Fri, 3 Mar 2023 14:29:53 -0800 Subject: [PATCH] finished circuit form Signed-off-by: Ava Hahn --- Readme.org | 5 -- src/config.rs | 9 ++-- src/stl.rs | 12 ++--- src/stl/boolean.rs | 18 ++++--- src/stl/control.rs | 98 ++++++++++++++++++++++++++++----------- tests/test_lib_bools.rs | 14 ++++-- tests/test_lib_control.rs | 41 ++++++++++++++++ 7 files changed, 144 insertions(+), 53 deletions(-) diff --git a/Readme.org b/Readme.org index d14f292..91d3883 100644 --- a/Readme.org +++ b/Readme.org @@ -136,11 +136,6 @@ This contains any executable target of this project. Notably the main shell file Note: this section will not show the status of each item unless you are viewing it with a proper orgmode viewer. Note: this section only tracks the state of incomplete TODO items. Having everything on here would be cluttered. -*** TODO Control Flow -**** DONE if form -**** DONE let 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 *** TODO Custom ast pretty print diff --git a/src/config.rs b/src/config.rs index f5b900f..516273e 100644 --- a/src/config.rs +++ b/src/config.rs @@ -59,11 +59,10 @@ 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() - }) + ")"; + let mut config_document = fs::read_to_string(filename).unwrap_or_else(|err: io::Error| { + eprintln!("{}", err); + "".to_string() + }) + ")"; config_document = "(".to_string() + &config_document; diff --git a/src/stl.rs b/src/stl.rs index f49b93d..92374f5 100644 --- a/src/stl.rs +++ b/src/stl.rs @@ -22,8 +22,8 @@ use std::env; use std::rc::Rc; pub mod append; -pub mod control; pub mod boolean; +pub mod control; //pub mod str; /// static_stdlib @@ -107,7 +107,7 @@ pub fn static_stdlib(syms: &mut SymTable) -> Result<(), String> { args: Args::Infinite, conditional_branches: false, value: ValueType::Internal(Rc::new(boolean::bool_and_callback)), - } + }, ); syms.insert( @@ -117,7 +117,7 @@ pub fn static_stdlib(syms: &mut SymTable) -> Result<(), String> { args: Args::Infinite, conditional_branches: false, value: ValueType::Internal(Rc::new(boolean::bool_or_callback)), - } + }, ); syms.insert( @@ -127,7 +127,7 @@ pub fn static_stdlib(syms: &mut SymTable) -> Result<(), String> { args: Args::Strict(vec![Type::Bool]), conditional_branches: false, value: ValueType::Internal(Rc::new(boolean::bool_not_callback)), - } + }, ); syms.insert( @@ -137,7 +137,7 @@ pub fn static_stdlib(syms: &mut SymTable) -> Result<(), String> { args: Args::Infinite, conditional_branches: false, value: ValueType::Internal(Rc::new(boolean::bool_iseq_callback)), - } + }, ); syms.insert( @@ -147,7 +147,7 @@ pub fn static_stdlib(syms: &mut SymTable) -> Result<(), String> { args: Args::Lazy(1), conditional_branches: true, value: ValueType::Internal(Rc::new(boolean::bool_toggle_callback)), - } + }, ); Ok(()) diff --git a/src/stl/boolean.rs b/src/stl/boolean.rs index 8bbbd0d..12e714b 100644 --- a/src/stl/boolean.rs +++ b/src/stl/boolean.rs @@ -65,28 +65,32 @@ pub fn bool_not_callback(ast: &Seg, _syms: &mut SymTable) -> Result pub fn bool_iseq_callback(ast: &Seg, _syms: &mut SymTable) -> Result { let head_ctr_ref = &*ast.car; - Ok(Ctr::Bool(ast.circuit(&mut |arg: &Ctr| -> bool {arg == head_ctr_ref}))) + Ok(Ctr::Bool( + ast.circuit(&mut |arg: &Ctr| -> bool { arg == head_ctr_ref }), + )) } 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(); + if let Ctr::Symbol(ref s) = *ast.car { + var_name = s.clone(); } else { - return Err("argument to toggle should be a symbol".to_string()) + 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")); + 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()) + return Err("can only toggle a boolean".to_string()); } } else { syms.insert(var_name, sym); - return Err("cannot toggle a function".to_string()) + return Err("cannot toggle a function".to_string()); } syms.insert(var_name, sym); diff --git a/src/stl/control.rs b/src/stl/control.rs index 4d59418..e260570 100644 --- a/src/stl/control.rs +++ b/src/stl/control.rs @@ -17,7 +17,7 @@ use crate::eval::eval; use crate::segment::{Ctr, Seg}; -use crate::sym::{SymTable, Symbol, ValueType, Args}; +use crate::sym::{Args, SymTable, Symbol, ValueType}; pub fn if_callback(ast: &Seg, syms: &mut SymTable) -> Result { let cond: bool; @@ -32,10 +32,7 @@ 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 - )? { + 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()); @@ -95,13 +92,13 @@ pub fn let_callback(ast: &Seg, syms: &mut SymTable) -> Result { if let Ctr::Seg(ref locals_form_list) = *ast.car { locals_form = locals_form_list; } else { - return Err("first element of let form must contain local variables".to_string()) + return Err("first element of let form must contain local variables".to_string()); } if let Ctr::Seg(ref eval_forms_head) = *ast.cdr { eval_forms = eval_forms_head; } else { - return Err("let form should contain one or more elements to evaluate".to_string()) + return Err("let form should contain one or more elements to evaluate".to_string()); } // process locals forms @@ -125,27 +122,30 @@ pub fn let_callback(ast: &Seg, syms: &mut SymTable) -> Result { } if let Err(e) = var_val_res { eprintln!("failed to evaluate definition of {}: {}", name, e); - return false + return false; } - localsyms.insert(name.clone(), Symbol { - name: name.clone(), - args: Args::None, - conditional_branches: false, - value: ValueType::VarForm(Box::new(*var_val_res.unwrap().clone())), - }); + localsyms.insert( + name.clone(), + Symbol { + name: name.clone(), + args: Args::None, + conditional_branches: false, + value: ValueType::VarForm(Box::new(*var_val_res.unwrap().clone())), + }, + ); } } else { eprintln!("improper declaration of {}: not a list", var_decl); - return false + return false; } } else { eprintln!("improper declaration form: {}", var_decl); - return false + return false; } true }) { - return Err("local variable declaration failure".to_string()) + return Err("local variable declaration failure".to_string()); } let mut result: Box = Box::new(Ctr::None); @@ -167,13 +167,13 @@ pub fn let_callback(ast: &Seg, syms: &mut SymTable) -> Result { if let Err(e) = res { eprintln!("error evaluating let form: {}", e); - return false + return false; } result = res.unwrap().clone(); true }) { - return Err("evaluation failure".to_string()) + return Err("evaluation failure".to_string()); } Ok((*result).clone()) @@ -200,7 +200,7 @@ pub fn while_callback(ast: &Seg, syms: &mut SymTable) -> Result { if let Ctr::Seg(ref eval) = *ast.cdr { eval_bodies_head = eval; } else { - return Err("expected N more bodies in while form".to_string()) + return Err("expected N more bodies in while form".to_string()); } loop { @@ -208,15 +208,19 @@ pub fn while_callback(ast: &Seg, syms: &mut SymTable) -> Result { 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} + if !b { + break; + } } } else { panic!() } } else if let Ctr::Bool(b) = cond_res { - if !b {break} + if !b { + break; + } } else { - return Err("first body of while form should evaluate to a bool".to_string()) + return Err("first body of while form should evaluate to a bool".to_string()); } if !eval_bodies_head.circuit(&mut |body: &Ctr| -> bool { @@ -242,7 +246,49 @@ pub fn while_callback(ast: &Seg, syms: &mut SymTable) -> Result { /* (circuit makes_a_bool makes_a_bool makes_a_bool makes_a_bool ... ) */ -pub fn circuit_callback(_ast: &Seg, _syms: &mut SymTable) -> Result { - todo!() -} +pub fn circuit_callback(ast: &Seg, syms: &mut SymTable) -> Result { + let mut cursor = 0; + let mut error: String = String::new(); + let result = ast.circuit(&mut |form: &Ctr| -> bool { + cursor += 1; + let operand: &Seg; + let mut expand_eval_res = false; + let outer_scope_seg: Seg; + if let Ctr::Seg(ref s) = form { + operand = s; + } else { + outer_scope_seg = Seg::from_mono(Box::new(form.clone())); + operand = &outer_scope_seg; + expand_eval_res = true; + } + let eval_result = eval(operand, syms); + match eval_result { + Err(s) => error = format!("eval failed at form {cursor}: {s}"), + Ok(s) => match *s { + Ctr::Bool(b) => return b, + + Ctr::Seg(s) if expand_eval_res => { + if let Ctr::Bool(b) = *s.car { + return b; + } else { + error = "impossible condition in circuit form".to_string(); + } + } + + _ => error = format!("{cursor} form did not evaluate to a boolean"), + }, + } + + false + }); + + if !result && !error.is_empty() { + Err(format!("circuit stopped at form {cursor}: {error}")) + } else { + if !result { + eprintln!("circuit stopped at form {cursor}"); + } + Ok(Ctr::Bool(result)) + } +} diff --git a/tests/test_lib_bools.rs b/tests/test_lib_bools.rs index a3f2151..03d91e5 100644 --- a/tests/test_lib_bools.rs +++ b/tests/test_lib_bools.rs @@ -160,7 +160,7 @@ mod bool_lib_tests { 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{ + if let Ctr::Bool(ref b) = *s.car { assert_eq!(false, *b) } else { panic!() @@ -171,7 +171,7 @@ mod bool_lib_tests { 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{ + if let Ctr::Bool(ref b) = *s.car { assert_eq!(true, *b) } else { panic!() @@ -197,7 +197,10 @@ mod bool_lib_tests { 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()); + 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()); @@ -227,7 +230,10 @@ mod bool_lib_tests { 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()); + 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 { diff --git a/tests/test_lib_control.rs b/tests/test_lib_control.rs index 0e130e6..4f35cf2 100644 --- a/tests/test_lib_control.rs +++ b/tests/test_lib_control.rs @@ -213,4 +213,45 @@ mod control_lib_tests { eval(&while_tree, &mut syms).unwrap(); eval(&check_tree, &mut syms).unwrap(); } + + #[test] + fn test_circuit_basic() { + let document = "(if (circuit true (and true true) true) (def result 'passed') ())"; + let test = "result"; + + let doc_tree = lex(&document.to_string()).unwrap(); + let test_tree = lex(&test.to_string()).unwrap(); + + let mut syms = SymTable::new(); + static_stdlib(&mut syms).unwrap(); + dynamic_stdlib(&mut syms).unwrap(); + + eval(&doc_tree, &mut syms).unwrap(); + let res = eval(&test_tree, &mut syms); + println!("{:#?}", res); + res.unwrap(); + } + + #[test] + fn test_circuit_fail() { + let document = "(if (circuit true (and false true) true) (def result 'passed') ())"; + let test = "result"; + + let doc_tree = lex(&document.to_string()).unwrap(); + let test_tree = lex(&test.to_string()).unwrap(); + + let mut syms = SymTable::new(); + static_stdlib(&mut syms).unwrap(); + dynamic_stdlib(&mut syms).unwrap(); + + eval(&doc_tree, &mut syms).unwrap(); + if let Err(s) = eval(&test_tree, &mut syms) { + assert_eq!( + s, + "error in call to result: undefined symbol: result".to_string() + ); + } else { + panic!(); + } + } }