From dc6342bc74cc5e9df92d9c0f7d1098b079e3d40f Mon Sep 17 00:00:00 2001 From: Ava Hahn Date: Sun, 5 Mar 2023 22:18:49 -0800 Subject: [PATCH] Several changes, see commit msg * clean up all tests * bugfix for zero value functions, and test * removed expand function, put in snippets * added doc strings to Symbol type * added doc strings to symbol declarations * implemented display for Args type * wrote a help function * wrote docstrings for all builtins and config vars --- Readme.org | 12 +- snippets/curiosity_expand_callback.rs | 7 + src/config.rs | 16 +- src/eval.rs | 5 +- src/segment.rs | 4 +- src/stl.rs | 260 ++++++++++++++++------- src/stl/append.rs | 8 - src/stl/control.rs | 3 +- src/sym.rs | 29 ++- tests/test_eval.rs | 104 ++-------- tests/test_func.rs | 286 +++++++++++--------------- tests/test_lex.rs | 157 +++++--------- tests/test_lib_append.rs | 58 ++---- tests/test_lib_bools.rs | 125 ++++------- tests/test_lib_control.rs | 131 ++++-------- tests/test_vars.rs | 47 ++++- 16 files changed, 575 insertions(+), 677 deletions(-) create mode 100644 snippets/curiosity_expand_callback.rs diff --git a/Readme.org b/Readme.org index 91d3883..d3ea378 100644 --- a/Readme.org +++ b/Readme.org @@ -136,13 +136,12 @@ 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 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 *** TODO Help function -**** TODO add doc string to function form -**** TODO write doc strings to all symbols -**** TODO help function outputs name and help doc +**** DONE add doc string to function form +**** DONE write doc strings to all symbols +**** DONE pretty printer for help doc +**** DONE help function outputs name, args, and help doc **** TODO help function outputs current value or function form pretty printed *** TODO Eval function *** TODO Input function @@ -152,7 +151,7 @@ Pull/Refactor the logic out of the configure functions. Optionally return a list of new variables and/or functions? Will need a concatenate function for tables *** TODO Main shell calls Load function on arg and exits -*** TODO Wrapping errors as they return from eval and call_sym +*** TODO Can enter multiple lines of text, with formatting in repl *** TODO arithmetic operations **** TODO typecast (int) **** TODO typecast (float) @@ -182,6 +181,7 @@ Will need a concatenate function for tables **** TODO Optional form of process which allows fd redirecting **** TODO Foreground process TTY **** TODO Background processes +**** TODO Update config env var docstring with functions loaded or unloaded *** TODO list operations **** DONE append **** DONE expand diff --git a/snippets/curiosity_expand_callback.rs b/snippets/curiosity_expand_callback.rs new file mode 100644 index 0000000..3267788 --- /dev/null +++ b/snippets/curiosity_expand_callback.rs @@ -0,0 +1,7 @@ +pub fn expand_callback(ast: &Seg, _syms: &mut SymTable) -> Result { + if let Ctr::Seg(_) = *ast.car { + Ok(*ast.car.clone()) + } else { + Err("non list passed to expand!".to_string()) + } +} diff --git a/src/config.rs b/src/config.rs index 516273e..8b563d1 100644 --- a/src/config.rs +++ b/src/config.rs @@ -29,15 +29,19 @@ fn prompt_default_callback(_: &Seg, _: &mut SymTable) -> Result { /* loads defaults, evaluates config script */ pub fn configure(filename: String, syms: &mut SymTable) -> Result<(), String> { - syms.insert( + /*syms.insert( "CFG_RELISH_POSIX".to_string(), Symbol { name: String::from("CFG_RELISH_POSIX"), args: Args::None, conditional_branches: false, + docs: "variable holding whether or not POSIX job control functions are to be loaded. +checked at shell startup by configuration daemon. not used afterwards. + +default value: not set".to_string(), value: ValueType::VarForm(Box::new(Ctr::String("0".to_string()))), }, - ); + );*/ syms.insert( "CFG_RELISH_ENV".to_string(), @@ -45,6 +49,12 @@ pub fn configure(filename: String, syms: &mut SymTable) -> Result<(), String> { name: String::from("CFG_RELISH_ENV"), args: Args::None, conditional_branches: false, + docs: "variable holding whether or not vars and other symbols should be linked to process environment variables. +If set/defined all calls to def will result in additions or subtractions from user environment variables. +checked at shell startup by configuration daemon. not used afterwards. + +default value: 1 (set) +".to_string(), value: ValueType::VarForm(Box::new(Ctr::String("1".to_string()))), }, ); @@ -55,6 +65,8 @@ pub fn configure(filename: String, syms: &mut SymTable) -> Result<(), String> { name: String::from("default relish prompt"), args: Args::None, conditional_branches: false, + docs: "function called to output prompt. this function is called with no arguments. +default value ()".to_string(), value: ValueType::Internal(Rc::new(prompt_default_callback)), }, ); diff --git a/src/eval.rs b/src/eval.rs index d34a05f..c6f7c7d 100644 --- a/src/eval.rs +++ b/src/eval.rs @@ -39,10 +39,7 @@ pub fn eval(ast: &Seg, syms: &mut SymTable) -> Result, String> { let mut none = false; while !none { match &**arg_car { - Ctr::Seg(ref inner) => match eval(inner, syms) { - Ok(res) => car = res, - Err(e) => return Err(format!("evaluation error: {}", e)), - }, + Ctr::Seg(ref inner) => car = eval(inner, syms)?, Ctr::Symbol(ref tok) => { let outer_scope_seg_holder: Seg; diff --git a/src/segment.rs b/src/segment.rs index 82c284b..5a4e4f2 100644 --- a/src/segment.rs +++ b/src/segment.rs @@ -240,9 +240,9 @@ impl fmt::Display for Ctr { Ctr::Float(s) => write!(f, "{}", s), Ctr::Bool(s) => { if *s { - write!(f, "T") + write!(f, "true") } else { - write!(f, "F") + write!(f, "false") } } Ctr::Seg(s) => write!(f, "{}", s), diff --git a/src/stl.rs b/src/stl.rs index 92374f5..9c011cb 100644 --- a/src/stl.rs +++ b/src/stl.rs @@ -36,26 +36,19 @@ pub fn static_stdlib(syms: &mut SymTable) -> Result<(), String> { name: String::from("append"), args: Args::Infinite, conditional_branches: false, + docs: "traverses any number of arguments collecting them into a list. +If the first argument is a list, all other arguments are added sequentially to the end of the list contained in the first argument.".to_string(), value: ValueType::Internal(Rc::new(append::append_callback)), }, ); - syms.insert( - "expand".to_string(), - Symbol { - name: String::from("expand"), - args: Args::Strict(vec![Type::Seg]), - conditional_branches: false, - value: ValueType::Internal(Rc::new(append::expand_callback)), - }, - ); - syms.insert( "echo".to_string(), Symbol { name: String::from("echo"), args: Args::Infinite, conditional_branches: false, + docs: "traverses any number of arguments. Prints their evaluated values on a new line for each.".to_string(), value: ValueType::Internal(Rc::new(_echo_callback)), }, ); @@ -66,6 +59,14 @@ pub fn static_stdlib(syms: &mut SymTable) -> Result<(), String> { name: String::from("if"), args: Args::Lazy(3), conditional_branches: true, + docs: "accepts three bodies, a condition, an unevaluated consequence, and an alternative consequence. +If the condition is evaluated to true, the first consequence is evaluated. +If the condition is evaluated to false, the second consequence is evaluated. +Otherwise, an error is thrown. + +example: (if my-state-switch + (do-my-thing) + (else-an-other-thing))".to_string(), value: ValueType::Internal(Rc::new(control::if_callback)), }, ); @@ -76,6 +77,19 @@ pub fn static_stdlib(syms: &mut SymTable) -> Result<(), String> { name: String::from("let"), args: Args::Infinite, conditional_branches: true, + docs: "creates a stack of local variables for a sequence of operations. +returns the result of the final operation. + +example: (let ((step1 'hello') + (step2 (concat step1 '-')) + (step3 (concat step2 'world'))) + (echo step3) + (some-func some-args)) + +In this example step1, step2, and step3 are created sequentially. +Then, the echo form is evaluated, printing 'hello-world'. +Finally, the some-func form is evaluated. +Since the call to some-func is the final form, its value is returned.".to_string(), value: ValueType::Internal(Rc::new(control::let_callback)), }, ); @@ -86,6 +100,13 @@ pub fn static_stdlib(syms: &mut SymTable) -> Result<(), String> { name: String::from("while"), args: Args::Infinite, conditional_branches: true, + docs: "traverses a list of N un-evaluated forms. +the first form is expected to evaluate to a boolean. if it evaluates to false, while will stop and return. Otherwise, while will evaluate each form in a loop. + +example: (while (check-my-state) + (do-thing-1 args) + (do-thing-2 args) + (edit-state my-state))".to_string(), value: ValueType::Internal(Rc::new(control::while_callback)), }, ); @@ -96,6 +117,16 @@ pub fn static_stdlib(syms: &mut SymTable) -> Result<(), String> { name: String::from("circuit"), args: Args::Infinite, conditional_branches: true, + docs: "traverses a list of N un-evaluated forms. +evaluates each one until it stops. Circuit will stop when a form errors during evaluation. +Circuit will also stop when a form does not evaluate to a boolean, or evaluates to false. + +example: (circuit (eq? (do-operation) myresult) + (and state1 state2 (boolean-operation3)) + false + (do-another-operation)) + +in this example, do-another-operation will not be called".to_string(), value: ValueType::Internal(Rc::new(control::circuit_callback)), }, ); @@ -106,6 +137,9 @@ pub fn static_stdlib(syms: &mut SymTable) -> Result<(), String> { name: String::from("and"), args: Args::Infinite, conditional_branches: false, + docs: "traverses a list of N arguments, all of which are expected to be boolean. +starts with arg1 AND arg2, and then calculates prev_result AND next_arg. +returns final result.".to_string(), value: ValueType::Internal(Rc::new(boolean::bool_and_callback)), }, ); @@ -116,6 +150,9 @@ pub fn static_stdlib(syms: &mut SymTable) -> Result<(), String> { name: String::from("or"), args: Args::Infinite, conditional_branches: false, + docs: "traverses a list of N arguments, all of which are expected to be boolean. +starts with arg1 OR arg2, and then calculates prev_result OR next_arg. +returns final result.".to_string(), value: ValueType::Internal(Rc::new(boolean::bool_or_callback)), }, ); @@ -126,6 +163,8 @@ pub fn static_stdlib(syms: &mut SymTable) -> Result<(), String> { name: String::from("not"), args: Args::Strict(vec![Type::Bool]), conditional_branches: false, + docs: "takes a single argument (expects a boolean). +returns false if arg is true or true if arg is false.".to_string(), value: ValueType::Internal(Rc::new(boolean::bool_not_callback)), }, ); @@ -136,6 +175,9 @@ pub fn static_stdlib(syms: &mut SymTable) -> Result<(), String> { name: String::from("eq?"), args: Args::Infinite, conditional_branches: false, + docs: "traverses a list of N arguments. +returns true if all arguments hold the same value. +NOTE: 1 and 1.0 are the same, but '1' 'one' or one (symbol) aren't".to_string(), value: ValueType::Internal(Rc::new(boolean::bool_iseq_callback)), }, ); @@ -146,10 +188,24 @@ pub fn static_stdlib(syms: &mut SymTable) -> Result<(), String> { name: String::from("toggle"), args: Args::Lazy(1), conditional_branches: true, + docs: "switches a boolean symbol between true or false. +Takes a single argument (a symbol). Looks it up in the variable table. +Either sets the symbol to true if it is currently false, or vice versa.".to_string(), value: ValueType::Internal(Rc::new(boolean::bool_toggle_callback)), }, ); + syms.insert( + "help".to_string(), + Symbol { + name: String::from("help"), + args: Args::Strict(vec![Type::Symbol]), + conditional_branches: true, + docs: "prints help text for a given symbol. Expects only one argument.".to_string(), + value: ValueType::Internal(Rc::new(_help_callback)), + }, + ); + Ok(()) } @@ -170,6 +226,18 @@ pub fn dynamic_stdlib(syms: &mut SymTable) -> Result<(), String> { name: String::from("define"), args: Args::Infinite, conditional_branches: true, + docs: "allows user to define functions and variables. + A call may take one of three forms: + 1. variable declaration: + Takes a name, doc string, and a value. + (def myvar 'my special variable' 'my var value') + 2. function declaration: + Takes a name, doc string, list of arguments, and one or more bodies to evaluate. + Result of evaluating the final body is returned. + (def myfunc 'does a thing' (myarg1 myarg2) (dothing myarg1 myarg2) (add myarg1 myarg2)) + 3. symbol un-definition: + Takes just a name. Removes variable from table. + (def useless-var)".to_string(), value: ValueType::Internal(Rc::new( move |ast: &Seg, syms: &mut SymTable| -> Result { _store_callback(ast, syms, env_cfg_user_form) @@ -191,80 +259,132 @@ fn _echo_callback(ast: &Seg, _syms: &mut SymTable) -> Result { Ok(Ctr::None) } +fn _help_callback(ast: &Seg, syms: &mut SymTable) -> Result { + if ast.len() != 1 { + return Err("help only takes a single argument".to_string()) + } + if let Ctr::Symbol(ref symbol) = *ast.car { + if let Some(ref sym) = syms.get(symbol) { + let args_str: String; + if let ValueType::VarForm(_) = sym.value { + args_str = "(its a variable)".to_string(); + } else { + args_str = sym.args.to_string(); + } + println!("NAME: {0}\n +ARGS: {1}\n +DOCUMENTATION:\n +{2}\n +CURRENT VALUE AND/OR BODY: +(TODO)", sym.name, args_str, sym.docs); + } else { + return Err("undefined symbol".to_string()) + } + } else { + return Err("help should only be called on a symbol".to_string()) + } + + Ok(Ctr::None) +} + fn _store_callback(ast: &Seg, syms: &mut SymTable, env_cfg: bool) -> Result { - let is_var = ast.len() == 2; + let is_var = ast.len() == 3; if let Ctr::Symbol(ref identifier) = *ast.car { match &*ast.cdr { - Ctr::Seg(data_tree) if is_var => match eval(&Box::new(data_tree), syms) { - Ok(seg) => { - if let Ctr::Seg(ref val) = *seg { - syms.insert( - identifier.clone(), - Symbol { - value: ValueType::VarForm(val.car.clone()), - name: identifier.clone(), - args: Args::None, - conditional_branches: false, - }, - ); - if env_cfg { - env::set_var(identifier.clone(), val.car.to_string()); - } - } else { - return Err("impossible args to export".to_string()); - } - } - Err(e) => return Err(format!("couldnt eval symbol: {}", e)), - }, - Ctr::Seg(data_tree) if !is_var => { - if let Ctr::Seg(ref args) = *data_tree.car { - let mut arg_list = vec![]; - if !args.circuit(&mut |c: &Ctr| -> bool { - if let Ctr::Symbol(ref arg) = c { - arg_list.push(arg.clone()); - true - } else { - false - } - }) { - return Err( - "all arguments defined for function must be of type symbol".to_string() - ); - }; + // define a symbol + Ctr::Seg(doc_tree) => { + if let Ctr::String(ref doc) = *doc_tree.car { + match &*doc_tree.cdr { + // define a variable + Ctr::Seg(data_tree) if is_var => match eval(&Box::new(data_tree), syms) { + Ok(seg) => { + if let Ctr::Seg(ref val) = *seg { + syms.insert( + identifier.clone(), + Symbol { + value: ValueType::VarForm(val.car.clone()), + name: identifier.clone(), + args: Args::None, + docs: doc.to_owned(), + conditional_branches: false, + }, + ); + if env_cfg { + env::set_var(identifier.clone(), val.car.to_string()); + } + } else { + return Err("impossible args to export".to_string()); + } + } + Err(e) => return Err(format!("couldnt eval symbol: {}", e)), + }, - if let Ctr::Seg(ref bodies) = *data_tree.cdr { - syms.insert( - identifier.clone(), - Symbol { - value: ValueType::FuncForm(UserFn { - ast: Box::new(bodies.clone()), - arg_syms: arg_list.clone(), - }), - name: identifier.clone(), - args: Args::Lazy(arg_list.len() as u128), - conditional_branches: false, - }, - ); - } else { - return Err( - "expected one or more function bodies in function definition" - .to_string(), - ); + // define a function + Ctr::Seg(data_tree) if !is_var => { + if let Ctr::Seg(ref args) = *data_tree.car { + let mut arg_list = vec![]; + if !args.circuit(&mut |c: &Ctr| -> bool { + if let Ctr::Symbol(ref arg) = c { + arg_list.push(arg.clone()); + true + } else if let Ctr::None = c { + // a user cannot type a None + // this case represents no args + true + } else { + false + } + }) { + return Err( + "all arguments defined for function must be of type symbol".to_string() + ); + }; + + if let Ctr::Seg(ref bodies) = *data_tree.cdr { + syms.insert( + identifier.clone(), + Symbol { + value: ValueType::FuncForm(UserFn { + ast: Box::new(bodies.clone()), + arg_syms: arg_list.clone(), + }), + name: identifier.clone(), + args: Args::Lazy(arg_list.len() as u128), + docs: doc.to_owned(), + conditional_branches: false, + }, + ); + } else { + return Err( + "expected one or more function bodies in function definition" + .to_string(), + ); + } + } else { + return Err("expected list of arguments in function definition".to_string()); + } + }, + + // theres a name and a doc string but nothing else + _ => return Err("have name and doc string, but no body.".to_string()) } } else { - return Err("expected list of arguments in function definition".to_string()); + return Err("doc string is a required argument".to_string()) } - } + }, + + // undefine a symbol Ctr::None => { syms.remove(&identifier.to_string()); if env_cfg { env::remove_var(identifier); } - } - _ => return Err("args not in standard form".to_string()), + }, + + _ => return Err("arguments not in standard form".to_string()), } } else { - return Err("first argument to export must be a symbol".to_string()); + return Err("first argument to export must be a symbol".to_string()) } Ok(Ctr::None) } diff --git a/src/stl/append.rs b/src/stl/append.rs index 2ae9aba..880efdf 100644 --- a/src/stl/append.rs +++ b/src/stl/append.rs @@ -46,11 +46,3 @@ pub fn append_callback(ast: &Seg, _syms: &mut SymTable) -> Result { Ok(Ctr::Seg(temp)) } } - -pub fn expand_callback(ast: &Seg, _syms: &mut SymTable) -> Result { - if let Ctr::Seg(_) = *ast.car { - Ok(*ast.car.clone()) - } else { - Err("non list passed to expand!".to_string()) - } -} diff --git a/src/stl/control.rs b/src/stl/control.rs index e260570..7b3dc25 100644 --- a/src/stl/control.rs +++ b/src/stl/control.rs @@ -131,6 +131,7 @@ pub fn let_callback(ast: &Seg, syms: &mut SymTable) -> Result { name: name.clone(), args: Args::None, conditional_branches: false, + docs: "variable used in let form".to_string(), value: ValueType::VarForm(Box::new(*var_val_res.unwrap().clone())), }, ); @@ -166,7 +167,7 @@ pub fn let_callback(ast: &Seg, syms: &mut SymTable) -> Result { } if let Err(e) = res { - eprintln!("error evaluating let form: {}", e); + eprintln!("{}", e); return false; } diff --git a/src/sym.rs b/src/sym.rs index d9db7c2..748ec54 100644 --- a/src/sym.rs +++ b/src/sym.rs @@ -19,6 +19,7 @@ use crate::eval::eval; use crate::segment::{Ctr, Seg, Type}; use std::collections::HashMap; use std::rc::Rc; +use std::fmt; #[derive(Clone)] pub struct SymTable(HashMap); @@ -66,6 +67,7 @@ pub struct Symbol { // for internal control flow constructs // eval() will not eval the args pub conditional_branches: bool, + pub docs: String, } impl SymTable { @@ -203,6 +205,23 @@ impl Args { } } +impl fmt::Display for Args { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Args::None => write!(f, "none"), + Args::Infinite => write!(f, "infinite, untyped"), + Args::Lazy(n) => write!(f, "{} args of any type", n), + Args::Strict(s) => { + write!(f, "types: ")?; + for arg in s { + write!(f, "{} ", arg)? + } + Ok(()) + } + } + } +} + impl Symbol { /* call * routine is called by eval when a symbol is expanded @@ -229,10 +248,7 @@ impl Symbol { evaluated_args = args; } - if let Err(msg) = self.args.validate_inputs(evaluated_args) { - return Err(format!("failure to call {}: {}", self.name, msg)); - } - + self.args.validate_inputs(evaluated_args)?; match &self.value { ValueType::VarForm(ref f) => Ok(Box::new(*f.clone())), ValueType::Internal(ref f) => Ok(Box::new(f(evaluated_args, syms)?)), @@ -251,6 +267,7 @@ impl Symbol { name: f.arg_syms[n].clone(), value: ValueType::VarForm(Box::new(evaluated_args[n].clone())), args: Args::None, + docs: format!("local argument to {}", f.arg_syms[n].clone()), conditional_branches: false, }, ) { @@ -265,7 +282,7 @@ impl Symbol { if let Ctr::Seg(ref data) = *iterate.car { match eval(data, syms) { Ok(ctr) => result = ctr, - Err(e) => return Err(e), + Err(e) => return Err(format!("evaluation failure\n{}", e)), } } else { let temp = Seg::from_mono(iterate.car.clone()); @@ -277,7 +294,7 @@ impl Symbol { result = ctr; } } - Err(e) => return Err(e), + Err(e) => return Err(format!("evaluation failure\n{}", e)), } } diff --git a/tests/test_eval.rs b/tests/test_eval.rs index 07b8164..44a4593 100644 --- a/tests/test_eval.rs +++ b/tests/test_eval.rs @@ -6,46 +6,19 @@ mod eval_tests { fn eval_simple() { let test_doc = "(1 2)".to_string(); let mut syms = SymTable::new(); - match lex(&test_doc) { - Err(e) => { - println!("Lexing error: {}\n", e); - assert!(false) - } - - Ok(initial_ast) => match eval(&initial_ast, &mut syms) { - Err(e) => { - println!("Evaluation error: {}\n", e); - assert!(false) - } - - Ok(reduced) => { - assert_eq!(reduced.to_string(), test_doc) - } - }, - } + let doc_tree = lex(&test_doc).unwrap(); + let reduced = *eval(&doc_tree, &mut syms).unwrap(); + assert_eq!(reduced.to_string(), test_doc); } #[test] fn eval_embedded_lists_no_funcs() { let test_doc = "(1 (1 2 3 4 5) 5)".to_string(); let mut syms = SymTable::new(); - match lex(&test_doc) { - Err(e) => { - println!("Lexing error: {}\n", e); - assert!(false) - } + let doc_tree = lex(&test_doc).unwrap(); + let reduced = *eval(&doc_tree, &mut syms).unwrap(); + assert_eq!(reduced.to_string(), test_doc); - Ok(initial_ast) => match eval(&initial_ast, &mut syms) { - Err(e) => { - println!("Evaluation error: {}\n", e); - assert!(false) - } - - Ok(reduced) => { - assert_eq!(reduced.to_string(), test_doc) - } - }, - } } #[test] @@ -58,6 +31,7 @@ mod eval_tests { name: String::from("echo"), args: Args::Lazy(1), conditional_branches: false, + docs: String::new(), value: ValueType::FuncForm(UserFn { arg_syms: vec!["input".to_string()], ast: Box::new(Seg::from( @@ -68,24 +42,9 @@ mod eval_tests { }; syms.insert(String::from("echo"), test_external_func); - - match lex(&test_doc) { - Err(e) => { - println!("Lexing error: {}\n", e); - assert!(false) - } - - Ok(initial_ast) => match eval(&initial_ast, &mut syms) { - Err(e) => { - println!("Evaluation error: {}\n", e); - assert!(false) - } - - Ok(reduced) => { - assert_eq!(reduced.to_string(), output) - } - }, - } + let doc_tree = lex(&test_doc).unwrap(); + let reduced = *eval(&doc_tree, &mut syms).unwrap(); + assert_eq!(reduced.to_string(), output); } #[test] @@ -98,6 +57,7 @@ mod eval_tests { name: String::from("echo"), args: Args::Lazy(1), conditional_branches: false, + docs: String::new(), value: ValueType::FuncForm(UserFn { arg_syms: vec!["input".to_string()], ast: Box::new(Seg::from( @@ -108,46 +68,26 @@ mod eval_tests { }; syms.insert(String::from("echo"), test_external_func); - match lex(&test_doc) { - Err(e) => { - println!("Lexing error: {}\n", e); - assert!(false) - } - - Ok(initial_ast) => match eval(&initial_ast, &mut syms) { - Err(e) => { - println!("Evaluation error: {}\n", e); - assert!(false) - } - - Ok(reduced) => { - assert_eq!(reduced.to_string(), output) - } - }, - } + let doc_tree = lex(&test_doc).unwrap(); + let reduced = *eval(&doc_tree, &mut syms).unwrap(); + assert_eq!(reduced.to_string(), output); } #[test] fn eval_bad_syms() { let test_doc = "(undefined)".to_string(); let mut syms = SymTable::new(); - match lex(&test_doc) { + let doc_tree = lex(&test_doc).unwrap(); + match eval(&doc_tree, &mut syms) { Err(e) => { - println!("Lexing error: {}\n", e); - assert!(false) + assert_eq!(e, "error in call to undefined: undefined symbol: undefined") } - Ok(initial_ast) => match eval(&initial_ast, &mut syms) { - Err(e) => { - assert_eq!(e, "error in call to undefined: undefined symbol: undefined") - } - - Ok(reduced) => { - println!("Eval succeeded when it shouldnt have"); - println!("see: {}", reduced); - assert!(false) - } - }, + Ok(reduced) => { + println!("Eval succeeded when it shouldnt have"); + println!("see: {}", reduced); + assert!(false) + } } } } diff --git a/tests/test_func.rs b/tests/test_func.rs index d29c974..43f1545 100644 --- a/tests/test_func.rs +++ b/tests/test_func.rs @@ -10,6 +10,7 @@ mod func_tests { let test_internal_func: Symbol = Symbol { name: String::from("test_func_in"), conditional_branches: false, + docs: String::new(), args: Args::Strict(vec![Type::Bool]), value: ValueType::Internal(Rc::new( |a: &Seg, _: &mut SymTable| -> Result { @@ -23,95 +24,63 @@ mod func_tests { )), }; let args = Seg::from(Box::new(Ctr::Bool(true)), Box::new(Ctr::None)); - syms.insert(String::from("test_func_in"), test_internal_func); - - if let Ok(ret) = syms.call_symbol(&"test_func_in".to_string(), &args, true) { - match *ret { - Ctr::Bool(b) => assert!(b), - _ => { - print!("invalid return from func!"); - assert!(false); - } - } - } else { - print!("call to function failed!"); - assert!(false); + if let Ctr::Bool(b) = *syms.call_symbol(&"test_func_in".to_string(), &args, true).unwrap() { + assert!(b) } } #[test] fn decl_and_call_external_func_singlet() { let mut syms = SymTable::new(); - match lex(&"input".to_string()) { - Err(e) => panic!("{}", e), - Ok(finner) => { - let test_external_func: Symbol = Symbol { - name: String::from("echo"), - conditional_branches: false, - args: Args::Lazy(1), - value: ValueType::FuncForm(UserFn { - arg_syms: vec!["input".to_string()], - ast: finner, - }), - }; + let finner = lex(&"input".to_string()).unwrap(); + let test_external_func: Symbol = Symbol { + name: String::from("echo"), + conditional_branches: false, + args: Args::Lazy(1), + docs: String::new(), + value: ValueType::FuncForm(UserFn { + arg_syms: vec!["input".to_string()], + ast: finner, + }), + }; - let args = Seg::from( - Box::new(Ctr::String("test".to_string())), - Box::new(Ctr::None), - ); + let args = Seg::from( + Box::new(Ctr::String("test".to_string())), + Box::new(Ctr::None), + ); - syms.insert(String::from("test_func_in"), test_external_func); - - match syms.call_symbol(&"test_func_in".to_string(), &args, true) { - Ok(ret) => match *ret { - Ctr::String(b) => assert!(b == "test"), - _ => { - print!("Invalid return from func. Got {:?}\n", ret); - assert!(false); - } - }, - Err(e) => { - print!("Call to function failed: {}\n", e); - assert!(false); - } - } - } + syms.insert(String::from("test_func_in"), test_external_func); + if let Ctr::Bool(b) = *syms.call_symbol(&"test_func_in".to_string(), &args, true).unwrap() { + assert!(b) } } #[test] fn decl_and_call_external_func_multi_body() { let mut syms = SymTable::new(); - match lex(&"(input input)".to_string()) { - Err(e) => panic!("{}", e), - Ok(finner) => { - let test_external_func: Symbol = Symbol { - name: String::from("echo_2"), - conditional_branches: false, - args: Args::Lazy(1), - value: ValueType::FuncForm(UserFn { - arg_syms: vec!["input".to_string()], - ast: finner, - }), - }; + let finner = lex(&"input".to_string()).unwrap(); + let test_external_func: Symbol = Symbol { + name: String::from("echo_2"), + conditional_branches: false, + args: Args::Lazy(1), + docs: String::new(), + value: ValueType::FuncForm(UserFn { + arg_syms: vec!["input".to_string()], + ast: finner, + }), + }; - let args = Seg::from( - Box::new(Ctr::String("test".to_string())), - Box::new(Ctr::None), - ); + let args = Seg::from( + Box::new(Ctr::String("test".to_string())), + Box::new(Ctr::None), + ); - syms.insert(String::from("echo_2"), test_external_func); - - match syms.call_symbol(&"echo_2".to_string(), &args, true) { - Ok(ret) => assert_eq!(ret.to_string(), "'test'"), - Err(e) => { - print!("Call to function failed: {}\n", e); - assert!(false); - } - } - } - } + syms.insert(String::from("echo_2"), test_external_func); + assert_eq!(*syms.call_symbol(&"echo_2".to_string(), &args, true) + .unwrap() + .to_string(), + "'test'".to_string()); } #[test] @@ -121,6 +90,7 @@ mod func_tests { name: String::from("test_inner"), conditional_branches: false, args: Args::Strict(vec![Type::Bool]), + docs: String::new(), value: ValueType::Internal(Rc::new( |a: &Seg, _: &mut SymTable| -> Result { let inner = a; @@ -137,39 +107,25 @@ mod func_tests { )), }; - match lex(&"((test_inner true))".to_string()) { - Err(e) => panic!("{}", e), - Ok(finner) => { - let outer_func: Symbol = Symbol { - name: String::from("test_outer"), - conditional_branches: false, - args: Args::Lazy(1), - value: ValueType::FuncForm(UserFn { - arg_syms: vec!["input".to_string()], - ast: finner, - }), - }; + let finner = lex(&"((test_inner true))".to_string()).unwrap(); + let outer_func: Symbol = Symbol { + name: String::from("test_outer"), + conditional_branches: false, + args: Args::Lazy(1), + docs: String::new(), + value: ValueType::FuncForm(UserFn { + arg_syms: vec!["input".to_string()], + ast: finner, + }), + }; - let args = Seg::from(Box::new(Ctr::Bool(true)), Box::new(Ctr::None)); - - syms.insert(String::from("test_inner"), inner_func); - syms.insert(String::from("test_outer"), outer_func); - - match syms.call_symbol(&"test_outer".to_string(), &args, true) { - Ok(ret) => match *ret { - Ctr::String(b) => assert!(b == "test"), - _ => { - print!("Invalid return from func. Got {:?}\n", ret); - assert!(false); - } - }, - Err(e) => { - print!("Call to function failed: {}\n", e); - assert!(false); - } - } - } - } + let args = Seg::from(Box::new(Ctr::Bool(true)), Box::new(Ctr::None)); + syms.insert(String::from("test_inner"), inner_func); + syms.insert(String::from("test_outer"), outer_func); + assert_eq!(syms.call_symbol(&"test_outer".to_string(), &args, true) + .unwrap() + .to_string(), + "'test'".to_string()); } #[test] @@ -179,6 +135,7 @@ mod func_tests { name: String::from("test_func_in"), conditional_branches: false, args: Args::Strict(vec![Type::Bool]), + docs: String::new(), value: ValueType::Internal(Rc::new( |a: &Seg, _: &mut SymTable| -> Result { let inner = a; @@ -193,75 +150,66 @@ mod func_tests { let args = Seg::from(Box::new(Ctr::Integer(1)), Box::new(Ctr::None)); syms.insert(String::from("test_func_in"), test_internal_func); - - if let Err(s) = syms.call_symbol(&"test_func_in".to_string(), &args, true) { - assert_eq!(s, "failure to call test_func_in: arg 1 expected to be bool"); - } else { - print!("call to function succeeded (shouldnt have)"); - assert!(false); - } + assert_eq!( + syms.call_symbol(&"test_func_in".to_string(), &args, true) + .err() + .unwrap(), + "arg 1 expected to be bool".to_string(), + ); } #[test] fn too_many_args() { let mut syms = SymTable::new(); - match lex(&"(input)".to_string()) { - Err(e) => panic!("{}", e), - Ok(finner) => { - let test_external_func: Symbol = Symbol { - name: String::from("echo"), - conditional_branches: false, - args: Args::Lazy(1), - value: ValueType::FuncForm(UserFn { - arg_syms: vec!["input".to_string()], - ast: finner, - }), - }; + let finner = lex(&"(input)".to_string()).unwrap(); + let test_external_func: Symbol = Symbol { + name: String::from("echo"), + conditional_branches: false, + args: Args::Lazy(1), + docs: String::new(), + value: ValueType::FuncForm(UserFn { + arg_syms: vec!["input".to_string()], + ast: finner, + }), + }; - let args = Seg::from( - Box::new(Ctr::String("test".to_string())), - Box::new(Ctr::Seg(Seg::from_mono(Box::new(Ctr::Integer(1))))), - ); + let args = Seg::from( + Box::new(Ctr::String("test".to_string())), + Box::new(Ctr::Seg(Seg::from_mono(Box::new(Ctr::Integer(1))))), + ); - syms.insert(String::from("test_func_in"), test_external_func); - - if let Err(s) = syms.call_symbol(&"test_func_in".to_string(), &args, true) { - assert_eq!(s, "failure to call echo: expected 1 args. Got 2."); - } else { - print!("call to function succeeded (shouldnt have)"); - assert!(false); - } - } - } + syms.insert(String::from("test_func_in"), test_external_func); + assert_eq!( + syms.call_symbol(&"test_func_in".to_string(), &args, true) + .err() + .unwrap(), + "expected 1 args. Got 2.".to_string(), + ); } #[test] fn too_few_args() { let mut syms = SymTable::new(); - match lex(&"(input)".to_string()) { - Err(e) => panic!("{}", e), - Ok(finner) => { - let test_external_func: Symbol = Symbol { - name: String::from("echo"), - conditional_branches: false, - args: Args::Lazy(1), - value: ValueType::FuncForm(UserFn { - arg_syms: vec!["input".to_string()], - ast: finner, - }), - }; + let finner = lex(&"(input)".to_string()).unwrap(); + let test_external_func: Symbol = Symbol { + name: String::from("echo"), + conditional_branches: false, + args: Args::Lazy(1), + docs: String::new(), + value: ValueType::FuncForm(UserFn { + arg_syms: vec!["input".to_string()], + ast: finner, + }), + }; - let args = Seg::new(); - syms.insert(String::from("test_func_in"), test_external_func); - - if let Err(s) = syms.call_symbol(&"test_func_in".to_string(), &args, true) { - assert_eq!(s, "failure to call echo: expected 1 args. Got 0."); - } else { - print!("call to function succeeded (shouldnt have)"); - assert!(false); - } - } - } + let args = Seg::new(); + syms.insert(String::from("test_func_in"), test_external_func); + assert_eq!( + syms.call_symbol(&"test_func_in".to_string(), &args, true) + .err() + .unwrap(), + "expected 1 args. Got 0.".to_string(), + ); } #[test] @@ -271,6 +219,7 @@ mod func_tests { name: String::from("test_func_in"), conditional_branches: false, args: Args::Strict(vec![Type::Bool]), + docs: String::new(), value: ValueType::Internal(Rc::new( |a: &Seg, _: &mut SymTable| -> Result { let inner = a; @@ -288,15 +237,12 @@ mod func_tests { ); syms.insert(String::from("test_func_in"), test_internal_func); - - if let Err(s) = syms.call_symbol(&"test_func_in".to_string(), &args, true) { - assert_eq!( - s, - "error in call to undefined-symbol: undefined symbol: undefined-symbol" - ); - } else { - print!("call to function succeeded (shouldnt have)"); - assert!(false); - } + assert_eq!( + syms.call_symbol(&"test_func_in".to_string(), &args, true) + .err() + .unwrap(), + "error in call to undefined-symbol: undefined symbol: undefined-symbol" + .to_string(), + ); } } diff --git a/tests/test_lex.rs b/tests/test_lex.rs index 0315bae..faa1e4a 100644 --- a/tests/test_lex.rs +++ b/tests/test_lex.rs @@ -4,131 +4,86 @@ mod lex_tests { #[test] fn test_lex_basic_pair() { let document = String::from("(hello 'world')"); - match lex(&document) { - Ok(tree) => { - assert_eq!(tree.to_string(), document); - } - Err(s) => { - print!("{}\n", s); - assert!(false); - } - } + assert_eq!( + lex(&document).unwrap().to_string(), + document + ); } #[test] fn test_lex_basic_list() { let document = String::from("(hello 'world' 1 2 3)"); - match lex(&document) { - Ok(tree) => { - assert_eq!(tree.to_string(), document); - } - Err(s) => { - print!("{}\n", s); - assert!(false); - } - } + assert_eq!( + lex(&document).unwrap().to_string(), + document + ); } #[test] fn test_lex_complex_list() { let document = String::from("(hello 'world' (1 2 (1 2 3)) 1 2 3)"); - match lex(&document) { - Ok(tree) => { - assert_eq!(tree.to_string(), document); - } - Err(s) => { - print!("{}\n", s); - assert!(false); - } - } + assert_eq!( + lex(&document).unwrap().to_string(), + document + ); } #[test] fn test_bad_symbol() { let document = String::from("(as;dd)"); let output: &str = "Problem lexing document: \"Unparsable token: as;dd\""; - match lex(&document) { - Ok(tree) => { - print!("Bad token yielded: {}\n", tree.to_string()); - assert!(false); - } - Err(s) => { - assert_eq!(s, output); - } - } + assert_eq!( + lex(&document).err().unwrap(), + output.to_string(), + ); } #[test] fn test_list_delim_in_str() { let document = String::from("('(')"); - match lex(&document) { - Ok(tree) => { - assert_eq!(tree.to_string(), document); - } - Err(s) => { - print!("{}\n", s); - assert!(false); - } - } + assert_eq!( + lex(&document).unwrap().to_string(), + document + ); } #[test] fn test_empty_string() { let document = String::from("('')"); - match lex(&document) { - Ok(tree) => { - assert_eq!(tree.to_string(), document); - } - Err(s) => { - print!("{}\n", s); - assert!(false); - } - } + assert_eq!( + lex(&document).unwrap().to_string(), + document + ); } #[test] fn test_unmatched_list_delim_flat() { let document = String::from("(one two"); let output: &str = "Problem lexing document: \"Unmatched list delimiter in input\""; - match lex(&document) { - Ok(tree) => { - print!("Bad token yielded: {}\n", tree.to_string()); - assert!(false); - } - Err(s) => { - assert_eq!(s, output); - } - } + assert_eq!( + lex(&document).err().unwrap(), + output.to_string(), + ); } #[test] fn test_unmatched_list_delim_complex() { let document = String::from("(one two (three)"); let output: &str = "Problem lexing document: \"Unmatched list delimiter in input\""; - match lex(&document) { - Ok(tree) => { - print!("Bad token yielded: {}\n", tree); - assert!(false); - } - Err(s) => { - assert_eq!(s, output); - } - } + assert_eq!( + lex(&document).err().unwrap(), + output.to_string(), + ); } #[test] fn test_comment() { let document = String::from("#!/bin/relish\n(one two)"); let output: &str = "(one two)"; - match lex(&document) { - Ok(tree) => { - assert_eq!(tree.to_string(), output); - } - Err(s) => { - print!("{}\n", s); - assert!(false); - } - } + assert_eq!( + lex(&document).unwrap().to_string(), + output.to_string(), + ); } #[test] @@ -136,44 +91,30 @@ mod lex_tests { let document = String::from("#!/bin/relish\n((one two)# another doc comment\n(three four))"); let output: &str = "((one two) (three four))"; - match lex(&document) { - Ok(tree) => { - assert_eq!(tree.to_string(), output.to_string()); - } - Err(s) => { - print!("{}\n", s); - assert!(false); - } - } + assert_eq!( + lex(&document).unwrap().to_string(), + output.to_string(), + ); } #[test] fn test_inline_comment() { let document = String::from("#!/bin/relish\n((one two)\n# another doc comment\nthree)"); let output: &str = "((one two) three)"; - match lex(&document) { - Ok(tree) => { - assert_eq!(tree.to_string(), output.to_string()); - } - Err(s) => { - print!("{}\n", s); - assert!(false); - } - } + assert_eq!( + lex(&document).unwrap().to_string(), + output.to_string(), + ); + } #[test] fn test_bad_token_list() { let document = String::from("(one t(wo)"); let output: &str = "Problem lexing document: \"list started in middle of another token\""; - match lex(&document) { - Ok(tree) => { - print!("Bad token yielded: {}\n", tree); - assert!(false); - } - Err(s) => { - assert_eq!(s, output); - } - } + assert_eq!( + lex(&document).err().unwrap(), + output.to_string(), + ); } } diff --git a/tests/test_lib_append.rs b/tests/test_lib_append.rs index 515c598..0b83366 100644 --- a/tests/test_lib_append.rs +++ b/tests/test_lib_append.rs @@ -1,5 +1,5 @@ mod append_lib_tests { - use relish::ast::{eval, lex, Ctr, SymTable}; + use relish::ast::{eval, lex, SymTable}; use relish::stdlib::{dynamic_stdlib, static_stdlib}; #[test] @@ -10,14 +10,10 @@ mod append_lib_tests { let mut syms = SymTable::new(); static_stdlib(&mut syms).unwrap(); dynamic_stdlib(&mut syms).unwrap(); - - if let Ok(tree) = lex(&document.to_string()) { - if let Ctr::Seg(ref s) = *eval(&tree, &mut syms).unwrap() { - assert_eq!(s.to_string(), result); - } - } else { - assert!(false) - } + assert_eq!( + *eval(&lex(&document.to_string()).unwrap(), &mut syms).unwrap().to_string(), + result.to_string(), + ); } #[test] @@ -29,13 +25,10 @@ mod append_lib_tests { static_stdlib(&mut syms).unwrap(); dynamic_stdlib(&mut syms).unwrap(); - if let Ok(tree) = lex(&document.to_string()) { - if let Ctr::Seg(ref s) = *eval(&tree, &mut syms).unwrap() { - assert_eq!(s.to_string(), result); - } - } else { - assert!(false) - } + assert_eq!( + *eval(&lex(&document.to_string()).unwrap(), &mut syms).unwrap().to_string(), + result.to_string(), + ); } #[test] @@ -47,13 +40,10 @@ mod append_lib_tests { static_stdlib(&mut syms).unwrap(); dynamic_stdlib(&mut syms).unwrap(); - if let Ok(tree) = lex(&document.to_string()) { - if let Ctr::Seg(ref s) = *eval(&tree, &mut syms).unwrap() { - assert_eq!(s.to_string(), result); - } - } else { - assert!(false); - } + assert_eq!( + *eval(&lex(&document.to_string()).unwrap(), &mut syms).unwrap().to_string(), + result.to_string(), + ); } #[test] @@ -65,13 +55,10 @@ mod append_lib_tests { static_stdlib(&mut syms).unwrap(); dynamic_stdlib(&mut syms).unwrap(); - if let Ok(tree) = lex(&document.to_string()) { - if let Ctr::Seg(ref s) = *eval(&tree, &mut syms).unwrap() { - assert_eq!(s.to_string(), result); - } - } else { - assert!(false); - } + assert_eq!( + *eval(&lex(&document.to_string()).unwrap(), &mut syms).unwrap().to_string(), + result.to_string(), + ); } #[test] @@ -83,12 +70,9 @@ mod append_lib_tests { static_stdlib(&mut syms).unwrap(); dynamic_stdlib(&mut syms).unwrap(); - if let Ok(tree) = lex(&document.to_string()) { - if let Ctr::Seg(ref s) = *eval(&tree, &mut syms).unwrap() { - assert_eq!(s.to_string(), result); - } - } else { - assert!(false); - } + assert_eq!( + *eval(&lex(&document.to_string()).unwrap(), &mut syms).unwrap().to_string(), + result.to_string(), + ); } } diff --git a/tests/test_lib_bools.rs b/tests/test_lib_bools.rs index 03d91e5..10eb09b 100644 --- a/tests/test_lib_bools.rs +++ b/tests/test_lib_bools.rs @@ -5,146 +5,99 @@ mod bool_lib_tests { #[test] fn test_and_true_chain() { let document = "(and true true true true true)"; - let result = true; - + let result = "true"; let mut syms = SymTable::new(); static_stdlib(&mut syms).unwrap(); dynamic_stdlib(&mut syms).unwrap(); - - if let Ok(tree) = lex(&document.to_string()) { - if let Ctr::Bool(b) = *eval(&tree, &mut syms).unwrap() { - assert_eq!(b, result); - } else { - assert!(false); - } - } else { - assert!(false); - } + assert_eq!( + *eval(&lex(&document.to_string()).unwrap(), &mut syms).unwrap().to_string(), + result.to_string(), + ); } #[test] fn test_and_true_chain_with_false() { let document = "(and true true false true true)"; - let result = false; - + let result = "false"; let mut syms = SymTable::new(); static_stdlib(&mut syms).unwrap(); dynamic_stdlib(&mut syms).unwrap(); - - if let Ok(tree) = lex(&document.to_string()) { - if let Ctr::Bool(i) = *eval(&tree, &mut syms).unwrap() { - assert_eq!(i, result); - } else { - assert!(false); - } - } else { - assert!(false); - } + assert_eq!( + *eval(&lex(&document.to_string()).unwrap(), &mut syms).unwrap().to_string(), + result.to_string(), + ); } #[test] fn test_and_false_chain() { let document = "(and false false false false false)"; - let result = false; - + let result = "false"; let mut syms = SymTable::new(); static_stdlib(&mut syms).unwrap(); dynamic_stdlib(&mut syms).unwrap(); - - if let Ok(tree) = lex(&document.to_string()) { - if let Ctr::Bool(i) = *eval(&tree, &mut syms).unwrap() { - assert_eq!(i, result); - } else { - assert!(false); - } - } else { - assert!(false); - } + assert_eq!( + *eval(&lex(&document.to_string()).unwrap(), &mut syms).unwrap().to_string(), + result.to_string(), + ); } #[test] fn test_or_true_chain() { let document = "(or true true true true true)"; - let result = true; - + let result = "true"; let mut syms = SymTable::new(); static_stdlib(&mut syms).unwrap(); dynamic_stdlib(&mut syms).unwrap(); + assert_eq!( + *eval(&lex(&document.to_string()).unwrap(), &mut syms).unwrap().to_string(), + result.to_string(), + ); - if let Ok(tree) = lex(&document.to_string()) { - if let Ctr::Bool(b) = *eval(&tree, &mut syms).unwrap() { - assert_eq!(b, result); - } else { - assert!(false); - } - } else { - assert!(false); - } } #[test] fn test_or_true_chain_with_false() { let document = "(or true true false true true)"; - let result = true; - + let result = "true"; let mut syms = SymTable::new(); static_stdlib(&mut syms).unwrap(); dynamic_stdlib(&mut syms).unwrap(); + assert_eq!( + *eval(&lex(&document.to_string()).unwrap(), &mut syms).unwrap().to_string(), + result.to_string(), + ); - if let Ok(tree) = lex(&document.to_string()) { - if let Ctr::Bool(i) = *eval(&tree, &mut syms).unwrap() { - assert_eq!(i, result); - } else { - assert!(false); - } - } else { - assert!(false); - } } #[test] fn test_or_false_chain() { - let document = "(or false false false false)"; - let result = false; - + let document = "(or false false false false false)"; + let result = "false"; let mut syms = SymTable::new(); static_stdlib(&mut syms).unwrap(); dynamic_stdlib(&mut syms).unwrap(); - - if let Ok(tree) = lex(&document.to_string()) { - if let Ctr::Bool(i) = *eval(&tree, &mut syms).unwrap() { - assert_eq!(i, result); - } else { - assert!(false); - } - } else { - assert!(false); - } + assert_eq!( + *eval(&lex(&document.to_string()).unwrap(), &mut syms).unwrap().to_string(), + result.to_string(), + ); } #[test] fn test_not() { let document = "(not true)"; - let result = false; - + let result = "false"; let mut syms = SymTable::new(); static_stdlib(&mut syms).unwrap(); dynamic_stdlib(&mut syms).unwrap(); - - if let Ok(tree) = lex(&document.to_string()) { - if let Ctr::Bool(i) = *eval(&tree, &mut syms).unwrap() { - assert_eq!(i, result); - } else { - assert!(false); - } - } else { - assert!(false); - } + assert_eq!( + *eval(&lex(&document.to_string()).unwrap(), &mut syms).unwrap().to_string(), + result.to_string(), + ); } #[test] fn test_toggle_a_bool() { - let document = "(def tester true)"; + let document = "(def tester '' true)"; let change = "(toggle tester)"; let check = "(tester)"; @@ -183,7 +136,7 @@ mod bool_lib_tests { #[test] fn test_toggle_errors_dont_lose_vars() { - let document = "(def tester 'oops')"; + let document = "(def tester '' 'oops')"; let change = "(toggle tester)"; let check = "(tester)"; @@ -216,7 +169,7 @@ mod bool_lib_tests { #[test] fn test_toggle_errors_dont_lose_funcs() { - let document = "(def tester (oops) oops)"; + let document = "(def tester '' (oops) oops)"; let change = "(toggle tester)"; let check = "(tester '1')"; diff --git a/tests/test_lib_control.rs b/tests/test_lib_control.rs index 4f35cf2..5e6d563 100644 --- a/tests/test_lib_control.rs +++ b/tests/test_lib_control.rs @@ -1,66 +1,44 @@ mod control_lib_tests { - use relish::ast::{eval, lex, Ctr, SymTable}; + use relish::ast::{eval, lex, SymTable}; use relish::stdlib::{dynamic_stdlib, static_stdlib}; #[test] fn test_if_first_case_singlet() { let document = "(if true 1 2)"; let result = 1; - let mut syms = SymTable::new(); static_stdlib(&mut syms).unwrap(); dynamic_stdlib(&mut syms).unwrap(); - - if let Ok(tree) = lex(&document.to_string()) { - if let Ctr::Integer(i) = *eval(&tree, &mut syms).unwrap() { - assert_eq!(i, result); - } else { - assert!(false); - } - } else { - assert!(false); - } + assert_eq!( + *eval(&lex(&document.to_string()).unwrap(), &mut syms).unwrap().to_string(), + result.to_string(), + ); } #[test] fn test_if_second_case_singlet() { let document = "(if false 1 2)"; let result = 2; - let mut syms = SymTable::new(); static_stdlib(&mut syms).unwrap(); dynamic_stdlib(&mut syms).unwrap(); - - if let Ok(tree) = lex(&document.to_string()) { - if let Ctr::Integer(i) = *eval(&tree, &mut syms).unwrap() { - assert_eq!(i, result); - } else { - eprintln!("{}", *eval(&tree, &mut syms).unwrap()); - assert!(false); - } - } else { - assert!(false); - } + assert_eq!( + *eval(&lex(&document.to_string()).unwrap(), &mut syms).unwrap().to_string(), + result.to_string(), + ); } #[test] fn test_complex_case_call() { let document = "(if true (append () 1) 2)"; let result = "(1)"; - let mut syms = SymTable::new(); static_stdlib(&mut syms).unwrap(); dynamic_stdlib(&mut syms).unwrap(); - - if let Ok(tree) = lex(&document.to_string()) { - if let Ctr::Seg(ref i) = *eval(&tree, &mut syms).unwrap() { - assert_eq!(i.to_string(), result); - } else { - assert!(false); - } - } else { - assert!(false); - } + assert_eq!( + *eval(&lex(&document.to_string()).unwrap(), &mut syms).unwrap().to_string(), + result.to_string(), + ); } #[test] @@ -70,45 +48,30 @@ mod control_lib_tests { (temp (append () temp '2'))) temp)"; let result = "('1' '2')"; - let mut syms = SymTable::new(); static_stdlib(&mut syms).unwrap(); dynamic_stdlib(&mut syms).unwrap(); - - if let Ok(tree) = lex(&document.to_string()) { - if let Ctr::Seg(ref i) = *eval(&tree, &mut syms).unwrap() { - assert_eq!(i.to_string(), result); - } else { - assert!(false); - } - } else { - assert!(false); - } + assert_eq!( + *eval(&lex(&document.to_string()).unwrap(), &mut syms).unwrap().to_string(), + result.to_string(), + ); } #[test] fn test_let_multibody_evals() { let document = "(let ((temp '1')) temp (append () temp '2'))"; let result = "('1' '2')"; - let mut syms = SymTable::new(); static_stdlib(&mut syms).unwrap(); dynamic_stdlib(&mut syms).unwrap(); - - if let Ok(tree) = lex(&document.to_string()) { - if let Ctr::Seg(ref i) = *eval(&tree, &mut syms).unwrap() { - assert_eq!(i.to_string(), result); - } else { - assert!(false); - } - } else { - assert!(false); - } + assert_eq!( + *eval(&lex(&document.to_string()).unwrap(), &mut syms).unwrap().to_string(), + result.to_string(), + ); } #[test] fn test_let_multiphase_local_multibody_evals() { - // prints 'first body' and then returns ('1' '2' '3') let document = "(let ( (temp '1') (temp (append () temp '2'))) @@ -116,33 +79,26 @@ mod control_lib_tests { (append temp '3'))"; let result = "('1' '2' '3')"; - let mut syms = SymTable::new(); static_stdlib(&mut syms).unwrap(); dynamic_stdlib(&mut syms).unwrap(); - - if let Ok(tree) = lex(&document.to_string()) { - if let Ctr::Seg(ref i) = *eval(&tree, &mut syms).unwrap() { - assert_eq!(i.to_string(), result); - } else { - assert!(false); - } - } else { - assert!(false); - } + assert_eq!( + *eval(&lex(&document.to_string()).unwrap(), &mut syms).unwrap().to_string(), + result.to_string(), + ); } #[test] fn test_while_basic() { - let switch_dec = "(def switch true)"; + 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) + (def prev '' switch) (toggle switch) (if switch - (def prev) + (def '' prev) ()))"; let test_check = "prev"; @@ -161,15 +117,15 @@ mod control_lib_tests { #[test] fn test_while_eval_cond() { - let switch_dec = "(def switch true)"; + 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) + (def prev '' switch) (toggle switch) (if switch - (def prev) + (def '' prev) ()))"; let test_check = "prev"; @@ -188,15 +144,15 @@ mod control_lib_tests { #[test] fn test_while_2_iter() { - let additional = "(def sw1 true)"; - let switch_dec = "(def sw2 true)"; + 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')))"; + (def sw1 '' false) + (def result '' 'yay')))"; let test_check = "result"; let another_tree = lex(&additional.to_string()).unwrap(); @@ -216,7 +172,7 @@ mod control_lib_tests { #[test] fn test_circuit_basic() { - let document = "(if (circuit true (and true true) true) (def result 'passed') ())"; + let document = "(if (circuit true (and true true) true) (def result '' 'passed') ())"; let test = "result"; let doc_tree = lex(&document.to_string()).unwrap(); @@ -228,13 +184,12 @@ mod control_lib_tests { 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 document = "(if (circuit true (and false true) true) (def result '' 'passed') ())"; let test = "result"; let doc_tree = lex(&document.to_string()).unwrap(); @@ -245,13 +200,9 @@ mod control_lib_tests { 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!(); - } + assert_eq!( + eval(&test_tree, &mut syms).err().unwrap(), + "error in call to result: undefined symbol: result".to_string() + ); } } diff --git a/tests/test_vars.rs b/tests/test_vars.rs index eb5a800..52f9ff3 100644 --- a/tests/test_vars.rs +++ b/tests/test_vars.rs @@ -4,7 +4,7 @@ mod var_lib_tests { #[test] fn test_variable_def_and_lookup() { - let doc1 = "(def test 1)"; + let doc1 = "(def test 'my test var' 1)"; let doc2 = "(test)"; let result = "(1)"; @@ -42,7 +42,7 @@ mod var_lib_tests { #[test] fn test_func_def_and_lookup() { - let doc1 = "(def test (hello) hello)"; + let doc1 = "(def test 'my test func' (hello) hello)"; let doc2 = "(test '1')"; let result = "1"; @@ -79,8 +79,8 @@ mod var_lib_tests { #[test] fn test_variable_def_redef_and_lookup() { - let doc1 = "(def test 1)"; - let doc2 = "(def test '2')"; + let doc1 = "(def test 'my test var' 1)"; + let doc2 = "(def test 'my test var' '2')"; let doc3 = "(test)"; let result = "('2')"; @@ -132,7 +132,7 @@ mod var_lib_tests { #[test] fn test_variable_def_undef_and_lookup_fail() { - let doc1 = "(def test 1)"; + let doc1 = "(def test 'my test var' 1)"; let doc2 = "(def test)"; let doc3 = "(test)"; @@ -185,4 +185,41 @@ mod var_lib_tests { assert!(false); } } + + #[test] + fn test_func_def_no_args() { + let doc1 = "(def test 'my test func' () '1')"; + let doc2 = "(test)"; + 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); + } + } }