This MR finishes up all remaining Pre V1 goals

* add a posix exit() builtin
* improve separation of concerns regarding standard library structure
This commit is contained in:
Ava Apples Affine 2023-05-25 23:08:44 +00:00
parent b3c0b80ee6
commit 3bbea6bea0
9 changed files with 753 additions and 752 deletions

View file

@ -487,11 +487,6 @@ This contains any executable target of this project. Notably [[file:src/bin/reli
* Current Status / TODO list * Current Status / TODO list
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 alpha tasks
- exit function (causes program to shut down and return code)
- probably push the symtable inserts into functions in individual stl modules (like posix.rs does)
- tag and release v0.3.0
** TODO v1.0 tasks ** TODO v1.0 tasks
- Be able to use features to compile without env or posix stuff - Be able to use features to compile without env or posix stuff

View file

@ -58,557 +58,12 @@ fn prompt_delimiter_default_callback(_: &Seg, _: &mut SymTable) -> Result<Ctr, T
/// inserts all stdlib functions that can be inserted without /// inserts all stdlib functions that can be inserted without
/// any kind of further configuration data into a symtable /// any kind of further configuration data into a symtable
pub fn static_stdlib(syms: &mut SymTable) -> Result<(), String> { pub fn static_stdlib(syms: &mut SymTable) -> Result<(), String> {
syms.insert( append::add_list_lib(syms);
"cons".to_string(), strings::add_string_lib(syms);
Symbol { decl::add_decl_lib_static(syms);
name: String::from("cons"), control::add_control_lib(syms);
args: Args::Infinite, boolean::add_bool_lib(syms);
conditional_branches: false, math::add_math_lib(syms);
docs: append::CONS_DOCSTRING.to_string(),
value: ValueType::Internal(Rc::new(append::cons_callback)),
..Default::default()
},
);
syms.insert(
"echo".to_string(),
Symbol {
name: String::from("echo"),
args: Args::Infinite,
conditional_branches: false,
docs: strings::ECHO_DOCSTRING.to_string(),
value: ValueType::Internal(Rc::new(strings::echo_callback)),
..Default::default()
},
);
syms.insert(
"concat".to_string(),
Symbol {
name: String::from("concat"),
args: Args::Infinite,
conditional_branches: false,
docs: strings::CONCAT_DOCSTRING.to_string(),
value: ValueType::Internal(Rc::new(strings::concat_callback)),
..Default::default()
},
);
syms.insert(
"substr?".to_string(),
Symbol {
name: String::from("substr?"),
args: Args::Strict(vec![Type::String, Type::String]),
conditional_branches: false,
docs: strings::SUBSTR_DOCSTRING.to_string(),
value: ValueType::Internal(Rc::new(strings::substr_callback)),
..Default::default()
},
);
syms.insert(
"split".to_string(),
Symbol {
name: String::from("split"),
args: Args::Strict(vec![Type::String, Type::String]),
conditional_branches: false,
docs: strings::SPLIT_DOCSTRING.to_string(),
value: ValueType::Internal(Rc::new(strings::split_callback)),
..Default::default()
},
);
syms.insert(
"strlen".to_string(),
Symbol {
name: String::from("strlen"),
args: Args::Lazy(1),
conditional_branches: false,
docs: strings::STRLEN_DOCSTRING.to_string(),
value: ValueType::Internal(Rc::new(strings::strlen_callback)),
..Default::default()
},
);
syms.insert(
"string".to_string(),
Symbol {
name: String::from("string"),
args: Args::Lazy(1),
conditional_branches: false,
docs: strings::STRCAST_DOCSTRING.to_string(),
value: ValueType::Internal(Rc::new(strings::strcast_callback)),
..Default::default()
},
);
syms.insert(
"if".to_string(),
Symbol {
name: String::from("if"),
args: Args::Lazy(3),
conditional_branches: true,
docs: control::IF_DOCSTRING.to_string(),
value: ValueType::Internal(Rc::new(control::if_callback)),
..Default::default()
},
);
syms.insert(
"let".to_string(),
Symbol {
name: String::from("let"),
args: Args::Infinite,
conditional_branches: true,
docs: control::LET_DOCSTRING.to_string(),
value: ValueType::Internal(Rc::new(control::let_callback)),
..Default::default()
},
);
syms.insert(
"while".to_string(),
Symbol {
name: String::from("while"),
args: Args::Infinite,
conditional_branches: true,
docs: control::WHILE_DOCSTRING.to_string(),
value: ValueType::Internal(Rc::new(control::while_callback)),
..Default::default()
},
);
syms.insert(
"circuit".to_string(),
Symbol {
name: String::from("circuit"),
args: Args::Infinite,
conditional_branches: true,
docs: control::CIRCUIT_DOCSTRING.to_string(),
value: ValueType::Internal(Rc::new(control::circuit_callback)),
..Default::default()
},
);
syms.insert(
"and".to_string(),
Symbol {
name: String::from("and"),
args: Args::Infinite,
conditional_branches: false,
docs: boolean::AND_DOCSTRING.to_string(),
value: ValueType::Internal(Rc::new(boolean::and_callback)),
..Default::default()
},
);
syms.insert(
"bool".to_string(),
Symbol {
name: String::from("bool"),
args: Args::Infinite,
conditional_branches: false,
docs: boolean::BOOLCAST_DOCSTRING.to_string(),
value: ValueType::Internal(Rc::new(boolean::boolcast_callback)),
..Default::default()
},
);
syms.insert(
"or".to_string(),
Symbol {
name: String::from("or"),
args: Args::Infinite,
conditional_branches: false,
docs: boolean::OR_DOCSTRING.to_string(),
value: ValueType::Internal(Rc::new(boolean::or_callback)),
..Default::default()
},
);
syms.insert(
"not".to_string(),
Symbol {
name: String::from("not"),
args: Args::Strict(vec![Type::Bool]),
conditional_branches: false,
docs: boolean::NOT_DOCSTRING.to_string(),
value: ValueType::Internal(Rc::new(boolean::not_callback)),
..Default::default()
},
);
syms.insert(
"eq?".to_string(),
Symbol {
name: String::from("eq?"),
args: Args::Infinite,
conditional_branches: false,
docs: boolean::ISEQ_DOCSTRING.to_string(),
value: ValueType::Internal(Rc::new(boolean::iseq_callback)),
..Default::default()
},
);
syms.insert(
"toggle".to_string(),
Symbol {
name: String::from("toggle"),
args: Args::Lazy(1),
conditional_branches: true,
docs: boolean::TOGGLE_DOCSTRING.to_string(),
value: ValueType::Internal(Rc::new(boolean::toggle_callback)),
..Default::default()
},
);
syms.insert(
"help".to_string(),
Symbol {
name: String::from("help"),
args: Args::Strict(vec![Type::Symbol]),
conditional_branches: true,
docs: decl::HELP_DOCSTRING.to_string(),
value: ValueType::Internal(Rc::new(decl::help_callback)),
..Default::default()
},
);
syms.insert(
"set?".to_string(),
Symbol {
name: String::from("set?"),
args: Args::Strict(vec![Type::Symbol]),
conditional_branches: true,
docs: decl::ISSET_DOCSTRING.to_string(),
value: ValueType::Internal(Rc::new(decl::isset_callback)),
..Default::default()
},
);
syms.insert(
"env".to_string(),
Symbol {
name: String::from("env"),
args: Args::None,
conditional_branches: false,
docs: decl::ENV_DOCSTRING.to_string(),
value: ValueType::Internal(Rc::new(decl::env_callback)),
..Default::default()
},
);
syms.insert(
"add".to_string(),
Symbol {
name: String::from("add"),
args: Args::Infinite,
conditional_branches: false,
docs: math::ADD_DOCSTRING.to_string(),
value: ValueType::Internal(Rc::new(math::add_callback)),
..Default::default()
},
);
syms.insert(
"sub".to_string(),
Symbol {
name: String::from("sub"),
args: Args::Infinite,
conditional_branches: false,
docs: math::SUB_DOCSTRING.to_string(),
value: ValueType::Internal(Rc::new(math::sub_callback)),
..Default::default()
},
);
syms.insert(
"div".to_string(),
Symbol {
name: String::from("div"),
args: Args::Lazy(2),
conditional_branches: false,
docs: math::DIV_DOCSTRING.to_string(),
value: ValueType::Internal(Rc::new(math::div_callback)),
..Default::default()
},
);
syms.insert(
"mul".to_string(),
Symbol {
name: String::from("mul"),
args: Args::Infinite,
conditional_branches: false,
docs: math::MUL_DOCSTRING.to_string(),
value: ValueType::Internal(Rc::new(math::mul_callback)),
..Default::default()
},
);
syms.insert(
"int".to_string(),
Symbol {
name: String::from("int"),
args: Args::Lazy(1),
conditional_branches: false,
docs: math::INTCAST_DOCSTRING.to_string(),
value: ValueType::Internal(Rc::new(math::intcast_callback)),
..Default::default()
},
);
syms.insert(
"float".to_string(),
Symbol {
name: String::from("float"),
args: Args::Lazy(1),
conditional_branches: false,
docs: math::FLOATCAST_DOCSTRING.to_string(),
value: ValueType::Internal(Rc::new(math::floatcast_callback)),
..Default::default()
},
);
syms.insert(
"len".to_string(),
Symbol {
name: String::from("len"),
args: Args::Strict(vec![Type::Seg]),
conditional_branches: false,
docs: append::LEN_DOCSTRING.to_string(),
value: ValueType::Internal(Rc::new(append::len_callback)),
..Default::default()
},
);
syms.insert(
"car".to_string(),
Symbol {
name: String::from("car"),
args: Args::Strict(vec![Type::Seg]),
conditional_branches: false,
docs: append::CAR_DOCSTRING.to_string(),
value: ValueType::Internal(Rc::new(append::car_callback)),
..Default::default()
},
);
syms.insert(
"cdr".to_string(),
Symbol {
name: String::from("cdr"),
args: Args::Strict(vec![Type::Seg]),
conditional_branches: false,
docs: append::CDR_DOCSTRING.to_string(),
value: ValueType::Internal(Rc::new(append::cdr_callback)),
..Default::default()
},
);
syms.insert(
"pop".to_string(),
Symbol {
name: String::from("pop"),
args: Args::Strict(vec![Type::Seg]),
conditional_branches: false,
docs: append::POP_DOCSTRING.to_string(),
value: ValueType::Internal(Rc::new(append::pop_callback)),
..Default::default()
},
);
syms.insert(
"dq".to_string(),
Symbol {
name: String::from("dequeue"),
args: Args::Strict(vec![Type::Seg]),
conditional_branches: false,
docs: append::DEQUEUE_DOCSTRING.to_string(),
value: ValueType::Internal(Rc::new(append::dequeue_callback)),
..Default::default()
},
);
syms.insert(
"reverse".to_string(),
Symbol {
name: String::from("reverse"),
args: Args::Strict(vec![Type::Seg]),
conditional_branches: false,
docs: append::REVERSE_DOCSTRING.to_string(),
value: ValueType::Internal(Rc::new(append::reverse_callback)),
..Default::default()
},
);
syms.insert(
"exp".to_string(),
Symbol {
name: String::from("exp"),
args: Args::Lazy(2),
conditional_branches: false,
docs: math::EXP_DOCSTRING.to_string(),
value: ValueType::Internal(Rc::new(math::exp_callback)),
..Default::default()
},
);
syms.insert(
"mod".to_string(),
Symbol {
name: String::from("mod"),
args: Args::Lazy(2),
conditional_branches: false,
docs: math::MOD_DOCSTRING.to_string(),
value: ValueType::Internal(Rc::new(math::mod_callback)),
..Default::default()
},
);
syms.insert(
"gt?".to_string(),
Symbol {
name: String::from("gt?"),
args: Args::Lazy(2),
conditional_branches: false,
docs: math::ISGT_DOCSTRING.to_string(),
value: ValueType::Internal(Rc::new(math::isgt_callback)),
..Default::default()
},
);
syms.insert(
"lt?".to_string(),
Symbol {
name: String::from("lt?"),
args: Args::Lazy(2),
conditional_branches: false,
docs: math::ISLT_DOCSTRING.to_string(),
value: ValueType::Internal(Rc::new(math::islt_callback)),
..Default::default()
},
);
syms.insert(
"gte?".to_string(),
Symbol {
name: String::from("gt?"),
args: Args::Lazy(2),
conditional_branches: false,
docs: math::ISGTE_DOCSTRING.to_string(),
value: ValueType::Internal(Rc::new(math::isgte_callback)),
..Default::default()
},
);
syms.insert(
"lte?".to_string(),
Symbol {
name: String::from("lt?"),
args: Args::Lazy(2),
conditional_branches: false,
docs: math::ISLTE_DOCSTRING.to_string(),
value: ValueType::Internal(Rc::new(math::islte_callback)),
..Default::default()
},
);
syms.insert(
"inc".to_string(),
Symbol {
name: String::from("inc"),
args: Args::Lazy(1),
conditional_branches: true,
docs: math::INC_DOCSTRING.to_string(),
value: ValueType::Internal(Rc::new(math::inc_callback)),
..Default::default()
},
);
syms.insert(
"dec".to_string(),
Symbol {
name: String::from("dec"),
args: Args::Lazy(1),
conditional_branches: true,
docs: math::DEC_DOCSTRING.to_string(),
value: ValueType::Internal(Rc::new(math::dec_callback)),
..Default::default()
},
);
syms.insert(
"quote".to_string(),
Symbol {
name: String::from("quote"),
args: Args::Lazy(1),
conditional_branches: true,
docs: decl::QUOTE_DOCSTRING.to_string(),
value: ValueType::Internal(Rc::new(decl::quote_callback)),
..Default::default()
},
);
syms.insert(
"q".to_string(),
Symbol {
name: String::from("quote"),
args: Args::Lazy(1),
conditional_branches: true,
docs: decl::QUOTE_DOCSTRING.to_string(),
value: ValueType::Internal(Rc::new(decl::quote_callback)),
..Default::default()
},
);
syms.insert(
"eval".to_string(),
Symbol {
name: String::from("eval"),
args: Args::Lazy(1),
conditional_branches: true,
docs: decl::EVAL_DOCSTRING.to_string(),
value: ValueType::Internal(Rc::new(decl::eval_callback)),
..Default::default()
},
);
syms.insert(
"lambda".to_string(),
Symbol {
name: String::from("lambda"),
args: Args::Lazy(2),
conditional_branches: true,
docs: decl::LAMBDA_DOCSTRING.to_string(),
value: ValueType::Internal(Rc::new(decl::lambda_callback)),
..Default::default()
}
);
syms.insert(
"get-doc".to_string(),
Symbol {
name: String::from("get-doc"),
args: Args::Strict(vec![Type::Symbol]),
conditional_branches: false,
docs: decl::GETDOC_DOCSTRING.to_string(),
value: ValueType::Internal(Rc::new(decl::getdoc_callback)),
..Default::default()
}
);
syms.insert(
"set-doc".to_string(),
Symbol {
name: String::from("get-doc"),
args: Args::Strict(vec![Type::Symbol, Type::String]),
conditional_branches: false,
docs: decl::SETDOC_DOCSTRING.to_string(),
value: ValueType::Internal(Rc::new(decl::setdoc_callback)),
..Default::default()
}
);
syms.insert( syms.insert(
"call".to_string(), "call".to_string(),
@ -622,18 +77,6 @@ pub fn static_stdlib(syms: &mut SymTable) -> Result<(), String> {
} }
); );
syms.insert(
"input".to_string(),
Symbol {
name: String::from("input"),
args: Args::Strict(vec![Type::String]),
conditional_branches: false,
docs: strings::INPUT_DOCSTRING.to_string(),
value: ValueType::Internal(Rc::new(strings::input_callback)),
..Default::default()
}
);
Ok(()) Ok(())
} }
@ -648,22 +91,7 @@ pub fn dynamic_stdlib(syms: &mut SymTable, shell: Option<Rc<RefCell<posix::Shell
.to_string() .to_string()
.eq("true"); .eq("true");
// this also depends on the value of CFG_RELISH_ENV decl::add_decl_lib_dynamic(syms, env_cfg_user_form);
syms.insert(
"def".to_string(),
Symbol {
name: String::from("define"),
args: Args::Infinite,
conditional_branches: true,
docs: decl::STORE_DOCSTRING.to_string(),
value: ValueType::Internal(Rc::new(
move |ast: &Seg, syms: &mut SymTable| -> Result<Ctr, Traceback> {
decl::store_callback(ast, syms, env_cfg_user_form)
},
)),
..Default::default()
},
);
// This should be replaced by actual compiler conditionals in the future // This should be replaced by actual compiler conditionals in the future
if let Some(shell_state) = shell { if let Some(shell_state) = shell {
@ -675,17 +103,6 @@ pub fn dynamic_stdlib(syms: &mut SymTable, shell: Option<Rc<RefCell<posix::Shell
if posix_cfg_user_form { if posix_cfg_user_form {
posix::load_posix_shell(syms, shell_state); posix::load_posix_shell(syms, shell_state);
syms.insert(
"cd".to_string(),
Symbol {
name: String::from("cd"),
args: Args::Strict(vec![Type::String]),
conditional_branches: false,
docs: posix::CD_DOCSTRING.to_string(),
value: ValueType::Internal(Rc::new(posix::cd_callback)),
..Default::default()
},
);
} }
} }

View file

@ -14,14 +14,14 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
use crate::segment::{Ctr, Seg}; use crate::segment::{Ctr, Seg, Type};
use crate::sym::SymTable; use crate::sym::{SymTable, Symbol, Args, ValueType};
use crate::error::{Traceback, start_trace}; use crate::error::{Traceback, start_trace};
use std::rc::Rc;
pub const CONS_DOCSTRING: &str = "traverses any number of arguments collecting them into a list. const CONS_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."; If the first argument is a list, all other arguments are added sequentially to the end of the list contained in the first argument.";
fn cons_callback(ast: &Seg, _syms: &mut SymTable) -> Result<Ctr, Traceback> {
pub fn cons_callback(ast: &Seg, _syms: &mut SymTable) -> Result<Ctr, Traceback> {
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();
if let Ctr::Seg(ref list) = *ast.cdr { if let Ctr::Seg(ref list) = *ast.cdr {
@ -51,11 +51,10 @@ pub fn cons_callback(ast: &Seg, _syms: &mut SymTable) -> Result<Ctr, Traceback>
} }
} }
pub const LEN_DOCSTRING: &str = "Takes a single argument, expected to be a list. const LEN_DOCSTRING: &str = "Takes a single argument, expected to be a list.
Returns the length of said list. Returns the length of said list.
Interpreter will panic if the length of the list is greater than the max value of a signed 128 bit integer"; Interpreter will panic if the length of the list is greater than the max value of a signed 128 bit integer";
fn len_callback(ast: &Seg, _syms: &mut SymTable) -> Result<Ctr, Traceback> {
pub fn len_callback(ast: &Seg, _syms: &mut SymTable) -> Result<Ctr, Traceback> {
if let Ctr::Seg(ref s) = *ast.car { if let Ctr::Seg(ref s) = *ast.car {
if let Ctr::None = *s.car { if let Ctr::None = *s.car {
Ok(Ctr::Integer(0)) Ok(Ctr::Integer(0))
@ -67,11 +66,10 @@ pub fn len_callback(ast: &Seg, _syms: &mut SymTable) -> Result<Ctr, Traceback> {
} }
} }
pub const CAR_DOCSTRING: &str = "Takes a single argument, expected to be a list. const CAR_DOCSTRING: &str = "Takes a single argument, expected to be a list.
Returns a copy of the first value in that list. Returns a copy of the first value in that list.
If the list is empty, returns an err to avoid passing back a null value."; If the list is empty, returns an err to avoid passing back a null value.";
fn car_callback(ast: &Seg, _syms: &mut SymTable) -> Result<Ctr, Traceback> {
pub fn car_callback(ast: &Seg, _syms: &mut SymTable) -> Result<Ctr, Traceback> {
if let Ctr::Seg(ref s) = *ast.car { if let Ctr::Seg(ref s) = *ast.car {
if let Ctr::None = *s.car { if let Ctr::None = *s.car {
Err(start_trace(("len", "input is empty").into())) Err(start_trace(("len", "input is empty").into()))
@ -83,11 +81,10 @@ pub fn car_callback(ast: &Seg, _syms: &mut SymTable) -> Result<Ctr, Traceback> {
} }
} }
pub const CDR_DOCSTRING: &str = "Takes a single argument, expected to be a list. const CDR_DOCSTRING: &str = "Takes a single argument, expected to be a list.
Returns a copy of the last value in that list. Returns a copy of the last value in that list.
If the list is empty, returns an err to avoid passing back a null value."; If the list is empty, returns an err to avoid passing back a null value.";
fn cdr_callback(ast: &Seg, _syms: &mut SymTable) -> Result<Ctr, Traceback> {
pub fn cdr_callback(ast: &Seg, _syms: &mut SymTable) -> Result<Ctr, Traceback> {
if let Ctr::Seg(ref s) = *ast.car { if let Ctr::Seg(ref s) = *ast.car {
let mut ret = &s.car; let mut ret = &s.car;
let mut iter = &s.cdr; let mut iter = &s.cdr;
@ -105,14 +102,13 @@ pub fn cdr_callback(ast: &Seg, _syms: &mut SymTable) -> Result<Ctr, Traceback> {
} }
} }
pub const POP_DOCSTRING: &str = "Takes a single argument, expected to be a list. const POP_DOCSTRING: &str = "Takes a single argument, expected to be a list.
Returns a list containing the first element, and the rest of the elements. Returns a list containing the first element, and the rest of the elements.
Example: (pop (1 2 3)) -> (1 (2 3)). Example: (pop (1 2 3)) -> (1 (2 3)).
The head can then be accessed by car and the rest can be accessed by cdr."; The head can then be accessed by car and the rest can be accessed by cdr.";
fn pop_callback(ast: &Seg, _syms: &mut SymTable) -> Result<Ctr, Traceback> {
pub fn pop_callback(ast: &Seg, _syms: &mut SymTable) -> Result<Ctr, Traceback> {
if let Ctr::Seg(ref s) = *ast.car { if let Ctr::Seg(ref s) = *ast.car {
if s.len() < 1 { if s.len() < 1 {
return Err(start_trace(("pop", "input is empty").into())); return Err(start_trace(("pop", "input is empty").into()));
@ -130,14 +126,13 @@ pub fn pop_callback(ast: &Seg, _syms: &mut SymTable) -> Result<Ctr, Traceback> {
} }
} }
pub const DEQUEUE_DOCSTRING: &str = "Takes a single argument, expected to be a list. const DEQUEUE_DOCSTRING: &str = "Takes a single argument, expected to be a list.
Returns a list containing the last element, and the rest of the elements. Returns a list containing the last element, and the rest of the elements.
Example: (dq (1 2 3)) -> (3 (1 2)). Example: (dq (1 2 3)) -> (3 (1 2)).
The last element can then be accessed by car and the rest can be accessed by cdr."; The last element can then be accessed by car and the rest can be accessed by cdr.";
fn dequeue_callback(ast: &Seg, _syms: &mut SymTable) -> Result<Ctr, Traceback> {
pub fn dequeue_callback(ast: &Seg, _syms: &mut SymTable) -> Result<Ctr, Traceback> {
if let Ctr::Seg(ref s) = *ast.car { if let Ctr::Seg(ref s) = *ast.car {
if s.len() < 1 { if s.len() < 1 {
return Err(start_trace(("dequeue", "expected an input").into())); return Err(start_trace(("dequeue", "expected an input").into()));
@ -169,10 +164,9 @@ pub fn dequeue_callback(ast: &Seg, _syms: &mut SymTable) -> Result<Ctr, Tracebac
} }
} }
pub const REVERSE_DOCSTRING: &str = "Takes a single argument, expected to be a list. const REVERSE_DOCSTRING: &str = "Takes a single argument, expected to be a list.
Returns the same list, but in reverse order."; Returns the same list, but in reverse order.";
fn reverse_callback(ast: &Seg, _syms: &mut SymTable) -> Result<Ctr, Traceback> {
pub fn reverse_callback(ast: &Seg, _syms: &mut SymTable) -> Result<Ctr, Traceback> {
if let Ctr::Seg(ref s) = *ast.car { if let Ctr::Seg(ref s) = *ast.car {
let mut ret = Seg::new(); let mut ret = Seg::new();
s.circuit_reverse(&mut |arg: &Ctr| -> bool { s.circuit_reverse(&mut |arg: &Ctr| -> bool {
@ -185,3 +179,89 @@ pub fn reverse_callback(ast: &Seg, _syms: &mut SymTable) -> Result<Ctr, Tracebac
Err(start_trace(("reverse", "input is not a list").into())) Err(start_trace(("reverse", "input is not a list").into()))
} }
} }
pub fn add_list_lib(syms: &mut SymTable) {
syms.insert(
"cons".to_string(),
Symbol {
name: String::from("cons"),
args: Args::Infinite,
conditional_branches: false,
docs: CONS_DOCSTRING.to_string(),
value: ValueType::Internal(Rc::new(cons_callback)),
..Default::default()
},
);
syms.insert(
"len".to_string(),
Symbol {
name: String::from("len"),
args: Args::Strict(vec![Type::Seg]),
conditional_branches: false,
docs: LEN_DOCSTRING.to_string(),
value: ValueType::Internal(Rc::new(len_callback)),
..Default::default()
},
);
syms.insert(
"car".to_string(),
Symbol {
name: String::from("car"),
args: Args::Strict(vec![Type::Seg]),
conditional_branches: false,
docs: CAR_DOCSTRING.to_string(),
value: ValueType::Internal(Rc::new(car_callback)),
..Default::default()
},
);
syms.insert(
"cdr".to_string(),
Symbol {
name: String::from("cdr"),
args: Args::Strict(vec![Type::Seg]),
conditional_branches: false,
docs: CDR_DOCSTRING.to_string(),
value: ValueType::Internal(Rc::new(cdr_callback)),
..Default::default()
},
);
syms.insert(
"pop".to_string(),
Symbol {
name: String::from("pop"),
args: Args::Strict(vec![Type::Seg]),
conditional_branches: false,
docs: POP_DOCSTRING.to_string(),
value: ValueType::Internal(Rc::new(pop_callback)),
..Default::default()
},
);
syms.insert(
"dq".to_string(),
Symbol {
name: String::from("dequeue"),
args: Args::Strict(vec![Type::Seg]),
conditional_branches: false,
docs: DEQUEUE_DOCSTRING.to_string(),
value: ValueType::Internal(Rc::new(dequeue_callback)),
..Default::default()
},
);
syms.insert(
"reverse".to_string(),
Symbol {
name: String::from("reverse"),
args: Args::Strict(vec![Type::Seg]),
conditional_branches: false,
docs: REVERSE_DOCSTRING.to_string(),
value: ValueType::Internal(Rc::new(reverse_callback)),
..Default::default()
},
);
}

View file

@ -14,16 +14,16 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
use crate::segment::{Ctr, Seg}; use crate::segment::{Ctr, Seg, Type};
use crate::error::{Traceback, start_trace}; use crate::error::{Traceback, start_trace};
use crate::sym::{SymTable, ValueType}; use crate::sym::{SymTable, ValueType, Args, Symbol};
use std::rc::Rc;
pub const AND_DOCSTRING: &str = const AND_DOCSTRING: &str =
"traverses a list of N arguments, all of which are expected to be boolean. "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. starts with arg1 AND arg2, and then calculates prev_result AND next_arg.
returns final result."; returns final result.";
fn and_callback(ast: &Seg, _syms: &mut SymTable) -> Result<Ctr, Traceback> {
pub fn and_callback(ast: &Seg, _syms: &mut SymTable) -> Result<Ctr, Traceback> {
let mut type_error = false; let mut type_error = false;
let mut cursor = 0; let mut cursor = 0;
let result = ast.circuit(&mut |arg: &Ctr| -> bool { let result = ast.circuit(&mut |arg: &Ctr| -> bool {
@ -43,12 +43,11 @@ pub fn and_callback(ast: &Seg, _syms: &mut SymTable) -> Result<Ctr, Traceback> {
} }
} }
pub const OR_DOCSTRING: &str = const OR_DOCSTRING: &str =
"traverses a list of N arguments, all of which are expected to be boolean. "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. starts with arg1 OR arg2, and then calculates prev_result OR next_arg.
returns final result."; returns final result.";
fn or_callback(ast: &Seg, _syms: &mut SymTable) -> Result<Ctr, Traceback> {
pub fn or_callback(ast: &Seg, _syms: &mut SymTable) -> Result<Ctr, Traceback> {
let mut result = false; let mut result = false;
let mut cursor = 0; let mut cursor = 0;
let correct_types = ast.circuit(&mut |arg: &Ctr| -> bool { let correct_types = ast.circuit(&mut |arg: &Ctr| -> bool {
@ -68,10 +67,9 @@ pub fn or_callback(ast: &Seg, _syms: &mut SymTable) -> Result<Ctr, Traceback> {
} }
} }
pub const NOT_DOCSTRING: &str = "takes a single argument (expects a boolean). const NOT_DOCSTRING: &str = "takes a single argument (expects a boolean).
returns false if arg is true or true if arg is false."; returns false if arg is true or true if arg is false.";
fn not_callback(ast: &Seg, _syms: &mut SymTable) -> Result<Ctr, Traceback> {
pub fn not_callback(ast: &Seg, _syms: &mut SymTable) -> Result<Ctr, Traceback> {
if let Ctr::Bool(b) = *ast.car { if let Ctr::Bool(b) = *ast.car {
Ok(Ctr::Bool(!b)) Ok(Ctr::Bool(!b))
} else { } else {
@ -79,22 +77,20 @@ pub fn not_callback(ast: &Seg, _syms: &mut SymTable) -> Result<Ctr, Traceback> {
} }
} }
pub const ISEQ_DOCSTRING: &str = "traverses a list of N arguments. const ISEQ_DOCSTRING: &str = "traverses a list of N arguments.
returns true if all arguments hold the same value. 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"; NOTE: 1 and 1.0 are the same, but '1' 'one' or one (symbol) aren't";
fn iseq_callback(ast: &Seg, _syms: &mut SymTable) -> Result<Ctr, Traceback> {
pub fn iseq_callback(ast: &Seg, _syms: &mut SymTable) -> Result<Ctr, Traceback> {
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 const TOGGLE_DOCSTRING: &str = "switches a boolean symbol between true or false. 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. 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."; Either sets the symbol to true if it is currently false, or vice versa.";
fn toggle_callback(ast: &Seg, syms: &mut SymTable) -> Result<Ctr, Traceback> {
pub fn toggle_callback(ast: &Seg, syms: &mut SymTable) -> Result<Ctr, Traceback> {
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();
@ -121,13 +117,11 @@ pub fn toggle_callback(ast: &Seg, syms: &mut SymTable) -> Result<Ctr, Traceback>
Ok(Ctr::None) Ok(Ctr::None)
} }
const BOOLCAST_DOCSTRING: &str = "takes one argument of any type.
pub const BOOLCAST_DOCSTRING: &str = "takes one argument of any type.
attempts to cast argument to a bool. attempts to cast argument to a bool.
Strings will cast to a bool if they are 'true' or 'false'. Strings will cast to a bool if they are 'true' or 'false'.
Integers and Floats will cast to true if they are 0 and false otherwise."; Integers and Floats will cast to true if they are 0 and false otherwise.";
fn boolcast_callback(ast: &Seg, _syms: &mut SymTable) -> Result<Ctr, Traceback> {
pub fn boolcast_callback(ast: &Seg, _syms: &mut SymTable) -> Result<Ctr, Traceback> {
match &*ast.car { match &*ast.car {
Ctr::Bool(_) => Ok(*ast.car.clone()), Ctr::Bool(_) => Ok(*ast.car.clone()),
Ctr::String(s) => { Ctr::String(s) => {
@ -145,3 +139,77 @@ pub fn boolcast_callback(ast: &Seg, _syms: &mut SymTable) -> Result<Ctr, Traceba
ast.car.to_type())).into())), ast.car.to_type())).into())),
} }
} }
pub fn add_bool_lib(syms: &mut SymTable) {
syms.insert(
"and".to_string(),
Symbol {
name: String::from("and"),
args: Args::Infinite,
conditional_branches: false,
docs: AND_DOCSTRING.to_string(),
value: ValueType::Internal(Rc::new(and_callback)),
..Default::default()
},
);
syms.insert(
"bool".to_string(),
Symbol {
name: String::from("bool"),
args: Args::Infinite,
conditional_branches: false,
docs: BOOLCAST_DOCSTRING.to_string(),
value: ValueType::Internal(Rc::new(boolcast_callback)),
..Default::default()
},
);
syms.insert(
"or".to_string(),
Symbol {
name: String::from("or"),
args: Args::Infinite,
conditional_branches: false,
docs: OR_DOCSTRING.to_string(),
value: ValueType::Internal(Rc::new(or_callback)),
..Default::default()
},
);
syms.insert(
"not".to_string(),
Symbol {
name: String::from("not"),
args: Args::Strict(vec![Type::Bool]),
conditional_branches: false,
docs: NOT_DOCSTRING.to_string(),
value: ValueType::Internal(Rc::new(not_callback)),
..Default::default()
},
);
syms.insert(
"eq?".to_string(),
Symbol {
name: String::from("eq?"),
args: Args::Infinite,
conditional_branches: false,
docs: ISEQ_DOCSTRING.to_string(),
value: ValueType::Internal(Rc::new(iseq_callback)),
..Default::default()
},
);
syms.insert(
"toggle".to_string(),
Symbol {
name: String::from("toggle"),
args: Args::Lazy(1),
conditional_branches: true,
docs: TOGGLE_DOCSTRING.to_string(),
value: ValueType::Internal(Rc::new(toggle_callback)),
..Default::default()
},
);
}

View file

@ -18,9 +18,10 @@
use crate::eval::eval; use crate::eval::eval;
use crate::error::{Traceback, start_trace}; use crate::error::{Traceback, start_trace};
use crate::segment::{Ctr, Seg}; use crate::segment::{Ctr, Seg};
use crate::sym::{SymTable, Symbol}; use crate::sym::{SymTable, Symbol, ValueType, Args};
use std::rc::Rc;
pub const IF_DOCSTRING: &str = const IF_DOCSTRING: &str =
"accepts three bodies, a condition, an unevaluated consequence, and an alternative consequence. "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 true, the first consequence is evaluated.
If the condition is evaluated to false, the second consequence is evaluated. If the condition is evaluated to false, the second consequence is evaluated.
@ -29,8 +30,7 @@ Otherwise, an error is thrown.
example: (if my-state-switch example: (if my-state-switch
(do-my-thing) (do-my-thing)
(else-an-other-thing))"; (else-an-other-thing))";
fn if_callback(ast: &Seg, syms: &mut SymTable) -> Result<Ctr, Traceback> {
pub fn if_callback(ast: &Seg, syms: &mut SymTable) -> Result<Ctr, Traceback> {
let cond: bool; let cond: bool;
match *ast.car { match *ast.car {
Ctr::Seg(ref cond_form) => { Ctr::Seg(ref cond_form) => {
@ -119,7 +119,7 @@ pub fn if_callback(ast: &Seg, syms: &mut SymTable) -> Result<Ctr, Traceback> {
} }
} }
pub const LET_DOCSTRING: &str = "creates a stack of local variables for a sequence of operations. const LET_DOCSTRING: &str = "creates a stack of local variables for a sequence of operations.
returns the result of the final operation. returns the result of the final operation.
example: (let ((step1 'hello') example: (let ((step1 'hello')
@ -132,8 +132,7 @@ In this example step1, step2, and step3 are created sequentially.
Then, the echo form is evaluated, printing 'hello-world'. Then, the echo form is evaluated, printing 'hello-world'.
Finally, the some-func form is evaluated. Finally, the some-func form is evaluated.
Since the call to some-func is the final form, its value is returned."; Since the call to some-func is the final form, its value is returned.";
fn let_callback(ast: &Seg, syms: &mut SymTable) -> Result<Ctr, Traceback> {
pub fn let_callback(ast: &Seg, syms: &mut SymTable) -> Result<Ctr, Traceback> {
let mut localsyms = syms.clone(); let mut localsyms = syms.clone();
let mut locals = vec![]; let mut locals = vec![];
let locals_form: &Seg; let locals_form: &Seg;
@ -250,15 +249,14 @@ pub fn let_callback(ast: &Seg, syms: &mut SymTable) -> Result<Ctr, Traceback> {
Ok((*result).clone()) Ok((*result).clone())
} }
pub const WHILE_DOCSTRING: &str = "traverses a list of N un-evaluated forms. 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. 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) example: (while (check-my-state)
(do-thing-1 args) (do-thing-1 args)
(do-thing-2 args) (do-thing-2 args)
(edit-state my-state))"; (edit-state my-state))";
fn while_callback(ast: &Seg, syms: &mut SymTable) -> Result<Ctr, Traceback> {
pub fn while_callback(ast: &Seg, syms: &mut SymTable) -> Result<Ctr, Traceback> {
let eval_cond: &Seg; let eval_cond: &Seg;
let outer_maybe: Seg; let outer_maybe: Seg;
let eval_bodies_head: &Seg; let eval_bodies_head: &Seg;
@ -319,7 +317,7 @@ pub fn while_callback(ast: &Seg, syms: &mut SymTable) -> Result<Ctr, Traceback>
Ok(*(result.unwrap()).clone()) Ok(*(result.unwrap()).clone())
} }
pub const CIRCUIT_DOCSTRING: &str = "traverses a list of N un-evaluated forms. 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. 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. Circuit will also stop when a form does not evaluate to a boolean, or evaluates to false.
@ -329,8 +327,7 @@ example: (circuit (eq? (do-operation) myresult)
(do-another-operation)) (do-another-operation))
in this example, do-another-operation will not be called"; in this example, do-another-operation will not be called";
fn circuit_callback(ast: &Seg, syms: &mut SymTable) -> Result<Ctr, Traceback> {
pub fn circuit_callback(ast: &Seg, syms: &mut SymTable) -> Result<Ctr, Traceback> {
let mut cursor = 0; let mut cursor = 0;
let mut err_trace = Traceback::new(); let mut err_trace = Traceback::new();
let result = ast.circuit(&mut |form: &Ctr| -> bool { let result = ast.circuit(&mut |form: &Ctr| -> bool {
@ -383,3 +380,53 @@ pub fn circuit_callback(ast: &Seg, syms: &mut SymTable) -> Result<Ctr, Traceback
Ok(Ctr::Bool(result)) Ok(Ctr::Bool(result))
} }
} }
pub fn add_control_lib(syms: &mut SymTable) {
syms.insert(
"if".to_string(),
Symbol {
name: String::from("if"),
args: Args::Lazy(3),
conditional_branches: true,
docs: IF_DOCSTRING.to_string(),
value: ValueType::Internal(Rc::new(if_callback)),
..Default::default()
},
);
syms.insert(
"let".to_string(),
Symbol {
name: String::from("let"),
args: Args::Infinite,
conditional_branches: true,
docs: LET_DOCSTRING.to_string(),
value: ValueType::Internal(Rc::new(let_callback)),
..Default::default()
},
);
syms.insert(
"while".to_string(),
Symbol {
name: String::from("while"),
args: Args::Infinite,
conditional_branches: true,
docs: WHILE_DOCSTRING.to_string(),
value: ValueType::Internal(Rc::new(while_callback)),
..Default::default()
},
);
syms.insert(
"circuit".to_string(),
Symbol {
name: String::from("circuit"),
args: Args::Infinite,
conditional_branches: true,
docs: CIRCUIT_DOCSTRING.to_string(),
value: ValueType::Internal(Rc::new(circuit_callback)),
..Default::default()
},
);
}

View file

@ -19,12 +19,12 @@ use crate::eval::eval;
use crate::error::{Traceback, start_trace}; use crate::error::{Traceback, start_trace};
use crate::segment::{Ctr, Seg, Type}; use crate::segment::{Ctr, Seg, Type};
use crate::stdlib::{CONSOLE_XDIM_VNAME, RELISH_DEFAULT_CONS_WIDTH}; use crate::stdlib::{CONSOLE_XDIM_VNAME, RELISH_DEFAULT_CONS_WIDTH};
use crate::sym::{SymTable, Symbol, UserFn, ValueType}; use crate::sym::{SymTable, Symbol, UserFn, ValueType, Args};
use std::env; use std::env;
use std::rc::Rc;
pub const QUOTE_DOCSTRING: &str = "takes a single unevaluated tree and returns it as it is: unevaluated."; const QUOTE_DOCSTRING: &str = "takes a single unevaluated tree and returns it as it is: unevaluated.";
fn quote_callback(ast: &Seg, _syms: &mut SymTable) -> Result<Ctr, Traceback> {
pub fn quote_callback(ast: &Seg, _syms: &mut SymTable) -> Result<Ctr, Traceback> {
if ast.len() > 1 { if ast.len() > 1 {
Err(start_trace(("quote", "do not quote more than one thing at a time").into())) Err(start_trace(("quote", "do not quote more than one thing at a time").into()))
} else { } else {
@ -32,12 +32,11 @@ pub fn quote_callback(ast: &Seg, _syms: &mut SymTable) -> Result<Ctr, Traceback>
} }
} }
pub const EVAL_DOCSTRING: &str = "takes an unevaluated argument and evaluates it. const EVAL_DOCSTRING: &str = "takes an unevaluated argument and evaluates it.
Specifically, does one pass of the tree simplification algorithm. Specifically, does one pass of the tree simplification algorithm.
If you have a variable referencing another variable you will get the If you have a variable referencing another variable you will get the
referenced variable."; referenced variable.";
fn eval_callback(ast: &Seg, syms: &mut SymTable) -> Result<Ctr, Traceback> {
pub fn eval_callback(ast: &Seg, syms: &mut SymTable) -> Result<Ctr, Traceback> {
if ast.len() > 1 { if ast.len() > 1 {
Err(start_trace( Err(start_trace(
("eval", "do not eval more than one thing at a time") ("eval", "do not eval more than one thing at a time")
@ -96,9 +95,8 @@ pub fn eval_callback(ast: &Seg, syms: &mut SymTable) -> Result<Ctr, Traceback> {
} }
} }
pub const HELP_DOCSTRING: &str = "prints help text for a given symbol. Expects only one argument."; 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, Traceback> {
pub fn help_callback(ast: &Seg, syms: &mut SymTable) -> Result<Ctr, Traceback> {
if ast.len() != 1 { if ast.len() != 1 {
return Err(start_trace(("help", "expected one input").into())); return Err(start_trace(("help", "expected one input").into()));
} }
@ -129,10 +127,9 @@ CURRENT VALUE AND/OR BODY:
Ok(Ctr::None) Ok(Ctr::None)
} }
pub const ISSET_DOCSTRING: &str = "accepts a single argument: a symbol. const ISSET_DOCSTRING: &str = "accepts a single argument: a symbol.
returns true or false according to whether or not the symbol is found in the symbol table."; returns true or false according to whether or not the symbol is found in the symbol table.";
fn isset_callback(ast: &Seg, syms: &mut SymTable) -> Result<Ctr, Traceback> {
pub fn isset_callback(ast: &Seg, syms: &mut SymTable) -> Result<Ctr, Traceback> {
if ast.len() != 1 { if ast.len() != 1 {
Err(start_trace(("set?", "expcted one input").into())) Err(start_trace(("set?", "expcted one input").into()))
} else { } else {
@ -148,10 +145,9 @@ pub fn isset_callback(ast: &Seg, syms: &mut SymTable) -> Result<Ctr, Traceback>
} }
} }
pub const ENV_DOCSTRING: &str = "takes no arguments const ENV_DOCSTRING: &str = "takes no arguments
prints out all available symbols and their associated values"; prints out all available symbols and their associated values";
fn env_callback(_ast: &Seg, syms: &mut SymTable) -> Result<Ctr, Traceback> {
pub fn env_callback(_ast: &Seg, syms: &mut SymTable) -> Result<Ctr, Traceback> {
// get width of current output // get width of current output
let xdim: i128; let xdim: i128;
if let Ctr::Integer(dim) = *syms if let Ctr::Integer(dim) = *syms
@ -226,7 +222,7 @@ pub fn env_callback(_ast: &Seg, syms: &mut SymTable) -> Result<Ctr, Traceback> {
Ok(Ctr::None) Ok(Ctr::None)
} }
pub const LAMBDA_DOCSTRING: &str = "Takes two arguments of any type. const LAMBDA_DOCSTRING: &str = "Takes two arguments of any type.
No args are evaluated when lambda is called. No args are evaluated when lambda is called.
Lambda makes sure the first argument is a list of symbols (or 'arguments') to the lambda function. Lambda makes sure the first argument is a list of symbols (or 'arguments') to the lambda function.
The next arg is stored in a tree to evaluate on demand. The next arg is stored in a tree to evaluate on demand.
@ -236,8 +232,7 @@ This can then be evaluated like so:
((lambda (x y) (add x y)) 1 2) ((lambda (x y) (add x y)) 1 2)
which is functionally equivalent to: which is functionally equivalent to:
(add 1 2)"; (add 1 2)";
fn lambda_callback(
pub fn lambda_callback(
ast: &Seg, ast: &Seg,
_syms: &mut SymTable _syms: &mut SymTable
) -> Result<Ctr, Traceback> { ) -> Result<Ctr, Traceback> {
@ -274,13 +269,12 @@ pub fn lambda_callback(
} }
} }
pub const GETDOC_DOCSTRING: &str = "accepts an unevaluated symbol, returns the doc string. const GETDOC_DOCSTRING: &str = "accepts an unevaluated symbol, returns the doc string.
Returns an error if symbol is undefined. Returns an error if symbol is undefined.
Note: make sure to quote the input like this: Note: make sure to quote the input like this:
(get-doc (quote symbol-name))"; (get-doc (quote symbol-name))";
fn getdoc_callback(ast: &Seg, syms: &mut SymTable) -> Result<Ctr, Traceback> {
pub fn getdoc_callback(ast: &Seg, syms: &mut SymTable) -> Result<Ctr, Traceback> {
if let Ctr::Symbol(ref symbol) = *ast.car { if let Ctr::Symbol(ref symbol) = *ast.car {
if let Some(sym) = syms.get(symbol) { if let Some(sym) = syms.get(symbol) {
Ok(Ctr::String(sym.docs.clone())) Ok(Ctr::String(sym.docs.clone()))
@ -292,13 +286,12 @@ pub fn getdoc_callback(ast: &Seg, syms: &mut SymTable) -> Result<Ctr, Traceback>
} }
} }
pub const SETDOC_DOCSTRING: &str = "accepts a symbol and a doc string. const SETDOC_DOCSTRING: &str = "accepts a symbol and a doc string.
Returns an error if symbol is undefined, otherwise sets the symbols docstring to the argument. Returns an error if symbol is undefined, otherwise sets the symbols docstring to the argument.
Note: make sure to quote the input like this: Note: make sure to quote the input like this:
(set-doc (quote symbol-name) my-new-docs)"; (set-doc (quote symbol-name) my-new-docs)";
fn setdoc_callback(ast: &Seg, syms: &mut SymTable) -> Result<Ctr, Traceback> {
pub fn setdoc_callback(ast: &Seg, syms: &mut SymTable) -> Result<Ctr, Traceback> {
if ast.len() != 2 { if ast.len() != 2 {
Err(start_trace( Err(start_trace(
("set-doc", "expected two inputs") ("set-doc", "expected two inputs")
@ -336,7 +329,7 @@ pub fn setdoc_callback(ast: &Seg, syms: &mut SymTable) -> Result<Ctr, Traceback>
} }
} }
pub const STORE_DOCSTRING: &str = "allows user to define functions and variables. const STORE_DOCSTRING: &str = "allows user to define functions and variables.
A call may take one of three forms: A call may take one of three forms:
1. variable declaration: 1. variable declaration:
Takes a name, doc string, and a value. Takes a name, doc string, and a value.
@ -351,9 +344,7 @@ pub const STORE_DOCSTRING: &str = "allows user to define functions and variables
Additionally, passing a tree as a name will trigger def to evaluate the tree and try to derive Additionally, passing a tree as a name will trigger def to evaluate the tree and try to derive
a value from it. If it does not return a "; a value from it. If it does not return a ";
fn store_callback(ast: &Seg, syms: &mut SymTable, env_cfg: bool) -> Result<Ctr, Traceback> {
pub fn store_callback(ast: &Seg, syms: &mut SymTable, env_cfg: bool) -> Result<Ctr, Traceback> {
let is_var = ast.len() == 3; let is_var = ast.len() == 3;
let name: String; let name: String;
let docs: String; let docs: String;
@ -509,3 +500,131 @@ pub fn store_callback(ast: &Seg, syms: &mut SymTable, env_cfg: bool) -> Result<C
.into())) .into()))
} }
} }
pub fn add_decl_lib_static(syms: &mut SymTable) {
syms.insert(
"help".to_string(),
Symbol {
name: String::from("help"),
args: Args::Strict(vec![Type::Symbol]),
conditional_branches: true,
docs: HELP_DOCSTRING.to_string(),
value: ValueType::Internal(Rc::new(help_callback)),
..Default::default()
},
);
syms.insert(
"set?".to_string(),
Symbol {
name: String::from("set?"),
args: Args::Strict(vec![Type::Symbol]),
conditional_branches: true,
docs: ISSET_DOCSTRING.to_string(),
value: ValueType::Internal(Rc::new(isset_callback)),
..Default::default()
},
);
syms.insert(
"env".to_string(),
Symbol {
name: String::from("env"),
args: Args::None,
conditional_branches: false,
docs: ENV_DOCSTRING.to_string(),
value: ValueType::Internal(Rc::new(env_callback)),
..Default::default()
},
);
syms.insert(
"quote".to_string(),
Symbol {
name: String::from("quote"),
args: Args::Lazy(1),
conditional_branches: true,
docs: QUOTE_DOCSTRING.to_string(),
value: ValueType::Internal(Rc::new(quote_callback)),
..Default::default()
},
);
syms.insert(
"q".to_string(),
Symbol {
name: String::from("quote"),
args: Args::Lazy(1),
conditional_branches: true,
docs: QUOTE_DOCSTRING.to_string(),
value: ValueType::Internal(Rc::new(quote_callback)),
..Default::default()
},
);
syms.insert(
"eval".to_string(),
Symbol {
name: String::from("eval"),
args: Args::Lazy(1),
conditional_branches: true,
docs: EVAL_DOCSTRING.to_string(),
value: ValueType::Internal(Rc::new(eval_callback)),
..Default::default()
},
);
syms.insert(
"lambda".to_string(),
Symbol {
name: String::from("lambda"),
args: Args::Lazy(2),
conditional_branches: true,
docs: LAMBDA_DOCSTRING.to_string(),
value: ValueType::Internal(Rc::new(lambda_callback)),
..Default::default()
}
);
syms.insert(
"get-doc".to_string(),
Symbol {
name: String::from("get-doc"),
args: Args::Strict(vec![Type::Symbol]),
conditional_branches: false,
docs: GETDOC_DOCSTRING.to_string(),
value: ValueType::Internal(Rc::new(getdoc_callback)),
..Default::default()
}
);
syms.insert(
"set-doc".to_string(),
Symbol {
name: String::from("get-doc"),
args: Args::Strict(vec![Type::Symbol, Type::String]),
conditional_branches: false,
docs: SETDOC_DOCSTRING.to_string(),
value: ValueType::Internal(Rc::new(setdoc_callback)),
..Default::default()
}
);
}
pub fn add_decl_lib_dynamic(syms: &mut SymTable, env: bool) {
syms.insert(
"def".to_string(),
Symbol {
name: String::from("define"),
args: Args::Infinite,
conditional_branches: true,
docs: STORE_DOCSTRING.to_string(),
value: ValueType::Internal(Rc::new(
move |ast: &Seg, syms: &mut SymTable| -> Result<Ctr, Traceback> {
store_callback(ast, syms, env)
},
)),
..Default::default()
},
);
}

View file

@ -15,8 +15,9 @@
*/ */
use crate::segment::{Ctr, Seg}; use crate::segment::{Ctr, Seg};
use crate::sym::{SymTable, ValueType}; use crate::sym::{SymTable, ValueType, Symbol, Args};
use crate::error::{Traceback, start_trace}; use crate::error::{Traceback, start_trace};
use std::rc::Rc;
fn isnumeric(arg: &Ctr) -> bool { fn isnumeric(arg: &Ctr) -> bool {
match arg { match arg {
@ -26,12 +27,11 @@ fn isnumeric(arg: &Ctr) -> bool {
} }
} }
pub const ADD_DOCSTRING: &str = const ADD_DOCSTRING: &str =
"traverses over N args, which must all evaluate to an Integer or Float. "traverses over N args, which must all evaluate to an Integer or Float.
Adds each arg up to a final result. WARNING: does not acocunt for under/overflows. Adds each arg up to a final result. WARNING: does not acocunt for under/overflows.
Consult source code for a better understanding of how extreme values will be handled."; Consult source code for a better understanding of how extreme values will be handled.";
fn add_callback(ast: &Seg, _: &mut SymTable) -> Result<Ctr, Traceback> {
pub fn add_callback(ast: &Seg, _: &mut SymTable) -> Result<Ctr, Traceback> {
let mut res = Ctr::Integer(0); let mut res = Ctr::Integer(0);
let mut culprit: Ctr = Ctr::None; let mut culprit: Ctr = Ctr::None;
let type_consistent = ast.circuit(&mut |c: &Ctr| -> bool { let type_consistent = ast.circuit(&mut |c: &Ctr| -> bool {
@ -53,11 +53,10 @@ pub fn add_callback(ast: &Seg, _: &mut SymTable) -> Result<Ctr, Traceback> {
} }
} }
pub const SUB_DOCSTRING: &str = "traverses over N args, which must all evaluate to an Integer or Float. const SUB_DOCSTRING: &str = "traverses over N args, which must all evaluate to an Integer or Float.
Subtracts each arg from the first leading to a final result. WARNING: does not acocunt for under/overflows. Subtracts each arg from the first leading to a final result. WARNING: does not acocunt for under/overflows.
Consult source code for a better understanding of how extreme values will be handled."; Consult source code for a better understanding of how extreme values will be handled.";
fn sub_callback(ast: &Seg, _: &mut SymTable) -> Result<Ctr, Traceback> {
pub fn sub_callback(ast: &Seg, _: &mut SymTable) -> Result<Ctr, Traceback> {
if !isnumeric(ast.car.as_ref()) { if !isnumeric(ast.car.as_ref()) {
return Err(start_trace( return Err(start_trace(
("sub", format!("{} is not a number!", ast.car.as_ref())) ("sub", format!("{} is not a number!", ast.car.as_ref()))
@ -91,11 +90,10 @@ pub fn sub_callback(ast: &Seg, _: &mut SymTable) -> Result<Ctr, Traceback> {
} }
} }
pub const DIV_DOCSTRING: &str = "takes two args, which must both evaluate to an Integer or Float. const DIV_DOCSTRING: &str = "takes two args, which must both evaluate to an Integer or Float.
divides arg1 by arg2. WARNING: does not acocunt for under/overflows or float precision. divides arg1 by arg2. WARNING: does not acocunt for under/overflows or float precision.
Consult source code for a better understanding of how extreme values will be handled."; Consult source code for a better understanding of how extreme values will be handled.";
fn div_callback(ast: &Seg, _: &mut SymTable) -> Result<Ctr, Traceback> {
pub fn div_callback(ast: &Seg, _: &mut SymTable) -> Result<Ctr, Traceback> {
let first = *ast.car.clone(); let first = *ast.car.clone();
if !isnumeric(&first) { if !isnumeric(&first) {
return Err(start_trace( return Err(start_trace(
@ -118,12 +116,11 @@ pub fn div_callback(ast: &Seg, _: &mut SymTable) -> Result<Ctr, Traceback> {
} }
} }
pub const MUL_DOCSTRING: &str = const MUL_DOCSTRING: &str =
"traverses over N args, which must all evaluate to an Integer or Float. "traverses over N args, which must all evaluate to an Integer or Float.
Multiplies each arg up to a final result. WARNING: does not acocunt for under/overflows. Multiplies each arg up to a final result. WARNING: does not acocunt for under/overflows.
Consult source code for a better understanding of how extreme values will be handled."; Consult source code for a better understanding of how extreme values will be handled.";
fn mul_callback(ast: &Seg, _: &mut SymTable) -> Result<Ctr, Traceback> {
pub fn mul_callback(ast: &Seg, _: &mut SymTable) -> Result<Ctr, Traceback> {
let mut res = Ctr::Integer(1); let mut res = Ctr::Integer(1);
let mut culprit: Ctr = Ctr::None; let mut culprit: Ctr = Ctr::None;
let type_consistent = ast.circuit(&mut |c: &Ctr| -> bool { let type_consistent = ast.circuit(&mut |c: &Ctr| -> bool {
@ -145,12 +142,11 @@ pub fn mul_callback(ast: &Seg, _: &mut SymTable) -> Result<Ctr, Traceback> {
} }
} }
pub const INTCAST_DOCSTRING: &str = "takes a single arg and attempts to cast it to an Integer. const INTCAST_DOCSTRING: &str = "takes a single arg and attempts to cast it to an Integer.
This will work for a float or a potentially a string. This will work for a float or a potentially a string.
If the cast to Integer fails, it will return Nothing and print an error. If the cast to Integer fails, it will return Nothing and print an error.
Casting a float to an int will drop its decimal."; Casting a float to an int will drop its decimal.";
fn intcast_callback(ast: &Seg, _: &mut SymTable) -> Result<Ctr, Traceback> {
pub fn intcast_callback(ast: &Seg, _: &mut SymTable) -> Result<Ctr, Traceback> {
// special case for float // special case for float
if let Ctr::Float(f) = *ast.car { if let Ctr::Float(f) = *ast.car {
Ok(Ctr::Integer(f as i128)) Ok(Ctr::Integer(f as i128))
@ -170,12 +166,11 @@ pub fn intcast_callback(ast: &Seg, _: &mut SymTable) -> Result<Ctr, Traceback> {
} }
} }
pub const FLOATCAST_DOCSTRING: &str = "takes a single arg and attempts to cast it to a float. const FLOATCAST_DOCSTRING: &str = "takes a single arg and attempts to cast it to a float.
This will work for an integer or potentially a string. This will work for an integer or potentially a string.
If the cast to integer fails, this function will return nothing and print an error. If the cast to integer fails, this function will return nothing and print an error.
Casting an integer to a float can result in bad behaviour since float nodes are based on 64bit floats and int nodes are based on 128 bit integers."; Casting an integer to a float can result in bad behaviour since float nodes are based on 64bit floats and int nodes are based on 128 bit integers.";
fn floatcast_callback(ast: &Seg, _: &mut SymTable) -> Result<Ctr, Traceback> {
pub fn floatcast_callback(ast: &Seg, _: &mut SymTable) -> Result<Ctr, Traceback> {
// special case for float // special case for float
if let Ctr::Integer(i) = *ast.car { if let Ctr::Integer(i) = *ast.car {
Ok(Ctr::Float(i as f64)) Ok(Ctr::Float(i as f64))
@ -195,7 +190,7 @@ pub fn floatcast_callback(ast: &Seg, _: &mut SymTable) -> Result<Ctr, Traceback>
} }
} }
pub const EXP_DOCSTRING: &str = "Takes two args, both expected to be numeric. const EXP_DOCSTRING: &str = "Takes two args, both expected to be numeric.
Returns the first arg to the power of the second arg. Returns the first arg to the power of the second arg.
Does not handle overflow or underflow. Does not handle overflow or underflow.
@ -203,8 +198,7 @@ PANIC CASES:
- arg1 is a float and arg2 is greater than an int32 - arg1 is a float and arg2 is greater than an int32
- an integer exceeding the size of a float64 is raised to a float power - an integer exceeding the size of a float64 is raised to a float power
- an integer is rased to the power of another integer exceeding the max size of an unsigned 32bit integer"; - an integer is rased to the power of another integer exceeding the max size of an unsigned 32bit integer";
fn exp_callback(ast: &Seg, _: &mut SymTable) -> Result<Ctr, Traceback> {
pub fn exp_callback(ast: &Seg, _: &mut SymTable) -> Result<Ctr, Traceback> {
let first = *ast.car.clone(); let first = *ast.car.clone();
if !isnumeric(&first) { if !isnumeric(&first) {
return Err(start_trace( return Err(start_trace(
@ -247,7 +241,7 @@ pub fn exp_callback(ast: &Seg, _: &mut SymTable) -> Result<Ctr, Traceback> {
} }
} }
pub const MOD_DOCSTRING: &str = "Takes two args, both expected to be numeric. const MOD_DOCSTRING: &str = "Takes two args, both expected to be numeric.
Returns a list of two values: the modulus and the remainder. Returns a list of two values: the modulus and the remainder.
Example: (mod 5 3) -> (1 2) Example: (mod 5 3) -> (1 2)
@ -255,8 +249,7 @@ PANIC CASES:
- A float is modulo an integer larger than a max f64 - A float is modulo an integer larger than a max f64
- An integer larger than a max f64 is modulo a float - An integer larger than a max f64 is modulo a float
"; ";
fn mod_callback(ast: &Seg, _: &mut SymTable) -> Result<Ctr, Traceback> {
pub fn mod_callback(ast: &Seg, _: &mut SymTable) -> Result<Ctr, Traceback> {
let first = *ast.car.clone(); let first = *ast.car.clone();
if !isnumeric(&first) { if !isnumeric(&first) {
return Err(start_trace( return Err(start_trace(
@ -315,11 +308,10 @@ pub fn mod_callback(ast: &Seg, _: &mut SymTable) -> Result<Ctr, Traceback> {
Ok(Ctr::Seg(ret)) Ok(Ctr::Seg(ret))
} }
pub const ISGT_DOCSTRING: &str = "takes two args, which must both evaluate to an Integer or Float. const ISGT_DOCSTRING: &str = "takes two args, which must both evaluate to an Integer or Float.
Returns true or false according to whether the first argument is bigger than the second argument. Returns true or false according to whether the first argument is bigger than the second argument.
May panic if an integer larger than a max f64 is compared to a float."; May panic if an integer larger than a max f64 is compared to a float.";
fn isgt_callback(ast: &Seg, _: &mut SymTable) -> Result<Ctr, Traceback> {
pub fn isgt_callback(ast: &Seg, _: &mut SymTable) -> Result<Ctr, Traceback> {
let first = *ast.car.clone(); let first = *ast.car.clone();
if !isnumeric(&first) { if !isnumeric(&first) {
return Err(start_trace( return Err(start_trace(
@ -361,11 +353,10 @@ pub fn isgt_callback(ast: &Seg, _: &mut SymTable) -> Result<Ctr, Traceback> {
} }
} }
pub const ISLT_DOCSTRING: &str = "takes two args, which must both evaluate to an Integer or Float. const ISLT_DOCSTRING: &str = "takes two args, which must both evaluate to an Integer or Float.
Returns true or false according to whether the first argument is smaller than the second argument. Returns true or false according to whether the first argument is smaller than the second argument.
May panic if an integer larger than a max f64 is compared to a float."; May panic if an integer larger than a max f64 is compared to a float.";
fn islt_callback(ast: &Seg, _: &mut SymTable) -> Result<Ctr, Traceback> {
pub fn islt_callback(ast: &Seg, _: &mut SymTable) -> Result<Ctr, Traceback> {
let first = *ast.car.clone(); let first = *ast.car.clone();
if !isnumeric(&first) { if !isnumeric(&first) {
return Err(start_trace( return Err(start_trace(
@ -409,11 +400,10 @@ pub fn islt_callback(ast: &Seg, _: &mut SymTable) -> Result<Ctr, Traceback> {
} }
} }
pub const ISGTE_DOCSTRING: &str = "takes two args, which must both evaluate to an Integer or Float. const ISGTE_DOCSTRING: &str = "takes two args, which must both evaluate to an Integer or Float.
Returns true or false according to whether the first argument is greater than or equal to the second argument. Returns true or false according to whether the first argument is greater than or equal to the second argument.
May panic if an integer larger than a max f64 is compared to a float."; May panic if an integer larger than a max f64 is compared to a float.";
fn isgte_callback(ast: &Seg, syms: &mut SymTable) -> Result<Ctr, Traceback> {
pub fn isgte_callback(ast: &Seg, syms: &mut SymTable) -> Result<Ctr, Traceback> {
match islt_callback(ast, syms) { match islt_callback(ast, syms) {
Ok(s) => if let Ctr::Bool(b) = s { Ok(s) => if let Ctr::Bool(b) = s {
Ok(Ctr::Bool(!b)) Ok(Ctr::Bool(!b))
@ -426,11 +416,10 @@ pub fn isgte_callback(ast: &Seg, syms: &mut SymTable) -> Result<Ctr, Traceback>
} }
} }
pub const ISLTE_DOCSTRING: &str = "takes two args, which must both evaluate to an Integer or Float. const ISLTE_DOCSTRING: &str = "takes two args, which must both evaluate to an Integer or Float.
Returns true or false according to whether the first argument is less than or equal to the second argument. Returns true or false according to whether the first argument is less than or equal to the second argument.
May panic if an integer larger than a max f64 is compared to a float."; May panic if an integer larger than a max f64 is compared to a float.";
fn islte_callback(ast: &Seg, syms: &mut SymTable) -> Result<Ctr, Traceback> {
pub fn islte_callback(ast: &Seg, syms: &mut SymTable) -> Result<Ctr, Traceback> {
match isgt_callback(ast, syms) { match isgt_callback(ast, syms) {
Ok(s) => if let Ctr::Bool(b) = s { Ok(s) => if let Ctr::Bool(b) = s {
Ok(Ctr::Bool(!b)) Ok(Ctr::Bool(!b))
@ -443,7 +432,7 @@ pub fn islte_callback(ast: &Seg, syms: &mut SymTable) -> Result<Ctr, Traceback>
} }
} }
pub const INC_DOCSTRING: &str = "Accepts a single argument, expects it to be a symbol. const INC_DOCSTRING: &str = "Accepts a single argument, expects it to be a symbol.
The symbol is fetched from the symbol table. The symbol is fetched from the symbol table.
If the symbol is not an integer an error is returned. If the symbol is not an integer an error is returned.
The symbol is redefined as symbol + 1. The symbol is redefined as symbol + 1.
@ -451,8 +440,7 @@ The symbol is redefined as symbol + 1.
This call is similar to the following: This call is similar to the following:
(def counter '' (add counter 1)) (def counter '' (add counter 1))
with the caveat that your docstring is preserved."; with the caveat that your docstring is preserved.";
fn inc_callback(ast: &Seg, syms: &mut SymTable) -> Result<Ctr, Traceback> {
pub fn inc_callback(ast: &Seg, syms: &mut SymTable) -> Result<Ctr, Traceback> {
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();
@ -491,7 +479,7 @@ pub fn inc_callback(ast: &Seg, syms: &mut SymTable) -> Result<Ctr, Traceback> {
Ok(Ctr::None) Ok(Ctr::None)
} }
pub const DEC_DOCSTRING: &str = "Accepts a single argument, expects it to be a symbol. const DEC_DOCSTRING: &str = "Accepts a single argument, expects it to be a symbol.
The symbol is fetched from the symbol table. The symbol is fetched from the symbol table.
If the symbol is not an integer an error is returned. If the symbol is not an integer an error is returned.
The symbol is redefined as symbol - 1. The symbol is redefined as symbol - 1.
@ -499,8 +487,7 @@ The symbol is redefined as symbol - 1.
This call is similar to the following: This call is similar to the following:
(def counter '' (sub counter 1)) (def counter '' (sub counter 1))
with the caveat that your docstring is preserved."; with the caveat that your docstring is preserved.";
fn dec_callback(ast: &Seg, syms: &mut SymTable) -> Result<Ctr, Traceback> {
pub fn dec_callback(ast: &Seg, syms: &mut SymTable) -> Result<Ctr, Traceback> {
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();
@ -538,3 +525,174 @@ pub fn dec_callback(ast: &Seg, syms: &mut SymTable) -> Result<Ctr, Traceback> {
syms.insert(var_name, sym); syms.insert(var_name, sym);
Ok(Ctr::None) Ok(Ctr::None)
} }
pub fn add_math_lib(syms: &mut SymTable) {
syms.insert(
"add".to_string(),
Symbol {
name: String::from("add"),
args: Args::Infinite,
conditional_branches: false,
docs: ADD_DOCSTRING.to_string(),
value: ValueType::Internal(Rc::new(add_callback)),
..Default::default()
},
);
syms.insert(
"sub".to_string(),
Symbol {
name: String::from("sub"),
args: Args::Infinite,
conditional_branches: false,
docs: SUB_DOCSTRING.to_string(),
value: ValueType::Internal(Rc::new(sub_callback)),
..Default::default()
},
);
syms.insert(
"div".to_string(),
Symbol {
name: String::from("div"),
args: Args::Lazy(2),
conditional_branches: false,
docs: DIV_DOCSTRING.to_string(),
value: ValueType::Internal(Rc::new(div_callback)),
..Default::default()
},
);
syms.insert(
"mul".to_string(),
Symbol {
name: String::from("mul"),
args: Args::Infinite,
conditional_branches: false,
docs: MUL_DOCSTRING.to_string(),
value: ValueType::Internal(Rc::new(mul_callback)),
..Default::default()
},
);
syms.insert(
"int".to_string(),
Symbol {
name: String::from("int"),
args: Args::Lazy(1),
conditional_branches: false,
docs: INTCAST_DOCSTRING.to_string(),
value: ValueType::Internal(Rc::new(intcast_callback)),
..Default::default()
},
);
syms.insert(
"float".to_string(),
Symbol {
name: String::from("float"),
args: Args::Lazy(1),
conditional_branches: false,
docs: FLOATCAST_DOCSTRING.to_string(),
value: ValueType::Internal(Rc::new(floatcast_callback)),
..Default::default()
},
);
syms.insert(
"exp".to_string(),
Symbol {
name: String::from("exp"),
args: Args::Lazy(2),
conditional_branches: false,
docs: EXP_DOCSTRING.to_string(),
value: ValueType::Internal(Rc::new(exp_callback)),
..Default::default()
},
);
syms.insert(
"mod".to_string(),
Symbol {
name: String::from("mod"),
args: Args::Lazy(2),
conditional_branches: false,
docs: MOD_DOCSTRING.to_string(),
value: ValueType::Internal(Rc::new(mod_callback)),
..Default::default()
},
);
syms.insert(
"gt?".to_string(),
Symbol {
name: String::from("gt?"),
args: Args::Lazy(2),
conditional_branches: false,
docs: ISGT_DOCSTRING.to_string(),
value: ValueType::Internal(Rc::new(isgt_callback)),
..Default::default()
},
);
syms.insert(
"lt?".to_string(),
Symbol {
name: String::from("lt?"),
args: Args::Lazy(2),
conditional_branches: false,
docs: ISLT_DOCSTRING.to_string(),
value: ValueType::Internal(Rc::new(islt_callback)),
..Default::default()
},
);
syms.insert(
"gte?".to_string(),
Symbol {
name: String::from("gt?"),
args: Args::Lazy(2),
conditional_branches: false,
docs: ISGTE_DOCSTRING.to_string(),
value: ValueType::Internal(Rc::new(isgte_callback)),
..Default::default()
},
);
syms.insert(
"lte?".to_string(),
Symbol {
name: String::from("lt?"),
args: Args::Lazy(2),
conditional_branches: false,
docs: ISLTE_DOCSTRING.to_string(),
value: ValueType::Internal(Rc::new(islte_callback)),
..Default::default()
},
);
syms.insert(
"inc".to_string(),
Symbol {
name: String::from("inc"),
args: Args::Lazy(1),
conditional_branches: true,
docs: INC_DOCSTRING.to_string(),
value: ValueType::Internal(Rc::new(inc_callback)),
..Default::default()
},
);
syms.insert(
"dec".to_string(),
Symbol {
name: String::from("dec"),
args: Args::Lazy(1),
conditional_branches: true,
docs: DEC_DOCSTRING.to_string(),
value: ValueType::Internal(Rc::new(dec_callback)),
..Default::default()
},
);
}

View file

@ -31,7 +31,7 @@ use {
run, run,
}, },
libc::{ libc::{
sigaddset, sigemptyset, sigprocmask, sigaddset, sigemptyset, sigprocmask, exit,
SIGINT, SIGCHLD, SIGTTOU, SIGTTIN, SIGQUIT, SIGTSTP, SIGINT, SIGCHLD, SIGTTOU, SIGTTIN, SIGQUIT, SIGTSTP,
SIG_BLOCK, SIG_UNBLOCK SIG_BLOCK, SIG_UNBLOCK
}, },
@ -633,6 +633,19 @@ fn q_callback(_ast: &Seg, _syms: &SymTable, state: &mut ShellState) -> Result<Ct
Ok(Ctr::Integer(state.last_exit_code.into())) Ok(Ctr::Integer(state.last_exit_code.into()))
} }
const EXIT_DOCSTRING: &str = "Takes on integer input. calls libc exit() with given argument.";
fn exit_callback(ast: &Seg, _syms: &mut SymTable) -> Result<Ctr, Traceback> {
let mut ret = -1;
if let Ctr::Integer(i) = *ast.car {
ret = i;
} else {
eprintln!("WARNING: input was not an integer. Exiting -1.")
}
unsafe {
exit(ret as i32);
}
}
const BG_DOCSTRING: &str = "Calls a binary off disk with given arguments. const BG_DOCSTRING: &str = "Calls a binary off disk with given arguments.
Arguments may be of any type except lambda. If a symbol is not defined it will be passed as a string. Arguments may be of any type except lambda. If a symbol is not defined it will be passed as a string.
first argument (command name) will be found on path (or an error returned). first argument (command name) will be found on path (or an error returned).
@ -681,7 +694,7 @@ fn bg_callback(ast: &Seg, syms: &mut SymTable, state: &mut ShellState) -> Result
} }
} }
pub const FG_DOCSTRING: &str = "takes one argument (an integer). const FG_DOCSTRING: &str = "takes one argument (an integer).
Integer is presumed to be a PID of a running background job. Integer is presumed to be a PID of a running background job.
If PID corresponds with a background job, fg will try to foreground it. If PID corresponds with a background job, fg will try to foreground it.
If PID is not a current running background job, fg will return an error. If PID is not a current running background job, fg will return an error.
@ -689,7 +702,7 @@ If PID is not a current running background job, fg will return an error.
Examples: Examples:
(bg vim) (fg 13871) (bg vim) (fg 13871)
"; ";
pub fn fg_callback( fn fg_callback(
ast: &Seg, ast: &Seg,
_syms: &mut SymTable, _syms: &mut SymTable,
shell_state: &mut ShellState shell_state: &mut ShellState
@ -709,9 +722,9 @@ pub fn fg_callback(
} }
} }
pub const CD_DOCSTRING: &str = const CD_DOCSTRING: &str =
"Expects 1 argument (a string). Changes to a new directory"; "Expects 1 argument (a string). Changes to a new directory";
pub fn cd_callback(ast: &Seg, _syms: &mut SymTable) -> Result<Ctr, Traceback> { fn cd_callback(ast: &Seg, _syms: &mut SymTable) -> Result<Ctr, Traceback> {
if let Ctr::String(ref dir) = *ast.car { if let Ctr::String(ref dir) = *ast.car {
let dirp = Path::new(dir); let dirp = Path::new(dir);
if let Err(s) = set_current_dir(&dirp) { if let Err(s) = set_current_dir(&dirp) {
@ -913,4 +926,28 @@ pub fn load_posix_shell(syms: &mut SymTable, shell_state: Rc<RefCell<ShellState>
..Default::default() ..Default::default()
}, },
); );
syms.insert(
String::from("exit"),
Symbol {
name: String::from("exit"),
args: Args::Strict(vec![Type::Integer]),
conditional_branches: false,
docs: String::from(EXIT_DOCSTRING),
value: ValueType::Internal(Rc::new(exit_callback)),
..Default::default()
},
);
syms.insert(
"cd".to_string(),
Symbol {
name: String::from("cd"),
args: Args::Strict(vec![Type::String]),
conditional_branches: false,
docs: CD_DOCSTRING.to_string(),
value: ValueType::Internal(Rc::new(cd_callback)),
..Default::default()
},
);
} }

View file

@ -15,16 +15,16 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
use crate::segment::{Ctr, Seg}; use crate::segment::{Ctr, Seg, Type};
use crate::sym::SymTable; use crate::sym::{SymTable, Symbol, ValueType, Args};
use crate::error::{Traceback, start_trace}; use crate::error::{Traceback, start_trace};
use std::io::Write; use std::io::Write;
use std::io; use std::io;
use std::rc::Rc;
pub const ECHO_DOCSTRING: &str = const ECHO_DOCSTRING: &str =
"traverses any number of arguments. Prints their evaluated values on a new line for each."; "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, Traceback> {
pub fn echo_callback(ast: &Seg, _syms: &mut SymTable) -> Result<Ctr, Traceback> {
ast.circuit(&mut |arg: &Ctr| match arg { ast.circuit(&mut |arg: &Ctr| match arg {
Ctr::String(s) => print!("{}", s) == (), Ctr::String(s) => print!("{}", s) == (),
_ => print!("{}", arg) == (), _ => print!("{}", arg) == (),
@ -33,11 +33,10 @@ pub fn echo_callback(ast: &Seg, _syms: &mut SymTable) -> Result<Ctr, Traceback>
Ok(Ctr::None) Ok(Ctr::None)
} }
pub const CONCAT_DOCSTRING: &str = "Iterates over N args of any type other than SYMBOL. const CONCAT_DOCSTRING: &str = "Iterates over N args of any type other than SYMBOL.
converts each argument to a string. Combines all strings. converts each argument to a string. Combines all strings.
Returns final (combined) string."; Returns final (combined) string.";
fn concat_callback(ast: &Seg, _syms: &mut SymTable) -> Result<Ctr, Traceback> {
pub fn concat_callback(ast: &Seg, _syms: &mut SymTable) -> Result<Ctr, Traceback> {
let mut string = String::from(""); let mut string = String::from("");
if !ast.circuit(&mut |arg: &Ctr| { if !ast.circuit(&mut |arg: &Ctr| {
match arg { match arg {
@ -60,11 +59,10 @@ pub fn concat_callback(ast: &Seg, _syms: &mut SymTable) -> Result<Ctr, Traceback
return Ok(Ctr::String(string)); return Ok(Ctr::String(string));
} }
pub const STRLEN_DOCSTRING: &str = "Takes a single arg of any type. const STRLEN_DOCSTRING: &str = "Takes a single arg of any type.
Arg is converted to a string if not already a string. Arg is converted to a string if not already a string.
Returns string length of arg."; Returns string length of arg.";
fn strlen_callback(ast: &Seg, _syms: &mut SymTable) -> Result<Ctr, Traceback> {
pub fn strlen_callback(ast: &Seg, _syms: &mut SymTable) -> Result<Ctr, Traceback> {
match &*ast.car { match &*ast.car {
Ctr::Symbol(s) => Ok(Ctr::Integer(s.len() as i128)), Ctr::Symbol(s) => Ok(Ctr::Integer(s.len() as i128)),
Ctr::String(s) => Ok(Ctr::Integer(s.len() as i128)), Ctr::String(s) => Ok(Ctr::Integer(s.len() as i128)),
@ -78,10 +76,9 @@ pub fn strlen_callback(ast: &Seg, _syms: &mut SymTable) -> Result<Ctr, Traceback
} }
} }
pub const STRCAST_DOCSTRING: &str = "Takes a single arg of any type. const STRCAST_DOCSTRING: &str = "Takes a single arg of any type.
Arg is converted to a string and returned."; Arg is converted to a string and returned.";
fn strcast_callback(ast: &Seg, _syms: &mut SymTable) -> Result<Ctr, Traceback> {
pub fn strcast_callback(ast: &Seg, _syms: &mut SymTable) -> Result<Ctr, Traceback> {
match &*ast.car { match &*ast.car {
Ctr::Symbol(s) => Ok(Ctr::String(s.clone())), Ctr::Symbol(s) => Ok(Ctr::String(s.clone())),
Ctr::String(_) => Ok(*ast.car.clone()), Ctr::String(_) => Ok(*ast.car.clone()),
@ -95,10 +92,9 @@ pub fn strcast_callback(ast: &Seg, _syms: &mut SymTable) -> Result<Ctr, Tracebac
} }
} }
pub const SUBSTR_DOCSTRING: &str = const SUBSTR_DOCSTRING: &str =
"Takes two strings. Returns true if string1 contains at least one instance of string2"; "Takes two strings. Returns true if string1 contains at least one instance of string2";
fn substr_callback(ast: &Seg, _syms: &mut SymTable) -> Result<Ctr, Traceback> {
pub fn substr_callback(ast: &Seg, _syms: &mut SymTable) -> Result<Ctr, Traceback> {
let parent_str: String; let parent_str: String;
if let Ctr::String(ref s) = *ast.car { if let Ctr::String(ref s) = *ast.car {
parent_str = s.to_string(); parent_str = s.to_string();
@ -129,10 +125,9 @@ pub fn substr_callback(ast: &Seg, _syms: &mut SymTable) -> Result<Ctr, Traceback
Ok(Ctr::Bool(parent_str.contains(&child_str))) Ok(Ctr::Bool(parent_str.contains(&child_str)))
} }
pub const SPLIT_DOCSTRING: &str = "Takes two strings. String 1 is a source string and string 2 is a delimiter. const SPLIT_DOCSTRING: &str = "Takes two strings. String 1 is a source string and string 2 is a delimiter.
Returns a list of substrings from string 1 that were found delimited by string 2."; Returns a list of substrings from string 1 that were found delimited by string 2.";
fn split_callback(ast: &Seg, _syms: &mut SymTable) -> Result<Ctr, Traceback> {
pub fn split_callback(ast: &Seg, _syms: &mut SymTable) -> Result<Ctr, Traceback> {
let parent_str: String; let parent_str: String;
if let Ctr::String(ref s) = *ast.car { if let Ctr::String(ref s) = *ast.car {
parent_str = s.to_string(); parent_str = s.to_string();
@ -168,11 +163,10 @@ pub fn split_callback(ast: &Seg, _syms: &mut SymTable) -> Result<Ctr, Traceback>
Ok(Ctr::Seg(ret)) Ok(Ctr::Seg(ret))
} }
pub const INPUT_DOCSTRING: &str = "Takes one argument (string) and prints it. const INPUT_DOCSTRING: &str = "Takes one argument (string) and prints it.
Then prompts for user input. Then prompts for user input.
User input is returned as a string"; User input is returned as a string";
fn input_callback(ast: &Seg, _syms: &mut SymTable) -> Result<Ctr, Traceback> {
pub fn input_callback(ast: &Seg, _syms: &mut SymTable) -> Result<Ctr, Traceback> {
if let Ctr::String(ref s) = *ast.car { if let Ctr::String(ref s) = *ast.car {
print!("{}", s); print!("{}", s);
let _= io::stdout().flush(); let _= io::stdout().flush();
@ -185,3 +179,89 @@ pub fn input_callback(ast: &Seg, _syms: &mut SymTable) -> Result<Ctr, Traceback>
.into())) .into()))
} }
} }
pub fn add_string_lib(syms: &mut SymTable) {
syms.insert(
"echo".to_string(),
Symbol {
name: String::from("echo"),
args: Args::Infinite,
conditional_branches: false,
docs: ECHO_DOCSTRING.to_string(),
value: ValueType::Internal(Rc::new(echo_callback)),
..Default::default()
},
);
syms.insert(
"concat".to_string(),
Symbol {
name: String::from("concat"),
args: Args::Infinite,
conditional_branches: false,
docs: CONCAT_DOCSTRING.to_string(),
value: ValueType::Internal(Rc::new(concat_callback)),
..Default::default()
},
);
syms.insert(
"substr?".to_string(),
Symbol {
name: String::from("substr?"),
args: Args::Strict(vec![Type::String, Type::String]),
conditional_branches: false,
docs: SUBSTR_DOCSTRING.to_string(),
value: ValueType::Internal(Rc::new(substr_callback)),
..Default::default()
},
);
syms.insert(
"split".to_string(),
Symbol {
name: String::from("split"),
args: Args::Strict(vec![Type::String, Type::String]),
conditional_branches: false,
docs: SPLIT_DOCSTRING.to_string(),
value: ValueType::Internal(Rc::new(split_callback)),
..Default::default()
},
);
syms.insert(
"strlen".to_string(),
Symbol {
name: String::from("strlen"),
args: Args::Lazy(1),
conditional_branches: false,
docs: STRLEN_DOCSTRING.to_string(),
value: ValueType::Internal(Rc::new(strlen_callback)),
..Default::default()
},
);
syms.insert(
"string".to_string(),
Symbol {
name: String::from("string"),
args: Args::Lazy(1),
conditional_branches: false,
docs: STRCAST_DOCSTRING.to_string(),
value: ValueType::Internal(Rc::new(strcast_callback)),
..Default::default()
},
);
syms.insert(
"input".to_string(),
Symbol {
name: String::from("input"),
args: Args::Strict(vec![Type::String]),
conditional_branches: false,
docs: INPUT_DOCSTRING.to_string(),
value: ValueType::Internal(Rc::new(input_callback)),
..Default::default()
}
);
}