add reverse, snippet for prepend implementation
This commit is contained in:
parent
6961fcc9fa
commit
928c9b91ed
6 changed files with 288 additions and 25 deletions
25
Readme.org
25
Readme.org
|
|
@ -312,14 +312,6 @@ Will need a concatenate function for tables
|
||||||
*** TODO Main shell calls Load function on arg and exits
|
*** TODO Main shell calls Load function on arg and exits
|
||||||
*** TODO Can enter multiple lines of text, with formatting in repl
|
*** TODO Can enter multiple lines of text, with formatting in repl
|
||||||
*** TODO append -> cons
|
*** 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 Shell module
|
||||||
**** TODO Support for single quoting string literals
|
**** TODO Support for single quoting string literals
|
||||||
**** TODO Args parsing helper
|
**** TODO Args parsing helper
|
||||||
|
|
@ -328,23 +320,6 @@ Will need a concatenate function for tables
|
||||||
**** TODO Foreground process TTY
|
**** TODO Foreground process TTY
|
||||||
**** TODO Background processes
|
**** TODO Background processes
|
||||||
**** TODO Update config env var docstring with functions loaded or unloaded
|
**** 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 lambda
|
||||||
*** TODO bool cast
|
*** TODO bool cast
|
||||||
*** TODO file operations
|
*** TODO file operations
|
||||||
|
|
|
||||||
5
snippets/prepend.rls
Normal file
5
snippets/prepend.rls
Normal file
|
|
@ -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)))
|
||||||
|
|
@ -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<F: FnMut(&Ctr) -> 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<Ctr>) -> Seg {
|
pub fn from_mono(arg: Box<Ctr>) -> Seg {
|
||||||
Seg {
|
Seg {
|
||||||
car: arg,
|
car: arg,
|
||||||
|
|
|
||||||
33
src/stl.rs
33
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(
|
syms.insert(
|
||||||
"exp".to_string(),
|
"exp".to_string(),
|
||||||
Symbol {
|
Symbol {
|
||||||
|
|
|
||||||
|
|
@ -103,3 +103,84 @@ pub fn cdr_callback(ast: &Seg, _syms: &mut SymTable) -> Result<Ctr, String> {
|
||||||
Err("impossible condition: argument to cdr not a list".to_string())
|
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<Ctr, String> {
|
||||||
|
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<Ctr, String> {
|
||||||
|
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<Ctr, String> {
|
||||||
|
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())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -187,4 +187,159 @@ mod append_lib_tests {
|
||||||
result.to_string(),
|
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 = "(<nil>)";
|
||||||
|
|
||||||
|
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 = "(<nil>)";
|
||||||
|
|
||||||
|
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 = "(<nil>)";
|
||||||
|
|
||||||
|
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(),
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue