Fully fledged lambdas, along with efficiency tweaks across the ast
This commit is contained in:
parent
b0bd369c1d
commit
8efa1dbaad
10 changed files with 264 additions and 70 deletions
44
Readme.org
44
Readme.org
|
|
@ -152,7 +152,34 @@ CURRENT VALUE AND/OR BODY:
|
||||||
#+END_SRC
|
#+END_SRC
|
||||||
|
|
||||||
*** TODO Quote and Eval
|
*** TODO Quote and Eval
|
||||||
*** TODO Lambda
|
*** Lambda
|
||||||
|
Another form of homoiconicity is the *anonymous function*.
|
||||||
|
This is a nameless function being passed around as data.
|
||||||
|
It can be bound to a variable, or called directly.
|
||||||
|
An *anonymous function* is created with the ~lambda~ function.
|
||||||
|
|
||||||
|
Here is an example of a lambda function:
|
||||||
|
#+BEGIN_SRC lisp
|
||||||
|
(lambda (x y) (add x y))
|
||||||
|
;; | ^ this is the function body
|
||||||
|
;; +-> this is the argument list
|
||||||
|
#+END_SRC
|
||||||
|
|
||||||
|
The result of the lambda call is returned as a piece of data.
|
||||||
|
It can later be called inline or bound to a variable.
|
||||||
|
|
||||||
|
Here is an example of an inline lambda call:
|
||||||
|
#+BEGIN_SRC lisp
|
||||||
|
((lambda (x y) (add x y)) 1 2)
|
||||||
|
#+END_SRC
|
||||||
|
This call returns ~3~.
|
||||||
|
|
||||||
|
Here is the lambda bound to a variable inside a let statement:
|
||||||
|
#+BEGIN_SRC lisp
|
||||||
|
(let ((adder (lambda (x y) (add x y)))) ;; let form contains one local var
|
||||||
|
(adder 1 2)) ;; local var (lambda) called here
|
||||||
|
#+END_SRC
|
||||||
|
|
||||||
*** TODO Defining variables and functions
|
*** TODO Defining variables and functions
|
||||||
**** TODO Anatomy
|
**** TODO Anatomy
|
||||||
**** TODO Naming conventions
|
**** TODO Naming conventions
|
||||||
|
|
@ -322,21 +349,6 @@ 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 Lambda
|
|
||||||
IMPLEMENTATION:
|
|
||||||
- [-] implement a new Ctr type to encapsulate function and args
|
|
||||||
- [ ] need a new case in store for when bound to a var
|
|
||||||
(honestly store should be rewritten)
|
|
||||||
- [ ] need a case in eval that mirrors the function call case
|
|
||||||
|
|
||||||
DOCUMENTATION:
|
|
||||||
- [ ] 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 list contains via circuit
|
||||||
*** TODO Map function
|
*** TODO Map function
|
||||||
- DOCUMENTATION + TEST:
|
- DOCUMENTATION + TEST:
|
||||||
|
|
|
||||||
15
src/eval.rs
15
src/eval.rs
|
|
@ -16,13 +16,14 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
use crate::segment::{Ctr, Seg};
|
use crate::segment::{Ctr, Seg};
|
||||||
use crate::sym::SymTable;
|
use crate::sym::{SymTable, call_lambda};
|
||||||
|
|
||||||
/* iterates over a syntax tree
|
/* iterates over a syntax tree
|
||||||
* returns a NEW LIST of values
|
* returns a NEW LIST of values
|
||||||
* representing the simplest possible form of the input
|
* representing the simplest possible form of the input
|
||||||
*/
|
*/
|
||||||
pub fn eval(ast: &Seg, syms: &mut SymTable) -> Result<Box<Ctr>, String> {
|
pub fn eval(ast: &Seg, syms: &mut SymTable) -> Result<Box<Ctr>, String> {
|
||||||
|
println!("E: {}", ast);
|
||||||
// data to return
|
// data to return
|
||||||
let mut ret = Box::from(Ctr::None);
|
let mut ret = Box::from(Ctr::None);
|
||||||
let mut first = true;
|
let mut first = true;
|
||||||
|
|
@ -39,7 +40,17 @@ pub fn eval(ast: &Seg, syms: &mut SymTable) -> Result<Box<Ctr>, String> {
|
||||||
let mut none = false;
|
let mut none = false;
|
||||||
while !none {
|
while !none {
|
||||||
match &**arg_car {
|
match &**arg_car {
|
||||||
Ctr::Seg(ref inner) => car = eval(inner, syms)?,
|
Ctr::Seg(ref inner) => {
|
||||||
|
let interm = eval(inner, syms)?;
|
||||||
|
if let Ctr::Lambda(ref l) = *interm {
|
||||||
|
match call_lambda(l, arg_cdr, syms) {
|
||||||
|
Ok(s) => return Ok(s.clone()),
|
||||||
|
Err(s) => return Err(format!("err in call to lambda: {}", s)),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
car = interm;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
Ctr::Symbol(ref tok) => {
|
Ctr::Symbol(ref tok) => {
|
||||||
let outer_scope_seg_holder: Seg;
|
let outer_scope_seg_holder: Seg;
|
||||||
|
|
|
||||||
|
|
@ -244,7 +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::Lambda(s) => Ctr::Lambda(s.clone()),
|
||||||
Ctr::None => Ctr::None,
|
Ctr::None => Ctr::None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
11
src/stl.rs
11
src/stl.rs
|
|
@ -492,6 +492,17 @@ pub fn static_stdlib(syms: &mut SymTable) -> Result<(), String> {
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
syms.insert(
|
||||||
|
"lambda".to_string(),
|
||||||
|
Symbol {
|
||||||
|
name: String::from("lambda"),
|
||||||
|
args: Args::Lazy(2),
|
||||||
|
conditional_branches: true,
|
||||||
|
docs: decl::LAMBDA_DOCSTRING.to_string(),
|
||||||
|
value: ValueType::Internal(Rc::new(decl::lambda_callback)),
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -127,7 +127,6 @@ Integers and Floats will cast to true if they are 0 and false otherwise.";
|
||||||
pub fn boolcast_callback(ast: &Seg, _syms: &mut SymTable) -> Result<Ctr, String> {
|
pub fn boolcast_callback(ast: &Seg, _syms: &mut SymTable) -> Result<Ctr, String> {
|
||||||
match &*ast.car {
|
match &*ast.car {
|
||||||
Ctr::Bool(_) => Ok(*ast.car.clone()),
|
Ctr::Bool(_) => Ok(*ast.car.clone()),
|
||||||
Ctr::Symbol(_) => Err("not clear how to cast a symbol to a bool".to_string()),
|
|
||||||
Ctr::String(s) => {
|
Ctr::String(s) => {
|
||||||
if s == "true" {
|
if s == "true" {
|
||||||
Ok(Ctr::Bool(true))
|
Ok(Ctr::Bool(true))
|
||||||
|
|
@ -139,7 +138,7 @@ pub fn boolcast_callback(ast: &Seg, _syms: &mut SymTable) -> Result<Ctr, String>
|
||||||
},
|
},
|
||||||
Ctr::Integer(i) => Ok(Ctr::Bool(*i == 0)),
|
Ctr::Integer(i) => Ok(Ctr::Bool(*i == 0)),
|
||||||
Ctr::Float(f) => Ok(Ctr::Bool(*f == 0.0)),
|
Ctr::Float(f) => Ok(Ctr::Bool(*f == 0.0)),
|
||||||
Ctr::Seg(_) => Err("cannot convert list to a boolean".to_string()),
|
_ => Err(format!("cannot convert a {} to a boolean",
|
||||||
Ctr::None => Err("Impossible task: cannot convert none to bool".to_string()),
|
ast.car.to_type())),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@
|
||||||
|
|
||||||
use crate::eval::eval;
|
use crate::eval::eval;
|
||||||
use crate::segment::{Ctr, Seg};
|
use crate::segment::{Ctr, Seg};
|
||||||
use crate::sym::{Args, SymTable, Symbol, ValueType};
|
use crate::sym::{SymTable, Symbol};
|
||||||
|
|
||||||
pub const IF_DOCSTRING: &str =
|
pub const IF_DOCSTRING: &str =
|
||||||
"accepts three bodies, a condition, an unevaluated consequence, and an alternative consequence.
|
"accepts three bodies, a condition, an unevaluated consequence, and an alternative consequence.
|
||||||
|
|
@ -151,15 +151,15 @@ pub fn let_callback(ast: &Seg, syms: &mut SymTable) -> Result<Ctr, String> {
|
||||||
|
|
||||||
localsyms.insert(
|
localsyms.insert(
|
||||||
name.clone(),
|
name.clone(),
|
||||||
Symbol {
|
Symbol::from_ast(
|
||||||
name: name.clone(),
|
name, &"variable used in let form".to_string(),
|
||||||
args: Args::None,
|
&Seg::from_mono(Box::new(*var_val_res.unwrap().clone())),
|
||||||
conditional_branches: false,
|
None),
|
||||||
docs: "variable used in let form".to_string(),
|
|
||||||
value: ValueType::VarForm(Box::new(*var_val_res.unwrap().clone())),
|
|
||||||
},
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
} else if let Ctr::None = *var_form.car {
|
||||||
|
// nothing to declare
|
||||||
|
return true;
|
||||||
} else {
|
} else {
|
||||||
eprintln!("improper declaration of {}: not a list", var_decl);
|
eprintln!("improper declaration of {}: not a list", var_decl);
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -176,6 +176,7 @@ pub fn let_callback(ast: &Seg, syms: &mut SymTable) -> Result<Ctr, String> {
|
||||||
let mut result: Box<Ctr> = Box::new(Ctr::None);
|
let mut result: Box<Ctr> = Box::new(Ctr::None);
|
||||||
if !eval_forms.circuit(&mut |eval_form: &Ctr| -> bool {
|
if !eval_forms.circuit(&mut |eval_form: &Ctr| -> bool {
|
||||||
let res: Result<Box<Ctr>, String>;
|
let res: Result<Box<Ctr>, String>;
|
||||||
|
println!("let: {}", eval_form);
|
||||||
if let Ctr::Seg(ref eval_tree) = eval_form {
|
if let Ctr::Seg(ref eval_tree) = eval_form {
|
||||||
res = eval(&eval_tree, &mut localsyms);
|
res = eval(&eval_tree, &mut localsyms);
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@
|
||||||
|
|
||||||
use crate::eval::eval;
|
use crate::eval::eval;
|
||||||
use crate::segment::{Ctr, Seg};
|
use crate::segment::{Ctr, Seg};
|
||||||
use crate::sym::{Args, SymTable, Symbol, UserFn, ValueType};
|
use crate::sym::{SymTable, Symbol, UserFn, ValueType};
|
||||||
use std::env;
|
use std::env;
|
||||||
|
|
||||||
pub const QUOTE_DOCSTRING: &str = "takes a single unevaluated tree and returns it as it is: unevaluated.";
|
pub const QUOTE_DOCSTRING: &str = "takes a single unevaluated tree and returns it as it is: unevaluated.";
|
||||||
|
|
@ -101,6 +101,7 @@ pub const STORE_DOCSTRING: &str = "allows user to define functions and variables
|
||||||
(def useless-var)";
|
(def useless-var)";
|
||||||
|
|
||||||
pub fn store_callback(ast: &Seg, syms: &mut SymTable, env_cfg: bool) -> Result<Ctr, String> {
|
pub fn store_callback(ast: &Seg, syms: &mut SymTable, env_cfg: bool) -> Result<Ctr, String> {
|
||||||
|
println!("def: {}", ast);
|
||||||
let is_var = ast.len() == 3;
|
let is_var = ast.len() == 3;
|
||||||
if let Ctr::Symbol(ref identifier) = *ast.car {
|
if let Ctr::Symbol(ref identifier) = *ast.car {
|
||||||
match &*ast.cdr {
|
match &*ast.cdr {
|
||||||
|
|
@ -109,27 +110,40 @@ pub fn store_callback(ast: &Seg, syms: &mut SymTable, env_cfg: bool) -> Result<C
|
||||||
if let Ctr::String(ref doc) = *doc_tree.car {
|
if let Ctr::String(ref doc) = *doc_tree.car {
|
||||||
match &*doc_tree.cdr {
|
match &*doc_tree.cdr {
|
||||||
// define a variable
|
// define a variable
|
||||||
Ctr::Seg(data_tree) if is_var => match eval(&Box::new(data_tree), syms) {
|
Ctr::Seg(data_tree) if is_var => {
|
||||||
Ok(seg) => {
|
let eval_arg: &Seg;
|
||||||
if let Ctr::Seg(ref val) = *seg {
|
let outer_maybe_eval_seg: Seg;
|
||||||
|
let mut expand = false;
|
||||||
|
if let Ctr::Seg(ref eval_me) = *data_tree.car {
|
||||||
|
eval_arg = eval_me;
|
||||||
|
} else {
|
||||||
|
outer_maybe_eval_seg = Seg::from_mono(data_tree.car.clone());
|
||||||
|
eval_arg = &outer_maybe_eval_seg;
|
||||||
|
expand = true;
|
||||||
|
}
|
||||||
|
match eval(eval_arg, syms) {
|
||||||
|
Ok(ctr) => {
|
||||||
|
let mut body = ctr;
|
||||||
|
if expand {
|
||||||
|
if let Ctr::Seg(ref s) = *body {
|
||||||
|
body = s.car.clone();
|
||||||
|
} else {
|
||||||
|
return Err("impossible expansion".to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
syms.insert(
|
syms.insert(
|
||||||
identifier.clone(),
|
identifier.clone(),
|
||||||
Symbol {
|
Symbol::from_ast(
|
||||||
value: ValueType::VarForm(val.car.clone()),
|
identifier, doc,
|
||||||
name: identifier.clone(),
|
&Seg::from_mono(body.clone()), None
|
||||||
args: Args::None,
|
),
|
||||||
docs: doc.to_owned(),
|
|
||||||
conditional_branches: false,
|
|
||||||
},
|
|
||||||
);
|
);
|
||||||
if env_cfg {
|
if env_cfg {
|
||||||
env::set_var(identifier.clone(), val.car.to_string());
|
env::set_var(identifier.clone(), body.to_string());
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
return Err("impossible args to export".to_string());
|
|
||||||
}
|
}
|
||||||
|
Err(e) => return Err(format!("couldnt eval symbol: {}", e)),
|
||||||
}
|
}
|
||||||
Err(e) => return Err(format!("couldnt eval symbol: {}", e)),
|
|
||||||
},
|
},
|
||||||
|
|
||||||
// define a function
|
// define a function
|
||||||
|
|
@ -157,16 +171,10 @@ pub fn store_callback(ast: &Seg, syms: &mut SymTable, env_cfg: bool) -> Result<C
|
||||||
if let Ctr::Seg(ref bodies) = *data_tree.cdr {
|
if let Ctr::Seg(ref bodies) = *data_tree.cdr {
|
||||||
syms.insert(
|
syms.insert(
|
||||||
identifier.clone(),
|
identifier.clone(),
|
||||||
Symbol {
|
Symbol::from_ast(
|
||||||
value: ValueType::FuncForm(UserFn {
|
identifier, doc, bodies,
|
||||||
ast: Box::new(bodies.clone()),
|
Some(arg_list),
|
||||||
arg_syms: arg_list.clone(),
|
),
|
||||||
}),
|
|
||||||
name: identifier.clone(),
|
|
||||||
args: Args::Lazy(arg_list.len() as u128),
|
|
||||||
docs: doc.to_owned(),
|
|
||||||
conditional_branches: false,
|
|
||||||
},
|
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
return Err(
|
return Err(
|
||||||
|
|
@ -266,6 +274,9 @@ pub fn lambda_callback(
|
||||||
if let Ctr::Symbol(ref s) = *arg {
|
if let Ctr::Symbol(ref s) = *arg {
|
||||||
args.push(s.clone());
|
args.push(s.clone());
|
||||||
true
|
true
|
||||||
|
} else if let Ctr::None = *arg {
|
||||||
|
// no args case
|
||||||
|
true
|
||||||
} else {
|
} else {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
@ -273,10 +284,14 @@ pub fn lambda_callback(
|
||||||
Err("all elements of first argumnets must be symbols".to_string())
|
Err("all elements of first argumnets must be symbols".to_string())
|
||||||
} else {
|
} else {
|
||||||
if let Ctr::Seg(ref eval_head) = *ast.cdr {
|
if let Ctr::Seg(ref eval_head) = *ast.cdr {
|
||||||
Ok(Ctr::Lambda(UserFn{
|
if let Ctr::Seg(_) = *eval_head.car {
|
||||||
ast: Box::new(eval_head.clone()),
|
Ok(Ctr::Lambda(UserFn{
|
||||||
arg_syms: args,
|
ast: Box::new(eval_head.clone()),
|
||||||
}))
|
arg_syms: args,
|
||||||
|
}))
|
||||||
|
} else {
|
||||||
|
Err("function body must be in list form".to_string())
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
Err("not enough args".to_string())
|
Err("not enough args".to_string())
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -46,6 +46,7 @@ pub fn concat_callback(ast: &Seg, _syms: &mut SymTable) -> Result<Ctr, String> {
|
||||||
Ctr::Float(f) => string.push_str(&f.to_string()),
|
Ctr::Float(f) => string.push_str(&f.to_string()),
|
||||||
Ctr::Bool(b) => string.push_str(&b.to_string()),
|
Ctr::Bool(b) => string.push_str(&b.to_string()),
|
||||||
Ctr::Seg(c) => string.push_str(&c.to_string()),
|
Ctr::Seg(c) => string.push_str(&c.to_string()),
|
||||||
|
Ctr::Lambda(l) => string.push_str(&l.to_string()),
|
||||||
Ctr::None => (),
|
Ctr::None => (),
|
||||||
}
|
}
|
||||||
true
|
true
|
||||||
|
|
@ -67,6 +68,7 @@ pub fn strlen_callback(ast: &Seg, _syms: &mut SymTable) -> Result<Ctr, String> {
|
||||||
Ctr::Float(f) => Ok(Ctr::Integer(f.to_string().len() as i128)),
|
Ctr::Float(f) => Ok(Ctr::Integer(f.to_string().len() as i128)),
|
||||||
Ctr::Bool(b) => Ok(Ctr::Integer(b.to_string().len() as i128)),
|
Ctr::Bool(b) => Ok(Ctr::Integer(b.to_string().len() as i128)),
|
||||||
Ctr::Seg(c) => Ok(Ctr::Integer(c.to_string().len() as i128)),
|
Ctr::Seg(c) => Ok(Ctr::Integer(c.to_string().len() as i128)),
|
||||||
|
Ctr::Lambda(l) => Ok(Ctr::Integer(l.to_string().len() as i128)),
|
||||||
// highly suspicious case below
|
// highly suspicious case below
|
||||||
Ctr::None => Ok(Ctr::Integer(0)),
|
Ctr::None => Ok(Ctr::Integer(0)),
|
||||||
}
|
}
|
||||||
|
|
@ -83,6 +85,7 @@ pub fn strcast_callback(ast: &Seg, _syms: &mut SymTable) -> Result<Ctr, String>
|
||||||
Ctr::Float(f) => Ok(Ctr::String(f.to_string())),
|
Ctr::Float(f) => Ok(Ctr::String(f.to_string())),
|
||||||
Ctr::Bool(b) => Ok(Ctr::String(b.to_string())),
|
Ctr::Bool(b) => Ok(Ctr::String(b.to_string())),
|
||||||
Ctr::Seg(c) => Ok(Ctr::String(c.to_string())),
|
Ctr::Seg(c) => Ok(Ctr::String(c.to_string())),
|
||||||
|
Ctr::Lambda(l) => Ok(Ctr::String(l.to_string())),
|
||||||
// highly suspicious case below
|
// highly suspicious case below
|
||||||
Ctr::None => Ok(Ctr::String(String::new())),
|
Ctr::None => Ok(Ctr::String(String::new())),
|
||||||
}
|
}
|
||||||
|
|
|
||||||
80
src/sym.rs
80
src/sym.rs
|
|
@ -229,11 +229,15 @@ impl fmt::Display for Args {
|
||||||
|
|
||||||
impl fmt::Display for UserFn {
|
impl fmt::Display for UserFn {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
write!(f, "(lambda: (");
|
write!(f, "(lambda (")?;
|
||||||
for i in self.arg_syms {
|
let mut arg_iter = (&self.arg_syms).into_iter();
|
||||||
write!(f, "{} ", i);
|
if let Some(elem) = arg_iter.next() {
|
||||||
|
write!(f, "{}", elem)?;
|
||||||
|
for i in arg_iter {
|
||||||
|
write!(f, " {}", i)?;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
write!(f, ") {})", self.ast);
|
write!(f, ") {})", self.ast.car)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
@ -265,12 +269,7 @@ impl Symbol {
|
||||||
let outer_scope_seg_storage: Seg;
|
let outer_scope_seg_storage: Seg;
|
||||||
let outer_scope_eval: Box<Ctr>;
|
let outer_scope_eval: Box<Ctr>;
|
||||||
if !self.conditional_branches {
|
if !self.conditional_branches {
|
||||||
let outer_scope_eval_result = eval(args, syms);
|
outer_scope_eval = eval(args, syms)?;
|
||||||
// dont listen to clippy. using ? will move the value.
|
|
||||||
if let Err(s) = outer_scope_eval_result {
|
|
||||||
return Err(s);
|
|
||||||
}
|
|
||||||
outer_scope_eval = outer_scope_eval_result.unwrap();
|
|
||||||
match *outer_scope_eval {
|
match *outer_scope_eval {
|
||||||
Ctr::Seg(ref segment) => evaluated_args = segment,
|
Ctr::Seg(ref segment) => evaluated_args = segment,
|
||||||
_ => {
|
_ => {
|
||||||
|
|
@ -282,6 +281,7 @@ impl Symbol {
|
||||||
evaluated_args = args;
|
evaluated_args = args;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
println!("args: {}", evaluated_args);
|
||||||
self.args.validate_inputs(evaluated_args)?;
|
self.args.validate_inputs(evaluated_args)?;
|
||||||
match &self.value {
|
match &self.value {
|
||||||
ValueType::VarForm(ref f) => Ok(Box::new(*f.clone())),
|
ValueType::VarForm(ref f) => Ok(Box::new(*f.clone())),
|
||||||
|
|
@ -351,4 +351,64 @@ impl Symbol {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn from_ast(
|
||||||
|
id: &String,
|
||||||
|
doc: &String,
|
||||||
|
ast: &Seg,
|
||||||
|
arg_list: Option<Vec<String>>,
|
||||||
|
) -> Symbol {
|
||||||
|
let args: Args;
|
||||||
|
let value: ValueType;
|
||||||
|
|
||||||
|
if let Some(ref arg_syms) = arg_list {
|
||||||
|
println!("def a func form");
|
||||||
|
value = ValueType::FuncForm(UserFn{
|
||||||
|
ast: Box::new(ast.clone()),
|
||||||
|
arg_syms: arg_syms.clone(),
|
||||||
|
});
|
||||||
|
args = Args::Lazy(arg_syms.len() as u128);
|
||||||
|
} else if let Ctr::Lambda(ref l) = *ast.car {
|
||||||
|
println!("def a func form (lambda)");
|
||||||
|
args = Args::Lazy(l.arg_syms.len() as u128);
|
||||||
|
value = ValueType::FuncForm(l.clone());
|
||||||
|
} else {
|
||||||
|
println!("def a var form");
|
||||||
|
args = Args::None;
|
||||||
|
value = ValueType::VarForm(ast.car.clone());
|
||||||
|
}
|
||||||
|
|
||||||
|
Symbol {
|
||||||
|
name: id.clone(),
|
||||||
|
docs: doc.clone(),
|
||||||
|
conditional_branches: false,
|
||||||
|
args, value,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn call_lambda(
|
||||||
|
lam: &UserFn,
|
||||||
|
args_ctr: &Box<Ctr>,
|
||||||
|
syms: &mut SymTable,
|
||||||
|
) -> Result<Box<Ctr>, String> {
|
||||||
|
let temp_sym = Symbol {
|
||||||
|
name: String::from("anonymous"),
|
||||||
|
conditional_branches: false,
|
||||||
|
docs: String::from("user defined lambda"),
|
||||||
|
args: Args::Lazy(lam.arg_syms.len() as u128),
|
||||||
|
value: ValueType::FuncForm(lam.clone()),
|
||||||
|
};
|
||||||
|
|
||||||
|
let args: &Seg;
|
||||||
|
let outer_scope_maybe_args: Seg;
|
||||||
|
if let Ctr::Seg(ref args_head) = **args_ctr {
|
||||||
|
args = args_head;
|
||||||
|
} else {
|
||||||
|
outer_scope_maybe_args = Seg::from_mono(
|
||||||
|
Box::new(*args_ctr.clone()));
|
||||||
|
args = &outer_scope_maybe_args;
|
||||||
|
}
|
||||||
|
|
||||||
|
temp_sym.call(args, syms)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
mod var_lib_tests {
|
mod decl_lib_tests {
|
||||||
use relish::ast::{eval, lex, Ctr, SymTable};
|
use relish::ast::{eval, lex, Ctr, SymTable};
|
||||||
use relish::stdlib::{dynamic_stdlib, static_stdlib};
|
use relish::stdlib::{dynamic_stdlib, static_stdlib};
|
||||||
|
|
||||||
|
|
@ -297,4 +297,86 @@ mod var_lib_tests {
|
||||||
result.to_string(),
|
result.to_string(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_lambda_str_equivalency_list() {
|
||||||
|
let document = "(lambda (x y) (add x y))";
|
||||||
|
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(),
|
||||||
|
document.to_string(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_lambda_str_equivalency_no_args() {
|
||||||
|
let document = "(lambda () (add 1 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(),
|
||||||
|
document.to_string(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_lambda_inline_call() {
|
||||||
|
let document = "((lambda (x y) (add x y)) 1 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();
|
||||||
|
println!("final return: {}", it);
|
||||||
|
if let Ctr::Integer(i) = it {
|
||||||
|
assert_eq!(i, 3)
|
||||||
|
} else {
|
||||||
|
panic!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_lambda_let_call() {
|
||||||
|
let document = "(let ((adder (lambda (x y) (add x y))))
|
||||||
|
(adder 1 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();
|
||||||
|
println!("{}", it);
|
||||||
|
if let Ctr::Integer(i) = it {
|
||||||
|
assert_eq!(i, 3)
|
||||||
|
} else {
|
||||||
|
panic!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_lambda_var_bound_call() {
|
||||||
|
let document = "(let (())
|
||||||
|
(def adder 'my adder' (lambda (x y) (add x y)))
|
||||||
|
(adder 1 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();
|
||||||
|
println!("{}", it);
|
||||||
|
if let Ctr::Integer(i) = it {
|
||||||
|
assert_eq!(i, 3)
|
||||||
|
} else {
|
||||||
|
panic!()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue