From d4121a734a32df6f97b44541d9a14e0e34457262 Mon Sep 17 00:00:00 2001 From: Ava Hahn Date: Wed, 8 Mar 2023 11:39:54 -0800 Subject: [PATCH] Add simple string functions --- src/stl.rs | 63 +++++++--- src/stl/str.rs | 103 ++++++---------- src/stl/strings.rs | 117 ++++++++++++++++++ tests/test_lib_str.rs | 273 +++++++++++++++++++++++++----------------- 4 files changed, 366 insertions(+), 190 deletions(-) create mode 100644 src/stl/strings.rs diff --git a/src/stl.rs b/src/stl.rs index e092ca7..1fd3e95 100644 --- a/src/stl.rs +++ b/src/stl.rs @@ -24,7 +24,7 @@ pub mod boolean; pub mod control; pub mod decl; pub mod math; -//pub mod str; +pub mod strings; /// static_stdlib /// inserts all stdlib functions that can be inserted without @@ -47,8 +47,52 @@ pub fn static_stdlib(syms: &mut SymTable) -> Result<(), String> { name: String::from("echo"), args: Args::Infinite, conditional_branches: false, - docs: ECHO_DOCSTRING.to_string(), - value: ValueType::Internal(Rc::new(_echo_callback)), + docs: strings::ECHO_DOCSTRING.to_string(), + value: ValueType::Internal(Rc::new(strings::echo_callback)), + }, + ); + + 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)), + }, + ); + + syms.insert( + "contains".to_string(), + Symbol { + name: String::from("contains"), + args: Args::Strict(vec![Type::String, Type::String]), + conditional_branches: false, + docs: strings::CONTAINS_DOCSTRING.to_string(), + value: ValueType::Internal(Rc::new(strings::contains_callback)), + }, + ); + + 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)), + }, + ); + + 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)), }, ); @@ -402,16 +446,3 @@ pub fn dynamic_stdlib(syms: &mut SymTable) -> Result<(), String> { Ok(()) } - -pub const ECHO_DOCSTRING: &str = - "traverses any number of arguments. Prints their evaluated values on a new line for each."; - -fn _echo_callback(ast: &Seg, _syms: &mut SymTable) -> Result { - if ast.len() == 1 { - println!("{}", ast.car); - } else { - ast.circuit(&mut |arg: &Ctr| print!("{}", arg) == ()); - } - - Ok(Ctr::None) -} diff --git a/src/stl/str.rs b/src/stl/str.rs index 418d252..ac926b0 100644 --- a/src/stl/str.rs +++ b/src/stl/str.rs @@ -14,73 +14,44 @@ * 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; - }, - )), - }; +use crate::sym::SymTable; +use crate::segment::{Seg, Ctr}; + + +pub const ECHO_DOCSTRING: &str = + "traverses any number of arguments. Prints their evaluated values on a new line for each."; + +pub fn echo_callback(ast: &Seg, _syms: &mut SymTable) -> Result { + if ast.len() == 1 { + println!("{}", ast.car); + } else { + ast.circuit(&mut |arg: &Ctr| print!("{}", arg) == ()); + } + + Ok(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); - }, - )), - }; +pub const CONCAT_DOCSTRING: &str = "Iterates over N args of any type other than SYMBOL. +converts each argument to a string. Combines all strings. +Returns final (combined) string."; + +pub fn concat_callback(ast: &Seg, _syms: &mut SymTable) -> Result { + let mut string = String::from(""); + if !ast.circuit(&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!("dont know what to do witha symbol here") + } + return Ctr::String(string); } -*/ diff --git a/src/stl/strings.rs b/src/stl/strings.rs new file mode 100644 index 0000000..896fb51 --- /dev/null +++ b/src/stl/strings.rs @@ -0,0 +1,117 @@ +/* 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::segment::{Ctr, Seg}; +use crate::sym::SymTable; + +pub const ECHO_DOCSTRING: &str = + "traverses any number of arguments. Prints their evaluated values on a new line for each."; + +pub fn echo_callback(ast: &Seg, _syms: &mut SymTable) -> Result { + if ast.len() == 1 { + println!("{}", ast.car); + } else { + ast.circuit(&mut |arg: &Ctr| print!("{}", arg) == ()); + } + + Ok(Ctr::None) +} + +pub const CONCAT_DOCSTRING: &str = "Iterates over N args of any type other than SYMBOL. +converts each argument to a string. Combines all strings. +Returns final (combined) string."; + +pub fn concat_callback(ast: &Seg, _syms: &mut SymTable) -> Result { + let mut string = String::from(""); + if !ast.circuit(&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(&c.to_string()), + Ctr::None => (), + } + true + }) { + eprintln!("dont know what to do witha symbol here") + } + return Ok(Ctr::String(string)); +} + +pub const STRLEN_DOCSTRING: &str = "Takes a single arg of any type. +Arg is converted to a string if not already a string. +Returns string length of arg."; + +pub fn strlen_callback(ast: &Seg, _syms: &mut SymTable) -> Result { + match &*ast.car { + Ctr::Symbol(s) => Ok(Ctr::Integer(s.len() as i128)), + Ctr::String(s) => Ok(Ctr::Integer(s.len() as i128)), + Ctr::Integer(i) => Ok(Ctr::Integer(i.to_string().len() as i128)), + Ctr::Float(f) => Ok(Ctr::Integer(f.to_string().len() as i128)), + Ctr::Bool(b) => Ok(Ctr::Integer(b.to_string().len() as i128)), + Ctr::Seg(c) => Ok(Ctr::Integer(c.to_string().len() as i128)), + // highly suspicious case below + Ctr::None => Ok(Ctr::Integer(0)), + } +} + +pub const STRCAST_DOCSTRING: &str = "Takes a single arg of any type. +Arg is converted to a string and returned."; + +pub fn strcast_callback(ast: &Seg, _syms: &mut SymTable) -> Result { + match &*ast.car { + Ctr::Symbol(s) => Ok(Ctr::String(s.clone())), + Ctr::String(_) => Ok(*ast.car.clone()), + Ctr::Integer(i) => Ok(Ctr::String(i.to_string())), + Ctr::Float(f) => Ok(Ctr::String(f.to_string())), + Ctr::Bool(b) => Ok(Ctr::String(b.to_string())), + Ctr::Seg(c) => Ok(Ctr::String(c.to_string())), + // highly suspicious case below + Ctr::None => Ok(Ctr::String(String::new())), + } +} + +pub const CONTAINS_DOCSTRING: &str = + "Takes two strings. Returns true if string1 contains at least one instance of string2"; + +pub fn contains_callback(ast: &Seg, _syms: &mut SymTable) -> Result { + let parent_str: String; + if let Ctr::String(ref s) = *ast.car { + parent_str = s.to_string(); + } else { + return Err("first argument must be a string".to_string()); + } + + let second_arg_obj: &Ctr; + let child_str: String; + if let Ctr::Seg(ref s) = *ast.cdr { + second_arg_obj = &*s.car; + } else { + return Err("impossible error: needs two arguments".to_string()); + } + + if let Ctr::String(ref s) = &*second_arg_obj { + child_str = s.clone(); + } else { + return Err("second argument must be a string".to_string()); + } + + Ok(Ctr::Bool(parent_str.contains(&child_str))) +} diff --git a/tests/test_lib_str.rs b/tests/test_lib_str.rs index 6f45e56..45a304c 100644 --- a/tests/test_lib_str.rs +++ b/tests/test_lib_str.rs @@ -1,127 +1,184 @@ -/*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; +mod str_lib_tests { + use relish::ast::{eval, lex, SymTable}; + use relish::stdlib::{dynamic_stdlib, static_stdlib}; #[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), - }, - }, - } + let result = "'test'"; + let mut syms = SymTable::new(); + static_stdlib(&mut syms).unwrap(); + dynamic_stdlib(&mut syms).unwrap(); + assert_eq!( + *eval(&lex(&document.to_string()).unwrap(), &mut syms) + .unwrap() + .to_string(), + result.to_string(), + ); } #[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), - }, - }, - } + let result = "'test123'"; + let mut syms = SymTable::new(); + static_stdlib(&mut syms).unwrap(); + dynamic_stdlib(&mut syms).unwrap(); + assert_eq!( + *eval(&lex(&document.to_string()).unwrap(), &mut syms) + .unwrap() + .to_string(), + result.to_string(), + ); } #[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) - } - } + let result = "''"; + let mut syms = SymTable::new(); + static_stdlib(&mut syms).unwrap(); + dynamic_stdlib(&mut syms).unwrap(); + assert_eq!( + *eval(&lex(&document.to_string()).unwrap(), &mut syms) + .unwrap() + .to_string(), + result.to_string(), + ); + } - match lex(document.to_string()) { - Err(s) => { - println!("Couldnt lex {}: {}\n", document, s); - assert!(false); - } + #[test] + fn test_strlen_str() { + let document = "(strlen 'test')"; + let result = 4; + let mut syms = SymTable::new(); + static_stdlib(&mut syms).unwrap(); + dynamic_stdlib(&mut syms).unwrap(); + assert_eq!( + *eval(&lex(&document.to_string()).unwrap(), &mut syms) + .unwrap() + .to_string(), + result.to_string(), + ); + } - Ok(tree) => match eval(tree, vt.clone(), ft.clone(), false) { - Err(s) => { - println!("Couldnt eval {}: {}\n", document, s); - assert!(false); - } + #[test] + fn test_strlen_int() { + let document = "(strlen 1000)"; + let result = 4; + let mut syms = SymTable::new(); + static_stdlib(&mut syms).unwrap(); + dynamic_stdlib(&mut syms).unwrap(); + assert_eq!( + *eval(&lex(&document.to_string()).unwrap(), &mut syms) + .unwrap() + .to_string(), + result.to_string(), + ); + } - 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_strlen_float() { + let document = "(strlen 10.2)"; + let result = 4; + let mut syms = SymTable::new(); + static_stdlib(&mut syms).unwrap(); + dynamic_stdlib(&mut syms).unwrap(); + assert_eq!( + *eval(&lex(&document.to_string()).unwrap(), &mut syms) + .unwrap() + .to_string(), + result.to_string(), + ); + } + + #[test] + fn test_strlen_seg() { + let document = "(strlen (1 2 3))"; + let result = 7; + let mut syms = SymTable::new(); + static_stdlib(&mut syms).unwrap(); + dynamic_stdlib(&mut syms).unwrap(); + assert_eq!( + *eval(&lex(&document.to_string()).unwrap(), &mut syms) + .unwrap() + .to_string(), + result.to_string(), + ); + } + + #[test] + fn test_strlen_bool() { + let document = "(strlen true)"; + let result = 4; + let mut syms = SymTable::new(); + static_stdlib(&mut syms).unwrap(); + dynamic_stdlib(&mut syms).unwrap(); + assert_eq!( + *eval(&lex(&document.to_string()).unwrap(), &mut syms) + .unwrap() + .to_string(), + result.to_string(), + ); + } + + #[test] + fn test_strcast_i() { + let document = "(string 4)"; + let result = "'4'"; + let mut syms = SymTable::new(); + static_stdlib(&mut syms).unwrap(); + dynamic_stdlib(&mut syms).unwrap(); + assert_eq!( + *eval(&lex(&document.to_string()).unwrap(), &mut syms) + .unwrap() + .to_string(), + result.to_string(), + ); + } + + #[test] + fn test_strcast_seg() { + let document = "(string (1 2 3))"; + let result = "'(1 2 3)'"; + let mut syms = SymTable::new(); + static_stdlib(&mut syms).unwrap(); + dynamic_stdlib(&mut syms).unwrap(); + assert_eq!( + *eval(&lex(&document.to_string()).unwrap(), &mut syms) + .unwrap() + .to_string(), + result.to_string(), + ); + } + + #[test] + fn test_contains() { + let document = "(contains 'bigger' 'ger')"; + let result = "true"; + let mut syms = SymTable::new(); + static_stdlib(&mut syms).unwrap(); + dynamic_stdlib(&mut syms).unwrap(); + assert_eq!( + *eval(&lex(&document.to_string()).unwrap(), &mut syms) + .unwrap() + .to_string(), + result.to_string(), + ); + } + + #[test] + fn test_doesnt_contain() { + let document = "(contains 'smaller' 'ger')"; + let result = "false"; + let mut syms = SymTable::new(); + static_stdlib(&mut syms).unwrap(); + dynamic_stdlib(&mut syms).unwrap(); + assert_eq!( + *eval(&lex(&document.to_string()).unwrap(), &mut syms) + .unwrap() + .to_string(), + result.to_string(), + ); } } -*/