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')))