From 7fd47c18126b04cb4ed45b9ef927b6020d3f1a9c Mon Sep 17 00:00:00 2001 From: Ava Hahn Date: Tue, 7 Mar 2023 14:29:31 -0800 Subject: [PATCH] added list len, front, and back --- Readme.org | 29 ++++++++--- src/stl.rs | 33 +++++++++++++ src/stl/append.rs | 54 +++++++++++++++++++++ src/sym.rs | 9 ++-- tests/test_lib_append.rs | 102 +++++++++++++++++++++++++++++++++++++++ 5 files changed, 215 insertions(+), 12 deletions(-) diff --git a/Readme.org b/Readme.org index c1cda85..2c2f183 100644 --- a/Readme.org +++ b/Readme.org @@ -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 diff --git a/src/stl.rs b/src/stl.rs index d0a7ff2..1ca2ff4 100644 --- a/src/stl.rs +++ b/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(()) } diff --git a/src/stl/append.rs b/src/stl/append.rs index f50ab06..cf4d6a5 100644 --- a/src/stl/append.rs +++ b/src/stl/append.rs @@ -49,3 +49,57 @@ pub fn append_callback(ast: &Seg, _syms: &mut SymTable) -> Result { 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 { + 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 { + 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 { + 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()) + } +} diff --git a/src/sym.rs b/src/sym.rs index 876ee17..344715e 100644 --- a/src/sym.rs +++ b/src/sym.rs @@ -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(()) diff --git a/tests/test_lib_append.rs b/tests/test_lib_append.rs index 919ea68..1d10491 100644 --- a/tests/test_lib_append.rs +++ b/tests/test_lib_append.rs @@ -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(), + ); + } }