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:
parent
4b587f11ab
commit
dc6342bc74
16 changed files with 575 additions and 677 deletions
12
Readme.org
12
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 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.
|
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 Custom ast pretty print
|
||||||
*** TODO Help function
|
*** TODO Help function
|
||||||
**** TODO add doc string to function form
|
**** DONE add doc string to function form
|
||||||
**** TODO write doc strings to all symbols
|
**** DONE write doc strings to all symbols
|
||||||
**** TODO help function outputs name and help doc
|
**** 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 help function outputs current value or function form pretty printed
|
||||||
*** TODO Eval function
|
*** TODO Eval function
|
||||||
*** TODO Input 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?
|
Optionally return a list of new variables and/or functions?
|
||||||
Will need a concatenate function for tables
|
Will need a concatenate function for tables
|
||||||
*** TODO Main shell calls Load function on arg and exits
|
*** 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 arithmetic operations
|
||||||
**** TODO typecast (int)
|
**** TODO typecast (int)
|
||||||
**** TODO typecast (float)
|
**** TODO typecast (float)
|
||||||
|
|
@ -182,6 +181,7 @@ Will need a concatenate function for tables
|
||||||
**** TODO Optional form of process which allows fd redirecting
|
**** TODO Optional form of process which allows fd redirecting
|
||||||
**** TODO Foreground process TTY
|
**** TODO Foreground process TTY
|
||||||
**** TODO Background processes
|
**** TODO Background processes
|
||||||
|
**** TODO Update config env var docstring with functions loaded or unloaded
|
||||||
*** TODO list operations
|
*** TODO list operations
|
||||||
**** DONE append
|
**** DONE append
|
||||||
**** DONE expand
|
**** DONE expand
|
||||||
|
|
|
||||||
7
snippets/curiosity_expand_callback.rs
Normal file
7
snippets/curiosity_expand_callback.rs
Normal 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())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -29,15 +29,19 @@ fn prompt_default_callback(_: &Seg, _: &mut SymTable) -> Result<Ctr, String> {
|
||||||
|
|
||||||
/* loads defaults, evaluates config script */
|
/* loads defaults, evaluates config script */
|
||||||
pub fn configure(filename: String, syms: &mut SymTable) -> Result<(), String> {
|
pub fn configure(filename: String, syms: &mut SymTable) -> Result<(), String> {
|
||||||
syms.insert(
|
/*syms.insert(
|
||||||
"CFG_RELISH_POSIX".to_string(),
|
"CFG_RELISH_POSIX".to_string(),
|
||||||
Symbol {
|
Symbol {
|
||||||
name: String::from("CFG_RELISH_POSIX"),
|
name: String::from("CFG_RELISH_POSIX"),
|
||||||
args: Args::None,
|
args: Args::None,
|
||||||
conditional_branches: false,
|
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()))),
|
value: ValueType::VarForm(Box::new(Ctr::String("0".to_string()))),
|
||||||
},
|
},
|
||||||
);
|
);*/
|
||||||
|
|
||||||
syms.insert(
|
syms.insert(
|
||||||
"CFG_RELISH_ENV".to_string(),
|
"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"),
|
name: String::from("CFG_RELISH_ENV"),
|
||||||
args: Args::None,
|
args: Args::None,
|
||||||
conditional_branches: false,
|
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()))),
|
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"),
|
name: String::from("default relish prompt"),
|
||||||
args: Args::None,
|
args: Args::None,
|
||||||
conditional_branches: false,
|
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)),
|
value: ValueType::Internal(Rc::new(prompt_default_callback)),
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -39,10 +39,7 @@ pub fn eval(ast: &Seg, syms: &mut SymTable) -> Result<Box<Ctr>, String> {
|
||||||
let mut none = false;
|
let mut none = false;
|
||||||
while !none {
|
while !none {
|
||||||
match &**arg_car {
|
match &**arg_car {
|
||||||
Ctr::Seg(ref inner) => match eval(inner, syms) {
|
Ctr::Seg(ref inner) => car = eval(inner, syms)?,
|
||||||
Ok(res) => car = res,
|
|
||||||
Err(e) => return Err(format!("evaluation error: {}", e)),
|
|
||||||
},
|
|
||||||
|
|
||||||
Ctr::Symbol(ref tok) => {
|
Ctr::Symbol(ref tok) => {
|
||||||
let outer_scope_seg_holder: Seg;
|
let outer_scope_seg_holder: Seg;
|
||||||
|
|
|
||||||
|
|
@ -240,9 +240,9 @@ impl fmt::Display for Ctr {
|
||||||
Ctr::Float(s) => write!(f, "{}", s),
|
Ctr::Float(s) => write!(f, "{}", s),
|
||||||
Ctr::Bool(s) => {
|
Ctr::Bool(s) => {
|
||||||
if *s {
|
if *s {
|
||||||
write!(f, "T")
|
write!(f, "true")
|
||||||
} else {
|
} else {
|
||||||
write!(f, "F")
|
write!(f, "false")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ctr::Seg(s) => write!(f, "{}", s),
|
Ctr::Seg(s) => write!(f, "{}", s),
|
||||||
|
|
|
||||||
260
src/stl.rs
260
src/stl.rs
|
|
@ -36,26 +36,19 @@ pub fn static_stdlib(syms: &mut SymTable) -> Result<(), String> {
|
||||||
name: String::from("append"),
|
name: String::from("append"),
|
||||||
args: Args::Infinite,
|
args: Args::Infinite,
|
||||||
conditional_branches: false,
|
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)),
|
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(
|
syms.insert(
|
||||||
"echo".to_string(),
|
"echo".to_string(),
|
||||||
Symbol {
|
Symbol {
|
||||||
name: String::from("echo"),
|
name: String::from("echo"),
|
||||||
args: Args::Infinite,
|
args: Args::Infinite,
|
||||||
conditional_branches: false,
|
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)),
|
value: ValueType::Internal(Rc::new(_echo_callback)),
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
@ -66,6 +59,14 @@ pub fn static_stdlib(syms: &mut SymTable) -> Result<(), String> {
|
||||||
name: String::from("if"),
|
name: String::from("if"),
|
||||||
args: Args::Lazy(3),
|
args: Args::Lazy(3),
|
||||||
conditional_branches: true,
|
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)),
|
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"),
|
name: String::from("let"),
|
||||||
args: Args::Infinite,
|
args: Args::Infinite,
|
||||||
conditional_branches: true,
|
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)),
|
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"),
|
name: String::from("while"),
|
||||||
args: Args::Infinite,
|
args: Args::Infinite,
|
||||||
conditional_branches: true,
|
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)),
|
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"),
|
name: String::from("circuit"),
|
||||||
args: Args::Infinite,
|
args: Args::Infinite,
|
||||||
conditional_branches: true,
|
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)),
|
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"),
|
name: String::from("and"),
|
||||||
args: Args::Infinite,
|
args: Args::Infinite,
|
||||||
conditional_branches: false,
|
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)),
|
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"),
|
name: String::from("or"),
|
||||||
args: Args::Infinite,
|
args: Args::Infinite,
|
||||||
conditional_branches: false,
|
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)),
|
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"),
|
name: String::from("not"),
|
||||||
args: Args::Strict(vec![Type::Bool]),
|
args: Args::Strict(vec![Type::Bool]),
|
||||||
conditional_branches: false,
|
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)),
|
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?"),
|
name: String::from("eq?"),
|
||||||
args: Args::Infinite,
|
args: Args::Infinite,
|
||||||
conditional_branches: false,
|
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)),
|
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"),
|
name: String::from("toggle"),
|
||||||
args: Args::Lazy(1),
|
args: Args::Lazy(1),
|
||||||
conditional_branches: true,
|
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)),
|
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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -170,6 +226,18 @@ pub fn dynamic_stdlib(syms: &mut SymTable) -> Result<(), String> {
|
||||||
name: String::from("define"),
|
name: String::from("define"),
|
||||||
args: Args::Infinite,
|
args: Args::Infinite,
|
||||||
conditional_branches: true,
|
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(
|
value: ValueType::Internal(Rc::new(
|
||||||
move |ast: &Seg, syms: &mut SymTable| -> Result<Ctr, String> {
|
move |ast: &Seg, syms: &mut SymTable| -> Result<Ctr, String> {
|
||||||
_store_callback(ast, syms, env_cfg_user_form)
|
_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)
|
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> {
|
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 {
|
if let Ctr::Symbol(ref identifier) = *ast.car {
|
||||||
match &*ast.cdr {
|
match &*ast.cdr {
|
||||||
Ctr::Seg(data_tree) if is_var => match eval(&Box::new(data_tree), syms) {
|
// define a symbol
|
||||||
Ok(seg) => {
|
Ctr::Seg(doc_tree) => {
|
||||||
if let Ctr::Seg(ref val) = *seg {
|
if let Ctr::String(ref doc) = *doc_tree.car {
|
||||||
syms.insert(
|
match &*doc_tree.cdr {
|
||||||
identifier.clone(),
|
// define a variable
|
||||||
Symbol {
|
Ctr::Seg(data_tree) if is_var => match eval(&Box::new(data_tree), syms) {
|
||||||
value: ValueType::VarForm(val.car.clone()),
|
Ok(seg) => {
|
||||||
name: identifier.clone(),
|
if let Ctr::Seg(ref val) = *seg {
|
||||||
args: Args::None,
|
syms.insert(
|
||||||
conditional_branches: false,
|
identifier.clone(),
|
||||||
},
|
Symbol {
|
||||||
);
|
value: ValueType::VarForm(val.car.clone()),
|
||||||
if env_cfg {
|
name: identifier.clone(),
|
||||||
env::set_var(identifier.clone(), val.car.to_string());
|
args: Args::None,
|
||||||
}
|
docs: doc.to_owned(),
|
||||||
} else {
|
conditional_branches: false,
|
||||||
return Err("impossible args to export".to_string());
|
},
|
||||||
}
|
);
|
||||||
}
|
if env_cfg {
|
||||||
Err(e) => return Err(format!("couldnt eval symbol: {}", e)),
|
env::set_var(identifier.clone(), val.car.to_string());
|
||||||
},
|
}
|
||||||
Ctr::Seg(data_tree) if !is_var => {
|
} else {
|
||||||
if let Ctr::Seg(ref args) = *data_tree.car {
|
return Err("impossible args to export".to_string());
|
||||||
let mut arg_list = vec![];
|
}
|
||||||
if !args.circuit(&mut |c: &Ctr| -> bool {
|
}
|
||||||
if let Ctr::Symbol(ref arg) = c {
|
Err(e) => return Err(format!("couldnt eval symbol: {}", e)),
|
||||||
arg_list.push(arg.clone());
|
},
|
||||||
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 {
|
// define a function
|
||||||
syms.insert(
|
Ctr::Seg(data_tree) if !is_var => {
|
||||||
identifier.clone(),
|
if let Ctr::Seg(ref args) = *data_tree.car {
|
||||||
Symbol {
|
let mut arg_list = vec![];
|
||||||
value: ValueType::FuncForm(UserFn {
|
if !args.circuit(&mut |c: &Ctr| -> bool {
|
||||||
ast: Box::new(bodies.clone()),
|
if let Ctr::Symbol(ref arg) = c {
|
||||||
arg_syms: arg_list.clone(),
|
arg_list.push(arg.clone());
|
||||||
}),
|
true
|
||||||
name: identifier.clone(),
|
} else if let Ctr::None = c {
|
||||||
args: Args::Lazy(arg_list.len() as u128),
|
// a user cannot type a None
|
||||||
conditional_branches: false,
|
// this case represents no args
|
||||||
},
|
true
|
||||||
);
|
} else {
|
||||||
} else {
|
false
|
||||||
return Err(
|
}
|
||||||
"expected one or more function bodies in function definition"
|
}) {
|
||||||
.to_string(),
|
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 {
|
} 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 => {
|
Ctr::None => {
|
||||||
syms.remove(&identifier.to_string());
|
syms.remove(&identifier.to_string());
|
||||||
if env_cfg {
|
if env_cfg {
|
||||||
env::remove_var(identifier);
|
env::remove_var(identifier);
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
_ => return Err("args not in standard form".to_string()),
|
|
||||||
|
_ => return Err("arguments not in standard form".to_string()),
|
||||||
}
|
}
|
||||||
} else {
|
} 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)
|
Ok(Ctr::None)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -46,11 +46,3 @@ pub fn append_callback(ast: &Seg, _syms: &mut SymTable) -> Result<Ctr, String> {
|
||||||
Ok(Ctr::Seg(temp))
|
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())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -131,6 +131,7 @@ pub fn let_callback(ast: &Seg, syms: &mut SymTable) -> Result<Ctr, String> {
|
||||||
name: name.clone(),
|
name: name.clone(),
|
||||||
args: Args::None,
|
args: Args::None,
|
||||||
conditional_branches: false,
|
conditional_branches: false,
|
||||||
|
docs: "variable used in let form".to_string(),
|
||||||
value: ValueType::VarForm(Box::new(*var_val_res.unwrap().clone())),
|
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 {
|
if let Err(e) = res {
|
||||||
eprintln!("error evaluating let form: {}", e);
|
eprintln!("{}", e);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
29
src/sym.rs
29
src/sym.rs
|
|
@ -19,6 +19,7 @@ use crate::eval::eval;
|
||||||
use crate::segment::{Ctr, Seg, Type};
|
use crate::segment::{Ctr, Seg, Type};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
use std::fmt;
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct SymTable(HashMap<String, Symbol>);
|
pub struct SymTable(HashMap<String, Symbol>);
|
||||||
|
|
@ -66,6 +67,7 @@ pub struct Symbol {
|
||||||
// for internal control flow constructs
|
// for internal control flow constructs
|
||||||
// eval() will not eval the args
|
// eval() will not eval the args
|
||||||
pub conditional_branches: bool,
|
pub conditional_branches: bool,
|
||||||
|
pub docs: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SymTable {
|
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 {
|
impl Symbol {
|
||||||
/* call
|
/* call
|
||||||
* routine is called by eval when a symbol is expanded
|
* routine is called by eval when a symbol is expanded
|
||||||
|
|
@ -229,10 +248,7 @@ impl Symbol {
|
||||||
evaluated_args = args;
|
evaluated_args = args;
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Err(msg) = self.args.validate_inputs(evaluated_args) {
|
self.args.validate_inputs(evaluated_args)?;
|
||||||
return Err(format!("failure to call {}: {}", self.name, msg));
|
|
||||||
}
|
|
||||||
|
|
||||||
match &self.value {
|
match &self.value {
|
||||||
ValueType::VarForm(ref f) => Ok(Box::new(*f.clone())),
|
ValueType::VarForm(ref f) => Ok(Box::new(*f.clone())),
|
||||||
ValueType::Internal(ref f) => Ok(Box::new(f(evaluated_args, syms)?)),
|
ValueType::Internal(ref f) => Ok(Box::new(f(evaluated_args, syms)?)),
|
||||||
|
|
@ -251,6 +267,7 @@ impl Symbol {
|
||||||
name: f.arg_syms[n].clone(),
|
name: f.arg_syms[n].clone(),
|
||||||
value: ValueType::VarForm(Box::new(evaluated_args[n].clone())),
|
value: ValueType::VarForm(Box::new(evaluated_args[n].clone())),
|
||||||
args: Args::None,
|
args: Args::None,
|
||||||
|
docs: format!("local argument to {}", f.arg_syms[n].clone()),
|
||||||
conditional_branches: false,
|
conditional_branches: false,
|
||||||
},
|
},
|
||||||
) {
|
) {
|
||||||
|
|
@ -265,7 +282,7 @@ impl Symbol {
|
||||||
if let Ctr::Seg(ref data) = *iterate.car {
|
if let Ctr::Seg(ref data) = *iterate.car {
|
||||||
match eval(data, syms) {
|
match eval(data, syms) {
|
||||||
Ok(ctr) => result = ctr,
|
Ok(ctr) => result = ctr,
|
||||||
Err(e) => return Err(e),
|
Err(e) => return Err(format!("evaluation failure\n{}", e)),
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let temp = Seg::from_mono(iterate.car.clone());
|
let temp = Seg::from_mono(iterate.car.clone());
|
||||||
|
|
@ -277,7 +294,7 @@ impl Symbol {
|
||||||
result = ctr;
|
result = ctr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(e) => return Err(e),
|
Err(e) => return Err(format!("evaluation failure\n{}", e)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,46 +6,19 @@ mod eval_tests {
|
||||||
fn eval_simple() {
|
fn eval_simple() {
|
||||||
let test_doc = "(1 2)".to_string();
|
let test_doc = "(1 2)".to_string();
|
||||||
let mut syms = SymTable::new();
|
let mut syms = SymTable::new();
|
||||||
match lex(&test_doc) {
|
let doc_tree = lex(&test_doc).unwrap();
|
||||||
Err(e) => {
|
let reduced = *eval(&doc_tree, &mut syms).unwrap();
|
||||||
println!("Lexing error: {}\n", e);
|
assert_eq!(reduced.to_string(), test_doc);
|
||||||
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)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn eval_embedded_lists_no_funcs() {
|
fn eval_embedded_lists_no_funcs() {
|
||||||
let test_doc = "(1 (1 2 3 4 5) 5)".to_string();
|
let test_doc = "(1 (1 2 3 4 5) 5)".to_string();
|
||||||
let mut syms = SymTable::new();
|
let mut syms = SymTable::new();
|
||||||
match lex(&test_doc) {
|
let doc_tree = lex(&test_doc).unwrap();
|
||||||
Err(e) => {
|
let reduced = *eval(&doc_tree, &mut syms).unwrap();
|
||||||
println!("Lexing error: {}\n", e);
|
assert_eq!(reduced.to_string(), test_doc);
|
||||||
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)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
@ -58,6 +31,7 @@ mod eval_tests {
|
||||||
name: String::from("echo"),
|
name: String::from("echo"),
|
||||||
args: Args::Lazy(1),
|
args: Args::Lazy(1),
|
||||||
conditional_branches: false,
|
conditional_branches: false,
|
||||||
|
docs: String::new(),
|
||||||
value: ValueType::FuncForm(UserFn {
|
value: ValueType::FuncForm(UserFn {
|
||||||
arg_syms: vec!["input".to_string()],
|
arg_syms: vec!["input".to_string()],
|
||||||
ast: Box::new(Seg::from(
|
ast: Box::new(Seg::from(
|
||||||
|
|
@ -68,24 +42,9 @@ mod eval_tests {
|
||||||
};
|
};
|
||||||
|
|
||||||
syms.insert(String::from("echo"), test_external_func);
|
syms.insert(String::from("echo"), test_external_func);
|
||||||
|
let doc_tree = lex(&test_doc).unwrap();
|
||||||
match lex(&test_doc) {
|
let reduced = *eval(&doc_tree, &mut syms).unwrap();
|
||||||
Err(e) => {
|
assert_eq!(reduced.to_string(), output);
|
||||||
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)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
@ -98,6 +57,7 @@ mod eval_tests {
|
||||||
name: String::from("echo"),
|
name: String::from("echo"),
|
||||||
args: Args::Lazy(1),
|
args: Args::Lazy(1),
|
||||||
conditional_branches: false,
|
conditional_branches: false,
|
||||||
|
docs: String::new(),
|
||||||
value: ValueType::FuncForm(UserFn {
|
value: ValueType::FuncForm(UserFn {
|
||||||
arg_syms: vec!["input".to_string()],
|
arg_syms: vec!["input".to_string()],
|
||||||
ast: Box::new(Seg::from(
|
ast: Box::new(Seg::from(
|
||||||
|
|
@ -108,46 +68,26 @@ mod eval_tests {
|
||||||
};
|
};
|
||||||
|
|
||||||
syms.insert(String::from("echo"), test_external_func);
|
syms.insert(String::from("echo"), test_external_func);
|
||||||
match lex(&test_doc) {
|
let doc_tree = lex(&test_doc).unwrap();
|
||||||
Err(e) => {
|
let reduced = *eval(&doc_tree, &mut syms).unwrap();
|
||||||
println!("Lexing error: {}\n", e);
|
assert_eq!(reduced.to_string(), output);
|
||||||
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)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn eval_bad_syms() {
|
fn eval_bad_syms() {
|
||||||
let test_doc = "(undefined)".to_string();
|
let test_doc = "(undefined)".to_string();
|
||||||
let mut syms = SymTable::new();
|
let mut syms = SymTable::new();
|
||||||
match lex(&test_doc) {
|
let doc_tree = lex(&test_doc).unwrap();
|
||||||
|
match eval(&doc_tree, &mut syms) {
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
println!("Lexing error: {}\n", e);
|
assert_eq!(e, "error in call to undefined: undefined symbol: undefined")
|
||||||
assert!(false)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(initial_ast) => match eval(&initial_ast, &mut syms) {
|
Ok(reduced) => {
|
||||||
Err(e) => {
|
println!("Eval succeeded when it shouldnt have");
|
||||||
assert_eq!(e, "error in call to undefined: undefined symbol: undefined")
|
println!("see: {}", reduced);
|
||||||
}
|
assert!(false)
|
||||||
|
}
|
||||||
Ok(reduced) => {
|
|
||||||
println!("Eval succeeded when it shouldnt have");
|
|
||||||
println!("see: {}", reduced);
|
|
||||||
assert!(false)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ mod func_tests {
|
||||||
let test_internal_func: Symbol = Symbol {
|
let test_internal_func: Symbol = Symbol {
|
||||||
name: String::from("test_func_in"),
|
name: String::from("test_func_in"),
|
||||||
conditional_branches: false,
|
conditional_branches: false,
|
||||||
|
docs: String::new(),
|
||||||
args: Args::Strict(vec![Type::Bool]),
|
args: Args::Strict(vec![Type::Bool]),
|
||||||
value: ValueType::Internal(Rc::new(
|
value: ValueType::Internal(Rc::new(
|
||||||
|a: &Seg, _: &mut SymTable| -> Result<Ctr, String> {
|
|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));
|
let args = Seg::from(Box::new(Ctr::Bool(true)), Box::new(Ctr::None));
|
||||||
|
|
||||||
syms.insert(String::from("test_func_in"), test_internal_func);
|
syms.insert(String::from("test_func_in"), test_internal_func);
|
||||||
|
if let Ctr::Bool(b) = *syms.call_symbol(&"test_func_in".to_string(), &args, true).unwrap() {
|
||||||
if let Ok(ret) = syms.call_symbol(&"test_func_in".to_string(), &args, true) {
|
assert!(b)
|
||||||
match *ret {
|
|
||||||
Ctr::Bool(b) => assert!(b),
|
|
||||||
_ => {
|
|
||||||
print!("invalid return from func!");
|
|
||||||
assert!(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
print!("call to function failed!");
|
|
||||||
assert!(false);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn decl_and_call_external_func_singlet() {
|
fn decl_and_call_external_func_singlet() {
|
||||||
let mut syms = SymTable::new();
|
let mut syms = SymTable::new();
|
||||||
match lex(&"input".to_string()) {
|
let finner = lex(&"input".to_string()).unwrap();
|
||||||
Err(e) => panic!("{}", e),
|
let test_external_func: Symbol = Symbol {
|
||||||
Ok(finner) => {
|
name: String::from("echo"),
|
||||||
let test_external_func: Symbol = Symbol {
|
conditional_branches: false,
|
||||||
name: String::from("echo"),
|
args: Args::Lazy(1),
|
||||||
conditional_branches: false,
|
docs: String::new(),
|
||||||
args: Args::Lazy(1),
|
value: ValueType::FuncForm(UserFn {
|
||||||
value: ValueType::FuncForm(UserFn {
|
arg_syms: vec!["input".to_string()],
|
||||||
arg_syms: vec!["input".to_string()],
|
ast: finner,
|
||||||
ast: finner,
|
}),
|
||||||
}),
|
};
|
||||||
};
|
|
||||||
|
|
||||||
let args = Seg::from(
|
let args = Seg::from(
|
||||||
Box::new(Ctr::String("test".to_string())),
|
Box::new(Ctr::String("test".to_string())),
|
||||||
Box::new(Ctr::None),
|
Box::new(Ctr::None),
|
||||||
);
|
);
|
||||||
|
|
||||||
syms.insert(String::from("test_func_in"), test_external_func);
|
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() {
|
||||||
match syms.call_symbol(&"test_func_in".to_string(), &args, true) {
|
assert!(b)
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn decl_and_call_external_func_multi_body() {
|
fn decl_and_call_external_func_multi_body() {
|
||||||
let mut syms = SymTable::new();
|
let mut syms = SymTable::new();
|
||||||
match lex(&"(input input)".to_string()) {
|
let finner = lex(&"input".to_string()).unwrap();
|
||||||
Err(e) => panic!("{}", e),
|
let test_external_func: Symbol = Symbol {
|
||||||
Ok(finner) => {
|
name: String::from("echo_2"),
|
||||||
let test_external_func: Symbol = Symbol {
|
conditional_branches: false,
|
||||||
name: String::from("echo_2"),
|
args: Args::Lazy(1),
|
||||||
conditional_branches: false,
|
docs: String::new(),
|
||||||
args: Args::Lazy(1),
|
value: ValueType::FuncForm(UserFn {
|
||||||
value: ValueType::FuncForm(UserFn {
|
arg_syms: vec!["input".to_string()],
|
||||||
arg_syms: vec!["input".to_string()],
|
ast: finner,
|
||||||
ast: finner,
|
}),
|
||||||
}),
|
};
|
||||||
};
|
|
||||||
|
|
||||||
let args = Seg::from(
|
let args = Seg::from(
|
||||||
Box::new(Ctr::String("test".to_string())),
|
Box::new(Ctr::String("test".to_string())),
|
||||||
Box::new(Ctr::None),
|
Box::new(Ctr::None),
|
||||||
);
|
);
|
||||||
|
|
||||||
syms.insert(String::from("echo_2"), test_external_func);
|
syms.insert(String::from("echo_2"), test_external_func);
|
||||||
|
assert_eq!(*syms.call_symbol(&"echo_2".to_string(), &args, true)
|
||||||
match syms.call_symbol(&"echo_2".to_string(), &args, true) {
|
.unwrap()
|
||||||
Ok(ret) => assert_eq!(ret.to_string(), "'test'"),
|
.to_string(),
|
||||||
Err(e) => {
|
"'test'".to_string());
|
||||||
print!("Call to function failed: {}\n", e);
|
|
||||||
assert!(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
@ -121,6 +90,7 @@ mod func_tests {
|
||||||
name: String::from("test_inner"),
|
name: String::from("test_inner"),
|
||||||
conditional_branches: false,
|
conditional_branches: false,
|
||||||
args: Args::Strict(vec![Type::Bool]),
|
args: Args::Strict(vec![Type::Bool]),
|
||||||
|
docs: String::new(),
|
||||||
value: ValueType::Internal(Rc::new(
|
value: ValueType::Internal(Rc::new(
|
||||||
|a: &Seg, _: &mut SymTable| -> Result<Ctr, String> {
|
|a: &Seg, _: &mut SymTable| -> Result<Ctr, String> {
|
||||||
let inner = a;
|
let inner = a;
|
||||||
|
|
@ -137,39 +107,25 @@ mod func_tests {
|
||||||
)),
|
)),
|
||||||
};
|
};
|
||||||
|
|
||||||
match lex(&"((test_inner true))".to_string()) {
|
let finner = lex(&"((test_inner true))".to_string()).unwrap();
|
||||||
Err(e) => panic!("{}", e),
|
let outer_func: Symbol = Symbol {
|
||||||
Ok(finner) => {
|
name: String::from("test_outer"),
|
||||||
let outer_func: Symbol = Symbol {
|
conditional_branches: false,
|
||||||
name: String::from("test_outer"),
|
args: Args::Lazy(1),
|
||||||
conditional_branches: false,
|
docs: String::new(),
|
||||||
args: Args::Lazy(1),
|
value: ValueType::FuncForm(UserFn {
|
||||||
value: ValueType::FuncForm(UserFn {
|
arg_syms: vec!["input".to_string()],
|
||||||
arg_syms: vec!["input".to_string()],
|
ast: finner,
|
||||||
ast: finner,
|
}),
|
||||||
}),
|
};
|
||||||
};
|
|
||||||
|
|
||||||
let args = Seg::from(Box::new(Ctr::Bool(true)), Box::new(Ctr::None));
|
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_inner"), inner_func);
|
syms.insert(String::from("test_outer"), outer_func);
|
||||||
syms.insert(String::from("test_outer"), outer_func);
|
assert_eq!(syms.call_symbol(&"test_outer".to_string(), &args, true)
|
||||||
|
.unwrap()
|
||||||
match syms.call_symbol(&"test_outer".to_string(), &args, true) {
|
.to_string(),
|
||||||
Ok(ret) => match *ret {
|
"'test'".to_string());
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
@ -179,6 +135,7 @@ mod func_tests {
|
||||||
name: String::from("test_func_in"),
|
name: String::from("test_func_in"),
|
||||||
conditional_branches: false,
|
conditional_branches: false,
|
||||||
args: Args::Strict(vec![Type::Bool]),
|
args: Args::Strict(vec![Type::Bool]),
|
||||||
|
docs: String::new(),
|
||||||
value: ValueType::Internal(Rc::new(
|
value: ValueType::Internal(Rc::new(
|
||||||
|a: &Seg, _: &mut SymTable| -> Result<Ctr, String> {
|
|a: &Seg, _: &mut SymTable| -> Result<Ctr, String> {
|
||||||
let inner = a;
|
let inner = a;
|
||||||
|
|
@ -193,75 +150,66 @@ mod func_tests {
|
||||||
let args = Seg::from(Box::new(Ctr::Integer(1)), Box::new(Ctr::None));
|
let args = Seg::from(Box::new(Ctr::Integer(1)), Box::new(Ctr::None));
|
||||||
|
|
||||||
syms.insert(String::from("test_func_in"), test_internal_func);
|
syms.insert(String::from("test_func_in"), test_internal_func);
|
||||||
|
assert_eq!(
|
||||||
if let Err(s) = syms.call_symbol(&"test_func_in".to_string(), &args, true) {
|
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");
|
.err()
|
||||||
} else {
|
.unwrap(),
|
||||||
print!("call to function succeeded (shouldnt have)");
|
"arg 1 expected to be bool".to_string(),
|
||||||
assert!(false);
|
);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn too_many_args() {
|
fn too_many_args() {
|
||||||
let mut syms = SymTable::new();
|
let mut syms = SymTable::new();
|
||||||
match lex(&"(input)".to_string()) {
|
let finner = lex(&"(input)".to_string()).unwrap();
|
||||||
Err(e) => panic!("{}", e),
|
let test_external_func: Symbol = Symbol {
|
||||||
Ok(finner) => {
|
name: String::from("echo"),
|
||||||
let test_external_func: Symbol = Symbol {
|
conditional_branches: false,
|
||||||
name: String::from("echo"),
|
args: Args::Lazy(1),
|
||||||
conditional_branches: false,
|
docs: String::new(),
|
||||||
args: Args::Lazy(1),
|
value: ValueType::FuncForm(UserFn {
|
||||||
value: ValueType::FuncForm(UserFn {
|
arg_syms: vec!["input".to_string()],
|
||||||
arg_syms: vec!["input".to_string()],
|
ast: finner,
|
||||||
ast: finner,
|
}),
|
||||||
}),
|
};
|
||||||
};
|
|
||||||
|
|
||||||
let args = Seg::from(
|
let args = Seg::from(
|
||||||
Box::new(Ctr::String("test".to_string())),
|
Box::new(Ctr::String("test".to_string())),
|
||||||
Box::new(Ctr::Seg(Seg::from_mono(Box::new(Ctr::Integer(1))))),
|
Box::new(Ctr::Seg(Seg::from_mono(Box::new(Ctr::Integer(1))))),
|
||||||
);
|
);
|
||||||
|
|
||||||
syms.insert(String::from("test_func_in"), test_external_func);
|
syms.insert(String::from("test_func_in"), test_external_func);
|
||||||
|
assert_eq!(
|
||||||
if let Err(s) = syms.call_symbol(&"test_func_in".to_string(), &args, true) {
|
syms.call_symbol(&"test_func_in".to_string(), &args, true)
|
||||||
assert_eq!(s, "failure to call echo: expected 1 args. Got 2.");
|
.err()
|
||||||
} else {
|
.unwrap(),
|
||||||
print!("call to function succeeded (shouldnt have)");
|
"expected 1 args. Got 2.".to_string(),
|
||||||
assert!(false);
|
);
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn too_few_args() {
|
fn too_few_args() {
|
||||||
let mut syms = SymTable::new();
|
let mut syms = SymTable::new();
|
||||||
match lex(&"(input)".to_string()) {
|
let finner = lex(&"(input)".to_string()).unwrap();
|
||||||
Err(e) => panic!("{}", e),
|
let test_external_func: Symbol = Symbol {
|
||||||
Ok(finner) => {
|
name: String::from("echo"),
|
||||||
let test_external_func: Symbol = Symbol {
|
conditional_branches: false,
|
||||||
name: String::from("echo"),
|
args: Args::Lazy(1),
|
||||||
conditional_branches: false,
|
docs: String::new(),
|
||||||
args: Args::Lazy(1),
|
value: ValueType::FuncForm(UserFn {
|
||||||
value: ValueType::FuncForm(UserFn {
|
arg_syms: vec!["input".to_string()],
|
||||||
arg_syms: vec!["input".to_string()],
|
ast: finner,
|
||||||
ast: finner,
|
}),
|
||||||
}),
|
};
|
||||||
};
|
|
||||||
|
|
||||||
let args = Seg::new();
|
let args = Seg::new();
|
||||||
syms.insert(String::from("test_func_in"), test_external_func);
|
syms.insert(String::from("test_func_in"), test_external_func);
|
||||||
|
assert_eq!(
|
||||||
if let Err(s) = syms.call_symbol(&"test_func_in".to_string(), &args, true) {
|
syms.call_symbol(&"test_func_in".to_string(), &args, true)
|
||||||
assert_eq!(s, "failure to call echo: expected 1 args. Got 0.");
|
.err()
|
||||||
} else {
|
.unwrap(),
|
||||||
print!("call to function succeeded (shouldnt have)");
|
"expected 1 args. Got 0.".to_string(),
|
||||||
assert!(false);
|
);
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
@ -271,6 +219,7 @@ mod func_tests {
|
||||||
name: String::from("test_func_in"),
|
name: String::from("test_func_in"),
|
||||||
conditional_branches: false,
|
conditional_branches: false,
|
||||||
args: Args::Strict(vec![Type::Bool]),
|
args: Args::Strict(vec![Type::Bool]),
|
||||||
|
docs: String::new(),
|
||||||
value: ValueType::Internal(Rc::new(
|
value: ValueType::Internal(Rc::new(
|
||||||
|a: &Seg, _: &mut SymTable| -> Result<Ctr, String> {
|
|a: &Seg, _: &mut SymTable| -> Result<Ctr, String> {
|
||||||
let inner = a;
|
let inner = a;
|
||||||
|
|
@ -288,15 +237,12 @@ mod func_tests {
|
||||||
);
|
);
|
||||||
|
|
||||||
syms.insert(String::from("test_func_in"), test_internal_func);
|
syms.insert(String::from("test_func_in"), test_internal_func);
|
||||||
|
assert_eq!(
|
||||||
if let Err(s) = syms.call_symbol(&"test_func_in".to_string(), &args, true) {
|
syms.call_symbol(&"test_func_in".to_string(), &args, true)
|
||||||
assert_eq!(
|
.err()
|
||||||
s,
|
.unwrap(),
|
||||||
"error in call to undefined-symbol: undefined symbol: undefined-symbol"
|
"error in call to undefined-symbol: undefined symbol: undefined-symbol"
|
||||||
);
|
.to_string(),
|
||||||
} else {
|
);
|
||||||
print!("call to function succeeded (shouldnt have)");
|
|
||||||
assert!(false);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,131 +4,86 @@ mod lex_tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_lex_basic_pair() {
|
fn test_lex_basic_pair() {
|
||||||
let document = String::from("(hello 'world')");
|
let document = String::from("(hello 'world')");
|
||||||
match lex(&document) {
|
assert_eq!(
|
||||||
Ok(tree) => {
|
lex(&document).unwrap().to_string(),
|
||||||
assert_eq!(tree.to_string(), document);
|
document
|
||||||
}
|
);
|
||||||
Err(s) => {
|
|
||||||
print!("{}\n", s);
|
|
||||||
assert!(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_lex_basic_list() {
|
fn test_lex_basic_list() {
|
||||||
let document = String::from("(hello 'world' 1 2 3)");
|
let document = String::from("(hello 'world' 1 2 3)");
|
||||||
match lex(&document) {
|
assert_eq!(
|
||||||
Ok(tree) => {
|
lex(&document).unwrap().to_string(),
|
||||||
assert_eq!(tree.to_string(), document);
|
document
|
||||||
}
|
);
|
||||||
Err(s) => {
|
|
||||||
print!("{}\n", s);
|
|
||||||
assert!(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_lex_complex_list() {
|
fn test_lex_complex_list() {
|
||||||
let document = String::from("(hello 'world' (1 2 (1 2 3)) 1 2 3)");
|
let document = String::from("(hello 'world' (1 2 (1 2 3)) 1 2 3)");
|
||||||
match lex(&document) {
|
assert_eq!(
|
||||||
Ok(tree) => {
|
lex(&document).unwrap().to_string(),
|
||||||
assert_eq!(tree.to_string(), document);
|
document
|
||||||
}
|
);
|
||||||
Err(s) => {
|
|
||||||
print!("{}\n", s);
|
|
||||||
assert!(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_bad_symbol() {
|
fn test_bad_symbol() {
|
||||||
let document = String::from("(as;dd)");
|
let document = String::from("(as;dd)");
|
||||||
let output: &str = "Problem lexing document: \"Unparsable token: as;dd\"";
|
let output: &str = "Problem lexing document: \"Unparsable token: as;dd\"";
|
||||||
match lex(&document) {
|
assert_eq!(
|
||||||
Ok(tree) => {
|
lex(&document).err().unwrap(),
|
||||||
print!("Bad token yielded: {}\n", tree.to_string());
|
output.to_string(),
|
||||||
assert!(false);
|
);
|
||||||
}
|
|
||||||
Err(s) => {
|
|
||||||
assert_eq!(s, output);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_list_delim_in_str() {
|
fn test_list_delim_in_str() {
|
||||||
let document = String::from("('(')");
|
let document = String::from("('(')");
|
||||||
match lex(&document) {
|
assert_eq!(
|
||||||
Ok(tree) => {
|
lex(&document).unwrap().to_string(),
|
||||||
assert_eq!(tree.to_string(), document);
|
document
|
||||||
}
|
);
|
||||||
Err(s) => {
|
|
||||||
print!("{}\n", s);
|
|
||||||
assert!(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_empty_string() {
|
fn test_empty_string() {
|
||||||
let document = String::from("('')");
|
let document = String::from("('')");
|
||||||
match lex(&document) {
|
assert_eq!(
|
||||||
Ok(tree) => {
|
lex(&document).unwrap().to_string(),
|
||||||
assert_eq!(tree.to_string(), document);
|
document
|
||||||
}
|
);
|
||||||
Err(s) => {
|
|
||||||
print!("{}\n", s);
|
|
||||||
assert!(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_unmatched_list_delim_flat() {
|
fn test_unmatched_list_delim_flat() {
|
||||||
let document = String::from("(one two");
|
let document = String::from("(one two");
|
||||||
let output: &str = "Problem lexing document: \"Unmatched list delimiter in input\"";
|
let output: &str = "Problem lexing document: \"Unmatched list delimiter in input\"";
|
||||||
match lex(&document) {
|
assert_eq!(
|
||||||
Ok(tree) => {
|
lex(&document).err().unwrap(),
|
||||||
print!("Bad token yielded: {}\n", tree.to_string());
|
output.to_string(),
|
||||||
assert!(false);
|
);
|
||||||
}
|
|
||||||
Err(s) => {
|
|
||||||
assert_eq!(s, output);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_unmatched_list_delim_complex() {
|
fn test_unmatched_list_delim_complex() {
|
||||||
let document = String::from("(one two (three)");
|
let document = String::from("(one two (three)");
|
||||||
let output: &str = "Problem lexing document: \"Unmatched list delimiter in input\"";
|
let output: &str = "Problem lexing document: \"Unmatched list delimiter in input\"";
|
||||||
match lex(&document) {
|
assert_eq!(
|
||||||
Ok(tree) => {
|
lex(&document).err().unwrap(),
|
||||||
print!("Bad token yielded: {}\n", tree);
|
output.to_string(),
|
||||||
assert!(false);
|
);
|
||||||
}
|
|
||||||
Err(s) => {
|
|
||||||
assert_eq!(s, output);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_comment() {
|
fn test_comment() {
|
||||||
let document = String::from("#!/bin/relish\n(one two)");
|
let document = String::from("#!/bin/relish\n(one two)");
|
||||||
let output: &str = "(one two)";
|
let output: &str = "(one two)";
|
||||||
match lex(&document) {
|
assert_eq!(
|
||||||
Ok(tree) => {
|
lex(&document).unwrap().to_string(),
|
||||||
assert_eq!(tree.to_string(), output);
|
output.to_string(),
|
||||||
}
|
);
|
||||||
Err(s) => {
|
|
||||||
print!("{}\n", s);
|
|
||||||
assert!(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
@ -136,44 +91,30 @@ mod lex_tests {
|
||||||
let document =
|
let document =
|
||||||
String::from("#!/bin/relish\n((one two)# another doc comment\n(three four))");
|
String::from("#!/bin/relish\n((one two)# another doc comment\n(three four))");
|
||||||
let output: &str = "((one two) (three four))";
|
let output: &str = "((one two) (three four))";
|
||||||
match lex(&document) {
|
assert_eq!(
|
||||||
Ok(tree) => {
|
lex(&document).unwrap().to_string(),
|
||||||
assert_eq!(tree.to_string(), output.to_string());
|
output.to_string(),
|
||||||
}
|
);
|
||||||
Err(s) => {
|
|
||||||
print!("{}\n", s);
|
|
||||||
assert!(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_inline_comment() {
|
fn test_inline_comment() {
|
||||||
let document = String::from("#!/bin/relish\n((one two)\n# another doc comment\nthree)");
|
let document = String::from("#!/bin/relish\n((one two)\n# another doc comment\nthree)");
|
||||||
let output: &str = "((one two) three)";
|
let output: &str = "((one two) three)";
|
||||||
match lex(&document) {
|
assert_eq!(
|
||||||
Ok(tree) => {
|
lex(&document).unwrap().to_string(),
|
||||||
assert_eq!(tree.to_string(), output.to_string());
|
output.to_string(),
|
||||||
}
|
);
|
||||||
Err(s) => {
|
|
||||||
print!("{}\n", s);
|
|
||||||
assert!(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_bad_token_list() {
|
fn test_bad_token_list() {
|
||||||
let document = String::from("(one t(wo)");
|
let document = String::from("(one t(wo)");
|
||||||
let output: &str = "Problem lexing document: \"list started in middle of another token\"";
|
let output: &str = "Problem lexing document: \"list started in middle of another token\"";
|
||||||
match lex(&document) {
|
assert_eq!(
|
||||||
Ok(tree) => {
|
lex(&document).err().unwrap(),
|
||||||
print!("Bad token yielded: {}\n", tree);
|
output.to_string(),
|
||||||
assert!(false);
|
);
|
||||||
}
|
|
||||||
Err(s) => {
|
|
||||||
assert_eq!(s, output);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
mod append_lib_tests {
|
mod append_lib_tests {
|
||||||
use relish::ast::{eval, lex, Ctr, SymTable};
|
use relish::ast::{eval, lex, SymTable};
|
||||||
use relish::stdlib::{dynamic_stdlib, static_stdlib};
|
use relish::stdlib::{dynamic_stdlib, static_stdlib};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
@ -10,14 +10,10 @@ mod append_lib_tests {
|
||||||
let mut syms = SymTable::new();
|
let mut syms = SymTable::new();
|
||||||
static_stdlib(&mut syms).unwrap();
|
static_stdlib(&mut syms).unwrap();
|
||||||
dynamic_stdlib(&mut syms).unwrap();
|
dynamic_stdlib(&mut syms).unwrap();
|
||||||
|
assert_eq!(
|
||||||
if let Ok(tree) = lex(&document.to_string()) {
|
*eval(&lex(&document.to_string()).unwrap(), &mut syms).unwrap().to_string(),
|
||||||
if let Ctr::Seg(ref s) = *eval(&tree, &mut syms).unwrap() {
|
result.to_string(),
|
||||||
assert_eq!(s.to_string(), result);
|
);
|
||||||
}
|
|
||||||
} else {
|
|
||||||
assert!(false)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
@ -29,13 +25,10 @@ mod append_lib_tests {
|
||||||
static_stdlib(&mut syms).unwrap();
|
static_stdlib(&mut syms).unwrap();
|
||||||
dynamic_stdlib(&mut syms).unwrap();
|
dynamic_stdlib(&mut syms).unwrap();
|
||||||
|
|
||||||
if let Ok(tree) = lex(&document.to_string()) {
|
assert_eq!(
|
||||||
if let Ctr::Seg(ref s) = *eval(&tree, &mut syms).unwrap() {
|
*eval(&lex(&document.to_string()).unwrap(), &mut syms).unwrap().to_string(),
|
||||||
assert_eq!(s.to_string(), result);
|
result.to_string(),
|
||||||
}
|
);
|
||||||
} else {
|
|
||||||
assert!(false)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
@ -47,13 +40,10 @@ mod append_lib_tests {
|
||||||
static_stdlib(&mut syms).unwrap();
|
static_stdlib(&mut syms).unwrap();
|
||||||
dynamic_stdlib(&mut syms).unwrap();
|
dynamic_stdlib(&mut syms).unwrap();
|
||||||
|
|
||||||
if let Ok(tree) = lex(&document.to_string()) {
|
assert_eq!(
|
||||||
if let Ctr::Seg(ref s) = *eval(&tree, &mut syms).unwrap() {
|
*eval(&lex(&document.to_string()).unwrap(), &mut syms).unwrap().to_string(),
|
||||||
assert_eq!(s.to_string(), result);
|
result.to_string(),
|
||||||
}
|
);
|
||||||
} else {
|
|
||||||
assert!(false);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
@ -65,13 +55,10 @@ mod append_lib_tests {
|
||||||
static_stdlib(&mut syms).unwrap();
|
static_stdlib(&mut syms).unwrap();
|
||||||
dynamic_stdlib(&mut syms).unwrap();
|
dynamic_stdlib(&mut syms).unwrap();
|
||||||
|
|
||||||
if let Ok(tree) = lex(&document.to_string()) {
|
assert_eq!(
|
||||||
if let Ctr::Seg(ref s) = *eval(&tree, &mut syms).unwrap() {
|
*eval(&lex(&document.to_string()).unwrap(), &mut syms).unwrap().to_string(),
|
||||||
assert_eq!(s.to_string(), result);
|
result.to_string(),
|
||||||
}
|
);
|
||||||
} else {
|
|
||||||
assert!(false);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
@ -83,12 +70,9 @@ mod append_lib_tests {
|
||||||
static_stdlib(&mut syms).unwrap();
|
static_stdlib(&mut syms).unwrap();
|
||||||
dynamic_stdlib(&mut syms).unwrap();
|
dynamic_stdlib(&mut syms).unwrap();
|
||||||
|
|
||||||
if let Ok(tree) = lex(&document.to_string()) {
|
assert_eq!(
|
||||||
if let Ctr::Seg(ref s) = *eval(&tree, &mut syms).unwrap() {
|
*eval(&lex(&document.to_string()).unwrap(), &mut syms).unwrap().to_string(),
|
||||||
assert_eq!(s.to_string(), result);
|
result.to_string(),
|
||||||
}
|
);
|
||||||
} else {
|
|
||||||
assert!(false);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,146 +5,99 @@ mod bool_lib_tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_and_true_chain() {
|
fn test_and_true_chain() {
|
||||||
let document = "(and true true true true true)";
|
let document = "(and true true true true true)";
|
||||||
let result = true;
|
let result = "true";
|
||||||
|
|
||||||
let mut syms = SymTable::new();
|
let mut syms = SymTable::new();
|
||||||
static_stdlib(&mut syms).unwrap();
|
static_stdlib(&mut syms).unwrap();
|
||||||
dynamic_stdlib(&mut syms).unwrap();
|
dynamic_stdlib(&mut syms).unwrap();
|
||||||
|
assert_eq!(
|
||||||
if let Ok(tree) = lex(&document.to_string()) {
|
*eval(&lex(&document.to_string()).unwrap(), &mut syms).unwrap().to_string(),
|
||||||
if let Ctr::Bool(b) = *eval(&tree, &mut syms).unwrap() {
|
result.to_string(),
|
||||||
assert_eq!(b, result);
|
);
|
||||||
} else {
|
|
||||||
assert!(false);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
assert!(false);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_and_true_chain_with_false() {
|
fn test_and_true_chain_with_false() {
|
||||||
let document = "(and true true false true true)";
|
let document = "(and true true false true true)";
|
||||||
let result = false;
|
let result = "false";
|
||||||
|
|
||||||
let mut syms = SymTable::new();
|
let mut syms = SymTable::new();
|
||||||
static_stdlib(&mut syms).unwrap();
|
static_stdlib(&mut syms).unwrap();
|
||||||
dynamic_stdlib(&mut syms).unwrap();
|
dynamic_stdlib(&mut syms).unwrap();
|
||||||
|
assert_eq!(
|
||||||
if let Ok(tree) = lex(&document.to_string()) {
|
*eval(&lex(&document.to_string()).unwrap(), &mut syms).unwrap().to_string(),
|
||||||
if let Ctr::Bool(i) = *eval(&tree, &mut syms).unwrap() {
|
result.to_string(),
|
||||||
assert_eq!(i, result);
|
);
|
||||||
} else {
|
|
||||||
assert!(false);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
assert!(false);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_and_false_chain() {
|
fn test_and_false_chain() {
|
||||||
let document = "(and false false false false false)";
|
let document = "(and false false false false false)";
|
||||||
let result = false;
|
let result = "false";
|
||||||
|
|
||||||
let mut syms = SymTable::new();
|
let mut syms = SymTable::new();
|
||||||
static_stdlib(&mut syms).unwrap();
|
static_stdlib(&mut syms).unwrap();
|
||||||
dynamic_stdlib(&mut syms).unwrap();
|
dynamic_stdlib(&mut syms).unwrap();
|
||||||
|
assert_eq!(
|
||||||
if let Ok(tree) = lex(&document.to_string()) {
|
*eval(&lex(&document.to_string()).unwrap(), &mut syms).unwrap().to_string(),
|
||||||
if let Ctr::Bool(i) = *eval(&tree, &mut syms).unwrap() {
|
result.to_string(),
|
||||||
assert_eq!(i, result);
|
);
|
||||||
} else {
|
|
||||||
assert!(false);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
assert!(false);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_or_true_chain() {
|
fn test_or_true_chain() {
|
||||||
let document = "(or true true true true true)";
|
let document = "(or true true true true true)";
|
||||||
let result = true;
|
let result = "true";
|
||||||
|
|
||||||
let mut syms = SymTable::new();
|
let mut syms = SymTable::new();
|
||||||
static_stdlib(&mut syms).unwrap();
|
static_stdlib(&mut syms).unwrap();
|
||||||
dynamic_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]
|
#[test]
|
||||||
fn test_or_true_chain_with_false() {
|
fn test_or_true_chain_with_false() {
|
||||||
let document = "(or true true false true true)";
|
let document = "(or true true false true true)";
|
||||||
let result = true;
|
let result = "true";
|
||||||
|
|
||||||
let mut syms = SymTable::new();
|
let mut syms = SymTable::new();
|
||||||
static_stdlib(&mut syms).unwrap();
|
static_stdlib(&mut syms).unwrap();
|
||||||
dynamic_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]
|
#[test]
|
||||||
fn test_or_false_chain() {
|
fn test_or_false_chain() {
|
||||||
let document = "(or false false false false)";
|
let document = "(or false false false false false)";
|
||||||
let result = false;
|
let result = "false";
|
||||||
|
|
||||||
let mut syms = SymTable::new();
|
let mut syms = SymTable::new();
|
||||||
static_stdlib(&mut syms).unwrap();
|
static_stdlib(&mut syms).unwrap();
|
||||||
dynamic_stdlib(&mut syms).unwrap();
|
dynamic_stdlib(&mut syms).unwrap();
|
||||||
|
assert_eq!(
|
||||||
if let Ok(tree) = lex(&document.to_string()) {
|
*eval(&lex(&document.to_string()).unwrap(), &mut syms).unwrap().to_string(),
|
||||||
if let Ctr::Bool(i) = *eval(&tree, &mut syms).unwrap() {
|
result.to_string(),
|
||||||
assert_eq!(i, result);
|
);
|
||||||
} else {
|
|
||||||
assert!(false);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
assert!(false);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_not() {
|
fn test_not() {
|
||||||
let document = "(not true)";
|
let document = "(not true)";
|
||||||
let result = false;
|
let result = "false";
|
||||||
|
|
||||||
let mut syms = SymTable::new();
|
let mut syms = SymTable::new();
|
||||||
static_stdlib(&mut syms).unwrap();
|
static_stdlib(&mut syms).unwrap();
|
||||||
dynamic_stdlib(&mut syms).unwrap();
|
dynamic_stdlib(&mut syms).unwrap();
|
||||||
|
assert_eq!(
|
||||||
if let Ok(tree) = lex(&document.to_string()) {
|
*eval(&lex(&document.to_string()).unwrap(), &mut syms).unwrap().to_string(),
|
||||||
if let Ctr::Bool(i) = *eval(&tree, &mut syms).unwrap() {
|
result.to_string(),
|
||||||
assert_eq!(i, result);
|
);
|
||||||
} else {
|
|
||||||
assert!(false);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
assert!(false);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_toggle_a_bool() {
|
fn test_toggle_a_bool() {
|
||||||
let document = "(def tester true)";
|
let document = "(def tester '' true)";
|
||||||
let change = "(toggle tester)";
|
let change = "(toggle tester)";
|
||||||
let check = "(tester)";
|
let check = "(tester)";
|
||||||
|
|
||||||
|
|
@ -183,7 +136,7 @@ mod bool_lib_tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_toggle_errors_dont_lose_vars() {
|
fn test_toggle_errors_dont_lose_vars() {
|
||||||
let document = "(def tester 'oops')";
|
let document = "(def tester '' 'oops')";
|
||||||
let change = "(toggle tester)";
|
let change = "(toggle tester)";
|
||||||
let check = "(tester)";
|
let check = "(tester)";
|
||||||
|
|
||||||
|
|
@ -216,7 +169,7 @@ mod bool_lib_tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_toggle_errors_dont_lose_funcs() {
|
fn test_toggle_errors_dont_lose_funcs() {
|
||||||
let document = "(def tester (oops) oops)";
|
let document = "(def tester '' (oops) oops)";
|
||||||
let change = "(toggle tester)";
|
let change = "(toggle tester)";
|
||||||
let check = "(tester '1')";
|
let check = "(tester '1')";
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,66 +1,44 @@
|
||||||
mod control_lib_tests {
|
mod control_lib_tests {
|
||||||
use relish::ast::{eval, lex, Ctr, SymTable};
|
use relish::ast::{eval, lex, SymTable};
|
||||||
use relish::stdlib::{dynamic_stdlib, static_stdlib};
|
use relish::stdlib::{dynamic_stdlib, static_stdlib};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_if_first_case_singlet() {
|
fn test_if_first_case_singlet() {
|
||||||
let document = "(if true 1 2)";
|
let document = "(if true 1 2)";
|
||||||
let result = 1;
|
let result = 1;
|
||||||
|
|
||||||
let mut syms = SymTable::new();
|
let mut syms = SymTable::new();
|
||||||
static_stdlib(&mut syms).unwrap();
|
static_stdlib(&mut syms).unwrap();
|
||||||
dynamic_stdlib(&mut syms).unwrap();
|
dynamic_stdlib(&mut syms).unwrap();
|
||||||
|
assert_eq!(
|
||||||
if let Ok(tree) = lex(&document.to_string()) {
|
*eval(&lex(&document.to_string()).unwrap(), &mut syms).unwrap().to_string(),
|
||||||
if let Ctr::Integer(i) = *eval(&tree, &mut syms).unwrap() {
|
result.to_string(),
|
||||||
assert_eq!(i, result);
|
);
|
||||||
} else {
|
|
||||||
assert!(false);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
assert!(false);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_if_second_case_singlet() {
|
fn test_if_second_case_singlet() {
|
||||||
let document = "(if false 1 2)";
|
let document = "(if false 1 2)";
|
||||||
let result = 2;
|
let result = 2;
|
||||||
|
|
||||||
let mut syms = SymTable::new();
|
let mut syms = SymTable::new();
|
||||||
static_stdlib(&mut syms).unwrap();
|
static_stdlib(&mut syms).unwrap();
|
||||||
dynamic_stdlib(&mut syms).unwrap();
|
dynamic_stdlib(&mut syms).unwrap();
|
||||||
|
assert_eq!(
|
||||||
if let Ok(tree) = lex(&document.to_string()) {
|
*eval(&lex(&document.to_string()).unwrap(), &mut syms).unwrap().to_string(),
|
||||||
if let Ctr::Integer(i) = *eval(&tree, &mut syms).unwrap() {
|
result.to_string(),
|
||||||
assert_eq!(i, result);
|
);
|
||||||
} else {
|
|
||||||
eprintln!("{}", *eval(&tree, &mut syms).unwrap());
|
|
||||||
assert!(false);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
assert!(false);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_complex_case_call() {
|
fn test_complex_case_call() {
|
||||||
let document = "(if true (append () 1) 2)";
|
let document = "(if true (append () 1) 2)";
|
||||||
let result = "(1)";
|
let result = "(1)";
|
||||||
|
|
||||||
let mut syms = SymTable::new();
|
let mut syms = SymTable::new();
|
||||||
static_stdlib(&mut syms).unwrap();
|
static_stdlib(&mut syms).unwrap();
|
||||||
dynamic_stdlib(&mut syms).unwrap();
|
dynamic_stdlib(&mut syms).unwrap();
|
||||||
|
assert_eq!(
|
||||||
if let Ok(tree) = lex(&document.to_string()) {
|
*eval(&lex(&document.to_string()).unwrap(), &mut syms).unwrap().to_string(),
|
||||||
if let Ctr::Seg(ref i) = *eval(&tree, &mut syms).unwrap() {
|
result.to_string(),
|
||||||
assert_eq!(i.to_string(), result);
|
);
|
||||||
} else {
|
|
||||||
assert!(false);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
assert!(false);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
@ -70,45 +48,30 @@ mod control_lib_tests {
|
||||||
(temp (append () temp '2')))
|
(temp (append () temp '2')))
|
||||||
temp)";
|
temp)";
|
||||||
let result = "('1' '2')";
|
let result = "('1' '2')";
|
||||||
|
|
||||||
let mut syms = SymTable::new();
|
let mut syms = SymTable::new();
|
||||||
static_stdlib(&mut syms).unwrap();
|
static_stdlib(&mut syms).unwrap();
|
||||||
dynamic_stdlib(&mut syms).unwrap();
|
dynamic_stdlib(&mut syms).unwrap();
|
||||||
|
assert_eq!(
|
||||||
if let Ok(tree) = lex(&document.to_string()) {
|
*eval(&lex(&document.to_string()).unwrap(), &mut syms).unwrap().to_string(),
|
||||||
if let Ctr::Seg(ref i) = *eval(&tree, &mut syms).unwrap() {
|
result.to_string(),
|
||||||
assert_eq!(i.to_string(), result);
|
);
|
||||||
} else {
|
|
||||||
assert!(false);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
assert!(false);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_let_multibody_evals() {
|
fn test_let_multibody_evals() {
|
||||||
let document = "(let ((temp '1')) temp (append () temp '2'))";
|
let document = "(let ((temp '1')) temp (append () temp '2'))";
|
||||||
let result = "('1' '2')";
|
let result = "('1' '2')";
|
||||||
|
|
||||||
let mut syms = SymTable::new();
|
let mut syms = SymTable::new();
|
||||||
static_stdlib(&mut syms).unwrap();
|
static_stdlib(&mut syms).unwrap();
|
||||||
dynamic_stdlib(&mut syms).unwrap();
|
dynamic_stdlib(&mut syms).unwrap();
|
||||||
|
assert_eq!(
|
||||||
if let Ok(tree) = lex(&document.to_string()) {
|
*eval(&lex(&document.to_string()).unwrap(), &mut syms).unwrap().to_string(),
|
||||||
if let Ctr::Seg(ref i) = *eval(&tree, &mut syms).unwrap() {
|
result.to_string(),
|
||||||
assert_eq!(i.to_string(), result);
|
);
|
||||||
} else {
|
|
||||||
assert!(false);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
assert!(false);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_let_multiphase_local_multibody_evals() {
|
fn test_let_multiphase_local_multibody_evals() {
|
||||||
// prints 'first body' and then returns ('1' '2' '3')
|
|
||||||
let document = "(let (
|
let document = "(let (
|
||||||
(temp '1')
|
(temp '1')
|
||||||
(temp (append () temp '2')))
|
(temp (append () temp '2')))
|
||||||
|
|
@ -116,33 +79,26 @@ mod control_lib_tests {
|
||||||
(append temp '3'))";
|
(append temp '3'))";
|
||||||
|
|
||||||
let result = "('1' '2' '3')";
|
let result = "('1' '2' '3')";
|
||||||
|
|
||||||
let mut syms = SymTable::new();
|
let mut syms = SymTable::new();
|
||||||
static_stdlib(&mut syms).unwrap();
|
static_stdlib(&mut syms).unwrap();
|
||||||
dynamic_stdlib(&mut syms).unwrap();
|
dynamic_stdlib(&mut syms).unwrap();
|
||||||
|
assert_eq!(
|
||||||
if let Ok(tree) = lex(&document.to_string()) {
|
*eval(&lex(&document.to_string()).unwrap(), &mut syms).unwrap().to_string(),
|
||||||
if let Ctr::Seg(ref i) = *eval(&tree, &mut syms).unwrap() {
|
result.to_string(),
|
||||||
assert_eq!(i.to_string(), result);
|
);
|
||||||
} else {
|
|
||||||
assert!(false);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
assert!(false);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_while_basic() {
|
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
|
// if prev is true, switch looped once and only once
|
||||||
// else prev will have a problematic type
|
// else prev will have a problematic type
|
||||||
let while_loop = "
|
let while_loop = "
|
||||||
(while switch
|
(while switch
|
||||||
(def prev switch)
|
(def prev '' switch)
|
||||||
(toggle switch)
|
(toggle switch)
|
||||||
(if switch
|
(if switch
|
||||||
(def prev)
|
(def '' prev)
|
||||||
()))";
|
()))";
|
||||||
let test_check = "prev";
|
let test_check = "prev";
|
||||||
|
|
||||||
|
|
@ -161,15 +117,15 @@ mod control_lib_tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_while_eval_cond() {
|
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
|
// if prev is true, switch looped once and only once
|
||||||
// else prev will have a problematic type
|
// else prev will have a problematic type
|
||||||
let while_loop = "
|
let while_loop = "
|
||||||
(while (or switch switch)
|
(while (or switch switch)
|
||||||
(def prev switch)
|
(def prev '' switch)
|
||||||
(toggle switch)
|
(toggle switch)
|
||||||
(if switch
|
(if switch
|
||||||
(def prev)
|
(def '' prev)
|
||||||
()))";
|
()))";
|
||||||
let test_check = "prev";
|
let test_check = "prev";
|
||||||
|
|
||||||
|
|
@ -188,15 +144,15 @@ mod control_lib_tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_while_2_iter() {
|
fn test_while_2_iter() {
|
||||||
let additional = "(def sw1 true)";
|
let additional = "(def sw1 '' true)";
|
||||||
let switch_dec = "(def sw2 true)";
|
let switch_dec = "(def sw2 '' true)";
|
||||||
// while should loop twice and define result
|
// while should loop twice and define result
|
||||||
let while_loop = "
|
let while_loop = "
|
||||||
(while sw1
|
(while sw1
|
||||||
(toggle sw2)
|
(toggle sw2)
|
||||||
(if (and sw1 sw2)
|
(if (and sw1 sw2)
|
||||||
(def sw1 false)
|
(def sw1 '' false)
|
||||||
(def result 'yay')))";
|
(def result '' 'yay')))";
|
||||||
let test_check = "result";
|
let test_check = "result";
|
||||||
|
|
||||||
let another_tree = lex(&additional.to_string()).unwrap();
|
let another_tree = lex(&additional.to_string()).unwrap();
|
||||||
|
|
@ -216,7 +172,7 @@ mod control_lib_tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_circuit_basic() {
|
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 test = "result";
|
||||||
|
|
||||||
let doc_tree = lex(&document.to_string()).unwrap();
|
let doc_tree = lex(&document.to_string()).unwrap();
|
||||||
|
|
@ -228,13 +184,12 @@ mod control_lib_tests {
|
||||||
|
|
||||||
eval(&doc_tree, &mut syms).unwrap();
|
eval(&doc_tree, &mut syms).unwrap();
|
||||||
let res = eval(&test_tree, &mut syms);
|
let res = eval(&test_tree, &mut syms);
|
||||||
println!("{:#?}", res);
|
|
||||||
res.unwrap();
|
res.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_circuit_fail() {
|
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 test = "result";
|
||||||
|
|
||||||
let doc_tree = lex(&document.to_string()).unwrap();
|
let doc_tree = lex(&document.to_string()).unwrap();
|
||||||
|
|
@ -245,13 +200,9 @@ mod control_lib_tests {
|
||||||
dynamic_stdlib(&mut syms).unwrap();
|
dynamic_stdlib(&mut syms).unwrap();
|
||||||
|
|
||||||
eval(&doc_tree, &mut syms).unwrap();
|
eval(&doc_tree, &mut syms).unwrap();
|
||||||
if let Err(s) = eval(&test_tree, &mut syms) {
|
assert_eq!(
|
||||||
assert_eq!(
|
eval(&test_tree, &mut syms).err().unwrap(),
|
||||||
s,
|
"error in call to result: undefined symbol: result".to_string()
|
||||||
"error in call to result: undefined symbol: result".to_string()
|
);
|
||||||
);
|
|
||||||
} else {
|
|
||||||
panic!();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ mod var_lib_tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_variable_def_and_lookup() {
|
fn test_variable_def_and_lookup() {
|
||||||
let doc1 = "(def test 1)";
|
let doc1 = "(def test 'my test var' 1)";
|
||||||
let doc2 = "(test)";
|
let doc2 = "(test)";
|
||||||
let result = "(1)";
|
let result = "(1)";
|
||||||
|
|
||||||
|
|
@ -42,7 +42,7 @@ mod var_lib_tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_func_def_and_lookup() {
|
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 doc2 = "(test '1')";
|
||||||
let result = "1";
|
let result = "1";
|
||||||
|
|
||||||
|
|
@ -79,8 +79,8 @@ mod var_lib_tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_variable_def_redef_and_lookup() {
|
fn test_variable_def_redef_and_lookup() {
|
||||||
let doc1 = "(def test 1)";
|
let doc1 = "(def test 'my test var' 1)";
|
||||||
let doc2 = "(def test '2')";
|
let doc2 = "(def test 'my test var' '2')";
|
||||||
let doc3 = "(test)";
|
let doc3 = "(test)";
|
||||||
let result = "('2')";
|
let result = "('2')";
|
||||||
|
|
||||||
|
|
@ -132,7 +132,7 @@ mod var_lib_tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_variable_def_undef_and_lookup_fail() {
|
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 doc2 = "(def test)";
|
||||||
let doc3 = "(test)";
|
let doc3 = "(test)";
|
||||||
|
|
||||||
|
|
@ -185,4 +185,41 @@ mod var_lib_tests {
|
||||||
assert!(false);
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue