add mod and exp functions, tests, and also some snippets for common
patterns in the readme
This commit is contained in:
parent
7fd47c1812
commit
0c5d14ed8e
4 changed files with 323 additions and 13 deletions
76
Readme.org
76
Readme.org
|
|
@ -37,17 +37,67 @@ https://matrix.to/#/#vomitorium:matrix.sunnypup.io
|
||||||
**** TODO Anatomy
|
**** TODO Anatomy
|
||||||
**** TODO Naming conventions
|
**** TODO Naming conventions
|
||||||
**** TODO Undefining variables and functions
|
**** TODO Undefining variables and functions
|
||||||
*** TODO Easy patterns
|
** Easy patterns
|
||||||
**** TODO while-let combo
|
This section can serve as a sort of cookbook for a user who is new to leveraging LISP languages or unsure of where to start with ~relish~.
|
||||||
**** TODO main loop application
|
More ideas may be explored in the file:snippets directory of this project.
|
||||||
**** TODO let destructuring
|
The author encourages any users to contribute their own personal favorites not already in this section either by adding them to the file:snippets folder, or to extend the documentation here.
|
||||||
**** TODO if-set?
|
*** while-let combo
|
||||||
*** TODO Builtin functions
|
#+BEGIN_SRC lisp
|
||||||
*** TODO Documentation
|
;; myiter = (1 (2 3 4 5 6))
|
||||||
**** TODO Tests
|
(def myiter 'iterator over a list' (head (1 2 3 4 5 6)))
|
||||||
**** TODO Help function
|
|
||||||
**** TODO Env function
|
;; iterate over each element in mylist
|
||||||
**** TODO Snippets directory
|
(while (gt? (len (cdr myiter)) 0) ;; while there are more elements to consume
|
||||||
|
(let ((elem (car myiter)) ;; elem = consumed element from myiter
|
||||||
|
(remaining (cdr myiter))) ;; remaining = rest of elements
|
||||||
|
(echo elem) ;; do a thing with the element, could be any operation
|
||||||
|
(def myiter (head remaining)))) ;; consume next element, loop
|
||||||
|
#+END_SRC
|
||||||
|
|
||||||
|
The while-let pattern can be used for many purposes. Above it is used to iterate over elements in a list. It can also be used to receive connections to a socket and write data to them.
|
||||||
|
*** TODO main loop application
|
||||||
|
- state switch (while-toggle)
|
||||||
|
- state calculation
|
||||||
|
*** TODO short-circuit guard
|
||||||
|
- circuit example
|
||||||
|
- while-not-circuit-do-more-work
|
||||||
|
*** let destructuring
|
||||||
|
~let~ is very useful for destructuring complex return types. If you have a function that may return a whole list of values you can then call it from ~let~ to consume the result data.
|
||||||
|
In this example a let form is used to destructure a call to ~head~. ~head~ returns a list consisting of ~(first-element rest-of-list)~ (for more information see ~(help head)~).
|
||||||
|
The ~let~ form starts with the output of ~head~ stored in ~head-struct~ (short for head-structured). The next variables defined are ~first~ and ~rest~ which contain individual elements from the return of the call to ~head~.
|
||||||
|
Finally, the bodies evaluated in the ~let~ form are able to operate on the head and the rest.
|
||||||
|
#+BEGIN_SRC lisp
|
||||||
|
;; individually access the top of a list
|
||||||
|
(let ((head-struct (head (1 2 3))
|
||||||
|
(first (car head-struct))
|
||||||
|
(rest (cdr head-struct)))
|
||||||
|
(echo "this is 1: " first)
|
||||||
|
(echo "this is 2, 3: " rest))
|
||||||
|
#+END_SRC
|
||||||
|
*** if-set?
|
||||||
|
One common pattern seen in bash scripts and makefiles is the set-variable-if-not-set pattern.
|
||||||
|
#+BEGIN_SRC shell
|
||||||
|
MYVAR ?= MY_SPECIAL_VALUE
|
||||||
|
#+END_SRC
|
||||||
|
Translated, can be seen below
|
||||||
|
#+BEGIN_SRC lisp
|
||||||
|
(if (set? myvar)
|
||||||
|
() ;; no need to do anything... or add a call here
|
||||||
|
(def myvar "MY_SPECIAL_VALUE"))
|
||||||
|
#+END_SRC
|
||||||
|
|
||||||
|
Alternatively this combination can be used to process flags in a script or application:
|
||||||
|
#+BEGIN_SRC lisp
|
||||||
|
(if (set? myflag)
|
||||||
|
(process-flag myflag)
|
||||||
|
())
|
||||||
|
#+END_SRC
|
||||||
|
** TODO Builtin functions
|
||||||
|
*** TODO Env function
|
||||||
|
** TODO Documentation
|
||||||
|
*** TODO Tests
|
||||||
|
*** TODO Help function
|
||||||
|
*** TODO Snippets directory
|
||||||
|
|
||||||
** Configuration
|
** Configuration
|
||||||
By default Relish will read from ~/.relishrc for configuration, but the default shell will also accept a filename from the RELISH_CFG_FILE environment variable.
|
By default Relish will read from ~/.relishrc for configuration, but the default shell will also accept a filename from the RELISH_CFG_FILE environment variable.
|
||||||
|
|
@ -158,8 +208,8 @@ Will need a concatenate function for tables
|
||||||
**** DONE sub
|
**** DONE sub
|
||||||
**** DONE div
|
**** DONE div
|
||||||
**** DONE mul
|
**** DONE mul
|
||||||
**** TODO exp
|
**** DONE exp
|
||||||
**** TODO mod
|
**** DONE mod
|
||||||
***** TODO Test for Let Destructuring
|
***** TODO Test for Let Destructuring
|
||||||
**** TODO inc
|
**** TODO inc
|
||||||
**** TODO dec
|
**** TODO dec
|
||||||
|
|
|
||||||
22
src/stl.rs
22
src/stl.rs
|
|
@ -283,6 +283,28 @@ pub fn static_stdlib(syms: &mut SymTable) -> Result<(), String> {
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
syms.insert(
|
||||||
|
"exp".to_string(),
|
||||||
|
Symbol {
|
||||||
|
name: String::from("exp"),
|
||||||
|
args: Args::Lazy(2),
|
||||||
|
conditional_branches: false,
|
||||||
|
docs: math::EXP_DOCSTRING.to_string(),
|
||||||
|
value: ValueType::Internal(Rc::new(math::exp_callback)),
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
syms.insert(
|
||||||
|
"mod".to_string(),
|
||||||
|
Symbol {
|
||||||
|
name: String::from("mod"),
|
||||||
|
args: Args::Lazy(2),
|
||||||
|
conditional_branches: false,
|
||||||
|
docs: math::MOD_DOCSTRING.to_string(),
|
||||||
|
value: ValueType::Internal(Rc::new(math::mod_callback)),
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -167,3 +167,99 @@ pub fn floatcast_callback(ast: &Seg, _: &mut SymTable) -> Result<Ctr, String> {
|
||||||
Err("float cast only takes an integer or a string".to_string())
|
Err("float cast only takes an integer or a string".to_string())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub const EXP_DOCSTRING: &str = "Takes two args, both expected to be numeric.
|
||||||
|
Returns the first arg to the power of the second arg.
|
||||||
|
Does not handle overflow or underflow.
|
||||||
|
|
||||||
|
PANIC CASES:
|
||||||
|
- arg1 is a float and arg2 is greater than an int32
|
||||||
|
- an integer exceeding the size of a float64 is raised to a float power
|
||||||
|
- an integer is rased to the power of another integer exceeding the max size of an unsigned 32bit integer";
|
||||||
|
|
||||||
|
pub fn exp_callback(ast: &Seg, _: &mut SymTable) -> Result<Ctr, String> {
|
||||||
|
let first = *ast.car.clone();
|
||||||
|
if !isnumeric(&first) {
|
||||||
|
return Err("first argument must be numeric".to_string());
|
||||||
|
}
|
||||||
|
let second: Ctr;
|
||||||
|
if let Ctr::Seg(ref s) = *ast.cdr {
|
||||||
|
second = *s.car.clone();
|
||||||
|
} else {
|
||||||
|
return Err("impossible error: needs two arguments".to_string());
|
||||||
|
}
|
||||||
|
if !isnumeric(&second) {
|
||||||
|
return Err("second argument must be numeric".to_string());
|
||||||
|
}
|
||||||
|
|
||||||
|
match first {
|
||||||
|
Ctr::Float(lf) => match second {
|
||||||
|
Ctr::Float(rf) => Ok(Ctr::Float(f64::powf(lf, rf))),
|
||||||
|
Ctr::Integer(ri) => Ok(Ctr::Float(f64::powi(lf, ri as i32))),
|
||||||
|
_ => Err("exp not implemented for these arguments".to_string()),
|
||||||
|
},
|
||||||
|
Ctr::Integer(li) => match second {
|
||||||
|
Ctr::Float(rf) => Ok(Ctr::Float(f64::powf(li as f64, rf))),
|
||||||
|
Ctr::Integer(ri) => Ok(Ctr::Integer(li.pow(ri as u32))),
|
||||||
|
_ => Err("exp not implemented for these arguments".to_string()),
|
||||||
|
},
|
||||||
|
|
||||||
|
_ => Err("exp not implemented for these arguments".to_string()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const MOD_DOCSTRING: &str = "Takes two args, both expected to be numeric.
|
||||||
|
Returns a list of two values: the modulus and the remainder.
|
||||||
|
Example: (mod 5 3) -> (1 2)
|
||||||
|
|
||||||
|
PANIC CASES:
|
||||||
|
- A float is modulo an integer larger than a max f64
|
||||||
|
- An integer larger than a max f64 is modulo a float
|
||||||
|
";
|
||||||
|
|
||||||
|
pub fn mod_callback(ast: &Seg, _: &mut SymTable) -> Result<Ctr, String> {
|
||||||
|
let first = *ast.car.clone();
|
||||||
|
if !isnumeric(&first) {
|
||||||
|
return Err("first argument must be numeric".to_string());
|
||||||
|
}
|
||||||
|
let second: Ctr;
|
||||||
|
if let Ctr::Seg(ref s) = *ast.cdr {
|
||||||
|
second = *s.car.clone();
|
||||||
|
} else {
|
||||||
|
return Err("impossible error: needs two arguments".to_string());
|
||||||
|
}
|
||||||
|
if !isnumeric(&second) {
|
||||||
|
return Err("second argument must be numeric".to_string());
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut ret = Seg::new();
|
||||||
|
|
||||||
|
match first {
|
||||||
|
Ctr::Float(lf) => match second {
|
||||||
|
Ctr::Float(rf) => {
|
||||||
|
ret.append(Box::new(Ctr::Integer((lf / rf) as i128)));
|
||||||
|
ret.append(Box::new(Ctr::Integer((lf % rf) as i128)));
|
||||||
|
},
|
||||||
|
Ctr::Integer(ri) => {
|
||||||
|
ret.append(Box::new(Ctr::Integer((lf / ri as f64) as i128)));
|
||||||
|
ret.append(Box::new(Ctr::Integer((lf % ri as f64) as i128)));
|
||||||
|
},
|
||||||
|
_ => return Err("mod not implemented for these arguments".to_string()),
|
||||||
|
},
|
||||||
|
Ctr::Integer(li) => match second {
|
||||||
|
Ctr::Float(rf) => {
|
||||||
|
ret.append(Box::new(Ctr::Integer((li as f64 / rf) as i128)));
|
||||||
|
ret.append(Box::new(Ctr::Integer((li as f64 % rf) as i128))); },
|
||||||
|
Ctr::Integer(ri) => {
|
||||||
|
ret.append(Box::new(Ctr::Integer(li / ri)));
|
||||||
|
ret.append(Box::new(Ctr::Integer(li % ri)));
|
||||||
|
},
|
||||||
|
_ => return Err("mod not implemented for these arguments".to_string()),
|
||||||
|
},
|
||||||
|
|
||||||
|
_ => return Err("mod not implemented for these arguments".to_string()),
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Ok(Ctr::Seg(ret))
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -145,4 +145,146 @@ mod math_lib_tests {
|
||||||
result.to_string(),
|
result.to_string(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_ii_exp() {
|
||||||
|
let document = "(exp 7 20)";
|
||||||
|
let result = 7i128.pow(20);
|
||||||
|
|
||||||
|
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_if_exp() {
|
||||||
|
let document = "(exp 1 10.2)";
|
||||||
|
let result = f64::powf(1f64, 10.2);
|
||||||
|
|
||||||
|
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_fi_exp() {
|
||||||
|
let document = "(exp 1.2 5)";
|
||||||
|
let result = f64::powf(1.2, 5f64);
|
||||||
|
|
||||||
|
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(),
|
||||||
|
format!("{:.5}", result),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_ff_exp() {
|
||||||
|
let document = "(exp 1.3 1.5)";
|
||||||
|
let result = f64::powf(1.3, 1.5);
|
||||||
|
|
||||||
|
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_ii_mod() {
|
||||||
|
let document = "(def test '' (mod 7 3))";
|
||||||
|
let check1 = "(car test)";
|
||||||
|
let result1 = "2";
|
||||||
|
let check2 = "(cdr test)";
|
||||||
|
let result2 = "1";
|
||||||
|
|
||||||
|
let mut syms = SymTable::new();
|
||||||
|
static_stdlib(&mut syms).unwrap();
|
||||||
|
dynamic_stdlib(&mut syms).unwrap();
|
||||||
|
let _ = *eval(&lex(&document.to_string()).unwrap(), &mut syms).unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
*eval(&lex(&check1.to_string()).unwrap(), &mut syms)
|
||||||
|
.unwrap()
|
||||||
|
.to_string(),
|
||||||
|
result1.to_string(),
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
*eval(&lex(&check2.to_string()).unwrap(), &mut syms)
|
||||||
|
.unwrap()
|
||||||
|
.to_string(),
|
||||||
|
result2.to_string(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_if_mod() {
|
||||||
|
let document = "(def test '' (mod 7 3.3))";
|
||||||
|
let check1 = "(car test)";
|
||||||
|
let result1 = "2";
|
||||||
|
|
||||||
|
let mut syms = SymTable::new();
|
||||||
|
static_stdlib(&mut syms).unwrap();
|
||||||
|
dynamic_stdlib(&mut syms).unwrap();
|
||||||
|
let _ = *eval(&lex(&document.to_string()).unwrap(), &mut syms).unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
*eval(&lex(&check1.to_string()).unwrap(), &mut syms)
|
||||||
|
.unwrap()
|
||||||
|
.to_string(),
|
||||||
|
result1.to_string(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_fi_mod() {
|
||||||
|
let document = "(def test '' (mod 7.2 2))";
|
||||||
|
let check1 = "(car test)";
|
||||||
|
let result1 = "3"; let mut syms = SymTable::new();
|
||||||
|
static_stdlib(&mut syms).unwrap();
|
||||||
|
dynamic_stdlib(&mut syms).unwrap();
|
||||||
|
let _ = *eval(&lex(&document.to_string()).unwrap(), &mut syms).unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
*eval(&lex(&check1.to_string()).unwrap(), &mut syms)
|
||||||
|
.unwrap()
|
||||||
|
.to_string(),
|
||||||
|
result1.to_string(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_ff_mod() {
|
||||||
|
let document = "(def test '' (mod 7.2 3.3))";
|
||||||
|
let check1 = "(car test)";
|
||||||
|
let result1 = "2";
|
||||||
|
|
||||||
|
let mut syms = SymTable::new();
|
||||||
|
static_stdlib(&mut syms).unwrap();
|
||||||
|
dynamic_stdlib(&mut syms).unwrap();
|
||||||
|
let _ = *eval(&lex(&document.to_string()).unwrap(), &mut syms).unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
*eval(&lex(&check1.to_string()).unwrap(), &mut syms)
|
||||||
|
.unwrap()
|
||||||
|
.to_string(),
|
||||||
|
result1.to_string(),
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue