diff --git a/Readme.org b/Readme.org
index 9ec1554..1e46b2b 100644
--- a/Readme.org
+++ b/Readme.org
@@ -100,49 +100,21 @@ This contains any executable target of this project. Notably the main shell file
* 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
-*** DONE Core interpreter stuffs
-**** DONE Lexing
-**** DONE Parsing
-**** DONE Evaluation
-**** DONE Function table
-**** DONE Variable table
-*** DONE Echo function
-*** DONE Redo segment.rs
-**** DONE Clone impl for Ctr
-(derived)
-**** DONE Derive Clone for Seg
-**** DONE ToString impl for Ctr
-**** DONE ToString impl for Seg
-**** DONE Display, Debug impls for Ctr
-**** DONE Derive Display, Debug for Seg
-**** DONE Default impl for Ctr
-**** DONE Derive Default for Seg
-**** WONTDO From/Into for Ctr
-**** DONE Iterator for Seg
-**** WONTDO Consider RefCell for tables, or using 'static lifetime
-one of these may fix borrow checker insanity
-**** DONE make FTable and VTable singleton modules (global mutable)
-**** WONTDO make FTable and VTable into private definitions
-**** DONE YEET AST EVERYWHERE. PASS AROUND A SEG REF.
-*** TODO refactor code into more digestible, better organized pieces
-**** DONE combine var and func table into global sym table
-**** DONE refactor and reimplement eval
-**** TODO (test and get it working)
-**** TODO lex/eval/e2e tests for var that holds callback func
*** TODO Rudimentary Control Flow
**** TODO if clause
**** TODO loop clause
**** TODO while clause
**** TODO circuit clause
-*** DONE Configuration
-**** DONE Function to load configuration into Variable and Function tables
-**** DONE Configure in main shell
-**** DONE manual verification of config settings
-**** DONE manual verification of config defaults
+*** TODO Configuration
+**** TODO get_stdlibphase1 -> configuration -> get_stdlibphase2
+**** TODO Function to load configuration into Variable and Function tables
+**** TODO Configure in main shell
+**** TODO manual verification of config settings
+**** TODO manual verification of config defaults
*** TODO Help function
*** TODO Env function
-*** DONE User variable declaration
-*** DONE User function declaration
+*** TODO User variable declaration tests
+*** TODO User function declaration tests
*** TODO Load (load a script) function
Pull/Refactor the logic out of the configure functions.
Optionally return a list of new variables and/or functions?
@@ -150,16 +122,14 @@ Will need a concatenate function for func tables
*** TODO Main shell calls Load function on arg and exits
*** TODO Custom error printing
*** TODO Custom ast pretty print
-*** DONE Implement Ctr, Ast to_string / Display trait
-*** TODO get_stdlibphase1 -> configuration -> get_stdlibphase2
*** TODO STDLIB
*** TODO Shell module
**** TODO Process launching with environment variables
**** TODO Foreground process TTY
**** TODO Background processes
-**** DONE append
+**** TODO append
**** TODO string operations
-***** DONE concatenate
+***** TODO concatenate
***** TODO substr by index
***** TODO tokenize by delimiter
***** TODO sprintf / string build
@@ -175,6 +145,3 @@ Will need a concatenate function for func tables
***** TODO TCP Listener
***** TODO HTTP Listener
***** TODO UDP Listener
-*** TODO Ensure full test coverage
-**** TODO Implement missing func, Lex, Eval tests
-**** TODO stdlib tests :)
diff --git a/src/bin/main.rs b/src/bin/main.rs
new file mode 100644
index 0000000..9565a5a
--- /dev/null
+++ b/src/bin/main.rs
@@ -0,0 +1,147 @@
+/* relish: versatile lisp shell
+ * 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 dirs::home_dir;
+use relish::ast::{ast_to_string, eval, func_call, lex, new_ast, Ctr, FTable, VTable};
+use relish::aux::configure;
+use rustyline::error::ReadlineError;
+use rustyline::Editor;
+use std::cell::RefCell;
+use std::env;
+use std::rc::Rc;
+
+fn main() {
+ let mut rl = Editor::<()>::new();
+
+ const HIST_FILE: &str = "/.relish_hist";
+ const CONFIG_FILE_DEFAULT: &str = "/.relishrc";
+
+ let mut hist: String = "".to_owned();
+ let mut cfg: String = "".to_owned();
+
+ if let Some(home) = home_dir() {
+ if let Some(h) = home.to_str() {
+ hist = h.to_owned() + HIST_FILE;
+ cfg = h.to_owned() + CONFIG_FILE_DEFAULT;
+ }
+ }
+
+ if hist != "" {
+ // ignore result. it loads or it doesnt.
+ let _ = rl.load_history(&hist);
+ }
+
+ let vt = Rc::new(RefCell::new(VTable::new()));
+ let conf_file;
+ let ft;
+ match env::var("RELISH_CFG_FILE") {
+ Ok(s) => {
+ conf_file = s
+ },
+ Err(e) => {
+ eprintln!("{}", e);
+ conf_file = cfg;
+ },
+ }
+
+ match configure(conf_file, vt.clone()) {
+ Ok(f) => ft = f,
+ Err(e) => {
+ ft = Rc::new(RefCell::new(FTable::new()));
+ eprintln!("{}", e);
+ },
+ }
+
+ loop {
+ let readline: Result;
+ // Rust is pain
+ let tmp_ft_clone = ft.clone();
+ // this is not okay
+ let t_ft_c_b = tmp_ft_clone.borrow();
+ let pfunc = t_ft_c_b.get("CFG_RELISH_PROMPT");
+ if let Some(fnc) = pfunc {
+ match func_call(
+ fnc.clone(),
+ new_ast(Ctr::None, Ctr::None),
+ vt.clone(),
+ ft.clone(),
+ ) {
+ Err(s) => {
+ eprintln!("Couldnt generate prompt: {}", s);
+ readline = rl.readline("");
+ }
+
+ Ok(c) => match c {
+ Ctr::Symbol(s) => readline = rl.readline(&s.to_owned()),
+ Ctr::String(s) => readline = rl.readline(&s),
+ Ctr::Integer(i) => readline = rl.readline(&format!("{}", i)),
+ Ctr::Float(f) => readline = rl.readline(&format!("{}", f)),
+ Ctr::Bool(b) => readline = rl.readline(&format!("{}", b)),
+ Ctr::Seg(c) => readline = rl.readline(&ast_to_string(c.clone())),
+ Ctr::None => readline = rl.readline(""),
+ },
+ }
+ } else {
+ readline = rl.readline("");
+ }
+
+ match readline {
+ Ok(line) => {
+ rl.add_history_entry(line.as_str());
+ let mut l = line.as_str().to_owned();
+ if !l.starts_with("(") {
+ l = "(".to_owned() + &l;
+ }
+
+ if !l.ends_with(")") {
+ l = l + ")";
+ }
+
+ match lex(l) {
+ Ok(a) => match eval(a.clone(), vt.clone(), ft.clone(), false) {
+ Ok(a) => match a {
+ Ctr::Symbol(s) => println!("{}", s),
+ Ctr::String(s) => println!("{}", s),
+ Ctr::Integer(i) => println!("{}", i),
+ Ctr::Float(f) => println!("{}", f),
+ Ctr::Bool(b) => println!("{}", b),
+ Ctr::Seg(c) => println!("{}", ast_to_string(c.clone())),
+ Ctr::None => (),
+ },
+ Err(s) => {
+ println!("{}", s);
+ }
+ },
+ Err(s) => {
+ println!("{}", s);
+ }
+ }
+ }
+
+ Err(ReadlineError::Interrupted) => break,
+
+ Err(ReadlineError::Eof) => return,
+ Err(err) => {
+ eprintln!("Prompt error: {:?}", err);
+ break;
+ }
+ }
+ }
+ if hist != "" {
+ rl.save_history(&hist).unwrap();
+ }
+}
diff --git a/src/config.rs b/src/config.rs
new file mode 100644
index 0000000..b0af11d
--- /dev/null
+++ b/src/config.rs
@@ -0,0 +1,93 @@
+/* relish: versatile lisp shell
+ * 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::eval::eval;
+use crate::func::{func_declare, Args, FTable, Function, Operation};
+use crate::lex::lex;
+use crate::segment::{Ast, Ctr};
+use crate::stl::get_stdlib;
+use crate::vars::{define, VTable};
+use std::cell::RefCell;
+use std::fs;
+use std::io::{self, Write};
+use std::rc::Rc;
+
+fn prompt_default_callback(_: Ast, _: Rc>, _: Rc>) -> Ctr {
+ return Ctr::String("λ ".to_string());
+}
+
+pub fn configure(filename: String, vars: Rc>) -> Result>, String> {
+ let funcs;
+
+ define(
+ vars.clone(),
+ String::from("CFG_RELISH_POSIX"),
+ Rc::new(Ctr::String(String::from("0"))),
+ );
+ define(
+ vars.clone(),
+ String::from("CFG_RELISH_ENV"),
+ Rc::new(Ctr::String(String::from("1"))),
+ );
+
+ match get_stdlib(vars.clone()) {
+ Ok(f) => funcs = f,
+ Err(s) => {
+ funcs = Rc::new(RefCell::new(FTable::new()));
+ println!("Couldnt get stdlib: {}", s)
+ },
+ }
+
+ match func_declare(
+ funcs.clone(),
+ Rc::new(RefCell::new(Function {
+ name: String::from("CFG_RELISH_PROMPT"),
+ loose_syms: false,
+ eval_lazy: false,
+ args: Args::Lazy(0),
+ function: Operation::Internal(Box::new(prompt_default_callback)),
+ })),
+ ) {
+ Some(e) => return Err(e),
+ None => {},
+ }
+
+ match fs::read_to_string(filename.clone()) {
+ Err(s) => {
+ return Err(format!("Couldnt open configuration file: {}", s));
+ }
+
+ Ok(raw_config) => {
+ let mut l = raw_config;
+ l = "(".to_owned() + &l + ")";
+
+ match lex(l) {
+ Err(s) => {
+ return Err(format!("Error in configuration: {}", s));
+ }
+
+ Ok(config) => {
+ if let Err(errst) = eval(config, vars, funcs.clone(), false) {
+ return Err(format!("Error loading {}: {}", filename.clone(), errst));
+ }
+ }
+ }
+ },
+ }
+
+ return Ok(funcs);
+}
diff --git a/src/control.rs b/src/control.rs
new file mode 100644
index 0000000..9032397
--- /dev/null
+++ b/src/control.rs
@@ -0,0 +1,40 @@
+/* relish: versatile lisp shell
+ * 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::append::get_append;
+use crate::func::{func_declare, FTable, Ast};
+use crate::segment::Ctr;
+use crate::str::{get_concat, get_echo};
+use crate::vars::{get_export, VTable};
+use std::cell::RefCell;
+use std::rc::Rc;
+
+pub fn get_if() -> Function {
+ return Function {
+ name: String::from("if"),
+ loose_syms: false,
+ eval_lazy: true,
+ args: Args::Lazy(-1),
+ function: Operation::Internal(
+ Box::new(|args: Ast, vars: Rc>, funcs: Rc>| -> Ctr {
+ // Either 2 long or 3 long.
+ // arg 1 must eval to a bool
+ // then eval arg 2 or 3
+ })
+ ),
+ };
+}
diff --git a/src/stl.rs b/src/stl.rs
index 293a3e2..c74cf8a 100644
--- a/src/stl.rs
+++ b/src/stl.rs
@@ -19,31 +19,36 @@ use crate::segment::{Ctr, Seg, Type};
use crate::eval::eval;
use crate::sym::{SymTable, Symbol, ValueType, Args, UserFn};
use std::env;
+use std::rc::Rc;
-/*
-// the stdlib var export function with env_sync on
-static LIB_STORE_ENV: Symbol = Symbol {
- name: String::from("export"),
- args: Args::Lazy(2),
- value: ValueType::Internal(Box::new( |ast: &Seg| -> Ctr {
- _store_callback(ast, true)
- },
- )),
- has_undefined_symbols: false,
-};
+fn store_stdlib(env: bool, syms: &mut SymTable) -> Result<(), String> {
+ syms.insert("def".to_string(), Symbol {
+ name: String::from("export"),
+ args: Args::Lazy(2),
+ conditional_branches: false,
+ value: ValueType::Internal(Rc::new( move |ast: &Seg, syms: &mut SymTable| -> Ctr {
+ _store_callback(ast, syms, env)
+ },
+ )),
+ });
-// the stdlib var export function with env_sync off
-pub static LIB_STORE_NO_ENV: Symbol = Symbol {
- name: String::from("export"),
- args: Args::Lazy(2),
- value: ValueType::Internal(Box::new( |ast: &Seg| -> Ctr {
- _store_callback(ast, false)
- },
- )),
- has_undefined_symbols: false,
-};*/
+ syms.insert("append".to_string(), Symbol {
+ name: String::from("append"),
+ args: Args::Infinite,
+ conditional_branches: false,
+ value: ValueType::Internal(Rc::new(_append_callback)),
+ });
+
+ Ok(())
+}
+
+
+fn _append_callback (_ast: &Seg, _syms: &mut SymTable) -> Ctr {
+ // if car is a list, append cdr
+ // otherwise create a list out of all arguments
+ todo!()
+}
-// TODO : declare function if arg list is long enough
fn _store_callback (ast: &Seg, syms: &mut SymTable, env_cfg: bool) -> Ctr {
let is_var = ast.len() == 2;
if let Ctr::Symbol(ref identifier) = *ast.car {
diff --git a/src/str.rs b/src/str.rs
new file mode 100644
index 0000000..191d4ec
--- /dev/null
+++ b/src/str.rs
@@ -0,0 +1,85 @@
+/* relish: versatile lisp shell
+ * 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::func::{Args, FTable, Function, Operation};
+use crate::segment::{ast_as_string, circuit, Ast, Ctr};
+use crate::vars::VTable;
+use std::cell::RefCell;
+use std::rc::Rc;
+
+// Current primitive is to use a get_NNNNN function defined for each library function which returns a not-previously-owned
+// copy of the Function struct.
+pub fn get_echo() -> Function {
+ return Function {
+ name: String::from("echo"),
+ loose_syms: false,
+ eval_lazy: false,
+ args: Args::Lazy(-1),
+ function: Operation::Internal(Box::new(
+ |a: Ast, _b: Rc>, _c: Rc>| -> Ctr {
+ let mut string = String::from("");
+ if !circuit(a, &mut |arg: &Ctr| {
+ match arg {
+ // should be a thing here
+ Ctr::Symbol(_) => return false,
+ Ctr::String(s) => string.push_str(&s),
+ Ctr::Integer(i) => string.push_str(&i.to_string()),
+ Ctr::Float(f) => string.push_str(&f.to_string()),
+ Ctr::Bool(b) => string.push_str(&b.to_string()),
+ Ctr::Seg(c) => string.push_str(ast_as_string(c.clone(), true).as_str()),
+ Ctr::None => (),
+ }
+ println!("{}", string);
+ return true;
+ }) {
+ eprintln!("circuit loop in echo should not have returned false")
+ }
+ return Ctr::None;
+ },
+ )),
+ };
+}
+
+pub fn get_concat() -> Function {
+ return Function {
+ name: String::from("concat"),
+ loose_syms: false,
+ eval_lazy: false,
+ args: Args::Lazy(-1),
+ function: Operation::Internal(Box::new(
+ |a: Ast, _b: Rc>, _c: Rc>| -> Ctr {
+ let mut string = String::from("");
+ if !circuit(a, &mut |arg: &Ctr| {
+ match arg {
+ // should be a thing here
+ Ctr::Symbol(_) => return false,
+ Ctr::String(s) => string.push_str(&s),
+ Ctr::Integer(i) => string.push_str(&i.to_string()),
+ Ctr::Float(f) => string.push_str(&f.to_string()),
+ Ctr::Bool(b) => string.push_str(&b.to_string()),
+ Ctr::Seg(c) => string.push_str(ast_as_string(c.clone(), true).as_str()),
+ Ctr::None => (),
+ }
+ return true;
+ }) {
+ eprintln!("circuit loop in concat should not have returned false")
+ }
+ return Ctr::String(string);
+ },
+ )),
+ };
+}
diff --git a/src/sym.rs b/src/sym.rs
index bafa4b3..c5e087d 100644
--- a/src/sym.rs
+++ b/src/sym.rs
@@ -18,7 +18,7 @@
use crate::eval::eval;
use crate::segment::{Seg, Ctr, Type};
use std::collections::HashMap;
-
+use std::rc::Rc;
pub struct SymTable(HashMap);
#[derive(Debug, Clone)]
@@ -39,7 +39,7 @@ pub struct UserFn {
*/
#[derive(Clone)]
pub enum ValueType {
- Internal(Box Ctr>),
+ Internal(Rc Ctr>),
FuncForm(UserFn),
VarForm(Box)
}
diff --git a/tests/stl.rs b/tests/stl.rs
new file mode 100644
index 0000000..b9b4188
--- /dev/null
+++ b/tests/stl.rs
@@ -0,0 +1,62 @@
+/* relish: versatile lisp shell
+ * 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::append::get_append;
+use crate::func::{func_declare, FTable};
+use crate::segment::Ctr;
+use crate::str::{get_concat, get_echo};
+use crate::control::{get_if};
+use crate::vars::{get_export, VTable};
+use std::cell::RefCell;
+use std::rc::Rc;
+
+pub fn get_stdlib(conf: Rc>) -> Result>, String> {
+ let ft = Rc::new(RefCell::new(FTable::new()));
+ if let Some(s) = func_declare(ft.clone(), Rc::new(RefCell::new(get_echo()))) {
+ return Err(s);
+ }
+ if let Some(s) = func_declare(ft.clone(), Rc::new(RefCell::new(get_append()))) {
+ return Err(s);
+ }
+ if let Some(s) = func_declare(ft.clone(), Rc::new(RefCell::new(get_concat()))) {
+ return Err(s);
+ }
+
+ let mut cfg_env = true;
+ match conf.borrow().get(&String::from("CFG_RELISH_ENV")) {
+ None => {
+ println!("CFG_RELISH_ENV not defined. defaulting to ON.")
+ }
+ Some(ctr) => match (**ctr).clone() {
+ Ctr::String(ref s) => cfg_env = s.eq("0"),
+ _ => {
+ println!("Invalid value for CFG_RELISH_ENV. must be a string (0 or 1).");
+ println!("Defaulting CFG_RELISH_ENV to ON");
+ }
+ },
+ }
+
+ if let Some(s) = func_declare(ft.clone(), Rc::new(RefCell::new(get_export(cfg_env)))) {
+ return Err(s);
+ }
+
+ if let Some(s) = func_declare(ft.clone(), Rc::new(RefCell::new(get_if()))) {
+ return Err(s);
+ }
+
+ return Ok(ft);
+}
diff --git a/tests/test_lib_append.rs b/tests/test_lib_append.rs
new file mode 100644
index 0000000..7bcbc9c
--- /dev/null
+++ b/tests/test_lib_append.rs
@@ -0,0 +1,166 @@
+mod append_lib_tests {
+ use relish::ast::{ast_to_string, eval, lex, Ctr, FTable, VTable};
+ use relish::stdlib::get_stdlib;
+ use std::cell::RefCell;
+ use std::rc::Rc;
+
+ #[test]
+ fn test_append_to_empty_list() {
+ let document = "(append () 1 2 3)";
+ let result = "(1 2 3)";
+ let vt = Rc::new(RefCell::new(VTable::new()));
+ let ft: Rc>;
+ match get_stdlib(vt.clone()) {
+ Ok(f) => ft = f,
+ Err(s) => {
+ ft = Rc::new(RefCell::new(FTable::new()));
+ println!("Couldnt get stdlib: {}!", s);
+ assert!(false)
+ }
+ }
+
+ match lex(document.to_string()) {
+ Err(s) => {
+ println!("Couldnt lex {}: {}\n", document, s);
+ assert!(false);
+ }
+
+ Ok(tree) => match eval(tree, vt.clone(), ft.clone(), false) {
+ Err(s) => {
+ println!("Couldnt eval {}: {}\n", document, s);
+ assert!(false);
+ }
+
+ Ok(ctr) => match ctr {
+ Ctr::Symbol(_) => assert!(false),
+ Ctr::String(_) => assert!(false),
+ Ctr::Integer(_) => assert!(false),
+ Ctr::Float(_) => assert!(false),
+ Ctr::Bool(_) => assert!(false),
+ Ctr::Seg(s) => assert_eq!(ast_to_string(s), result),
+ Ctr::None => assert!(false),
+ },
+ },
+ }
+ }
+
+ #[test]
+ fn test_append_to_full_list() {
+ let document = "(append (1 2) 3)";
+ let result = "(1 2 3)";
+ let vt = Rc::new(RefCell::new(VTable::new()));
+ let ft: Rc>;
+ match get_stdlib(vt.clone()) {
+ Ok(f) => ft = f,
+ Err(s) => {
+ ft = Rc::new(RefCell::new(FTable::new()));
+ println!("Couldnt get stdlib: {}!", s);
+ assert!(false)
+ }
+ }
+
+ match lex(document.to_string()) {
+ Err(s) => {
+ println!("Couldnt lex {}: {}\n", document, s);
+ assert!(false);
+ }
+
+ Ok(tree) => match eval(tree, vt.clone(), ft.clone(), false) {
+ Err(s) => {
+ println!("Couldnt eval {}: {}\n", document, s);
+ assert!(false);
+ }
+
+ Ok(ctr) => match ctr {
+ Ctr::Symbol(_) => assert!(false),
+ Ctr::String(_) => assert!(false),
+ Ctr::Integer(_) => assert!(false),
+ Ctr::Float(_) => assert!(false),
+ Ctr::Bool(_) => assert!(false),
+ Ctr::Seg(s) => assert_eq!(ast_to_string(s), result),
+ Ctr::None => assert!(false),
+ },
+ },
+ }
+ }
+
+ #[test]
+ fn test_mono_append() {
+ let document = "(append)";
+ let result = "()";
+ let vt = Rc::new(RefCell::new(VTable::new()));
+ let ft: Rc>;
+ match get_stdlib(vt.clone()) {
+ Ok(f) => ft = f,
+ Err(s) => {
+ ft = Rc::new(RefCell::new(FTable::new()));
+ println!("Couldnt get stdlib: {}!", s);
+ assert!(false)
+ }
+ }
+
+ match lex(document.to_string()) {
+ Err(s) => {
+ println!("Couldnt lex {}: {}\n", document, s);
+ assert!(false);
+ }
+
+ Ok(tree) => match eval(tree, vt.clone(), ft.clone(), false) {
+ Err(s) => {
+ println!("Couldnt eval {}: {}\n", document, s);
+ assert!(false);
+ }
+
+ Ok(ctr) => match ctr {
+ Ctr::Symbol(_) => assert!(false),
+ Ctr::String(_) => assert!(false),
+ Ctr::Integer(_) => assert!(false),
+ Ctr::Float(_) => assert!(false),
+ Ctr::Bool(_) => assert!(false),
+ Ctr::Seg(s) => assert_eq!(ast_to_string(s), result),
+ Ctr::None => assert!(false),
+ },
+ },
+ }
+ }
+
+ #[test]
+ fn test_append_no_list() {
+ let document = "(append 'test' 1 2 3)";
+ let result = "('test' 1 2 3)";
+ let vt = Rc::new(RefCell::new(VTable::new()));
+ let ft: Rc>;
+ match get_stdlib(vt.clone()) {
+ Ok(f) => ft = f,
+ Err(s) => {
+ ft = Rc::new(RefCell::new(FTable::new()));
+ println!("Couldnt get stdlib: {}!", s);
+ assert!(false)
+ }
+ }
+
+ match lex(document.to_string()) {
+ Err(s) => {
+ println!("Couldnt lex {}: {}\n", document, s);
+ assert!(false);
+ }
+
+ Ok(tree) => match eval(tree, vt.clone(), ft.clone(), false) {
+ Err(s) => {
+ println!("Couldnt eval {}: {}\n", document, s);
+ assert!(false);
+ }
+
+ Ok(ctr) => match ctr {
+ Ctr::Symbol(_) => assert!(false),
+ Ctr::String(_) => assert!(false),
+ Ctr::Integer(_) => assert!(false),
+ Ctr::Float(_) => assert!(false),
+ Ctr::Bool(_) => assert!(false),
+ Ctr::Seg(s) => assert_eq!(ast_to_string(s), result),
+ Ctr::None => assert!(false),
+ },
+ },
+ }
+ }
+}
diff --git a/tests/test_lib_str.rs b/tests/test_lib_str.rs
new file mode 100644
index 0000000..382b65e
--- /dev/null
+++ b/tests/test_lib_str.rs
@@ -0,0 +1,126 @@
+mod str_lib_tests {
+ use relish::ast::{eval, lex, Ctr, FTable, VTable};
+ use relish::stdlib::get_stdlib;
+ use std::cell::RefCell;
+ use std::rc::Rc;
+
+ #[test]
+ fn test_simple_concat() {
+ let document = "(concat 'test')";
+ let result = "test";
+ let vt = Rc::new(RefCell::new(VTable::new()));
+ let ft: Rc>;
+ match get_stdlib(vt.clone()) {
+ Ok(f) => ft = f,
+ Err(s) => {
+ ft = Rc::new(RefCell::new(FTable::new()));
+ println!("Couldnt get stdlib: {}!", s);
+ assert!(false)
+ }
+ }
+
+ match lex(document.to_string()) {
+ Err(s) => {
+ println!("Couldnt lex {}: {}\n", document, s);
+ assert!(false);
+ }
+
+ Ok(tree) => match eval(tree, vt.clone(), ft.clone(), false) {
+ Err(s) => {
+ println!("Couldnt eval {}: {}\n", document, s);
+ assert!(false);
+ }
+
+ Ok(ctr) => match ctr {
+ Ctr::Symbol(_) => assert!(false),
+ Ctr::String(s) => assert_eq!(s, result),
+ Ctr::Integer(_) => assert!(false),
+ Ctr::Float(_) => assert!(false),
+ Ctr::Bool(_) => assert!(false),
+ Ctr::Seg(_) => assert!(false),
+ Ctr::None => assert!(false),
+ },
+ },
+ }
+ }
+
+ #[test]
+ fn test_poly_concat() {
+ let document = "(concat 'test' 1 2 3)";
+ let result = "test123";
+ let vt = Rc::new(RefCell::new(VTable::new()));
+ let ft: Rc>;
+ match get_stdlib(vt.clone()) {
+ Ok(f) => ft = f,
+ Err(s) => {
+ ft = Rc::new(RefCell::new(FTable::new()));
+ println!("Couldnt get stdlib: {}!", s);
+ assert!(false)
+ }
+ }
+
+ match lex(document.to_string()) {
+ Err(s) => {
+ println!("Couldnt lex {}: {}\n", document, s);
+ assert!(false);
+ }
+
+ Ok(tree) => match eval(tree, vt.clone(), ft.clone(), false) {
+ Err(s) => {
+ println!("Couldnt eval {}: {}\n", document, s);
+ assert!(false);
+ }
+
+ Ok(ctr) => match ctr {
+ Ctr::Symbol(_) => assert!(false),
+ Ctr::String(s) => assert_eq!(s, result),
+ Ctr::Integer(_) => assert!(false),
+ Ctr::Float(_) => assert!(false),
+ Ctr::Bool(_) => assert!(false),
+ Ctr::Seg(_) => assert!(false),
+ Ctr::None => assert!(false),
+ },
+ },
+ }
+ }
+
+ #[test]
+ fn test_empty_concat() {
+ let document = "(concat)";
+ let result = "";
+ let vt = Rc::new(RefCell::new(VTable::new()));
+ let ft: Rc>;
+ match get_stdlib(vt.clone()) {
+ Ok(f) => ft = f,
+ Err(s) => {
+ ft = Rc::new(RefCell::new(FTable::new()));
+ println!("Couldnt get stdlib: {}!", s);
+ assert!(false)
+ }
+ }
+
+ match lex(document.to_string()) {
+ Err(s) => {
+ println!("Couldnt lex {}: {}\n", document, s);
+ assert!(false);
+ }
+
+ Ok(tree) => match eval(tree, vt.clone(), ft.clone(), false) {
+ Err(s) => {
+ println!("Couldnt eval {}: {}\n", document, s);
+ assert!(false);
+ }
+
+ Ok(ctr) => match ctr {
+ Ctr::Symbol(_) => assert!(false),
+ Ctr::String(s) => assert_eq!(s, result),
+ Ctr::Integer(_) => assert!(false),
+ Ctr::Float(_) => assert!(false),
+ Ctr::Bool(_) => assert!(false),
+ Ctr::Seg(_) => assert!(false),
+ Ctr::None => assert!(false),
+ },
+ },
+ }
+ }
+}