diff --git a/Readme.org b/Readme.org
index 078f957..c84cb37 100644
--- a/Readme.org
+++ b/Readme.org
@@ -126,9 +126,9 @@ one of these may fix borrow checker insanity
**** DONE YEET AST EVERYWHERE. PASS AROUND A SEG REF.
*** TODO refactor code into more digestible, better organized pieces
**** DONE combine var and func table into global sym table
-**** TODO code deduplication and cleanup in sym table
-**** TODO refactor and reimplement eval
+**** DONE refactor and reimplement eval
**** TODO (test and get it working)
+**** TODO lex/eval/e2e tests for var that holds callback func
*** TODO Rudimentary Control Flow
**** TODO if clause
**** TODO loop clause
@@ -141,8 +141,8 @@ one of these may fix borrow checker insanity
**** DONE manual verification of config defaults
*** TODO Help function
*** TODO Env function
-*** TODO User variable declaration
-*** TODO User function declaration
+*** DONE User variable declaration
+*** DONE User function declaration
*** TODO Load (load a script) function
Pull/Refactor the logic out of the configure functions.
Optionally return a list of new variables and/or functions?
diff --git a/src/eval.rs b/src/eval.rs
index c09b6b8..ecb311d 100644
--- a/src/eval.rs
+++ b/src/eval.rs
@@ -15,22 +15,21 @@
* along with this program. If not, see .
*/
-
+use crate::sym::{SYM_TABLE, Symbol, ValueType};
use crate::segment::{Seg, Ctr};
-use crate::sym::SYM_TABLE;
/* iterates over a syntax tree
* returns a NEW LIST of values
* representing the simplest possible form of the input
*/
-
-pub fn eval(
+pub fn eval (
ast: &Seg,
- sym_loose: bool,
- call_lazy: bool,
+ expect_all_symbols_defined: bool,
+ simplify_function_branches: bool,
) -> Result, String> {
// data to return
let mut ret = Box::from(Ctr::None);
+ let mut first = true;
// to be assigned from cloned/evaled data
let mut car;
@@ -40,60 +39,69 @@ pub fn eval(
let mut arg_car = &ast.car;
let mut arg_cdr = &ast.cdr;
- // theres probably a better way to do this
- let mut binding_for_vtable_get;
-
- // doing an initial variable check here allows us
- // to find functions passed in as variables
- if let Ctr::Symbol(tok) = &**arg_car {
- binding_for_vtable_get = vars.get(tok.clone());
- if let Some(ref val) = binding_for_vtable_get {
- arg_car = &val;
- }
- }
-
- // Is ast a function call?
- if !call_lazy {
- if let Ctr::Symbol(ref tok) = &**arg_car {
- match *ast.cdr {
- Ctr::Seg(ref ast) => {
- if let Some(ref func) = funcs.get(tok.clone()) {
- return func.func_call(ast, vars, funcs);
- } else if !sym_loose {
- return Err(format!("Couldnt find definition of {}.", tok));
- }
- }
- Ctr::None => {
- if let Some(ref func) = funcs.get(tok.clone()) {
- return (*func).func_call(&Seg::new(), vars, funcs);
- } else if !sym_loose {
- return Err(format!("Couldnt find definition of {}.", tok.clone()));
- }
- }
- _ => return Err(format!("Arguments to function not a list!")),
- }
- }
- }
+ let table_handle = SYM_TABLE.lock().unwrap();
// iterate over ast and build out ret
let mut none = false;
while !none {
- match &**arg_car {
+ let mut prefetched_function: Option<&Symbol> = None;
+ while let Ctr::Symbol(ref tok) = **arg_car {
+ prefetched_function = table_handle.get(tok);
+ if let Some(sym_ref) = prefetched_function {
+ if let ValueType::VarForm(ref value) = sym_ref.value {
+ arg_car = value;
+ } else {
+ break;
+ }
+ } else if !expect_all_symbols_defined {
+ return Err(format!("evaluation error: undefined symbol {}", tok))
+ } else {
+ break
+ }
+ }
+
+ match **arg_car {
Ctr::Seg(ref inner) => {
- match eval(inner, vars, funcs, sym_loose, call_lazy) {
+ match eval(inner, expect_all_symbols_defined, simplify_function_branches) {
Ok(res) => car = res,
- Err(e) => return Err(format!("Evaluation error: {}", e)),
+ Err(e) => return Err(format!("evaluation error: {}", e)),
}
}
- Ctr::Symbol(ref tok) => {
- binding_for_vtable_get = vars.get(tok.clone());
- if let Some(ref val) = binding_for_vtable_get {
- car = val.clone();
- } else if sym_loose {
- car = arg_car.clone()
+ // im tired please simplify this
+ Ctr::Symbol(_) => {
+ if simplify_function_branches && first {
+ if let Some(func) = prefetched_function {
+ if let Ctr::Seg(ref candidates) = **arg_cdr {
+ let fc: Result, String>;
+ match eval(candidates, expect_all_symbols_defined, false) {
+ Ok(res) => {
+ match *res {
+ Ctr::Seg(ref args) => {
+ fc = func.call(args);
+ },
+ _ => {
+ fc = func.call(&Seg::from_mono(res.clone()))
+ }
+ }
+ match fc {
+ Ok(datum) => car = datum,
+ Err(e) => return Err(format!("call to {} failed: {}", func.name, e))
+ }
+ }
+ Err(e) => return Err(format!("evaluation error: {}", e))
+ }
+ } else {
+ match func.call(&Seg::new()) {
+ Ok(res) => car = res,
+ Err(e) => return Err(format!("call to {} failed: {}", func.name, e))
+ }
+ }
+ } else {
+ car = arg_car.clone();
+ }
} else {
- return Err(format!("Undefined variable: {}", tok.clone()));
+ car = arg_car.clone();
}
}
@@ -102,16 +110,25 @@ pub fn eval(
}
}
- match &**arg_cdr {
- Ctr::Symbol(ref tok) => {
- if let Some(val) = vars.get(tok.clone()) {
- cdr = val.clone();
- } else if sym_loose {
- cdr = ast.cdr.clone()
+ // weird tree but okay
+ while let Ctr::Symbol(ref tok) = **arg_cdr {
+ prefetched_function = table_handle.get(tok);
+ if let Some(sym_ref) = prefetched_function {
+ if let ValueType::VarForm(ref value) = sym_ref.value {
+ arg_cdr = value;
} else {
- return Err(format!("Undefined variable: {}", tok.clone()));
+ break;
}
+ } else if !expect_all_symbols_defined {
+ return Err(format!("evaluation error: undefined symbol {}", tok))
+ } else {
+ break
+ }
+ }
+ match **arg_cdr {
+ Ctr::Symbol(_) => {
+ cdr = arg_cdr.clone();
none = true;
}
@@ -125,7 +142,6 @@ pub fn eval(
arg_cdr = &next.cdr
}
- // if OTHER: clone and set, and then end
_ => {
cdr = ast.cdr.clone();
none = true;
@@ -137,7 +153,10 @@ pub fn eval(
none = true;
}
}
+
+ first = false;
}
- return Ok(ret);
+ Ok(ret)
}
+
diff --git a/src/lex.rs b/src/lex.rs
index 337849b..8209fd9 100644
--- a/src/lex.rs
+++ b/src/lex.rs
@@ -23,7 +23,7 @@ const UNMATCHED_LIST_DELIM: &str = "Unmatched list delimiter in input";
/* takes a line of user input
* returns an unsimplified tree of tokens.
*/
-pub fn lex<'a>(document: &'a String) -> Result, String> {
+pub fn lex(document: &String) -> Result, String> {
if !document.is_ascii() {
return Err("document may only contain ascii characters".to_string());
}
@@ -42,7 +42,7 @@ pub fn lex<'a>(document: &'a String) -> Result, String> {
* Returns Ok(Rc) if lexing passes
* Returns Err(String) if an error occurs
*/
-fn process<'a>(document: &'a String) -> Result, String> {
+fn process(document: &String) -> Result, String> {
let doc_len = document.len();
if doc_len == 0 {
@@ -87,7 +87,7 @@ fn process<'a>(document: &'a String) -> Result, String> {
// set alloc_list
if delim == ')' {
alloc_list = true;
- if ref_stack.len() < 1 {
+ if ref_stack.is_empty() {
return Err("too many end parens".to_string());
}
}
@@ -101,7 +101,7 @@ fn process<'a>(document: &'a String) -> Result, String> {
// try to generalize all whitespace
if !needs_alloc && char::is_whitespace(c) && !is_str {
// dont make empty tokens just because the document has consecutive whitespace
- if token.len() == 0 {
+ if token.is_empty() {
continue;
}
needs_alloc = true;
@@ -116,7 +116,7 @@ fn process<'a>(document: &'a String) -> Result, String> {
continue;
}
- if token != "" {
+ if !token.is_empty() {
return Err("list started in middle of another token".to_string());
}
@@ -147,7 +147,7 @@ fn process<'a>(document: &'a String) -> Result, String> {
* 2. Handle expansion of current list ref
*/
} else {
- if token.len() == 0 && !is_str && !alloc_list {
+ if token.is_empty() && !is_str && !alloc_list {
return Err("Empty token".to_string());
}
@@ -158,7 +158,7 @@ fn process<'a>(document: &'a String) -> Result, String> {
is_str = false;
token = String::new();
current_seg.append(obj);
- } else if token.len() > 0 {
+ } else if !token.is_empty() {
if token == "true" {
obj = Box::from(Ctr::Bool(true));
} else if token == "false" {
@@ -179,7 +179,7 @@ fn process<'a>(document: &'a String) -> Result, String> {
if alloc_list {
// return if we have finished the document
- if ref_stack.len() == 0 {
+ if ref_stack.is_empty() {
return Ok(Box::new(current_seg));
}
@@ -199,9 +199,10 @@ fn process<'a>(document: &'a String) -> Result, String> {
}
if is_str {
- return Err(UNMATCHED_STR_DELIM.to_string());
+ Err(UNMATCHED_STR_DELIM.to_string())
+ } else {
+ Err(UNMATCHED_LIST_DELIM.to_string())
}
- return Err(UNMATCHED_LIST_DELIM.to_string());
}
/* Returns true if token
@@ -209,13 +210,12 @@ fn process<'a>(document: &'a String) -> Result, String> {
*
* else returns false
*/
-fn tok_is_symbol(token: &String) -> Option {
- let tok = token.as_str();
- for t in tok.chars() {
- if !t.is_alphabetic() && !t.is_digit(10) && !(t == '-') && !(t == '_') {
+fn tok_is_symbol(token: &str) -> Option {
+ for t in token.chars() {
+ if !t.is_alphanumeric() && t != '-' && t != '_' {
return None;
}
}
- return Some(String::from(tok));
+ Some(String::from(token))
}
diff --git a/src/lib.rs b/src/lib.rs
index f2f1862..6ec94d4 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -19,24 +19,23 @@
mod config;
*/
mod eval;
-mod func;
mod lex;
mod segment;
-mod vars;
+mod sym;
+mod stl;
/*mod stl;
mod str;*/
extern crate lazy_static;
pub mod ast {
- pub use crate::eval::eval;
+// pub use crate::eval::eval;
pub use crate::lex::lex;
pub use crate::segment::{Ctr, Seg, Type};
- pub use crate::sym::{
+/* pub use crate::sym::{
SYM_TABLE, SymTable, Symbol,
- UserFn, ValueType, Args,
- LIB_EXPORT_ENV, LIB_EXPORT_NO_ENV
- };
+ UserFn, ValueType, Args
+ };*/
}
/*pub mod stdlib {
diff --git a/src/segment.rs b/src/segment.rs
index 1b463ae..9a686d3 100644
--- a/src/segment.rs
+++ b/src/segment.rs
@@ -20,13 +20,13 @@ use std::ops::Index;
// Container
#[derive(Debug, Default)]
-pub enum Ctr<'a> {
+pub enum Ctr {
Symbol(String),
String(String),
Integer(i128),
Float(f64),
Bool(bool),
- Seg(Seg<'a>),
+ Seg(Seg),
#[default]
None,
}
@@ -49,29 +49,29 @@ pub enum Type {
* I was going to call it Cell and then I learned about
* how important RefCells were in Rust
*/
-#[derive(Debug)]
-pub struct Seg<'a> {
+#[derive(Debug, Default)]
+pub struct Seg {
/* "Contents of Address Register"
* Historical way of referring to the first value in a cell.
*/
- pub car: Box>,
+ pub car: Box,
/* "Contents of Decrement Register"
* Historical way of referring to the second value in a cell.
*/
- pub cdr: Box>,
+ pub cdr: Box,
/* Stupid hack that makes rust look foolish.
* Needed to determine variance of lifetime.
* How this is an acceptable solution I have
* not a single clue.
*/
- _lifetime_variance_determinant: PhantomData<&'a ()>
+ _lifetime_variance_determinant: PhantomData<()>
}
static NOTHING: Ctr = Ctr::None;
-impl Ctr<'_> {
+impl Ctr {
pub fn to_type(&self) -> Type {
match self {
Ctr::Symbol(_s) => Type::Symbol,
@@ -86,14 +86,14 @@ impl Ctr<'_> {
}
-impl<'a> Seg<'a> {
+impl Seg {
/* recurs over tree assumed to be list in standard form
* appends object to end of list
*
* TODO: figure out how not to call CLONE on a CTR via obj arg
* TODO: return result
*/
- pub fn append<'b>(&mut self, obj: Box>) {
+ pub fn append(&mut self, obj: Box) {
if let Ctr::None = &*(self.car) {
self.car = obj;
return
@@ -127,16 +127,16 @@ impl<'a> Seg<'a> {
}
}
- pub fn from_mono(arg: Box>) -> Seg<'a> {
- return Seg{
+ pub fn from_mono(arg: Box) -> Seg {
+ Seg {
car: arg,
cdr: Box::new(Ctr::None),
_lifetime_variance_determinant: PhantomData,
}
}
- pub fn from(car: Box>, cdr: Box>) -> Seg<'a> {
- return Seg{
+ pub fn from(car: Box, cdr: Box) -> Seg {
+ Seg {
car,
cdr,
_lifetime_variance_determinant: PhantomData,
@@ -152,8 +152,12 @@ impl<'a> Seg<'a> {
len
}
- pub fn new() -> Seg<'a> {
- return Seg{
+ pub fn is_empty(&self) -> bool {
+ self.len() == 0
+ }
+
+ pub fn new() -> Seg {
+ Seg {
car: Box::new(Ctr::None),
cdr: Box::new(Ctr::None),
_lifetime_variance_determinant: PhantomData,
@@ -170,7 +174,7 @@ fn seg_to_string(s: &Seg, parens: bool) -> String {
}
string.push(' ');
match &*(s.cdr) {
- Ctr::Seg(inner) => string.push_str(&seg_to_string(&inner, false)),
+ Ctr::Seg(inner) => string.push_str(&seg_to_string(inner, false)),
Ctr::None => {string.pop();},
_ => string.push_str(&s.cdr.to_string()),
}
@@ -179,9 +183,9 @@ fn seg_to_string(s: &Seg, parens: bool) -> String {
string
}
-impl<'a> Clone for Seg<'a> {
- fn clone(&self) -> Seg<'a> {
- return Seg{
+impl Clone for Seg {
+ fn clone(&self) -> Seg {
+ Seg {
car: self.car.clone(),
cdr: self.cdr.clone(),
_lifetime_variance_determinant: PhantomData,
@@ -189,8 +193,8 @@ impl<'a> Clone for Seg<'a> {
}
}
-impl<'a> Index for Seg<'a> {
- type Output = Ctr<'a>;
+impl Index for Seg {
+ type Output = Ctr;
fn index(&self, idx: usize) -> &Self::Output {
if idx == 0 {
@@ -201,25 +205,25 @@ impl<'a> Index for Seg<'a> {
return s.index(idx - 1)
}
- return &NOTHING;
+ &NOTHING
}
}
-impl<'a> Clone for Ctr<'a> {
- fn clone(&self) -> Ctr<'a> {
+impl Clone for Ctr {
+ fn clone(&self) -> Ctr {
match self {
Ctr::Symbol(s) => Ctr::Symbol(s.clone()),
Ctr::String(s) => Ctr::String(s.clone()),
- Ctr::Integer(s) => Ctr::Integer(s.clone()),
- Ctr::Float(s) => Ctr::Float(s.clone()),
- Ctr::Bool(s) => Ctr::Bool(s.clone()),
+ Ctr::Integer(s) => Ctr::Integer(*s),
+ Ctr::Float(s) => Ctr::Float(*s),
+ Ctr::Bool(s) => Ctr::Bool(*s),
Ctr::Seg(s) => Ctr::Seg(s.clone()),
Ctr::None => Ctr::None,
}
}
}
-impl fmt::Display for Ctr<'_> {
+impl fmt::Display for Ctr {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Ctr::Symbol(s) => write!(f, "{}", s),
@@ -239,25 +243,38 @@ impl fmt::Display for Ctr<'_> {
}
}
-impl fmt::Display for Seg<'_> {
+impl fmt::Display for Seg {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", seg_to_string(self, true))
}
}
-impl Type {
- pub fn to_string(&self) -> String {
- let ret: &str;
- match self {
- Type::Symbol => ret = "symbol",
- Type::String => ret = "string",
- Type::Integer => ret = "integer",
- Type::Float => ret = "float",
- Type::Bool => ret = "bool",
- Type::Seg => ret = "segment",
- Type::None => ret = "none",
- }
+impl fmt::Display for Type {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ let ret: &str = match self {
+ Type::Symbol => "symbol",
+ Type::String => "string",
+ Type::Integer => "integer",
+ Type::Float => "float",
+ Type::Bool => "bool",
+ Type::Seg => "segment",
+ Type::None => "none",
+ };
- ret.to_owned()
+ write!(f, "{}", ret)
+ }
+}
+
+impl std::convert::From for Type {
+ fn from(value: String) -> Self {
+ match value.as_str() {
+ "symbol" => Type::Symbol,
+ "string" => Type::String,
+ "integer" => Type::Integer,
+ "float" => Type::Float,
+ "bool" => Type::Bool,
+ "segment" => Type::Seg,
+ _ => Type::None,
+ }
}
}
diff --git a/src/stl.rs b/src/stl.rs
new file mode 100644
index 0000000..a4334a1
--- /dev/null
+++ b/src/stl.rs
@@ -0,0 +1,118 @@
+/* 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::segment::{Ctr, Seg, Type};
+use crate::eval::eval;
+use crate::sym::{SYM_TABLE, Symbol, ValueType, Args, UserFn};
+use std::env;
+
+/*
+// the stdlib var export function with env_sync on
+static LIB_STORE_ENV: Symbol = Symbol {
+ name: String::from("export"),
+ args: Args::Lazy(2),
+ value: ValueType::Internal(Box::new( |ast: &Seg| -> Ctr {
+ _store_callback(ast, true)
+ },
+ )),
+ has_undefined_symbols: false,
+};
+
+// the stdlib var export function with env_sync off
+pub static LIB_STORE_NO_ENV: Symbol = Symbol {
+ name: String::from("export"),
+ args: Args::Lazy(2),
+ value: ValueType::Internal(Box::new( |ast: &Seg| -> Ctr {
+ _store_callback(ast, false)
+ },
+ )),
+ has_undefined_symbols: false,
+};*/
+
+// TODO : declare function if arg list is long enough
+fn _store_callback (ast: &Seg, env_cfg: bool) -> Ctr {
+ let mut table_handle = SYM_TABLE.lock().unwrap();
+ let is_var = ast.len() == 2;
+ if let Ctr::Symbol(ref identifier) = *ast.car {
+ match &*ast.cdr {
+ Ctr::Seg(data_tree) if is_var => match eval(&Box::new(data_tree), true, true) {
+ Ok(seg) => if let Ctr::Seg(ref val) = *seg {
+ table_handle.insert(identifier.clone(), Symbol{
+ value: ValueType::VarForm(val.car.clone()),
+ name: identifier.clone(),
+ args: Args::None,
+ has_undefined_symbols: false,
+ });
+ if env_cfg {
+ env::set_var(identifier.clone(), val.car.to_string());
+ }
+ } else {
+ eprintln!("impossible args to export")
+ },
+ Err(e) => eprintln!("couldnt eval symbol: {}", e),
+ },
+ 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 {
+ false
+ }
+ }) {
+ eprintln!("all arguments defined for function must be of type symbol");
+ return Ctr::None;
+ };
+
+ if let Ctr::Seg(ref bodies) = *data_tree.cdr {
+ table_handle.insert(identifier.clone(), Symbol{
+ value: ValueType::FuncForm(UserFn{
+ ast: Box::new(bodies.clone()),
+ arg_syms: arg_list.clone(),
+ }),
+ name: identifier.clone(),
+ args: Args::Strict(arg_list
+ .into_iter()
+ .map(Type::from)
+ .collect()),
+ has_undefined_symbols: false,
+ });
+ } else {
+ eprintln!("expected one or more function bodies in function definition");
+ return Ctr::None;
+ }
+ } else {
+ eprintln!("expected list of arguments in function definition");
+ return Ctr::None;
+ }
+ }
+ Ctr::None => {
+ table_handle.remove(&identifier.to_string());
+ if env_cfg {
+ env::remove_var(identifier);
+ }
+ },
+ _ => eprintln!("args not in standard form"),
+ }
+ } else {
+ eprintln!("first argument to export must be a symbol");
+ }
+ Ctr::None
+}
+
diff --git a/src/sym.rs b/src/sym.rs
new file mode 100644
index 0000000..e48e1fc
--- /dev/null
+++ b/src/sym.rs
@@ -0,0 +1,220 @@
+/* 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::{Seg, Ctr, Type};
+use std::collections::HashMap;
+use std::sync::Mutex;
+use lazy_static::lazy_static;
+
+pub type SymTable = HashMap;
+
+lazy_static! {
+ pub static ref SYM_TABLE: Mutex = {
+ Mutex::new(SymTable::new())
+ };
+}
+
+#[derive(Debug, Clone)]
+pub struct UserFn {
+ // Un-evaluated abstract syntax tree
+ // TODO: Intermediate evaluation to simplify branches with no argument in them
+ // Simplified branches must not have side effects.
+ // TODO: Apply Memoization?
+ pub ast: Box,
+ // list of argument string tokens
+ pub arg_syms: Vec,
+}
+
+/* A symbol may either be a pointer to a function
+ * or a syntax tree to eval with the arguments or
+ * a simple variable declaration (which can also
+ * be a macro)
+ */
+#[derive(Clone)]
+pub enum ValueType {
+ Internal(Box Ctr>),
+ FuncForm(UserFn),
+ VarForm(Box)
+}
+
+/* Function Args
+ * If Lazy, is an integer denoting number of args
+ * If Strict, is a list of type tags denoting argument type.
+ */
+#[derive(Clone)]
+pub enum Args {
+ Lazy(u128),
+ Strict(Vec),
+ Infinite,
+ None
+}
+
+#[derive(Clone)]
+pub struct Symbol {
+ pub value: ValueType,
+ pub name: String,
+ pub args: Args,
+ pub has_undefined_symbols: bool,
+}
+
+impl Args {
+ fn validate_inputs(&self, args: &Seg) -> Result<(), String> {
+ match self {
+ Args::None => {
+ if args.is_empty() {
+ return Ok(())
+ } else {
+ return Err("expected no args".to_string())
+ }
+ },
+ Args::Infinite => {
+ if !args.is_empty() {
+ return Ok(())
+ } else {
+ return Err("expected args but none were provided".to_string())
+ }
+ },
+
+ Args::Lazy(ref num) => {
+ let called_arg_count = args.len();
+ if *num == 0 {
+ if let Ctr::None = *args.car {
+ //pass
+ } else {
+ return Err("expected 0 args. Got one or more.".to_string());
+ }
+ } else if *num != called_arg_count {
+ return Err(format!(
+ "expected {} args. Got {}.",
+ num, called_arg_count
+ ));
+ }
+ }
+
+ Args::Strict(ref arg_types) => {
+ let mut idx: usize = 0;
+ let passes = args.circuit(&mut |c: &Ctr| -> bool {
+ if idx >= arg_types.len() {
+ return false;
+ }
+ if let Ctr::None = c {
+ return false;
+ }
+ if arg_types[idx] == c.to_type() {
+ idx += 1;
+ return true;
+ }
+ false
+ });
+
+ if passes && idx < (arg_types.len() - 1) {
+ return Err(format!(
+ "{} too few arguments",
+ arg_types.len() - (idx + 1)
+ ));
+ }
+
+ if !passes {
+ if idx < (arg_types.len() - 1) {
+ return Err(format!(
+ "argument {} is of wrong type (expected {})",
+ idx + 1,
+ arg_types[idx]
+ ));
+ }
+ if idx == (arg_types.len() - 1) {
+ return Err("too many arguments".to_string());
+ }
+ }
+ }
+ }
+
+ Ok(())
+ }
+}
+
+impl Symbol {
+ /* call
+ * routine is called by eval when a symbol is expanded
+ */
+ pub fn call(
+ &self,
+ args: &Seg,
+ ) -> Result, String> {
+ if let Err(msg) = self.args.validate_inputs(args) {
+ return Err(format!("failure to call {}: {}", self.name, msg));
+ }
+
+ match &self.value {
+ ValueType::VarForm(ref f) => Ok(Box::new(*f.clone())),
+ ValueType::Internal(ref f) => Ok(Box::new(f(args))),
+ ValueType::FuncForm(ref f) => {
+ // stores any value overwritten by local state
+ // If this ever becomes ASYNC this will need to
+ // become a more traditional stack design, and the
+ // global table will need to be released
+ let mut holding_table = SymTable::new();
+
+ // Prep var table for function execution
+ for n in 0..f.arg_syms.len() {
+ if let Some(old) = SYM_TABLE.lock().unwrap()
+ .insert(f.arg_syms[n].clone(), Symbol{
+ name: f.arg_syms[n].clone(),
+ value: ValueType::VarForm(Box::new(args[n].clone())),
+ args: Args::None,
+ has_undefined_symbols: false,
+ })
+ {
+ holding_table.insert(f.arg_syms[n].clone(), old);
+ }
+ }
+
+ // execute function
+ let mut result: Box;
+ let mut iterate = &*(f.ast);
+ loop {
+ if let Ctr::Seg(ref data) = *iterate.car {
+ match eval(data, !self.has_undefined_symbols, true) {
+ Ok(ctr) => result = ctr,
+ Err(e) => return Err(e),
+ }
+ } else {
+ panic!("function body not in standard form!")
+ }
+
+ match *iterate.cdr {
+ Ctr::Seg(ref next) => iterate = next,
+ Ctr::None => break,
+ _ => panic!("function body not in standard form!"),
+ }
+ }
+
+ // clear local vars and restore previous values
+ for n in 0..f.arg_syms.len() {
+ SYM_TABLE.lock().unwrap().remove(&f.arg_syms[n]);
+ if let Some(val) = holding_table.remove(&f.arg_syms[n]) {
+ SYM_TABLE.lock().unwrap().insert(f.arg_syms[n].clone(), val);
+ }
+ }
+
+ Ok(result)
+ }
+ }
+ }
+}
+
diff --git a/sym.rs b/sym.rs
deleted file mode 100644
index fc6a442..0000000
--- a/sym.rs
+++ /dev/null
@@ -1,294 +0,0 @@
-/* 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::{Seg, Ctr, Type};
-use crate::vars::{VAR_TABLE, VTable, LIB_EXPORT};
-use std::collections::HashMap;
-use std::convert::TryInto;
-use lazy_static::lazy_static;
-
-lazy_static! {
- pub static ref SYM_TABLE: SymTable<'static> = {
- let mut tab = SymTable::new();
- tab.declare(LIB_EXPORT);
- tab
- };
-}
-
-
-pub struct SymTable<'a> (HashMap);
-
-#[derive(Debug)]
-pub struct UserFn<'a> {
- // Un-evaluated abstract syntax tree
- // TODO: Intermediate evaluation to simplify branches with no argument in them
- // Simplified branches must not have side effects.
- // TODO: Apply Memoization?
- pub ast: Box>,
- // list of argument string tokens
- pub arg_syms: Vec,
-}
-
-/* A symbol may either be a pointer to a function
- * or a syntax tree to eval with the arguments or
- * a simple variable declaration (which can also
- * be a macro)
- */
-pub enum ValueType<'a> {
- Internal(Box Ctr<'a>>),
- UserFn(ExternalOperation<'a>),
- UserVar(Box>),
-}
-
-
-/* Function Args
- * If Lazy, is an integer denoting number of args
- * If Strict, is a list of type tags denoting argument type.
- */
-pub enum Args {
- // signed: -1 denotes infinite args
- Lazy(i128),
- Strict(Vec),
-}
-
-impl Args {
- fn validate_inputs(&self, args: &Seg) -> Result<(), String> {
- match self {
- Args::Lazy(ref num) => {
- let called_arg_count = args.len() as i128;
- if *num == 0 {
- if let Ctr::None = *args.car {
- //pass
- } else {
- return Err("Expected 0 args. Got one or more.".to_string());
- }
- } else if *num > -1 && (*num != called_arg_count) {
- return Err(format!(
- "Expected {} args. Got {}.",
- num, called_arg_count
- ));
- }
- }
-
- Args::Strict(ref arg_types) => {
- let mut idx: usize = 0;
- let passes = args.circuit(&mut |c: &Ctr| -> bool {
- if idx >= arg_types.len() {
- return false;
- }
- if let Ctr::None = c {
- return false;
- }
- if arg_types[idx] == c.to_type() {
- idx += 1;
- return true;
- }
- return false;
- });
-
- if passes && idx < (arg_types.len() - 1) {
- return Err(format!(
- "{} too few arguments",
- arg_types.len() - (idx + 1)
- ));
- }
-
- if !passes {
- if idx < (arg_types.len() - 1) {
- return Err(format!(
- "argument {} is of wrong type (expected {})",
- idx + 1,
- arg_types[idx].to_string()
- ));
- }
- if idx == (arg_types.len() - 1) {
- return Err("too many arguments".to_string());
- }
- }
- }
- }
-
- Ok(())
- }
-}
-
-
-pub struct Symbol {
- pub value: ValueType,
- pub name: String,
-
- // has no meaning for UserVar values
- pub args: Args,
-
- // dont fail on undefined symbol (passed to eval)
- // has no meaning for UserVar values
- pub loose_syms: bool,
-
- // dont evaluate args at all. leave that to the function
- // has no meaning for UserVar values
- pub eval_lazy: bool,
-}
-
-impl<'a, 'b> Symbol {
- /* call
- * routine is called by eval when a function call is detected
- */
- pub fn func_call(
- &self,
- args: &'b Seg<'b>,
- ) -> Result>, String> {
- // put args in simplest desired form
- let evaluated_args;
- match eval(args, self.loose_syms, self.eval_lazy) {
- Ok(arg_data) => {
- if let Ctr::Seg(ast) = *arg_data {
- evaluated_args = *
- } else {
- return Err("Panicking: eval returned not a list for function args.".to_string());
- }
- }
- Err(s) => {
- return Err(format!(
- "error evaluating args to {}: {}",
- self.name, s
- ))
- }
- }
-
- if let UserVar(_s) = self {
- return evaluated_args;
- }
-
- if let Err(msg) = self.args.validate_inputs(evaluated_args) {
- return Err(format!("failure to call {}: {}", self.name, msg));
- }
-
- /* corecursive with eval.
- * essentially calls eval on each body in the function.
- * result of the final body is returned.
- */
- match &self.function {
- Operation::Internal(ref f) => return Ok(Box::new(f(evaluated_args))),
- Operation::External(ref f) => {
- let mut holding_table = VTable::new();
-
- // Prep var table for function execution
- for n in 0..f.arg_syms.len() {
- let iter_arg = evaluated_args[n];
- if let Some(val) = VAR_TABLE.get(f.arg_syms[n]) {
- holding_table.insert(f.arg_syms[n].clone(), val);
- }
-
- VAR_TABLE.insert(f.arg_syms[n].clone(), Box::new(iter_arg));
- }
-
- // execute function
- let mut result: Box;
- let iterate = &*(f.ast);
- loop {
- if let Ctr::Seg(ref data) = *iterate.car {
- match eval(data, self.loose_syms, true) {
- Ok(ctr) => result = ctr,
- Err(e) => return Err(e),
- }
- } else {
- panic!("function body not in standard form!")
- }
-
- match *iterate.cdr {
- Ctr::Seg(ref next) => iterate = next,
- Ctr::None => break,
- _ => panic!("function body not in standard form!"),
- }
- }
-
- // clear local vars and restore previous values
- for n in 0..f.arg_syms.len() {
- VAR_TABLE.remove(f.arg_syms[n]);
- if let Some(val) = holding_table.get(f.arg_syms[n]) {
- VAR_TABLE.insert(f.arg_syms[n].clone(), val);
- }
- }
-
- return Ok(result);
- }
- }
- }
-}
-
-
-// the stdlib var export function with env_sync on
-lazy_static! {
- pub static ref LIB_EXPORT_ENV: Function = Function {
- name: String::from("export"),
- loose_syms: true,
- eval_lazy: true,
- args: Args::Lazy(2),
- function: Operation::Internal(Box::new( move |ast: &Seg| -> Ctr {
- _export_callback(ast, true)
- },
- )),
- };
-}
-
-// the stdlib var export function with env_sync off
-lazy_static! {
- pub static ref LIB_EXPORT_NO_ENV: Function = Function {
- name: String::from("export"),
- loose_syms: true,
- eval_lazy: true,
- args: Args::Lazy(2),
- function: Operation::Internal(Box::new( move |ast: &Seg| -> Ctr {
- _export_callback(ast, false)
- },
- )),
- };
-}
-
-
-fn _export_callback<'a> (ast: &Seg, env_cfg: bool) -> Ctr<'a> {
- if let Ctr::Symbol(ref identifier) = *ast.car {
- match &*ast.cdr {
- Ctr::Seg(data_tree) => match eval(&Box::new(data_tree), false, true) {
- Ok(seg) => match *seg {
- Ctr::Seg(val) => {
- SYM_TABLE.declare(Symbol {
- value: UserVar(val.car),
- name: identifier.clone(),
- });
- if env_cfg {
- env::set_var(identifier.clone(), val.car.to_string())
- }
- },
- _ => eprintln!("impossible args to export"),
- },
- Err(e) => eprintln!("couldnt eval symbol: {}", e),
- },
- Ctr::None => {
- VAR_TABLE.remove(identifier.to_string());
- if env_cfg {
- env::remove_var(identifier.to_string());
- }
- },
- _ => eprintln!("args not in standard form"),
- }
- } else {
- eprintln!("first argument to export must be a symbol");
- }
- return Ctr::None;
-}