added list len, front, and back

This commit is contained in:
Ava Hahn 2023-03-07 14:29:31 -08:00
parent 06d30ac263
commit 7fd47c1812
Signed by untrusted user who does not match committer: affine
GPG key ID: 3A4645B8CF806069
5 changed files with 215 additions and 12 deletions

View file

@ -1,19 +1,23 @@
#+Title: Relish: Rusty Expressive LIsp SHell
#+Author: Ava Hahn
Note: this document is best read within a dedicated ORG mode interpreter
Note: this document is best read using a dedicated ORG mode editor
* Purpose statement
The purpose of Relish is to create a highly expressive POSIX shell using a lisp interpreter.
The purpose of Relish is to create a highly portable, easy to integrate language that can be used in many environments.
* Goals
- Iterate on the ideas and designs that were tested with SHS
https://gitlab.com/whom/shs
- Act as both a high level scripting language and as a system shell
- To be as portable as possible
- To provide code and framework that can be embedded in other applications needing a user facing interpreter
- To be well tested code
- No unsafe code without extreme consideration and rigorous containment
- Create a usable POSIX shell
- Create usable applications/scripts
- To have quality code coverage
- No unsafe code
** Stretch Goals
- Create an interpreter that can be booted on one or more SOCs
- Create an interpreter that can be embedded in another application
- Create UI bindings
* Contact
- Matrix chat: #vomitorium:matrix.sunnypup.io
@ -156,9 +160,9 @@ Will need a concatenate function for tables
**** DONE mul
**** TODO exp
**** TODO mod
***** TODO Test for Let Destructuring
**** TODO inc
**** TODO dec
**** TODO int (float to int)
**** TODO gt?
**** TODO lt?
**** TODO snippets for gte and lte
@ -181,12 +185,21 @@ Will need a concatenate function for tables
*** 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 front (returns copy of first elem)
***** TODO normal positive test
***** TODO test for err case on empty list
**** TODO back (returns copy of last elem)
***** TODO normal positive test
***** TODO test for err case on empty list
**** TODO list len
*** TODO lambda
*** TODO bool cast
*** TODO file operations
**** TODO read-to-string
**** TODO write-to-file

View file

@ -250,6 +250,39 @@ pub fn static_stdlib(syms: &mut SymTable) -> Result<(), String> {
},
);
syms.insert(
"len".to_string(),
Symbol {
name: String::from("len"),
args: Args::Strict(vec![Type::Seg]),
conditional_branches: false,
docs: append::LEN_DOCSTRING.to_string(),
value: ValueType::Internal(Rc::new(append::len_callback)),
},
);
syms.insert(
"car".to_string(),
Symbol {
name: String::from("car"),
args: Args::Strict(vec![Type::Seg]),
conditional_branches: false,
docs: append::CAR_DOCSTRING.to_string(),
value: ValueType::Internal(Rc::new(append::car_callback)),
},
);
syms.insert(
"cdr".to_string(),
Symbol {
name: String::from("cdr"),
args: Args::Strict(vec![Type::Seg]),
conditional_branches: false,
docs: append::CDR_DOCSTRING.to_string(),
value: ValueType::Internal(Rc::new(append::cdr_callback)),
},
);
Ok(())
}

View file

@ -49,3 +49,57 @@ pub fn append_callback(ast: &Seg, _syms: &mut SymTable) -> Result<Ctr, String> {
Ok(Ctr::Seg(temp))
}
}
pub const LEN_DOCSTRING: &str = "Takes a single argument, expected to be a list.
Returns the length of said list.
Interpreter will panic if the length of the list is greater than the max value of a signed 128 bit integer";
pub fn len_callback(ast: &Seg, _syms: &mut SymTable) -> Result<Ctr, String> {
if let Ctr::Seg(ref s) = *ast.car {
if let Ctr::None = *s.car {
Ok(Ctr::Integer(0))
} else {
Ok(Ctr::Integer(s.len() as i128))
}
} else {
Err("impossible condition: argument to len not a list".to_string())
}
}
pub const CAR_DOCSTRING: &str = "Takes a single argument, expected to be a list.
Returns a copy of the first value in that list.
If the list is empty, returns an err to avoid passing back a null value.";
pub fn car_callback(ast: &Seg, _syms: &mut SymTable) -> Result<Ctr, String> {
if let Ctr::Seg(ref s) = *ast.car {
if let Ctr::None = *s.car {
Err("argument is empty".to_string())
} else {
Ok(*s.car.clone())
}
} else {
Err("impossible condition: argument to car not a list".to_string())
}
}
pub const CDR_DOCSTRING: &str = "Takes a single argument, expected to be a list.
Returns a copy of the last value in that list.
If the list is empty, returns an err to avoid passing back a null value.";
pub fn cdr_callback(ast: &Seg, _syms: &mut SymTable) -> Result<Ctr, String> {
if let Ctr::Seg(ref s) = *ast.car {
let mut ret = &s.car;
let mut iter = &s.cdr;
while let Ctr::Seg(ref next) = **iter {
ret = &next.car;
iter = &next.cdr;
}
if let Ctr::None = **ret {
Err("argument is empty".to_string())
} else {
Ok(*ret.clone())
}
} else {
Err("impossible condition: argument to cdr not a list".to_string())
}
}

View file

@ -145,14 +145,15 @@ impl Args {
} else {
return Err("expected no args".to_string());
}
}
},
Args::Infinite => {
if !args.is_empty() {
return Ok(());
} else {
return Err("expected args but none were provided".to_string());
}
}
},
Args::Lazy(ref num) => {
let called_arg_count = args.len();
@ -167,7 +168,7 @@ impl Args {
} else if let Ctr::None = *args.car {
return Err(format!("expected {} args. Got 0.", num,));
}
}
},
Args::Strict(ref arg_types) => {
let mut idx: usize = 0;
@ -202,7 +203,7 @@ impl Args {
return Err("too few arguments".to_string());
}
}
}
},
}
Ok(())

View file

@ -85,4 +85,106 @@ mod append_lib_tests {
result.to_string(),
);
}
#[test]
fn test_len_empty() {
let document = "(len ())";
let result = "0";
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_len_normal() {
let document = "(len (1 2 3))";
let result = "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_car_empty() {
let document = "(car ())";
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)
.err()
.unwrap()
.to_string(),
"error in call to car: argument is empty".to_string(),
);
}
#[test]
fn test_car_normal() {
let document = "(car (1 2 3))";
let result = "1";
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_cdr_empty() {
let document = "(cdr ())";
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)
.err()
.unwrap()
.to_string(),
"error in call to cdr: argument is empty".to_string(),
);
}
#[test]
fn test_cdr_normal() {
let document = "(cdr (1 2 3))";
let result = "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(),
);
}
}