help function displays values not

Signed-off-by: Ava Hahn <ava@aidanis.online>
This commit is contained in:
Ava Hahn 2023-03-06 15:12:59 -08:00
parent 8d79037d4d
commit de29bbf950
Signed by untrusted user who does not match committer: affine
GPG key ID: 3A4645B8CF806069
6 changed files with 127 additions and 103 deletions

View file

@ -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 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 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 Eval function
*** TODO Input function *** TODO Input function
*** TODO Env 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 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 (zfloat)
**** TODO add **** TODO add
**** TODO sub **** TODO sub
**** TODO div **** 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 Update config env var docstring with functions loaded or unloaded
*** TODO list operations *** TODO list operations
**** DONE append **** DONE append
**** DONE expand
**** TODO head (returns (head rest)) **** TODO head (returns (head rest))
**** TODO tail (returns (rest tail)) **** TODO tail (returns (rest tail))
**** TODO queue (append to front) **** TODO queue (append to front)
**** TODO snippet for dequeue **** TODO snippet for dequeue
**** TODO snippet for pop **** TODO snippet for pop
**** TODO list len
*** TODO lambda *** TODO lambda
*** TODO file operations *** TODO file operations
**** TODO read-to-string **** TODO read-to-string
@ -206,3 +199,4 @@ Will need a concatenate function for tables
**** TODO TCP Listener **** TODO TCP Listener
**** TODO HTTP Listener **** TODO HTTP Listener
**** TODO UDP Listener **** TODO UDP Listener
*** TODO Custom ast pretty print

View file

