diff --git a/Readme.org b/Readme.org index 00107d8..b6b385e 100644 --- a/Readme.org +++ b/Readme.org @@ -136,13 +136,6 @@ This contains any executable target of this project. Notably the main shell file Note: this section will not show the status of each item unless you are viewing it with a proper orgmode viewer. Note: this section only tracks the state of incomplete TODO items. Having everything on here would be cluttered. -*** TODO Custom ast pretty print -*** TODO Help function -**** 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 *** TODO Env function @@ -154,7 +147,7 @@ Will need a concatenate function for tables *** TODO Can enter multiple lines of text, with formatting in repl *** TODO arithmetic operations **** TODO typecast (int) -**** TODO typecast (float) +**** TODO typecast (zfloat) **** TODO add **** TODO sub **** TODO div @@ -185,12 +178,12 @@ Will need a concatenate function for tables **** TODO Update config env var docstring with functions loaded or unloaded *** TODO list operations **** DONE append -**** DONE expand **** TODO head (returns (head rest)) **** TODO tail (returns (rest tail)) **** TODO queue (append to front) **** TODO snippet for dequeue **** TODO snippet for pop +**** TODO list len *** TODO lambda *** TODO file operations **** TODO read-to-string @@ -206,3 +199,4 @@ Will need a concatenate function for tables **** TODO TCP Listener **** TODO HTTP Listener **** TODO UDP Listener +*** TODO Custom ast pretty print diff --git a/src/stl.rs b/src/stl.rs index b3587fa..0a57978 100644 --- a/src/stl.rs +++ b/src/stl.rs @@ -36,8 +36,7 @@ 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(), + docs: append::APPEND_DOCSTRING.to_string(), value: ValueType::Internal(Rc::new(append::append_callback)), }, ); @@ -48,7 +47,7 @@ If the first argument is a list, all other arguments are added sequentially to t 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(), + docs: ECHO_DOCSTRING.to_string(), value: ValueType::Internal(Rc::new(_echo_callback)), }, ); @@ -59,14 +58,7 @@ If the first argument is a list, all other arguments are added sequentially to t 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(), + docs: control::IF_DOCSTRING.to_string(), value: ValueType::Internal(Rc::new(control::if_callback)), }, ); @@ -77,20 +69,7 @@ example: (if my-state-switch 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(), + docs: control::LET_DOCSTRING.to_string(), value: ValueType::Internal(Rc::new(control::let_callback)), }, ); @@ -101,13 +80,7 @@ Since the call to some-func is the final form, its value is returned." 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(), + docs: control::WHILE_DOCSTRING.to_string(), value: ValueType::Internal(Rc::new(control::while_callback)), }, ); @@ -118,17 +91,7 @@ example: (while (check-my-state) 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(), + docs: control::CIRCUIT_DOCSTRING.to_string(), value: ValueType::Internal(Rc::new(control::circuit_callback)), }, ); @@ -139,11 +102,8 @@ in this example, do-another-operation will not be called" 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)), + docs: boolean::AND_DOCSTRING.to_string(), + value: ValueType::Internal(Rc::new(boolean::and_callback)), }, ); @@ -153,11 +113,8 @@ returns final result." 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)), + docs: boolean::OR_DOCSTRING.to_string(), + value: ValueType::Internal(Rc::new(boolean::or_callback)), }, ); @@ -167,10 +124,8 @@ returns final result." 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)), + docs: boolean::NOT_DOCSTRING.to_string(), + value: ValueType::Internal(Rc::new(boolean::not_callback)), }, ); @@ -180,11 +135,8 @@ returns false if arg is true or true if arg is false." 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)), + docs: boolean::ISEQ_DOCSTRING.to_string(), + value: ValueType::Internal(Rc::new(boolean::iseq_callback)), }, ); @@ -194,11 +146,8 @@ NOTE: 1 and 1.0 are the same, but '1' 'one' or one (symbol) aren't" 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)), + docs: boolean::TOGGLE_DOCSTRING.to_string(), + value: ValueType::Internal(Rc::new(boolean::toggle_callback)), }, ); @@ -208,7 +157,7 @@ Either sets the symbol to true if it is currently false, or vice versa." 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(), + docs: HELP_DOCSTRING.to_string(), value: ValueType::Internal(Rc::new(_help_callback)), }, ); @@ -233,19 +182,7 @@ 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(), + docs: STORE_DOCSTRING.to_string(), value: ValueType::Internal(Rc::new( move |ast: &Seg, syms: &mut SymTable| -> Result { _store_callback(ast, syms, env_cfg_user_form) @@ -257,6 +194,8 @@ pub fn dynamic_stdlib(syms: &mut SymTable) -> Result<(), String> { Ok(()) } +pub const ECHO_DOCSTRING: &str = "traverses any number of arguments. Prints their evaluated values on a new line for each."; + fn _echo_callback(ast: &Seg, _syms: &mut SymTable) -> Result { if ast.len() == 1 { println!("{}", ast.car); @@ -267,6 +206,8 @@ fn _echo_callback(ast: &Seg, _syms: &mut SymTable) -> Result { Ok(Ctr::None) } +pub const HELP_DOCSTRING: &str = "prints help text for a given symbol. Expects only one argument."; + fn _help_callback(ast: &Seg, syms: &mut SymTable) -> Result { if ast.len() != 1 { return Err("help only takes a single argument".to_string()); @@ -285,8 +226,8 @@ ARGS: {1}\n DOCUMENTATION:\n {2}\n CURRENT VALUE AND/OR BODY: -(TODO)", - sym.name, args_str, sym.docs +{3}", + sym.name, args_str, sym.docs, sym.value ); } else { return Err("undefined symbol".to_string()); @@ -298,6 +239,19 @@ CURRENT VALUE AND/OR BODY: Ok(Ctr::None) } +pub const STORE_DOCSTRING: &str = "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)"; + fn _store_callback(ast: &Seg, syms: &mut SymTable, env_cfg: bool) -> Result { let is_var = ast.len() == 3; if let Ctr::Symbol(ref identifier) = *ast.car { diff --git a/src/stl/append.rs b/src/stl/append.rs index 880efdf..f50ab06 100644 --- a/src/stl/append.rs +++ b/src/stl/append.rs @@ -17,6 +17,9 @@ use crate::segment::{Ctr, Seg}; use crate::sym::SymTable; +pub const APPEND_DOCSTRING: &str = "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."; + pub fn append_callback(ast: &Seg, _syms: &mut SymTable) -> Result { if let Ctr::Seg(ref s) = *ast.car { let mut temp = s.clone(); diff --git a/src/stl/boolean.rs b/src/stl/boolean.rs index 12e714b..e269539 100644 --- a/src/stl/boolean.rs +++ b/src/stl/boolean.rs @@ -17,7 +17,11 @@ use crate::segment::{Ctr, Seg}; use crate::sym::{SymTable, ValueType}; -pub fn bool_and_callback(ast: &Seg, _syms: &mut SymTable) -> Result { +pub const AND_DOCSTRING: &str = "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."; + +pub fn and_callback(ast: &Seg, _syms: &mut SymTable) -> Result { let mut type_error = false; let result = ast.circuit(&mut |arg: &Ctr| -> bool { if let Ctr::Bool(b) = *arg { @@ -36,7 +40,11 @@ pub fn bool_and_callback(ast: &Seg, _syms: &mut SymTable) -> Result } } -pub fn bool_or_callback(ast: &Seg, _syms: &mut SymTable) -> Result { +pub const OR_DOCSTRING: &str = "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."; + +pub fn or_callback(ast: &Seg, _syms: &mut SymTable) -> Result { let mut result = false; let correct_types = ast.circuit(&mut |arg: &Ctr| -> bool { if let Ctr::Bool(b) = *arg { @@ -55,7 +63,10 @@ pub fn bool_or_callback(ast: &Seg, _syms: &mut SymTable) -> Result } } -pub fn bool_not_callback(ast: &Seg, _syms: &mut SymTable) -> Result { +pub const NOT_DOCSTRING: &str = "takes a single argument (expects a boolean). +returns false if arg is true or true if arg is false."; + +pub fn not_callback(ast: &Seg, _syms: &mut SymTable) -> Result { if let Ctr::Bool(b) = *ast.car { Ok(Ctr::Bool(!b)) } else { @@ -63,14 +74,23 @@ pub fn bool_not_callback(ast: &Seg, _syms: &mut SymTable) -> Result } } -pub fn bool_iseq_callback(ast: &Seg, _syms: &mut SymTable) -> Result { +pub const ISEQ_DOCSTRING: &str = "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"; + +pub fn iseq_callback(ast: &Seg, _syms: &mut SymTable) -> Result { let head_ctr_ref = &*ast.car; Ok(Ctr::Bool( ast.circuit(&mut |arg: &Ctr| -> bool { arg == head_ctr_ref }), )) } -pub fn bool_toggle_callback(ast: &Seg, syms: &mut SymTable) -> Result { +pub const TOGGLE_DOCSTRING: &str = "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."; + + +pub fn toggle_callback(ast: &Seg, syms: &mut SymTable) -> Result { let var_name: String; if let Ctr::Symbol(ref s) = *ast.car { var_name = s.clone(); diff --git a/src/stl/control.rs b/src/stl/control.rs index 7b3dc25..f62ee62 100644 --- a/src/stl/control.rs +++ b/src/stl/control.rs @@ -19,6 +19,15 @@ use crate::eval::eval; use crate::segment::{Ctr, Seg}; use crate::sym::{Args, SymTable, Symbol, ValueType}; +pub const IF_DOCSTRING: &str = "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))"; + pub fn if_callback(ast: &Seg, syms: &mut SymTable) -> Result { let cond: bool; println!("Y: {}", *ast.car); @@ -85,6 +94,20 @@ pub fn if_callback(ast: &Seg, syms: &mut SymTable) -> Result { } } +pub const LET_DOCSTRING: &str = "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."; + pub fn let_callback(ast: &Seg, syms: &mut SymTable) -> Result { let mut localsyms = syms.clone(); let locals_form: &Seg; @@ -180,9 +203,14 @@ pub fn let_callback(ast: &Seg, syms: &mut SymTable) -> Result { Ok((*result).clone()) } -/* -(while (cond) evalme evalme evalme evalme ... ) - */ +pub const WHILE_DOCSTRING: &str = "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))"; + pub fn while_callback(ast: &Seg, syms: &mut SymTable) -> Result { let eval_cond: &Seg; let outer_maybe: Seg; @@ -244,9 +272,17 @@ pub fn while_callback(ast: &Seg, syms: &mut SymTable) -> Result { Ok(*(result.unwrap()).clone()) } -/* -(circuit makes_a_bool makes_a_bool makes_a_bool makes_a_bool ... ) -*/ +pub const CIRCUIT_DOCSTRING: &str = "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"; + pub fn circuit_callback(ast: &Seg, syms: &mut SymTable) -> Result { let mut cursor = 0; let mut error: String = String::new(); diff --git a/src/sym.rs b/src/sym.rs index 034e8b8..b92dc42 100644 --- a/src/sym.rs +++ b/src/sym.rs @@ -222,6 +222,23 @@ impl fmt::Display for Args { } } +impl fmt::Display for ValueType { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + ValueType::VarForm(ref s) => write!(f, "{}", s), + ValueType::Internal(_) => write!(f, ""), + ValueType::FuncForm(ref form) => { + write!(f, "args: ")?; + for sym in form.arg_syms.clone() { + write!(f, "{} ", sym)?; + } + write!(f, "\nform: {}", form.ast)?; + Ok(()) + }, + } + } +} + impl Symbol { /* call * routine is called by eval when a symbol is expanded