diff --git a/src/stl.rs b/src/stl.rs
index 38071c7..33388b4 100644
--- a/src/stl.rs
+++ b/src/stl.rs
@@ -15,15 +15,14 @@
* along with this program. If not, see .
*/
-use crate::eval::eval;
use crate::segment::{Ctr, Seg, Type};
-use crate::sym::{Args, SymTable, Symbol, UserFn, ValueType};
-use std::env;
+use crate::sym::{Args, SymTable, Symbol, ValueType};
use std::rc::Rc;
pub mod append;
pub mod boolean;
pub mod control;
+pub mod decl;
//pub mod str;
/// static_stdlib
@@ -157,8 +156,8 @@ pub fn static_stdlib(syms: &mut SymTable) -> Result<(), String> {
name: String::from("help"),
args: Args::Strict(vec![Type::Symbol]),
conditional_branches: true,
- docs: HELP_DOCSTRING.to_string(),
- value: ValueType::Internal(Rc::new(_help_callback)),
+ docs: decl::HELP_DOCSTRING.to_string(),
+ value: ValueType::Internal(Rc::new(decl::help_callback)),
},
);
@@ -168,8 +167,8 @@ pub fn static_stdlib(syms: &mut SymTable) -> Result<(), String> {
name: String::from("set?"),
args: Args::Strict(vec![Type::Symbol]),
conditional_branches: true,
- docs: ISSET_DOCSTRING.to_string(),
- value: ValueType::Internal(Rc::new(_isset_callback)),
+ docs: decl::ISSET_DOCSTRING.to_string(),
+ value: ValueType::Internal(Rc::new(decl::isset_callback)),
},
);
@@ -193,10 +192,10 @@ pub fn dynamic_stdlib(syms: &mut SymTable) -> Result<(), String> {
name: String::from("define"),
args: Args::Infinite,
conditional_branches: true,
- docs: STORE_DOCSTRING.to_string(),
+ docs: decl::STORE_DOCSTRING.to_string(),
value: ValueType::Internal(Rc::new(
move |ast: &Seg, syms: &mut SymTable| -> Result {
- _store_callback(ast, syms, env_cfg_user_form)
+ decl::store_callback(ast, syms, env_cfg_user_form)
},
)),
},
@@ -216,173 +215,3 @@ fn _echo_callback(ast: &Seg, _syms: &mut SymTable) -> Result {
Ok(Ctr::None)
}
-
-pub const HELP_DOCSTRING: &str = "prints help text for a given symbol. Expects only one argument.";
-
-fn _help_callback(ast: &Seg, syms: &mut SymTable) -> Result {
- if ast.len() != 1 {
- return Err("help only takes a single argument".to_string());
- }
- if let Ctr::Symbol(ref symbol) = *ast.car {
- if let Some(ref sym) = syms.get(symbol) {
- let args_str: String;
- if let ValueType::VarForm(_) = sym.value {
- args_str = "(its a variable)".to_string();
- } else {
- args_str = sym.args.to_string();
- }
- println!(
- "NAME: {0}\n
-ARGS: {1}\n
-DOCUMENTATION:\n
-{2}\n
-CURRENT VALUE AND/OR BODY:
-{3}",
- sym.name, args_str, sym.docs, sym.value
- );
- } else {
- return Err("undefined symbol".to_string());
- }
- } else {
- return Err("help should only be called on a symbol".to_string());
- }
-
- Ok(Ctr::None)
-}
-
-pub const STORE_DOCSTRING: &str = "allows user to define functions and variables.
- A call may take one of three forms:
- 1. variable declaration:
- Takes a name, doc string, and a value.
- (def myvar 'my special variable' 'my var value')
- 2. function declaration:
- Takes a name, doc string, list of arguments, and one or more bodies to evaluate.
- Result of evaluating the final body is returned.
- (def myfunc 'does a thing' (myarg1 myarg2) (dothing myarg1 myarg2) (add myarg1 myarg2))
- 3. symbol un-definition:
- Takes just a name. Removes variable from table.
- (def useless-var)";
-
-fn _store_callback(ast: &Seg, syms: &mut SymTable, env_cfg: bool) -> Result {
- let is_var = ast.len() == 3;
- if let Ctr::Symbol(ref identifier) = *ast.car {
- match &*ast.cdr {
- // define a symbol
- Ctr::Seg(doc_tree) => {
- if let Ctr::String(ref doc) = *doc_tree.car {
- match &*doc_tree.cdr {
- // define a variable
- Ctr::Seg(data_tree) if is_var => match eval(&Box::new(data_tree), syms) {
- Ok(seg) => {
- if let Ctr::Seg(ref val) = *seg {
- syms.insert(
- identifier.clone(),
- Symbol {
- value: ValueType::VarForm(val.car.clone()),
- name: identifier.clone(),
- args: Args::None,
- docs: doc.to_owned(),
- conditional_branches: false,
- },
- );
- if env_cfg {
- env::set_var(identifier.clone(), val.car.to_string());
- }
- } else {
- return Err("impossible args to export".to_string());
- }
- }
- Err(e) => return Err(format!("couldnt eval symbol: {}", e)),
- },
-
- // define a function
- Ctr::Seg(data_tree) if !is_var => {
- if let Ctr::Seg(ref args) = *data_tree.car {
- let mut arg_list = vec![];
- if !args.circuit(&mut |c: &Ctr| -> bool {
- if let Ctr::Symbol(ref arg) = c {
- arg_list.push(arg.clone());
- true
- } else if let Ctr::None = c {
- // a user cannot type a None
- // this case represents no args
- true
- } else {
- false
- }
- }) {
- return Err(
- "all arguments defined for function must be of type symbol"
- .to_string(),
- );
- };
-
- if let Ctr::Seg(ref bodies) = *data_tree.cdr {
- syms.insert(
- identifier.clone(),
- Symbol {
- value: ValueType::FuncForm(UserFn {
- ast: Box::new(bodies.clone()),
- arg_syms: arg_list.clone(),
- }),
- name: identifier.clone(),
- args: Args::Lazy(arg_list.len() as u128),
- docs: doc.to_owned(),
- conditional_branches: false,
- },
- );
- } else {
- return Err(
- "expected one or more function bodies in function definition"
- .to_string(),
- );
- }
- } else {
- return Err(
- "expected list of arguments in function definition".to_string()
- );
- }
- }
-
- // theres a name and a doc string but nothing else
- _ => return Err("have name and doc string, but no body.".to_string()),
- }
- } else {
- return Err("doc string is a required argument".to_string());
- }
- }
-
- // undefine a symbol
- Ctr::None => {
- syms.remove(&identifier.to_string());
- if env_cfg {
- env::remove_var(identifier);
- }
- }
-
- _ => return Err("arguments not in standard form".to_string()),
- }
- } else {
- return Err("first argument to export must be a symbol".to_string());
- }
- Ok(Ctr::None)
-}
-
-pub const ISSET_DOCSTRING: &str = "accepts a single argument: a symbol.
-returns true or false according to whether or not the symbol is found in the symbol table.";
-
-fn _isset_callback(ast: &Seg, syms: &mut SymTable) -> Result {
- if ast.len() != 1 {
- Err("help only takes a single argument".to_string())
- } else {
- if let Ctr::Symbol(ref symbol) = *ast.car {
- if let Some(_) = syms.get(symbol) {
- Ok(Ctr::Bool(true))
- } else {
- Ok(Ctr::Bool(false))
- }
- } else {
- Err("help should only be called on a symbol".to_string())
- }
- }
-}
diff --git a/src/stl/decl.rs b/src/stl/decl.rs
new file mode 100644
index 0000000..ff3f556
--- /dev/null
+++ b/src/stl/decl.rs
@@ -0,0 +1,191 @@
+/* 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 .
+ */
+
+use crate::eval::eval;
+use crate::segment::{Ctr, Seg};
+use crate::sym::{Args, SymTable, Symbol, UserFn, ValueType};
+use std::env;
+
+pub const HELP_DOCSTRING: &str = "prints help text for a given symbol. Expects only one argument.";
+
+pub fn help_callback(ast: &Seg, syms: &mut SymTable) -> Result {
+ if ast.len() != 1 {
+ return Err("help only takes a single argument".to_string());
+ }
+ if let Ctr::Symbol(ref symbol) = *ast.car {
+ if let Some(ref sym) = syms.get(symbol) {
+ let args_str: String;
+ if let ValueType::VarForm(_) = sym.value {
+ args_str = "(its a variable)".to_string();
+ } else {
+ args_str = sym.args.to_string();
+ }
+ println!(
+ "NAME: {0}\n
+ARGS: {1}\n
+DOCUMENTATION:\n
+{2}\n
+CURRENT VALUE AND/OR BODY:
+{3}",
+ sym.name, args_str, sym.docs, sym.value
+ );
+ } else {
+ return Err("undefined symbol".to_string());
+ }
+ } else {
+ return Err("help should only be called on a symbol".to_string());
+ }
+
+ Ok(Ctr::None)
+}
+
+pub const STORE_DOCSTRING: &str = "allows user to define functions and variables.
+ A call may take one of three forms:
+ 1. variable declaration:
+ Takes a name, doc string, and a value.
+ (def myvar 'my special variable' 'my var value')
+ 2. function declaration:
+ Takes a name, doc string, list of arguments, and one or more bodies to evaluate.
+ Result of evaluating the final body is returned.
+ (def myfunc 'does a thing' (myarg1 myarg2) (dothing myarg1 myarg2) (add myarg1 myarg2))
+ 3. symbol un-definition:
+ Takes just a name. Removes variable from table.
+ (def useless-var)";
+
+pub fn store_callback(ast: &Seg, syms: &mut SymTable, env_cfg: bool) -> Result {
+ let is_var = ast.len() == 3;
+ if let Ctr::Symbol(ref identifier) = *ast.car {
+ match &*ast.cdr {
+ // define a symbol
+ Ctr::Seg(doc_tree) => {
+ if let Ctr::String(ref doc) = *doc_tree.car {
+ match &*doc_tree.cdr {
+ // define a variable
+ Ctr::Seg(data_tree) if is_var => match eval(&Box::new(data_tree), syms) {
+ Ok(seg) => {
+ if let Ctr::Seg(ref val) = *seg {
+ syms.insert(
+ identifier.clone(),
+ Symbol {
+ value: ValueType::VarForm(val.car.clone()),
+ name: identifier.clone(),
+ args: Args::None,
+ docs: doc.to_owned(),
+ conditional_branches: false,
+ },
+ );
+ if env_cfg {
+ env::set_var(identifier.clone(), val.car.to_string());
+ }
+ } else {
+ return Err("impossible args to export".to_string());
+ }
+ }
+ Err(e) => return Err(format!("couldnt eval symbol: {}", e)),
+ },
+
+ // define a function
+ Ctr::Seg(data_tree) if !is_var => {
+ if let Ctr::Seg(ref args) = *data_tree.car {
+ let mut arg_list = vec![];
+ if !args.circuit(&mut |c: &Ctr| -> bool {
+ if let Ctr::Symbol(ref arg) = c {
+ arg_list.push(arg.clone());
+ true
+ } else if let Ctr::None = c {
+ // a user cannot type a None
+ // this case represents no args
+ true
+ } else {
+ false
+ }
+ }) {
+ return Err(
+ "all arguments defined for function must be of type symbol"
+ .to_string(),
+ );
+ };
+
+ if let Ctr::Seg(ref bodies) = *data_tree.cdr {
+ syms.insert(
+ identifier.clone(),
+ Symbol {
+ value: ValueType::FuncForm(UserFn {
+ ast: Box::new(bodies.clone()),
+ arg_syms: arg_list.clone(),
+ }),
+ name: identifier.clone(),
+ args: Args::Lazy(arg_list.len() as u128),
+ docs: doc.to_owned(),
+ conditional_branches: false,
+ },
+ );
+ } else {
+ return Err(
+ "expected one or more function bodies in function definition"
+ .to_string(),
+ );
+ }
+ } else {
+ return Err(
+ "expected list of arguments in function definition".to_string()
+ );
+ }
+ }
+
+ // theres a name and a doc string but nothing else
+ _ => return Err("have name and doc string, but no body.".to_string()),
+ }
+ } else {
+ return Err("doc string is a required argument".to_string());
+ }
+ }
+
+ // undefine a symbol
+ Ctr::None => {
+ syms.remove(&identifier.to_string());
+ if env_cfg {
+ env::remove_var(identifier);
+ }
+ }
+
+ _ => return Err("arguments not in standard form".to_string()),
+ }
+ } else {
+ return Err("first argument to export must be a symbol".to_string());
+ }
+ Ok(Ctr::None)
+}
+
+pub const ISSET_DOCSTRING: &str = "accepts a single argument: a symbol.
+returns true or false according to whether or not the symbol is found in the symbol table.";
+
+pub fn isset_callback(ast: &Seg, syms: &mut SymTable) -> Result {
+ if ast.len() != 1 {
+ Err("help only takes a single argument".to_string())
+ } else {
+ if let Ctr::Symbol(ref symbol) = *ast.car {
+ if let Some(_) = syms.get(symbol) {
+ Ok(Ctr::Bool(true))
+ } else {
+ Ok(Ctr::Bool(false))
+ }
+ } else {
+ Err("help should only be called on a symbol".to_string())
+ }
+ }
+}