@ -36,8 +36,7 @@ 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. docs: append::APPEND_DOCSTRING.to_string(),
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)),
}, },
); );
@ -48,7 +47,7 @@ If the first argument is a list, all other arguments are added sequentially to t
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(), docs: ECHO_DOCSTRING.to_string(),
value: ValueType::Internal(Rc::new(_echo_callback)), 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"), 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. docs: control::IF_DOCSTRING.to_string(),
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)),
}, },
); );
@ -77,20 +69,7 @@ example: (if my-state-switch
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. docs: control::LET_DOCSTRING.to_string(),
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)),
}, },
); );
@ -101,13 +80,7 @@ Since the call to some-func is the final form, its value is returned."
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. docs: control::WHILE_DOCSTRING.to_string(),
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)),
}, },
); );
@ -118,17 +91,7 @@ example: (while (check-my-state)
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. docs: control::CIRCUIT_DOCSTRING.to_string(),
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)),
}, },
); );
@ -139,11 +102,8 @@ in this example, do-another-operation will not be called"
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. docs: boolean::AND_DOCSTRING.to_string(),
starts with arg1 AND arg2, and then calculates prev_result AND next_arg. value: ValueType::Internal(Rc::new(boolean::and_callback)),
returns final result."
.to_string(),
value: ValueType::Internal(Rc::new(boolean::bool_and_callback)),
}, },
); );
@ -153,11 +113,8 @@ returns final result."
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. docs: boolean::OR_DOCSTRING.to_string(),
starts with arg1 OR arg2, and then calculates prev_result OR next_arg. value: ValueType::Internal(Rc::new(boolean::or_callback)),
returns final result."
.to_string(),
value: ValueType::Internal(Rc::new(boolean::bool_or_callback)),
}, },
); );
@ -167,10 +124,8 @@ returns final result."
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). docs: boolean::NOT_DOCSTRING.to_string(),
returns false if arg is true or true if arg is false." value: ValueType::Internal(Rc::new(boolean::not_callback)),
.to_string(),
value: ValueType::Internal(Rc::new(boolean::bool_not_callback)),
}, },
); );
@ -180,11 +135,8 @@ returns false if arg is true or true if arg is false."
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. docs: boolean::ISEQ_DOCSTRING.to_string(),
returns true if all arguments hold the same value. value: ValueType::Internal(Rc::new(boolean::iseq_callback)),
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)),
}, },
); );
@ -194,11 +146,8 @@ NOTE: 1 and 1.0 are the same, but '1' 'one' or one (symbol) aren't"
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. docs: boolean::TOGGLE_DOCSTRING.to_string(),
Takes a single argument (a symbol). Looks it up in the variable table. value: ValueType::Internal(Rc::new(boolean::toggle_callback)),
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)),
}, },
); );
@ -208,7 +157,7 @@ Either sets the symbol to true if it is currently false, or vice versa."
name: String::from("help"), name: String::from("help"),
args: Args::Strict(vec![Type::Symbol]), args: Args::Strict(vec![Type::Symbol]),
conditional_branches: true, 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)), value: ValueType::Internal(Rc::new(_help_callback)),
}, },
); );
@ -233,19 +182,7 @@ 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. docs: STORE_DOCSTRING.to_string(),
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)
@ -257,6 +194,8 @@ pub fn dynamic_stdlib(syms: &mut SymTable) -> Result<(), String> {
Ok(()) 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<Ctr, String> { fn _echo_callback(ast: &Seg, _syms: &mut SymTable) -> Result<Ctr, String> {
if ast.len() == 1 { if ast.len() == 1 {
println!("{}", ast.car); println!("{}", ast.car);
@ -267,6 +206,8 @@ fn _echo_callback(ast: &Seg, _syms: &mut SymTable) -> Result<Ctr, String> {
Ok(Ctr::None) 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<Ctr, String> { fn _help_callback(ast: &Seg, syms: &mut SymTable) -> Result<Ctr, String> {
if ast.len() != 1 { if ast.len() != 1 {
return Err("help only takes a single argument".to_string()); return Err("help only takes a single argument".to_string());
@ -285,8 +226,8 @@ ARGS: {1}\n
DOCUMENTATION:\n DOCUMENTATION:\n
{2}\n {2}\n
CURRENT VALUE AND/OR BODY: CURRENT VALUE AND/OR BODY:
(TODO)", {3}",
sym.name, args_str, sym.docs sym.name, args_str, sym.docs, sym.value
); );
} else { } else {
return Err("undefined symbol".to_string()); return Err("undefined symbol".to_string());
@ -298,6 +239,19 @@ CURRENT VALUE AND/OR BODY:
Ok(Ctr::None) 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<Ctr, String> { fn _store_callback(ast: &Seg, syms: &mut SymTable, env_cfg: bool) -> Result<Ctr, String> {
let is_var = ast.len() == 3; let is_var = ast.len() == 3;
if let Ctr::Symbol(ref identifier) = *ast.car { if let Ctr::Symbol(ref identifier) = *ast.car {

View file

@ -17,6 +17,9 @@
use crate::segment::{Ctr, Seg}; use crate::segment::{Ctr, Seg};
use crate::sym::SymTable; 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<Ctr, String> { pub fn append_callback(ast: &Seg, _syms: &mut SymTable) -> Result<Ctr, String> {
if let Ctr::Seg(ref s) = *ast.car { if let Ctr::Seg(ref s) = *ast.car {
let mut temp = s.clone(); let mut temp = s.clone();

View file

@ -17,7 +17,11 @@
use crate::segment::{Ctr, Seg}; use crate::segment::{Ctr, Seg};
use crate::sym::{SymTable, ValueType}; use crate::sym::{SymTable, ValueType};
pub fn bool_and_callback(ast: &Seg, _syms: &mut SymTable) -> Result<Ctr, String> { 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<Ctr, String> {
let mut type_error = false; let mut type_error = false;
let result = ast.circuit(&mut |arg: &Ctr| -> bool { let result = ast.circuit(&mut |arg: &Ctr| -> bool {
if let Ctr::Bool(b) = *arg { if let Ctr::Bool(b) = *arg {
@ -36,7 +40,11 @@ pub fn bool_and_callback(ast: &Seg, _syms: &mut SymTable) -> Result<Ctr, String>
} }
} }
pub fn bool_or_callback(ast: &Seg, _syms: &mut SymTable) -> Result<Ctr, String> { 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<Ctr, String> {
let mut result = false; let mut result = false;
let correct_types = ast.circuit(&mut |arg: &Ctr| -> bool { let correct_types = ast.circuit(&mut |arg: &Ctr| -> bool {
if let Ctr::Bool(b) = *arg { if let Ctr::Bool(b) = *arg {
@ -55,7 +63,10 @@ pub fn bool_or_callback(ast: &Seg, _syms: &mut SymTable) -> Result<Ctr, String>
} }
} }
pub fn bool_not_callback(ast: &Seg, _syms: &mut SymTable) -> Result<Ctr, String> { 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<Ctr, String> {
if let Ctr::Bool(b) = *ast.car { if let Ctr::Bool(b) = *ast.car {
Ok(Ctr::Bool(!b)) Ok(Ctr::Bool(!b))
} else { } else {
@ -63,14 +74,23 @@ pub fn bool_not_callback(ast: &Seg, _syms: &mut SymTable) -> Result<Ctr, String>
} }
} }
pub fn bool_iseq_callback(ast: &Seg, _syms: &mut SymTable) -> Result<Ctr, String> { 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<Ctr, String> {
let head_ctr_ref = &*ast.car; let head_ctr_ref = &*ast.car;
Ok(Ctr::Bool( Ok(Ctr::Bool(
ast.circuit(&mut |arg: &Ctr| -> bool { arg == head_ctr_ref }), ast.circuit(&mut |arg: &Ctr| -> bool { arg == head_ctr_ref }),
)) ))
} }
pub fn bool_toggle_callback(ast: &Seg, syms: &mut SymTable) -> Result<Ctr, String> { 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<Ctr, String> {
let var_name: String; let var_name: String;
if let Ctr::Symbol(ref s) = *ast.car { if let Ctr::Symbol(ref s) = *ast.car {
var_name = s.clone(); var_name = s.clone();

View file

@ -19,6 +19,15 @@ use crate::eval::eval;
use crate::segment::{Ctr, Seg}; use crate::segment::{Ctr, Seg};
use crate::sym::{Args, SymTable, Symbol, ValueType}; 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<Ctr, String> { pub fn if_callback(ast: &Seg, syms: &mut SymTable) -> Result<Ctr, String> {
let cond: bool; let cond: bool;
println!("Y: {}", *ast.car); println!("Y: {}", *ast.car);
@ -85,6 +94,20 @@ pub fn if_callback(ast: &Seg, syms: &mut SymTable) -> Result<Ctr, String> {
} }
} }
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<Ctr, String> { pub fn let_callback(ast: &Seg, syms: &mut SymTable) -> Result<Ctr, String> {
let mut localsyms = syms.clone(); let mut localsyms = syms.clone();
let locals_form: &Seg; let locals_form: &Seg;
@ -180,9 +203,14 @@ pub fn let_callback(ast: &Seg, syms: &mut SymTable) -> Result<Ctr, String> {
Ok((*result).clone()) Ok((*result).clone())
} }
/* pub const WHILE_DOCSTRING: &str = "traverses a list of N un-evaluated forms.
(while (cond) evalme evalme evalme evalme ... ) 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<Ctr, String> { pub fn while_callback(ast: &Seg, syms: &mut SymTable) -> Result<Ctr, String> {
let eval_cond: &Seg; let eval_cond: &Seg;
let outer_maybe: Seg; let outer_maybe: Seg;
@ -244,9 +272,17 @@ pub fn while_callback(ast: &Seg, syms: &mut SymTable) -> Result<Ctr, String> {
Ok(*(result.unwrap()).clone()) Ok(*(result.unwrap()).clone())
} }
/* pub const CIRCUIT_DOCSTRING: &str = "traverses a list of N un-evaluated forms.
(circuit makes_a_bool makes_a_bool makes_a_bool makes_a_bool ... ) 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<Ctr, String> { pub fn circuit_callback(ast: &Seg, syms: &mut SymTable) -> Result<Ctr, String> {
let mut cursor = 0; let mut cursor = 0;
let mut error: String = String::new(); let mut error: String = String::new();

View file

@ -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, "<builtin>"),
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 { impl Symbol {
/* call /* call
* routine is called by eval when a symbol is expanded * routine is called by eval when a symbol is expanded