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
This commit is contained in:
Ava Hahn 2023-03-05 22:18:49 -08:00
parent 4b587f11ab
commit dc6342bc74
Signed by untrusted user who does not match committer: affine
GPG key ID: 3A4645B8CF806069
16 changed files with 575 additions and 677 deletions

View file

@ -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

View file

@ -0,0 +1,7 @@
pub fn expand_callback(ast: &Seg, _syms: &mut SymTable) -> Result<Ctr, String> {
if let Ctr::Seg(_) = *ast.car {
Ok(*ast.car.clone())
} else {
Err("non list passed to expand!".to_string())
}
}

View file

@ -29,15 +29,19 @@ fn prompt_default_callback(_: &Seg, _: &mut SymTable) -> Result<Ctr, String> {
/* 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 (<lambda>)".to_string(),
value: ValueType::Internal(Rc::new(prompt_default_callback)),
},
);

View file

@ -39,10 +39,7 @@ pub fn eval(ast: &Seg, syms: &mut SymTable) -> Result<Box<Ctr>, 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;

View file

@ -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),

View file

@ -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<Ctr, String> {
_store_callback(ast, syms, env_cfg_user_form)
@ -191,80 +259,132 @@ fn _echo_callback(ast: &Seg, _syms: &mut SymTable) -> Result<Ctr, String> {
Ok(Ctr::None)
}
fn _help_callback(ast: &Seg, syms: &mut SymTable) -> Result<Ctr, String> {
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<Ctr, String> {
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)
}

View file

@ -46,11 +46,3 @@ pub fn append_callback(ast: &Seg, _syms: &mut SymTable) -> Result<Ctr, String> {
Ok(Ctr::Seg(temp))
}
}
pub fn expand_callback(ast: &Seg, _syms: &mut SymTable) -> Result<Ctr, String> {
if let Ctr::Seg(_) = *ast.car {
Ok(*ast.car.clone())
} else {
Err("non list passed to expand!".to_string())
}
}

View file

@ -131,6 +131,7 @@ pub fn let_callback(ast: &Seg, syms: &mut SymTable) -> Result<Ctr, String> {
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<Ctr, String> {
}
if let Err(e) = res {
eprintln!("error evaluating let form: {}", e);
eprintln!("{}", e);
return false;
}

View file

@ -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<String, Symbol>);
@ -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)),
}
}

View file

@ -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)
}
}
}
}

View file

@ -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<Ctr, String> {
@ -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<Ctr, String> {
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<Ctr, String> {
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<Ctr, String> {
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(),
);
}
}

View file

@ -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(),
);
}
}

View file

@ -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(),
);
}
}

View file

@ -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')";

View file

@ -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()
);
}
}

View file

@ -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);
}
}
}