WIP lambda
* typesystem extended * docstrings and callback * tests defined, not implemented
This commit is contained in:
parent
7befdc869b
commit
b0bd369c1d
4 changed files with 88 additions and 24 deletions
51
Readme.org
51
Readme.org
|
|
@ -45,7 +45,7 @@ top-level -> element1 -> "element2" -> 3 -> [] -> [] ->
|
||||||
|
|
||||||
Each node in memory has type information and potentially a cooresponding entry in a global symbol table.
|
Each node in memory has type information and potentially a cooresponding entry in a global symbol table.
|
||||||
|
|
||||||
**** data types
|
**** Data types
|
||||||
Relish leverages the following data types:
|
Relish leverages the following data types:
|
||||||
- Strings: delimited by ~'~, ~"~, or ~`~
|
- Strings: delimited by ~'~, ~"~, or ~`~
|
||||||
- Integers: up to 128 bit signed integers
|
- Integers: up to 128 bit signed integers
|
||||||
|
|
@ -53,10 +53,10 @@ Relish leverages the following data types:
|
||||||
- Booleans: ~true~ or ~false~
|
- Booleans: ~true~ or ~false~
|
||||||
- Symbols: an un-delimited chunk of text containing alphanumerics, ~-~, ~_~, or ~?~
|
- Symbols: an un-delimited chunk of text containing alphanumerics, ~-~, ~_~, or ~?~
|
||||||
|
|
||||||
Symbols and Functions are untyped. there is no restriction on what can be set/passed to what.....
|
Symbols and Functions can contain data of any type. there is no restriction on what can be set/passed to what.....
|
||||||
However, internally Relish is statically typed, and many builtin functions will get very picky about what types are passed to them.
|
However, internally Relish is typed, and many builtin functions will get very picky about what types are passed to them.
|
||||||
|
|
||||||
**** calling a function
|
**** Calling a function
|
||||||
S-Expressions can represent function calls in addition to trees of data. A function call is a list of data starting with a symbol that is defined to be a function:
|
S-Expressions can represent function calls in addition to trees of data. A function call is a list of data starting with a symbol that is defined to be a function:
|
||||||
#+BEGIN_SRC lisp
|
#+BEGIN_SRC lisp
|
||||||
(dothing arg1 arg2 arg3)
|
(dothing arg1 arg2 arg3)
|
||||||
|
|
@ -70,7 +70,7 @@ Function calls are executed as soon as the tree is evaluated. See the following
|
||||||
In this example, ~(add 5 2)~ is evaluated first, its result is then passed to ~(add 3 ...)~. In infix form: ~3 + (5 + 2)~.
|
In this example, ~(add 5 2)~ is evaluated first, its result is then passed to ~(add 3 ...)~. In infix form: ~3 + (5 + 2)~.
|
||||||
|
|
||||||
*** Control flow
|
*** Control flow
|
||||||
**** if
|
**** If
|
||||||
An *if form* is the most basic form of conditional evaluation offered by Relish.
|
An *if form* is the most basic form of conditional evaluation offered by Relish.
|
||||||
It is a function that takes lazily evaluated arguments: a condition, a then clause, and an else clause.
|
It is a function that takes lazily evaluated arguments: a condition, a then clause, and an else clause.
|
||||||
If the condition evaluates to true, the then clause is evaluated and the result returned.
|
If the condition evaluates to true, the then clause is evaluated and the result returned.
|
||||||
|
|
@ -89,7 +89,7 @@ If the condition evaluates to neither true nor false (a non-boolean value) a typ
|
||||||
(turn-on-my-flag global-state))
|
(turn-on-my-flag global-state))
|
||||||
#+END_SRC
|
#+END_SRC
|
||||||
|
|
||||||
**** while
|
**** While
|
||||||
Another popular control flow structure is the *while loop*.
|
Another popular control flow structure is the *while loop*.
|
||||||
This is implemented as a condition followed by one or more bodies that are lazily evaluated only if the condition is true.
|
This is implemented as a condition followed by one or more bodies that are lazily evaluated only if the condition is true.
|
||||||
Like the *if form*, if the conditional returns a non-boolean value the *while loop* will return an error.
|
Like the *if form*, if the conditional returns a non-boolean value the *while loop* will return an error.
|
||||||
|
|
@ -101,7 +101,7 @@ Like the *if form*, if the conditional returns a non-boolean value the *while lo
|
||||||
(toggle-my-flag global-state)) ;; this is also evaluated
|
(toggle-my-flag global-state)) ;; this is also evaluated
|
||||||
#+END_SRC
|
#+END_SRC
|
||||||
|
|
||||||
**** let
|
**** Let
|
||||||
*Let* is one of the most powerful forms Relish offers. The first body in a call to let is a list of lists.
|
*Let* is one of the most powerful forms Relish offers. The first body in a call to let is a list of lists.
|
||||||
Specifically, a list of variable declarations that lookf like this: ~(name value)~.
|
Specifically, a list of variable declarations that lookf like this: ~(name value)~.
|
||||||
Each successive variable definition can build off of the last one, like this: ~((step1 "hello") (step2 (concat step1 " ")) (step3 (concat step2 "world")))~.
|
Each successive variable definition can build off of the last one, like this: ~((step1 "hello") (step2 (concat step1 " ")) (step3 (concat step2 "world")))~.
|
||||||
|
|
@ -122,8 +122,8 @@ Here you can see the usefulness of being able to declare multiple variables in q
|
||||||
Each variable is in scope for the duration of the let statement and then dropped when the statement has concluded.
|
Each variable is in scope for the duration of the let statement and then dropped when the statement has concluded.
|
||||||
Thus, it is little cost to break complex calculations down into reusable parts.
|
Thus, it is little cost to break complex calculations down into reusable parts.
|
||||||
|
|
||||||
**** TODO circuit
|
**** TODO Circuit
|
||||||
**** not quite control flow
|
**** Not quite control flow
|
||||||
Several other functions use lazy evaluation of their arguments. The below list is non-exhaustive:
|
Several other functions use lazy evaluation of their arguments. The below list is non-exhaustive:
|
||||||
- toggle
|
- toggle
|
||||||
- inc
|
- inc
|
||||||
|
|
@ -151,7 +151,8 @@ CURRENT VALUE AND/OR BODY:
|
||||||
<builtin>
|
<builtin>
|
||||||
#+END_SRC
|
#+END_SRC
|
||||||
|
|
||||||
*** TODO quote and eval
|
*** TODO Quote and Eval
|
||||||
|
*** TODO Lambda
|
||||||
*** TODO Defining variables and functions
|
*** TODO Defining variables and functions
|
||||||
**** TODO Anatomy
|
**** TODO Anatomy
|
||||||
**** TODO Naming conventions
|
**** TODO Naming conventions
|
||||||
|
|
@ -321,22 +322,22 @@ This contains any executable target of this project. Notably the main shell file
|
||||||
Note: this section will not show the status of each item unless you are viewing it with a proper orgmode viewer.
|
Note: this section will not show the status of each item unless you are viewing it with a proper orgmode viewer.
|
||||||
Note: this section only tracks the state of incomplete TODO items. Having everything on here would be cluttered.
|
Note: this section only tracks the state of incomplete TODO items. Having everything on here would be cluttered.
|
||||||
|
|
||||||
*** TODO list contains via circuit
|
*** TODO Lambda
|
||||||
*** TODO Lambda hack
|
IMPLEMENTATION:
|
||||||
BAD IDEAS
|
- [-] implement a new Ctr type to encapsulate function and args
|
||||||
- lambda function can use illegal name parameter for symbol names, guaranteeing non collision in symbol table
|
- [ ] need a new case in store for when bound to a var
|
||||||
- add some incrementing variable to avoid lambdas colliding with lambdas
|
(honestly store should be rewritten)
|
||||||
- custom drop implementation for Ctr that only overrides Symbol, checks for illegal name conventions, and yeets from symtable
|
- [ ] need a case in eval that mirrors the function call case
|
||||||
(how do we get the sym table into the drop impl)
|
|
||||||
- when repl loops, clear lambdas out of the symtable
|
|
||||||
(shit hack and will only work for very simple cases. highly not ideal)
|
|
||||||
GOOD IDEA
|
|
||||||
- could implement a new Ctr type to encapsulate function and args
|
|
||||||
- would need a new case in store for when bound to a var but honestly store should be rewritten
|
|
||||||
- would need a case in eval that mirrors the function call case
|
|
||||||
|
|
||||||
DOCUMENTATION:
|
DOCUMENTATION:
|
||||||
- let case for creating and applying a lambda
|
- [ ] let case for creating and applying a lambda
|
||||||
|
|
||||||
|
TEST:
|
||||||
|
- [ ] lambda to_string input equivalency
|
||||||
|
- [ ] (eq? ((lambda (x y) (add x y)) 1 2) (add 1 2))
|
||||||
|
- [ ] let case for creating and applying a lambda
|
||||||
|
|
||||||
|
*** TODO list contains via circuit
|
||||||
*** TODO Map function
|
*** TODO Map function
|
||||||
- DOCUMENTATION + TEST:
|
- DOCUMENTATION + TEST:
|
||||||
apply a lambda to a list
|
apply a lambda to a list
|
||||||
|
|
@ -352,6 +353,7 @@ Optionally return a list of new variables and/or functions?
|
||||||
Will need a concatenate function for tables
|
Will need a concatenate function for tables
|
||||||
*** TODO Main shell calls Load function on arg and exits
|
*** TODO Main shell calls Load function on arg and exits
|
||||||
*** TODO Ship a relish-based stdlib
|
*** TODO Ship a relish-based stdlib
|
||||||
|
*** TODO Map library written in relish ("slowmap")
|
||||||
*** TODO FINISH DOCUMENTATION
|
*** TODO FINISH DOCUMENTATION
|
||||||
*** TODO Shell module-
|
*** TODO Shell module-
|
||||||
**** TODO only loadable via POSIX config var
|
**** TODO only loadable via POSIX config var
|
||||||
|
|
@ -368,6 +370,7 @@ Overload Load function to call a binary too
|
||||||
*** TODO Create a dedicated community channel on matrix.sunnypup.io
|
*** TODO Create a dedicated community channel on matrix.sunnypup.io
|
||||||
*** TODO Post to relevant channels
|
*** TODO Post to relevant channels
|
||||||
*** TODO Custom ast pretty print
|
*** TODO Custom ast pretty print
|
||||||
|
*** TODO Implement Compose for lambdas
|
||||||
*** TODO file operations
|
*** TODO file operations
|
||||||
**** TODO read-to-string
|
**** TODO read-to-string
|
||||||
**** TODO write-to-file
|
**** TODO write-to-file
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@
|
||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
use crate::sym::UserFn;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
use std::ops::{Add, Div, Index, Mul, Sub};
|
use std::ops::{Add, Div, Index, Mul, Sub};
|
||||||
|
|
@ -27,6 +28,7 @@ pub enum Ctr {
|
||||||
Float(f64),
|
Float(f64),
|
||||||
Bool(bool),
|
Bool(bool),
|
||||||
Seg(Seg),
|
Seg(Seg),
|
||||||
|
Lambda(UserFn),
|
||||||
#[default]
|
#[default]
|
||||||
None,
|
None,
|
||||||
}
|
}
|
||||||
|
|
@ -40,6 +42,7 @@ pub enum Type {
|
||||||
Float,
|
Float,
|
||||||
Bool,
|
Bool,
|
||||||
Seg,
|
Seg,
|
||||||
|
Lambda,
|
||||||
None,
|
None,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -80,6 +83,7 @@ impl Ctr {
|
||||||
Ctr::Float(_s) => Type::Float,
|
Ctr::Float(_s) => Type::Float,
|
||||||
Ctr::Bool(_s) => Type::Bool,
|
Ctr::Bool(_s) => Type::Bool,
|
||||||
Ctr::Seg(_s) => Type::Seg,
|
Ctr::Seg(_s) => Type::Seg,
|
||||||
|
Ctr::Lambda(_s) => Type::Lambda,
|
||||||
Ctr::None => Type::None,
|
Ctr::None => Type::None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -240,6 +244,7 @@ impl Clone for Ctr {
|
||||||
Ctr::Float(s) => Ctr::Float(*s),
|
Ctr::Float(s) => Ctr::Float(*s),
|
||||||
Ctr::Bool(s) => Ctr::Bool(*s),
|
Ctr::Bool(s) => Ctr::Bool(*s),
|
||||||
Ctr::Seg(s) => Ctr::Seg(s.clone()),
|
Ctr::Seg(s) => Ctr::Seg(s.clone()),
|
||||||
|
Ctr::Lambda(s) => Ctr::Seg(s.clone()),
|
||||||
Ctr::None => Ctr::None,
|
Ctr::None => Ctr::None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -260,6 +265,7 @@ impl fmt::Display for Ctr {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ctr::Seg(s) => write!(f, "{}", s),
|
Ctr::Seg(s) => write!(f, "{}", s),
|
||||||
|
Ctr::Lambda(l) => write!(f, "{}", l),
|
||||||
Ctr::None => Ok(()),
|
Ctr::None => Ok(()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -398,6 +404,7 @@ impl fmt::Display for Type {
|
||||||
Type::Float => "float",
|
Type::Float => "float",
|
||||||
Type::Bool => "bool",
|
Type::Bool => "bool",
|
||||||
Type::Seg => "segment",
|
Type::Seg => "segment",
|
||||||
|
Type::Lambda => "lambda",
|
||||||
Type::None => "none",
|
Type::None => "none",
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -414,6 +421,7 @@ impl std::convert::From<String> for Type {
|
||||||
"float" => Type::Float,
|
"float" => Type::Float,
|
||||||
"bool" => Type::Bool,
|
"bool" => Type::Bool,
|
||||||
"segment" => Type::Seg,
|
"segment" => Type::Seg,
|
||||||
|
"lambda" => Type::Lambda,
|
||||||
_ => Type::None,
|
_ => Type::None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -244,3 +244,44 @@ pub fn env_callback(_ast: &Seg, syms: &mut SymTable) -> Result<Ctr, String> {
|
||||||
}
|
}
|
||||||
Ok(Ctr::None)
|
Ok(Ctr::None)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub const LAMBDA_DOCSTRING: &str = "Takes two arguments of any type.
|
||||||
|
No args are evaluated when lambda is called.
|
||||||
|
Lambda makes sure the first argument is a list of symbols (or 'arguments') to the lambda function.
|
||||||
|
The next arg is stored in a tree to evaluate on demand.
|
||||||
|
|
||||||
|
Example: (lambda (x y) (add x y))
|
||||||
|
This can then be evaluated like so:
|
||||||
|
((lambda (x y) (add x y)) 1 2)
|
||||||
|
which is functionally equivalent to:
|
||||||
|
(add 1 2)";
|
||||||
|
|
||||||
|
pub fn lambda_callback(
|
||||||
|
ast: &Seg,
|
||||||
|
_syms: &mut SymTable
|
||||||
|
) -> Result<Ctr, String> {
|
||||||
|
let mut args = vec![];
|
||||||
|
if let Ctr::Seg(ref arg_head) = *ast.car {
|
||||||
|
if !arg_head.circuit(&mut |arg: &Ctr| -> bool {
|
||||||
|
if let Ctr::Symbol(ref s) = *arg {
|
||||||
|
args.push(s.clone());
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}) {
|
||||||
|
Err("all elements of first argumnets must be symbols".to_string())
|
||||||
|
} else {
|
||||||
|
if let Ctr::Seg(ref eval_head) = *ast.cdr {
|
||||||
|
Ok(Ctr::Lambda(UserFn{
|
||||||
|
ast: Box::new(eval_head.clone()),
|
||||||
|
arg_syms: args,
|
||||||
|
}))
|
||||||
|
} else {
|
||||||
|
Err("not enough args".to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Err("first argument should be a list of symbols".to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
12
src/sym.rs
12
src/sym.rs
|
|
@ -227,6 +227,18 @@ impl fmt::Display for Args {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for UserFn {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
write!(f, "(lambda: (");
|
||||||
|
for i in self.arg_syms {
|
||||||
|
write!(f, "{} ", i);
|
||||||
|
}
|
||||||
|
write!(f, ") {})", self.ast);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl fmt::Display for ValueType {
|
impl fmt::Display for ValueType {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue