diff --git a/Readme.org b/Readme.org
index cac5582..d13758d 100644
--- a/Readme.org
+++ b/Readme.org
@@ -127,7 +127,7 @@ 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
*** TODO Rudimentary Control Flow
**** DONE if clause
-**** TODO let clause
+**** DONE let clause
**** TODO while clause
**** TODO circuit clause
*** TODO Help function
@@ -143,22 +143,26 @@ Will need a concatenate function for tables
*** TODO Custom ast pretty print
*** TODO Shell module
**** TODO Process launching with environment variables
+**** TODO Optional form of process which allows fd redirecting
**** TODO Foreground process TTY
**** TODO Background processes
*** TODO list operations
+**** DONE append
+**** DONE expand
**** TODO head (returns (head rest))
**** TODO tail (returns (rest tail))
**** TODO queue (append to front)
**** TODO snippet for dequeue
**** TODO snippet for pop
*** TODO boolean operations
-**** TODO and (circuit)
-**** TODO or
+**** DONE and (circuit)
+**** DONE or
**** TODO xor
-**** TODO no
+**** DONE no
**** TODO eq?
**** TODO toggle
*** TODO string operations
+**** TODO typecast (string)
**** TODO contains
**** TODO len
**** TODO concat
@@ -166,6 +170,8 @@ Will need a concatenate function for tables
**** TODO split (on delimiter)
**** TODO strcons (sprintf but its all string tokens under the hood)
*** TODO arithmetic operations
+**** TODO typecast (int)
+**** TODO typecast (float)
**** TODO add
**** TODO sub
**** TODO div
diff --git a/src/stl.rs b/src/stl.rs
index e8211c6..ee9dba0 100644
--- a/src/stl.rs
+++ b/src/stl.rs
@@ -23,6 +23,7 @@ use std::rc::Rc;
pub mod append;
pub mod control;
+pub mod boolean;
//pub mod str;
/// static_stdlib
@@ -79,6 +80,66 @@ pub fn static_stdlib(syms: &mut SymTable) -> Result<(), String> {
},
);
+ syms.insert(
+ "and".to_string(),
+ Symbol {
+ name: String::from("and"),
+ args: Args::Infinite,
+ conditional_branches: false,
+ value: ValueType::Internal(Rc::new(boolean::bool_and_callback)),
+ }
+ );
+
+ syms.insert(
+ "or".to_string(),
+ Symbol {
+ name: String::from("or"),
+ args: Args::Infinite,
+ conditional_branches: false,
+ value: ValueType::Internal(Rc::new(boolean::bool_or_callback)),
+ }
+ );
+
+ syms.insert(
+ "xor".to_string(),
+ Symbol {
+ name: String::from("xor"),
+ args: Args::Infinite,
+ conditional_branches: false,
+ value: ValueType::Internal(Rc::new(boolean::bool_xor_callback)),
+ }
+ );
+
+ syms.insert(
+ "not".to_string(),
+ Symbol {
+ name: String::from("not"),
+ args: Args::Strict(vec![Type::Bool]),
+ conditional_branches: false,
+ value: ValueType::Internal(Rc::new(boolean::bool_not_callback)),
+ }
+ );
+
+ syms.insert(
+ "eq?".to_string(),
+ Symbol {
+ name: String::from("eq?"),
+ args: Args::Infinite,
+ conditional_branches: false,
+ value: ValueType::Internal(Rc::new(boolean::bool_iseq_callback)),
+ }
+ );
+
+ syms.insert(
+ "toggle".to_string(),
+ Symbol {
+ name: String::from("toggle"),
+ args: Args::Lazy(1),
+ conditional_branches: true,
+ value: ValueType::Internal(Rc::new(boolean::bool_toggle_callback)),
+ }
+ );
+
Ok(())
}
diff --git a/src/stl/boolean.rs b/src/stl/boolean.rs
new file mode 100644
index 0000000..675bc45
--- /dev/null
+++ b/src/stl/boolean.rs
@@ -0,0 +1,76 @@
+/* Copyright (C) 2021 Aidan Hahn
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+use crate::segment::{Ctr, Seg};
+use crate::sym::SymTable;
+
+pub fn bool_and_callback(ast: &Seg, _syms: &mut SymTable) -> Result {
+ let mut type_error = false;
+ let result = ast.circuit(&mut |arg: &Ctr| -> bool {
+ if let Ctr::Bool(b) = *arg {
+ b
+ } else {
+ eprintln!("{} is not a boolean", arg);
+ type_error = true;
+ false
+ }
+ });
+
+ if type_error {
+ Err("all arguments to and must evaluate to boolean".to_string())
+ } else {
+ Ok(Ctr::Bool(result))
+ }
+}
+
+pub fn bool_or_callback(ast: &Seg, _syms: &mut SymTable) -> Result {
+ let mut result = false;
+ let correct_types = ast.circuit(&mut |arg: &Ctr| -> bool {
+ if let Ctr::Bool(b) = *arg {
+ result = result || b;
+ true
+ } else {
+ eprintln!("{} is not a boolean", arg);
+ false
+ }
+ });
+
+ if !correct_types {
+ Err("all arguments to 'or' must evaluate to boolean".to_string())
+ } else {
+ Ok(Ctr::Bool(result))
+ }
+}
+
+pub fn bool_xor_callback(_ast: &Seg, _syms: &mut SymTable) -> Result {
+ todo!()
+}
+
+pub fn bool_not_callback(ast: &Seg, _syms: &mut SymTable) -> Result {
+ if let Ctr::Bool(b) = *ast.car {
+ Ok(Ctr::Bool(!b))
+ } else {
+ Err("impossible state: non bool given to not".to_string())
+ }
+}
+
+pub fn bool_iseq_callback(_ast: &Seg, _syms: &mut SymTable) -> Result {
+ todo!()
+}
+
+pub fn bool_toggle_callback(_ast: &Seg, _syms: &mut SymTable) -> Result {
+ todo!()
+}
diff --git a/tests/test_lib_bools.rs b/tests/test_lib_bools.rs
new file mode 100644
index 0000000..0bc9af0
--- /dev/null
+++ b/tests/test_lib_bools.rs
@@ -0,0 +1,144 @@
+mod bool_lib_tests {
+ use relish::ast::{eval, lex, Ctr, SymTable};
+ use relish::stdlib::{dynamic_stdlib, static_stdlib};
+
+ #[test]
+ fn test_and_true_chain() {
+ let document = "(and true true true true true)";
+ let result = true;
+
+ let mut syms = SymTable::new();
+ static_stdlib(&mut syms).unwrap();
+ dynamic_stdlib(&mut syms).unwrap();
+
+ if let Ok(tree) = lex(&document.to_string()) {
+ if let Ctr::Bool(b) = *eval(&tree, &mut syms).unwrap() {
+ assert_eq!(b, result);
+ } else {
+ assert!(false);
+ }
+ } else {
+ assert!(false);
+ }
+ }
+
+ #[test]
+ fn test_and_true_chain_with_false() {
+ let document = "(and true true false true true)";
+ let result = false;
+
+ let mut syms = SymTable::new();
+ static_stdlib(&mut syms).unwrap();
+ dynamic_stdlib(&mut syms).unwrap();
+
+ if let Ok(tree) = lex(&document.to_string()) {
+ if let Ctr::Bool(i) = *eval(&tree, &mut syms).unwrap() {
+ assert_eq!(i, result);
+ } else {
+ assert!(false);
+ }
+ } else {
+ assert!(false);
+ }
+ }
+
+ #[test]
+ fn test_and_false_chain() {
+ let document = "(and false false false false false)";
+ let result = false;
+
+ let mut syms = SymTable::new();
+ static_stdlib(&mut syms).unwrap();
+ dynamic_stdlib(&mut syms).unwrap();
+
+ if let Ok(tree) = lex(&document.to_string()) {
+ if let Ctr::Bool(i) = *eval(&tree, &mut syms).unwrap() {
+ assert_eq!(i, result);
+ } else {
+ assert!(false);
+ }
+ } else {
+ assert!(false);
+ }
+ }
+
+ #[test]
+ fn test_or_true_chain() {
+ let document = "(or true true true true true)";
+ let result = true;
+
+ let mut syms = SymTable::new();
+ static_stdlib(&mut syms).unwrap();
+ dynamic_stdlib(&mut syms).unwrap();
+
+ if let Ok(tree) = lex(&document.to_string()) {
+ if let Ctr::Bool(b) = *eval(&tree, &mut syms).unwrap() {
+ assert_eq!(b, result);
+ } else {
+ assert!(false);
+ }
+ } else {
+ assert!(false);
+ }
+ }
+
+ #[test]
+ fn test_or_true_chain_with_false() {
+ let document = "(or true true false true true)";
+ let result = true;
+
+ let mut syms = SymTable::new();
+ static_stdlib(&mut syms).unwrap();
+ dynamic_stdlib(&mut syms).unwrap();
+
+ if let Ok(tree) = lex(&document.to_string()) {
+ if let Ctr::Bool(i) = *eval(&tree, &mut syms).unwrap() {
+ assert_eq!(i, result);
+ } else {
+ assert!(false);
+ }
+ } else {
+ assert!(false);
+ }
+ }
+
+ #[test]
+ fn test_or_false_chain() {
+ let document = "(or false false false false)";
+ let result = false;
+
+ let mut syms = SymTable::new();
+ static_stdlib(&mut syms).unwrap();
+ dynamic_stdlib(&mut syms).unwrap();
+
+ if let Ok(tree) = lex(&document.to_string()) {
+ if let Ctr::Bool(i) = *eval(&tree, &mut syms).unwrap() {
+ assert_eq!(i, result);
+ } else {
+ assert!(false);
+ }
+ } else {
+ assert!(false);
+ }
+ }
+
+ #[test]
+ fn test_not() {
+ let document = "(not true)";
+ let result = false;
+
+ let mut syms = SymTable::new();
+ static_stdlib(&mut syms).unwrap();
+ dynamic_stdlib(&mut syms).unwrap();
+
+ if let Ok(tree) = lex(&document.to_string()) {
+ if let Ctr::Bool(i) = *eval(&tree, &mut syms).unwrap() {
+ assert_eq!(i, result);
+ } else {
+ assert!(false);
+ }
+ } else {
+ assert!(false);
+ }
+ }
+}
diff --git a/tests/test_lib_control.rs b/tests/test_lib_control.rs
index d561131..2727399 100644
--- a/tests/test_lib_control.rs
+++ b/tests/test_lib_control.rs
@@ -108,6 +108,7 @@ mod control_lib_tests {
#[test]
fn test_let_multiphase_local_multibody_evals() {
+ // prints 'first body' and then returns ('1' '2' '3')
let document = "(let (
(temp '1')
(temp (append () temp '2')))