added list len, front, and back
This commit is contained in:
parent
06d30ac263
commit
7fd47c1812
5 changed files with 215 additions and 12 deletions
29
Readme.org
29
Readme.org
|
|
@ -1,19 +1,23 @@
|
||||||
#+Title: Relish: Rusty Expressive LIsp SHell
|
#+Title: Relish: Rusty Expressive LIsp SHell
|
||||||
#+Author: Ava Hahn
|
#+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
|
* 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
|
* Goals
|
||||||
- Iterate on the ideas and designs that were tested with SHS
|
- Iterate on the ideas and designs that were tested with SHS
|
||||||
https://gitlab.com/whom/shs
|
https://gitlab.com/whom/shs
|
||||||
- Act as both a high level scripting language and as a system shell
|
- Create a usable POSIX shell
|
||||||
- To be as portable as possible
|
- Create usable applications/scripts
|
||||||
- To provide code and framework that can be embedded in other applications needing a user facing interpreter
|
- To have quality code coverage
|
||||||
- To be well tested code
|
- No unsafe code
|
||||||
- No unsafe code without extreme consideration and rigorous containment
|
|
||||||
|
** 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
|
* Contact
|
||||||
- Matrix chat: #vomitorium:matrix.sunnypup.io
|
- Matrix chat: #vomitorium:matrix.sunnypup.io
|
||||||
|
|
@ -156,9 +160,9 @@ Will need a concatenate function for tables
|
||||||
**** DONE mul
|
**** DONE mul
|
||||||
**** TODO exp
|
**** TODO exp
|
||||||
**** TODO mod
|
**** TODO mod
|
||||||
|
***** TODO Test for Let Destructuring
|
||||||
**** TODO inc
|
**** TODO inc
|
||||||
**** TODO dec
|
**** TODO dec
|
||||||
**** TODO int (float to int)
|
|
||||||
**** TODO gt?
|
**** TODO gt?
|
||||||
**** TODO lt?
|
**** TODO lt?
|
||||||
**** TODO snippets for gte and lte
|
**** TODO snippets for gte and lte
|
||||||
|
|
@ -181,12 +185,21 @@ Will need a concatenate function for tables
|
||||||
*** TODO list operations
|
*** TODO list operations
|
||||||
**** DONE append
|
**** DONE append
|
||||||
**** TODO head (returns (head rest))
|
**** TODO head (returns (head rest))
|
||||||
|
***** TODO let destructuring test
|
||||||
**** TODO tail (returns (rest tail))
|
**** TODO tail (returns (rest tail))
|
||||||
|
***** TODO let destructuring test
|
||||||
**** TODO queue (append to front)
|
**** TODO queue (append to front)
|
||||||
**** TODO snippet for dequeue
|
**** TODO snippet for dequeue
|
||||||
**** TODO snippet for pop
|
**** 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 list len
|
||||||
*** TODO lambda
|
*** TODO lambda
|
||||||
|
*** TODO bool cast
|
||||||
*** TODO file operations
|
*** TODO file operations
|
||||||
**** TODO read-to-string
|
**** TODO read-to-string
|
||||||
**** TODO write-to-file
|
**** TODO write-to-file
|
||||||
|
|
|
||||||
33
src/stl.rs
33
src/stl.rs
|
|
@ -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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -49,3 +49,57 @@ pub fn append_callback(ast: &Seg, _syms: &mut SymTable) -> Result<Ctr, String> {
|
||||||
Ok(Ctr::Seg(temp))
|
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())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -145,14 +145,15 @@ impl Args {
|
||||||
} else {
|
} else {
|
||||||
return Err("expected no args".to_string());
|
return Err("expected no args".to_string());
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
|
||||||
Args::Infinite => {
|
Args::Infinite => {
|
||||||
if !args.is_empty() {
|
if !args.is_empty() {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
} else {
|
} else {
|
||||||
return Err("expected args but none were provided".to_string());
|
return Err("expected args but none were provided".to_string());
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
|
||||||
Args::Lazy(ref num) => {
|
Args::Lazy(ref num) => {
|
||||||
let called_arg_count = args.len();
|
let called_arg_count = args.len();
|
||||||
|
|
@ -167,7 +168,7 @@ impl Args {
|
||||||
} else if let Ctr::None = *args.car {
|
} else if let Ctr::None = *args.car {
|
||||||
return Err(format!("expected {} args. Got 0.", num,));
|
return Err(format!("expected {} args. Got 0.", num,));
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
|
||||||
Args::Strict(ref arg_types) => {
|
Args::Strict(ref arg_types) => {
|
||||||
let mut idx: usize = 0;
|
let mut idx: usize = 0;
|
||||||
|
|
@ -202,7 +203,7 @@ impl Args {
|
||||||
return Err("too few arguments".to_string());
|
return Err("too few arguments".to_string());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
||||||
|
|
@ -85,4 +85,106 @@ mod append_lib_tests {
|
||||||
result.to_string(),
|
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(),
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue