From 928c9b91eda0b045620051d6ccfaa8632bece084 Mon Sep 17 00:00:00 2001 From: Ava Hahn Date: Wed, 8 Mar 2023 20:59:22 -0800 Subject: [PATCH] add reverse, snippet for prepend implementation --- Readme.org | 25 ------- snippets/prepend.rls | 5 ++ src/segment.rs | 14 ++++ src/stl.rs | 33 +++++++++ src/stl/append.rs | 81 ++++++++++++++++++++ tests/test_lib_append.rs | 155 +++++++++++++++++++++++++++++++++++++++ 6 files changed, 288 insertions(+), 25 deletions(-) create mode 100644 snippets/prepend.rls diff --git a/Readme.org b/Readme.org index e0ac447..d605ad3 100644 --- a/Readme.org +++ b/Readme.org @@ -312,14 +312,6 @@ Will need a concatenate function for tables *** TODO Main shell calls Load function on arg and exits *** TODO Can enter multiple lines of text, with formatting in repl *** TODO append -> cons -*** TODO string operations -**** DONE typecast (string) -**** DONE contains -**** DONE strlen -**** DONE concat -**** TODO substr by index -**** TODO split (on delimiter) -**** TODO strcons (sprintf but its all string tokens under the hood) *** TODO Shell module **** TODO Support for single quoting string literals **** TODO Args parsing helper @@ -328,23 +320,6 @@ Will need a concatenate function for tables **** TODO Foreground process TTY **** TODO Background processes **** TODO Update config env var docstring with functions loaded or unloaded -*** TODO list operations -**** DONE append -**** TODO head (returns (head rest)) -***** TODO let destructuring test -**** TODO tail (returns (rest tail)) -***** TODO let destructuring test -**** TODO queue (append to front) -**** TODO snippet for dequeue -**** TODO snippet for pop -**** TODO sublist by delimiter -**** DONE front (returns copy of first elem) -***** DONE normal positive test -***** DONE test for err case on empty list -**** DONE back (returns copy of last elem) -***** DONE normal positive test -***** DONE test for errcase on empty list -**** DONE list len *** TODO lambda *** TODO bool cast *** TODO file operations diff --git a/snippets/prepend.rls b/snippets/prepend.rls new file mode 100644 index 0000000..6216630 --- /dev/null +++ b/snippets/prepend.rls @@ -0,0 +1,5 @@ +#!/bin/relish + +(def prepend 'takes a list and appends an element to the back of it, returns prepended list' + (elem list) + (reverse (append (reverse list) elem))) \ No newline at end of file diff --git a/src/segment.rs b/src/segment.rs index 31cdbe8..a30a5fe 100644 --- a/src/segment.rs +++ b/src/segment.rs @@ -126,6 +126,20 @@ impl Seg { } } + /* applies a function across a list in standard form + * recurs before applying function to go in reverse + * function must take a Ctr and return a bool + * short circuits on the first false returned. + * also returns false on a non standard form list + */ + pub fn circuit_reverse bool>(&self, func: &mut F) -> bool { + match &*self.cdr { + Ctr::None => func(&self.car), + Ctr::Seg(l) => l.circuit_reverse(func) && func(&self.car), + _ => false, + } + } + pub fn from_mono(arg: Box) -> Seg { Seg { car: arg, diff --git a/src/stl.rs b/src/stl.rs index ca128d6..0fa5534 100644 --- a/src/stl.rs +++ b/src/stl.rs @@ -338,6 +338,39 @@ pub fn static_stdlib(syms: &mut SymTable) -> Result<(), String> { }, ); + syms.insert( + "pop".to_string(), + Symbol { + name: String::from("pop"), + args: Args::Strict(vec![Type::Seg]), + conditional_branches: false, + docs: append::POP_DOCSTRING.to_string(), + value: ValueType::Internal(Rc::new(append::pop_callback)), + }, + ); + + syms.insert( + "dq".to_string(), + Symbol { + name: String::from("dequeue"), + args: Args::Strict(vec![Type::Seg]), + conditional_branches: false, + docs: append::DEQUEUE_DOCSTRING.to_string(), + value: ValueType::Internal(Rc::new(append::dequeue_callback)), + }, + ); + + syms.insert( + "reverse".to_string(), + Symbol { + name: String::from("reverse"), + args: Args::Strict(vec![Type::Seg]), + conditional_branches: false, + docs: append::REVERSE_DOCSTRING.to_string(), + value: ValueType::Internal(Rc::new(append::reverse_callback)), + }, + ); + syms.insert( "exp".to_string(), Symbol { diff --git a/src/stl/append.rs b/src/stl/append.rs index cf4d6a5..6dbd9cc 100644 --- a/src/stl/append.rs +++ b/src/stl/append.rs @@ -103,3 +103,84 @@ pub fn cdr_callback(ast: &Seg, _syms: &mut SymTable) -> Result { Err("impossible condition: argument to cdr not a list".to_string()) } } + +pub const POP_DOCSTRING: &str = "Takes a single argument, expected to be a list. +Returns a list containing the first element, and the rest of the elements. + +Example: (pop (1 2 3)) -> (1 (2 3)). + +The head can then be accessed by car and the rest can be accessed by cdr."; + +pub fn pop_callback(ast: &Seg, _syms: &mut SymTable) -> Result { + if let Ctr::Seg(ref s) = *ast.car { + if s.len() < 1 { + return Err("cannot pop from empty list".to_string()); + } + let mut ret = Seg::new(); + ret.append(s.car.clone()); + if let Ctr::Seg(ref s2) = *s.cdr { + ret.append(Box::new(Ctr::Seg(s2.clone()))); + } else { + ret.append(Box::new(Ctr::Seg(Seg::new()))); + } + Ok(Ctr::Seg(ret)) + } else { + Err("Impossible condition: arg not a list".to_string()) + } +} + +pub const DEQUEUE_DOCSTRING: &str = "Takes a single argument, expected to be a list. +Returns a list containing the last element, and the rest of the elements. + +Example: (dq (1 2 3)) -> (3 (1 2)). + +The last element can then be accessed by car and the rest can be accessed by cdr."; + +pub fn dequeue_callback(ast: &Seg, _syms: &mut SymTable) -> Result { + if let Ctr::Seg(ref s) = *ast.car { + if s.len() < 1 { + return Err("cannot dequeue from empty list".to_string()); + } + let mut rest = Seg::new(); + let mut iter = s; + while *iter.cdr != Ctr::None { + rest.append(Box::new(*iter.car.clone())); + if let Ctr::Seg(ref next) = *iter.cdr { + iter = next; + } else { + break; + } + } + + let mut ret = Seg::new(); + match *iter.car { + Ctr::Seg(ref s) => { + ret.append(s.car.clone()); + } + _ => ret.append(iter.car.clone()), + } + + ret.append(Box::new(Ctr::Seg(rest))); + + Ok(Ctr::Seg(ret)) + } else { + Err("Impossible condition: arg not a list".to_string()) + } +} + +pub const REVERSE_DOCSTRING: &str = "Takes a single argument, expected to be a list. +Returns the same list, but in reverse order."; + +pub fn reverse_callback(ast: &Seg, _syms: &mut SymTable) -> Result { + if let Ctr::Seg(ref s) = *ast.car { + let mut ret = Seg::new(); + s.circuit_reverse(&mut |arg: &Ctr| -> bool { + ret.append(Box::new(arg.clone())); + true + }); + + Ok(Ctr::Seg(ret)) + } else { + Err("Impossible condition: arg not a list".to_string()) + } +} diff --git a/tests/test_lib_append.rs b/tests/test_lib_append.rs index 1d10491..73e297b 100644 --- a/tests/test_lib_append.rs +++ b/tests/test_lib_append.rs @@ -187,4 +187,159 @@ mod append_lib_tests { result.to_string(), ); } + + #[test] + fn test_pop() { + let document = "(def test '' (pop (1 2 3)))"; + let check1 = "(car test)"; + let result1 = "1"; + let check2 = "(cdr test)"; + let result2 = "(2 3)"; + + let mut syms = SymTable::new(); + static_stdlib(&mut syms).unwrap(); + dynamic_stdlib(&mut syms).unwrap(); + eval(&lex(&document.to_string()).unwrap(), &mut syms).unwrap(); + let ch1 = lex(&check1.to_string()).unwrap(); + let ch2 = lex(&check2.to_string()).unwrap(); + + assert_eq!( + *eval(&ch1, &mut syms).unwrap().to_string(), + result1.to_string(), + ); + + assert_eq!( + *eval(&ch2, &mut syms).unwrap().to_string(), + result2.to_string(), + ); + } + + #[test] + fn test_pop_mono() { + let document = "(def test '' (pop (1)))"; + let check1 = "(car test)"; + let result1 = "1"; + let check2 = "(cdr test)"; + let result2 = "()"; + + let mut syms = SymTable::new(); + static_stdlib(&mut syms).unwrap(); + dynamic_stdlib(&mut syms).unwrap(); + eval(&lex(&document.to_string()).unwrap(), &mut syms).unwrap(); + let ch1 = lex(&check1.to_string()).unwrap(); + let ch2 = lex(&check2.to_string()).unwrap(); + + assert_eq!( + *eval(&ch1, &mut syms).unwrap().to_string(), + result1.to_string(), + ); + + assert_eq!( + *eval(&ch2, &mut syms).unwrap().to_string(), + result2.to_string(), + ); + } + + #[test] + fn test_dq() { + let document = "(def test '' (dq (1 2 3)))"; + let check1 = "(car test)"; + let result1 = "3"; + let check2 = "(cdr test)"; + let result2 = "(1 2)"; + + let mut syms = SymTable::new(); + static_stdlib(&mut syms).unwrap(); + dynamic_stdlib(&mut syms).unwrap(); + eval(&lex(&document.to_string()).unwrap(), &mut syms).unwrap(); + let ch1 = lex(&check1.to_string()).unwrap(); + let ch2 = lex(&check2.to_string()).unwrap(); + + assert_eq!( + *eval(&ch1, &mut syms).unwrap().to_string(), + result1.to_string(), + ); + + assert_eq!( + *eval(&ch2, &mut syms).unwrap().to_string(), + result2.to_string(), + ); + } + + #[test] + fn test_dq_mono() { + let document = "(def test '' (dq (1)))"; + let check1 = "(car test)"; + let result1 = "1"; + let check2 = "(cdr test)"; + let result2 = "()"; + + let mut syms = SymTable::new(); + static_stdlib(&mut syms).unwrap(); + dynamic_stdlib(&mut syms).unwrap(); + eval(&lex(&document.to_string()).unwrap(), &mut syms).unwrap(); + let ch1 = lex(&check1.to_string()).unwrap(); + let ch2 = lex(&check2.to_string()).unwrap(); + + assert_eq!( + *eval(&ch1, &mut syms).unwrap().to_string(), + result1.to_string(), + ); + + assert_eq!( + *eval(&ch2, &mut syms).unwrap().to_string(), + result2.to_string(), + ); + } + + #[test] + fn test_reverse() { + let document = "(reverse ('test' 1 2 3))"; + let result = "(3 2 1 '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_reverse_mono() { + let document = "(reverse ('test'))"; + 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_reverse_nil() { + let document = "(reverse ())"; + 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(), + ); + } }