* fixed and wrote test for lambda and function as arg case
* added license to userlib tests * added map impl to userlib * userlib tests now run and pass * all args are evaluated individually
This commit is contained in:
parent
8a91560921
commit
dcb2969b0a
7 changed files with 148 additions and 40 deletions
|
|
@ -490,7 +490,7 @@ Note: this section only tracks the state of incomplete TODO items. Having everyt
|
||||||
- Lex function
|
- Lex function
|
||||||
- Read function (Input + Lex)
|
- Read function (Input + Lex)
|
||||||
- get type function
|
- get type function
|
||||||
- Shell module-
|
- Shell module
|
||||||
- Only loadable via POSIX config var
|
- Only loadable via POSIX config var
|
||||||
- Overload Load function to hook into this lib
|
- Overload Load function to hook into this lib
|
||||||
- arg processor because these are control flow
|
- arg processor because these are control flow
|
||||||
|
|
@ -503,6 +503,7 @@ Note: this section only tracks the state of incomplete TODO items. Having everyt
|
||||||
- logging library
|
- logging library
|
||||||
- make const all the error messages
|
- make const all the error messages
|
||||||
- Main shell calls Load function on arg and exits
|
- Main shell calls Load function on arg and exits
|
||||||
|
- Should globals be immutable?
|
||||||
|
|
||||||
** TODO alpha tasks
|
** TODO alpha tasks
|
||||||
- Rename to Flesh
|
- Rename to Flesh
|
||||||
|
|
@ -514,7 +515,6 @@ Note: this section only tracks the state of incomplete TODO items. Having everyt
|
||||||
- Custom ast pretty print
|
- Custom ast pretty print
|
||||||
- Implement Compose for lambdas
|
- Implement Compose for lambdas
|
||||||
Document this in relevant readme sections
|
Document this in relevant readme sections
|
||||||
- Userlib Map function
|
|
||||||
- Userlib Reduce function
|
- Userlib Reduce function
|
||||||
- File operations
|
- File operations
|
||||||
- read-to-string
|
- read-to-string
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,22 @@
|
||||||
|
#!/bin/relish
|
||||||
|
|
||||||
|
;; relish: versatile lisp shell
|
||||||
|
;; Copyright (C) 2021 Aidan Hahn
|
||||||
|
;;
|
||||||
|
;; This program is free software: you can redistribute it and/or modify
|
||||||
|
;; it under the terms of the GNU General Public License as published by
|
||||||
|
;; the Free Software Foundation, either version 3 of the License, or
|
||||||
|
;; (at your option) any later version.
|
||||||
|
;;
|
||||||
|
;; This program is distributed in the hope that it will be useful,
|
||||||
|
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
;; GNU General Public License for more details.
|
||||||
|
;;
|
||||||
|
;; You should have received a copy of the GNU General Public License
|
||||||
|
;; along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
;; USERLIB-TESTS
|
||||||
;; this file implements unit tests for handwritten userlib functions
|
;; this file implements unit tests for handwritten userlib functions
|
||||||
|
|
||||||
(def passed
|
(def passed
|
||||||
|
|
@ -14,30 +33,32 @@
|
||||||
(('set updates var'
|
(('set updates var'
|
||||||
(quote
|
(quote
|
||||||
(let ((test-val 0))
|
(let ((test-val 0))
|
||||||
(set test-val 3)
|
(set (q test-val) 3)
|
||||||
(eq? test-val 3))))
|
(eq? test-val 3))))
|
||||||
('prepend prepends to list'
|
('prepend prepends to list'
|
||||||
(quote
|
(quote
|
||||||
(let ((list (2 3 4))
|
(let ((list (2 3 4))
|
||||||
(list (prepend 1 list))
|
(list (prepend 1 list))
|
||||||
(list-head (head list)))
|
(list-head (pop list)))
|
||||||
(eq? (car list-head) 1))))
|
(eq? (car list-head) 1))))
|
||||||
|
|
||||||
|
('map applies function across list'
|
||||||
|
(quote
|
||||||
|
(let ((list (1 2 3))
|
||||||
|
(adder (lambda (x) (add 1 x))))
|
||||||
|
(eq? (map adder list) (2 3 4)))))
|
||||||
|
|
||||||
;; add more test cases here
|
;; add more test cases here
|
||||||
))
|
))
|
||||||
|
|
||||||
|
|
||||||
(def test-iter 'iterates over test cases'
|
|
||||||
(head test-cases))
|
|
||||||
|
|
||||||
;; run all test cases, print output
|
;; run all test cases, print output
|
||||||
(while (gt? (len (cdr test-iter))
|
(let ((test-iter (pop test-cases)))
|
||||||
0)
|
(while (gt? (len test-iter) 1)
|
||||||
(let ((test (car test-iter))
|
(let ((test (car test-iter))
|
||||||
(remaining (cdr test-iter))
|
(remaining (cdr test-iter))
|
||||||
(test-name (car test))
|
(test-name (car test))
|
||||||
(test-body (cdr test)))
|
(test-body (cdr test)))
|
||||||
(if (eval test-body)
|
(if (eval test-body)
|
||||||
(passed test-name)
|
(passed test-name)
|
||||||
(failed test-name))
|
(failed test-name))
|
||||||
(def test-iter '' (head remaining))))
|
(set (q test-iter) (pop remaining)))))
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
#!/bin/relish
|
||||||
|
|
||||||
;; relish: versatile lisp shell
|
;; relish: versatile lisp shell
|
||||||
;; Copyright (C) 2021 Aidan Hahn
|
;; Copyright (C) 2021 Aidan Hahn
|
||||||
;;
|
;;
|
||||||
|
|
@ -15,16 +17,50 @@
|
||||||
;; along with this program. If not, see <http://www.gnu.org/licenses/>.
|
;; along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
;; USERLIB
|
;; USERLIB
|
||||||
;; This file contains a plethora of useful features that are not shipped in the STL
|
;; This file contains useful features that are not shipped in the STL
|
||||||
|
|
||||||
|
;; this would be way faster as code in stl
|
||||||
|
;; but stl already suffers scope creep
|
||||||
(def prepend
|
(def prepend
|
||||||
'takes a list and appends an element to the back of it.
|
'takes a list and appends an element to the back of it.
|
||||||
returns prepended list'
|
returns prepended list'
|
||||||
(elem list)
|
(elem list)
|
||||||
(reverse (append (reverse list) elem)))
|
(reverse (cons (reverse list) elem)))
|
||||||
|
|
||||||
|
;; please dont misuse this tool
|
||||||
(def set
|
(def set
|
||||||
'sets an existing variable without touching its docstring'
|
'sets an existing variable without touching its docstring.
|
||||||
|
|
||||||
|
WARNING: abandon hope all ye who declare and then modify global variables!
|
||||||
|
If you find yourself struggling to debug a complex error in state access,
|
||||||
|
or you are having issues re-running commands in the shell consider the
|
||||||
|
following advice:
|
||||||
|
|
||||||
|
It is very much an anti pattern to mutate global variable that contain state
|
||||||
|
refactor your program: put iterators, counters, procedurally generated code,
|
||||||
|
and all other mutable state into a let loop.
|
||||||
|
|
||||||
|
A zen script in relish is one where each root level form (or eval at repl)
|
||||||
|
is self contained, and does not permanently modify any other one.
|
||||||
|
|
||||||
|
See the userlib tests for an easy to follow example of this.'
|
||||||
|
|
||||||
(var val)
|
(var val)
|
||||||
|
|
||||||
(let ((doc (get-doc var)))
|
(let ((doc (get-doc var)))
|
||||||
(def (eval var) doc val)))
|
(def (eval var) doc val)))
|
||||||
|
|
||||||
|
(def map
|
||||||
|
'Takes two arguments: a function and a list.
|
||||||
|
for each element in the list, the function is applied and the
|
||||||
|
result is added to a new list. Returns the new list.'
|
||||||
|
(func list)
|
||||||
|
(let ((list-iter (pop list))
|
||||||
|
(result ()))
|
||||||
|
(while (gt? (len list-iter) 1)
|
||||||
|
(let ((current (car list-iter))
|
||||||
|
(remaining (cdr list-iter))
|
||||||
|
(current-res (func current)))
|
||||||
|
(set (q result) (cons result current-res))
|
||||||
|
(set (q list-iter) (pop remaining))))
|
||||||
|
result))
|
||||||
|
|
|
||||||
|
|
@ -51,6 +51,8 @@ pub fn eval(ast: &Seg, syms: &mut SymTable) -> Result<Box<Ctr>, String> {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
Ctr::Lambda(ref l) => return Ok(call_lambda(l, arg_cdr, syms)?.clone()),
|
||||||
|
|
||||||
Ctr::Symbol(ref tok) => {
|
Ctr::Symbol(ref tok) => {
|
||||||
let outer_scope_seg_holder: Seg;
|
let outer_scope_seg_holder: Seg;
|
||||||
let args: &Seg;
|
let args: &Seg;
|
||||||
|
|
|
||||||
59
src/sym.rs
59
src/sym.rs
|
|
@ -110,7 +110,16 @@ impl SymTable {
|
||||||
let cond_args: &Seg;
|
let cond_args: &Seg;
|
||||||
let outer_scope_seg_holder: Seg;
|
let outer_scope_seg_holder: Seg;
|
||||||
if let ValueType::VarForm(ref val) = symbol.value {
|
if let ValueType::VarForm(ref val) = symbol.value {
|
||||||
return Ok(val.clone());
|
match **val {
|
||||||
|
Ctr::Lambda(ref l) if call_func => {
|
||||||
|
return call_lambda(
|
||||||
|
l,
|
||||||
|
&Box::new(Ctr::Seg(args.clone())),
|
||||||
|
self
|
||||||
|
)
|
||||||
|
},
|
||||||
|
_ => return Ok(val.clone()),
|
||||||
|
}
|
||||||
} else if call_func {
|
} else if call_func {
|
||||||
cond_args = args
|
cond_args = args
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -122,8 +131,12 @@ impl SymTable {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_function_type(&self, name: &String) -> Option<bool> {
|
pub fn is_function_type(&self, name: &String) -> Option<bool> {
|
||||||
if let ValueType::VarForm(_) = self.get(name)?.value {
|
if let ValueType::VarForm(ref val) = self.get(name)?.value {
|
||||||
Some(false)
|
if let Ctr::Lambda(_) = **val {
|
||||||
|
Some(true)
|
||||||
|
} else {
|
||||||
|
Some(false)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
Some(true)
|
Some(true)
|
||||||
}
|
}
|
||||||
|
|
@ -270,17 +283,36 @@ impl Symbol {
|
||||||
*/
|
*/
|
||||||
pub fn call(&self, args: &Seg, syms: &mut SymTable) -> Result<Box<Ctr>, String> {
|
pub fn call(&self, args: &Seg, syms: &mut SymTable) -> Result<Box<Ctr>, String> {
|
||||||
let evaluated_args: &Seg;
|
let evaluated_args: &Seg;
|
||||||
let outer_scope_seg_storage: Seg;
|
let mut outer_scope_seg_storage = Seg::new();
|
||||||
let outer_scope_eval: Box<Ctr>;
|
let mut errcon: String = String::new();
|
||||||
if !self.conditional_branches {
|
if !self.conditional_branches {
|
||||||
outer_scope_eval = eval(args, syms)?;
|
if !args.circuit(&mut |arg: &Ctr| -> bool {
|
||||||
match *outer_scope_eval {
|
if let Ctr::Seg(ref s) = arg {
|
||||||
Ctr::Seg(ref segment) => evaluated_args = segment,
|
let eval_res = eval(s, syms);
|
||||||
_ => {
|
if eval_res.is_err() {
|
||||||
outer_scope_seg_storage = Seg::from_mono(outer_scope_eval);
|
errcon = eval_res.err().unwrap();
|
||||||
evaluated_args = &outer_scope_seg_storage;
|
return false
|
||||||
|
}
|
||||||
|
outer_scope_seg_storage.append(eval_res.unwrap().clone());
|
||||||
|
} else if let Ctr::Symbol(ref s) = arg {
|
||||||
|
let eval_res = syms.call_symbol(
|
||||||
|
s,
|
||||||
|
&outer_scope_seg_storage,
|
||||||
|
false
|
||||||
|
);
|
||||||
|
if eval_res.is_err() {
|
||||||
|
errcon = eval_res.err().unwrap();
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
outer_scope_seg_storage.append(eval_res.unwrap().clone());
|
||||||
|
} else {
|
||||||
|
outer_scope_seg_storage.append(Box::new(arg.clone()));
|
||||||
}
|
}
|
||||||
|
true
|
||||||
|
}) {
|
||||||
|
return Err(format!("error evaluating args: {}", errcon))
|
||||||
}
|
}
|
||||||
|
evaluated_args = &outer_scope_seg_storage;
|
||||||
} else {
|
} else {
|
||||||
evaluated_args = args;
|
evaluated_args = args;
|
||||||
}
|
}
|
||||||
|
|
@ -294,7 +326,7 @@ impl Symbol {
|
||||||
// If this ever becomes ASYNC this will need to
|
// If this ever becomes ASYNC this will need to
|
||||||
// become a more traditional stack design, and the
|
// become a more traditional stack design, and the
|
||||||
// global table will need to be released
|
// global table will need to be released
|
||||||
let mut holding_table = SymTable::new();
|
let mut holding_table = SymTable::new();
|
||||||
|
|
||||||
// Prep var table for function execution
|
// Prep var table for function execution
|
||||||
for n in 0..f.arg_syms.len() {
|
for n in 0..f.arg_syms.len() {
|
||||||
|
|
@ -370,9 +402,6 @@ impl Symbol {
|
||||||
arg_syms: arg_syms.clone(),
|
arg_syms: arg_syms.clone(),
|
||||||
});
|
});
|
||||||
args = Args::Lazy(arg_syms.len() as u128);
|
args = Args::Lazy(arg_syms.len() as u128);
|
||||||
} else if let Ctr::Lambda(ref l) = *ast.car {
|
|
||||||
args = Args::Lazy(l.arg_syms.len() as u128);
|
|
||||||
value = ValueType::FuncForm(l.clone());
|
|
||||||
} else {
|
} else {
|
||||||
args = Args::None;
|
args = Args::None;
|
||||||
value = ValueType::VarForm(ast.car.clone());
|
value = ValueType::VarForm(ast.car.clone());
|
||||||
|
|
|
||||||
|
|
@ -252,7 +252,7 @@ mod func_tests {
|
||||||
syms.call_symbol(&"test_func_in".to_string(), &args, true)
|
syms.call_symbol(&"test_func_in".to_string(), &args, true)
|
||||||
.err()
|
.err()
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
"error in call to undefined-symbol: undefined symbol: undefined-symbol".to_string(),
|
"error evaluating args: undefined symbol: undefined-symbol".to_string(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -335,6 +335,26 @@ mod decl_lib_tests {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_lambda_arg_call() {
|
||||||
|
let document = "(let (())
|
||||||
|
(def appl '' (func item) (func item))
|
||||||
|
(def adder 'my adder' (lambda (x) (add x 1)))
|
||||||
|
(appl adder 2))";
|
||||||
|
let mut syms = SymTable::new();
|
||||||
|
static_stdlib(&mut syms).unwrap();
|
||||||
|
dynamic_stdlib(&mut syms).unwrap();
|
||||||
|
let it = *eval(
|
||||||
|
&lex(&document.to_string()).unwrap(),
|
||||||
|
&mut syms).unwrap();
|
||||||
|
if let Ctr::Integer(i) = it {
|
||||||
|
assert_eq!(i, 3)
|
||||||
|
} else {
|
||||||
|
println!("bad result: {}", it);
|
||||||
|
panic!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_setget_doc_string() {
|
fn test_setget_doc_string() {
|
||||||
let highly_inadvisable = "(set-doc (q help) 'help')";
|
let highly_inadvisable = "(set-doc (q help) 'help')";
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue