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), + }, + }, + } + } +}