diff --git a/Cargo.toml b/Cargo.toml
index 7214a92..db0c100 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -8,4 +8,5 @@ edition = "2018"
[dependencies]
dirs = "3.0"
-rustyline = "8.2.0"
\ No newline at end of file
+lazy_static = "1.4.0"
+rustyline = "8.2.0"
diff --git a/Readme.org b/Readme.org
index 81cddc5..078f957 100644
--- a/Readme.org
+++ b/Readme.org
@@ -119,15 +119,16 @@ Note: this section will not show the status of each item unless you are viewing
**** DONE Derive Default for Seg
**** WONTDO From/Into for Ctr
**** DONE Iterator for Seg
-**** TODO Consider RefCell for tables, or using 'static lifetime
+**** WONTDO Consider RefCell for tables, or using 'static lifetime
one of these may fix borrow checker insanity
-all you have to do is actually learn this shit
-just pay attention
+**** DONE make FTable and VTable singleton modules (global mutable)
+**** WONTDO make FTable and VTable into private definitions
**** DONE YEET AST EVERYWHERE. PASS AROUND A SEG REF.
*** TODO refactor code into more digestible, better organized pieces
-Just need to get core compiling at this point.
-Refactors made include splitting func_call into reasonable segments
-Reorganizing eval
+**** DONE combine var and func table into global sym table
+**** TODO code deduplication and cleanup in sym table
+**** TODO refactor and reimplement eval
+**** TODO (test and get it working)
*** TODO Rudimentary Control Flow
**** TODO if clause
**** TODO loop clause
@@ -175,5 +176,6 @@ Will need a concatenate function for func tables
***** TODO HTTP Listener
***** TODO UDP Listener
*** TODO Ensure full test coverage
-
-THE GOOD STUFF IS IN Repositories/temprl
+**** TODO Test for VTable overwrites in func_call
+**** TODO Implement missing VTable, FTable, Lex, Eval tests
+**** TODO stdlib tests :)
diff --git a/the_rewrite/src/eval.rs b/legacy_snippets/eval_draft2.rs
similarity index 98%
rename from the_rewrite/src/eval.rs
rename to legacy_snippets/eval_draft2.rs
index 1cd953c..02b94f0 100644
--- a/the_rewrite/src/eval.rs
+++ b/legacy_snippets/eval_draft2.rs
@@ -72,7 +72,7 @@ pub fn eval<'a, 'b>(
}
Ctr::None => {
if let Some(ref func) = funcs.get(tok.clone()) {
- return func.func_call(&Seg::new(), vars, funcs);
+ return (*func).func_call(&Seg::new(), vars, funcs);
} else if !sym_loose {
return Err(format!("Couldnt find definition of {}.", tok.clone()));
}
diff --git a/src/append.rs b/legacy_snippets/initial_rough_spaghetti_implementation/append.rs
similarity index 100%
rename from src/append.rs
rename to legacy_snippets/initial_rough_spaghetti_implementation/append.rs
diff --git a/src/bin/main.rs b/legacy_snippets/initial_rough_spaghetti_implementation/bin/main.rs
similarity index 100%
rename from src/bin/main.rs
rename to legacy_snippets/initial_rough_spaghetti_implementation/bin/main.rs
diff --git a/src/config.rs b/legacy_snippets/initial_rough_spaghetti_implementation/config.rs
similarity index 100%
rename from src/config.rs
rename to legacy_snippets/initial_rough_spaghetti_implementation/config.rs
diff --git a/src/control.rs b/legacy_snippets/initial_rough_spaghetti_implementation/control.rs
similarity index 100%
rename from src/control.rs
rename to legacy_snippets/initial_rough_spaghetti_implementation/control.rs
diff --git a/legacy_snippets/initial_rough_spaghetti_implementation/eval.rs b/legacy_snippets/initial_rough_spaghetti_implementation/eval.rs
new file mode 100644
index 0000000..23fbfc7
--- /dev/null
+++ b/legacy_snippets/initial_rough_spaghetti_implementation/eval.rs
@@ -0,0 +1,144 @@
+/* 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::func::{func_call, FTable};
+use crate::segment::{new_ast, Ast, Ctr};
+use crate::vars::VTable;
+use std::cell::RefCell;
+use std::rc::Rc;
+
+/* iterates over a syntax tree
+ * returns a NEW LIST of values
+ * representing the simplest possible form of the input
+ */
+pub fn eval(
+ ast: Ast,
+ vars: Rc>,
+ funcs: Rc>,
+ sym_loose: bool,
+) -> Result {
+ let mut car = ast.borrow().clone().car;
+ let mut cdr = ast.borrow().clone().cdr;
+ let ret = new_ast(Ctr::None, Ctr::None);
+ let mut iter = ret.clone();
+
+ // doing an initial variable check here allows us
+ // to find functions passed in as variables
+ if let Ctr::Symbol(ref tok) = car {
+ if let Some(val) = vars.borrow().get(tok) {
+ car = (**val).clone();
+ }
+ }
+
+ // another check to detect if we may have a function call
+ if let Ctr::Symbol(ref tok) = car {
+ match cdr.clone() {
+ Ctr::Seg(ast) => {
+ if let Some(func) = funcs.borrow().get(tok) {
+ return func_call(func.clone(), ast.clone(), vars.clone(), funcs.clone());
+ } else if !sym_loose {
+ return Err(format!("Couldnt find definition of {}.", tok));
+ }
+ }
+ Ctr::None => {
+ if let Some(func) = funcs.borrow().get(tok) {
+ return func_call(
+ func.clone(),
+ new_ast(Ctr::None, Ctr::None),
+ vars.clone(),
+ funcs.clone(),
+ );
+ } else if !sym_loose {
+ return Err(format!("Couldnt find definition of {}.", tok));
+ }
+ }
+ _ => return Err(format!("Arguments to function not a list!")),
+ }
+ }
+
+ let mut none = false;
+ while !none {
+ match car {
+ // if LIST: call eval inner on it with first_item=true
+ Ctr::Seg(ref inner) => {
+ match eval(inner.clone(), vars.clone(), funcs.clone(), sym_loose) {
+ Ok(res) => (*iter).borrow_mut().car = res,
+ Err(e) => return Err(format!("Evaluation error: {}", e)),
+ }
+ }
+
+ // if SYMBOL: unwrap naively
+ Ctr::Symbol(ref tok) => {
+ if let Some(val) = vars.borrow().get(&tok.clone()) {
+ (*iter).borrow_mut().car = (**val).clone();
+ } else if sym_loose {
+ (*iter).borrow_mut().car = Ctr::Symbol(tok.to_string());
+ } else {
+ return Err(format!("Undefined variable: {}", tok));
+ }
+ }
+
+ // if OTHER: clone and set
+ _ => {
+ (*iter).borrow_mut().car = car.clone();
+ }
+ }
+
+ match cdr {
+ // if SYMBOL: unwrap naively, then end
+ Ctr::Symbol(ref tok) => {
+ if let Some(val) = vars.borrow().get(&tok.clone()) {
+ (*iter).borrow_mut().car = (**val).clone();
+ } else if sym_loose {
+ (*iter).borrow_mut().car = Ctr::Symbol(tok.to_string());
+ } else {
+ return Err(format!("Undefined variable: {}", tok));
+ }
+
+ none = true;
+ }
+
+ // if LIST:
+ // - iter.cdr = new_ast(None, None)
+ // - iter = iter.cdr
+ // - car = cdr.car
+ // - cdr = cdr.cdr
+ // - LOOP
+ Ctr::Seg(next) => {
+ let n = new_ast(Ctr::None, Ctr::None);
+ iter.borrow_mut().cdr = Ctr::Seg(n.clone());
+ iter = n;
+ car = next.borrow().clone().car;
+ cdr = next.borrow().clone().cdr;
+ }
+
+ // if OTHER: clone and set, and then end
+ _ => {
+ (*iter).borrow_mut().cdr = cdr.clone();
+ none = true;
+ }
+ }
+
+ if let Ctr::None = car {
+ if let Ctr::None = cdr {
+ none = true;
+ }
+ }
+ }
+
+ return Ok(Ctr::Seg(ret));
+}
diff --git a/src/func.rs b/legacy_snippets/initial_rough_spaghetti_implementation/func.rs
similarity index 100%
rename from src/func.rs
rename to legacy_snippets/initial_rough_spaghetti_implementation/func.rs
diff --git a/the_rewrite/src/lex.rs b/legacy_snippets/initial_rough_spaghetti_implementation/lex.rs
similarity index 82%
rename from the_rewrite/src/lex.rs
rename to legacy_snippets/initial_rough_spaghetti_implementation/lex.rs
index 337849b..87700c8 100644
--- a/the_rewrite/src/lex.rs
+++ b/legacy_snippets/initial_rough_spaghetti_implementation/lex.rs
@@ -15,7 +15,7 @@
* along with this program. If not, see .
*/
-use crate::segment::{Ctr, Seg};
+use crate::segment::{list_append, Ctr, Seg};
const UNMATCHED_STR_DELIM: &str = "Unmatched string delimiter in input";
const UNMATCHED_LIST_DELIM: &str = "Unmatched list delimiter in input";
@@ -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<'a>(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<'a>(document: &'a String) -> Result>, String> {
let doc_len = document.len();
if doc_len == 0 {
@@ -120,7 +120,8 @@ fn process<'a>(document: &'a String) -> Result, String> {
return Err("list started in middle of another token".to_string());
}
- ref_stack.push(Seg::new());
+ ref_stack.push(Box::new(Seg::new()));
+
delim_stack.push(')');
}
// begin parsing a string
@@ -151,47 +152,42 @@ fn process<'a>(document: &'a String) -> Result, String> {
return Err("Empty token".to_string());
}
- let mut current_seg = ref_stack.pop().unwrap();
- let obj;
+ let mut current_seg = ref_stack.pop();
+ let mut obj;
if is_str {
- obj = Box::from(Ctr::String(token));
+ obj = Ctr::String(token);
is_str = false;
token = String::new();
- current_seg.append(obj);
} else if token.len() > 0 {
if token == "true" {
- obj = Box::from(Ctr::Bool(true));
+ obj = Ctr::Bool(true);
} else if token == "false" {
- obj = Box::from(Ctr::Bool(false));
+ obj = Ctr::Bool(false);
} else if let Ok(i) = token.parse::() {
- obj = Box::from(Ctr::Integer(i));
+ obj = Ctr::Integer(i);
} else if let Ok(f) = token.parse::() {
- obj = Box::from(Ctr::Float(f));
+ obj = Ctr::Float(f);
} else if let Some(s) = tok_is_symbol(&token) {
- obj = Box::from(Ctr::Symbol(s));
+ obj = Ctr::Symbol(s);
} else {
return Err(format!("Unparsable token: {}", token));
}
token = String::new();
- current_seg.append(obj.clone());
}
+ list_append(current_seg, obj);
+
if alloc_list {
// return if we have finished the document
if ref_stack.len() == 0 {
- return Ok(Box::new(current_seg));
+ return Ok(current_seg);
}
- let t = current_seg;
- current_seg = ref_stack.pop().unwrap();
- /* TODO: is there a way to do this that doesnt
- * involve needlessly copying heap data? I am
- * not sure what optimizations rustc performs
- * but I assume this should not end up copying
- * contained segments around.
- */
- current_seg.append(Box::from(Ctr::Seg(t)));
+ // shortening this will lead to naught but pain
+ obj = Ctr::Seg(current_seg.into_raw());
+ current_seg = ref_stack.pop();
+ list_append(current_seg, obj);
}
ref_stack.push(current_seg);
diff --git a/the_rewrite/src/lib.rs b/legacy_snippets/initial_rough_spaghetti_implementation/lib.rs
similarity index 80%
rename from the_rewrite/src/lib.rs
rename to legacy_snippets/initial_rough_spaghetti_implementation/lib.rs
index 534f3e5..6a48eef 100644
--- a/the_rewrite/src/lib.rs
+++ b/legacy_snippets/initial_rough_spaghetti_implementation/lib.rs
@@ -15,32 +15,35 @@
* along with this program. If not, see .
*/
-/*mod append;
+#![feature(derive_default_enum)]
+
+mod append;
mod config;
- */
mod eval;
mod func;
mod lex;
mod segment;
+mod stl;
+mod str;
mod vars;
-/*mod stl;
-mod str;*/
pub mod ast {
pub use crate::eval::eval;
- pub use crate::func::{Args, ExternalOperation, FTable, Function, Operation};
+ pub use crate::func::{
+ func_call, func_declare, Args, ExternalOperation, FTable, Function, Operation,
+ };
pub use crate::lex::lex;
pub use crate::segment::{Ctr, Seg, Type};
- pub use crate::vars::VTable;
+ pub use crate::vars::{define, VTable};
}
-/*pub mod stdlib {
+pub mod stdlib {
pub use crate::append::get_append;
pub use crate::stl::get_stdlib;
pub use crate::str::{get_concat, get_echo};
pub use crate::vars::get_export;
-}*/
+}
-/*pub mod aux {
+pub mod aux {
pub use crate::config::configure;
-}*/
+}
diff --git a/legacy_snippets/initial_rough_spaghetti_implementation/segment.rs b/legacy_snippets/initial_rough_spaghetti_implementation/segment.rs
new file mode 100644
index 0000000..3535aa0
--- /dev/null
+++ b/legacy_snippets/initial_rough_spaghetti_implementation/segment.rs
@@ -0,0 +1,210 @@
+/* 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 std::fmt;
+
+
+// Container
+#[derive(Debug, Clone, Default)]
+pub enum Ctr <'a> {
+ Symbol(String),
+ String(String),
+ Integer(i128),
+ Float(f64),
+ Bool(bool),
+ Seg(Seg<'a>),
+ #[default]
+ None,
+}
+
+// Type of Container
+#[derive(PartialEq, Clone)]
+pub enum Type {
+ Symbol,
+ String,
+ Integer,
+ Float,
+ Bool,
+ Seg,
+ None,
+}
+
+/* Segment
+ * Holds two Containers.
+ * Basic building block for more complex data structures.
+ */
+#[derive(Clone, Debug, Default)]
+pub struct Seg <'a> {
+ /* "Contents of Address Register"
+ * Historical way of referring to the first value in a cell.
+ */
+ pub car: &mut Ctr<'a>,
+
+ /* "Contents of Decrement Register"
+ * Historical way of referring to the second value in a cell.
+ */
+ pub cdr: &mut Ctr<'a>,
+}
+
+impl Ctr<'_> {
+ pub fn to_type(&self) -> Type {
+ match self {
+ Ctr::Symbol(_s) => Type::Symbol,
+ Ctr::String(_s) => Type::String,
+ Ctr::Integer(_s) => Type::Integer,
+ Ctr::Float(_s) => Type::Float,
+ Ctr::Bool(_s) => Type::Bool,
+ Ctr::Seg(_s) => Type::Seg,
+ Ctr::None => Type::None,
+ }
+ }
+
+}
+
+fn seg_to_string(s: &Seg, parens: bool) -> String {
+ let mut string = String::new();
+ match s.car {
+ Ctr::None => string.push_str(""),
+ _ => string.push_str(s.car),
+ }
+ string.push(' ');
+ match s.cdr {
+ Ctr::Seg(inner) => string.push_str(seg_to_string(inner, false)),
+ Ctr::None => {},
+ _ => string.push_str(s.cdr),
+ }
+
+ if parens {
+ String::from("(" + string + ")")
+ }
+}
+
+impl fmt::Display for Ctr <'_> {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ match self {
+ Ctr::Symbol(s) => write!(f, "{}", s),
+ Ctr::String(s) => write!(f, "\'{}\'", s),
+ Ctr::Integer(s) => write!(f, "{}", s),
+ Ctr::Float(s) => write!(f, "{}", s),
+ Ctr::Bool(s) => {
+ if s {
+ write!(f, "T")
+ } else {
+ write!(f, "F")
+ }
+ },
+ Ctr::Seg(s) => write!(f, "{}", s),
+ Ctr::None => Ok(),
+ }
+ }
+}
+
+impl fmt::Display for Seg<'_> {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ write!(f, "{}", seg_to_string(self, true))
+ }
+}
+
+impl Iterator for Seg<'_> {
+ fn next(&self) -> Option<&Seg> {
+ if let Ctr::Seg(s) = self.cdr {
+ Ok(s)
+ } else {
+ None()
+ }
+ }
+}
+
+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",
+ }
+
+ ret.to_owned()
+ }
+}
+
+/* applies a function across a list in standard form
+ * function must take a Ctr and return a bool
+ * short circuits on the first false returned.
+ * also returns false on a non standard form list
+ */
+pub fn circuit(list: &Seg, func: &mut F) -> bool {
+ if func(&list.car) {
+ match list.cdr {
+ Ctr::None => true,
+ Ctr::Seg(l) => circuit(l, func),
+ _ => false,
+ }
+ } else {
+ false
+ }
+}
+
+/* recurs over ast assumed to be list in standard form
+ * returns length
+ */
+pub fn list_len(list: &Seg) -> u128 {
+ let mut len = 0;
+ circuit(list, &circuit(&mut |c: &Ctr| -> bool { len += 1; true }))
+}
+
+/* recurs over tree assumed to be list in standard form
+ * returns clone of ctr at index provided
+ *
+ * TODO: return result (or option?)
+ */
+pub fn list_idx<'a>(list: &Seg, idx: u128) -> Ctr<'a> {
+ if idx > 0 {
+ if let Ctr::Seg(s) = list.car {
+ list_idx(s, idx - 1)
+ } else if idx == 1 {
+ list.cdr
+ } else {
+ Ctr::None
+ }
+ } else {
+ list.car
+ }
+}
+
+/* recurs over tree assumed to be list in standard form
+ * appends object to end of list
+ *
+ * TODO: return result
+ */
+pub fn list_append<'a>(list: &Seg, obj: Ctr) {
+ if let Ctr::None = list.car {
+ list.car = obj;
+ return
+ }
+
+ if let Ctr::Seg(s) = list.cdr {
+ list_append(s, obj)
+ }
+
+ if let Ctr::None = list.cdr {
+ list.cdr = Ctr::Seg(&Seg{car:obj, cdr:Ctr::None})
+ }
+}
diff --git a/the_rewrite/tests/test_eval.rs b/legacy_snippets/initial_rough_spaghetti_implementation/spaghetti_tests/test_eval.rs
similarity index 100%
rename from the_rewrite/tests/test_eval.rs
rename to legacy_snippets/initial_rough_spaghetti_implementation/spaghetti_tests/test_eval.rs
diff --git a/the_rewrite/tests/test_func.rs b/legacy_snippets/initial_rough_spaghetti_implementation/spaghetti_tests/test_func.rs
similarity index 100%
rename from the_rewrite/tests/test_func.rs
rename to legacy_snippets/initial_rough_spaghetti_implementation/spaghetti_tests/test_func.rs
diff --git a/the_rewrite/tests/test_lex.rs b/legacy_snippets/initial_rough_spaghetti_implementation/spaghetti_tests/test_lex.rs
similarity index 64%
rename from the_rewrite/tests/test_lex.rs
rename to legacy_snippets/initial_rough_spaghetti_implementation/spaghetti_tests/test_lex.rs
index 3469b4a..ff3d946 100644
--- a/the_rewrite/tests/test_lex.rs
+++ b/legacy_snippets/initial_rough_spaghetti_implementation/spaghetti_tests/test_lex.rs
@@ -3,10 +3,10 @@ mod lex_tests {
#[test]
fn test_lex_basic_pair() {
- let document = String::from("(hello 'world')");
- match lex(&document) {
+ let document: &str = "(hello 'world')";
+ match lex(document) {
Ok(tree) => {
- assert_eq!(tree.to_string(), document);
+ assert_eq!(tree, document);
}
Err(s) => {
print!("{}\n", s);
@@ -17,10 +17,10 @@ mod lex_tests {
#[test]
fn test_lex_basic_list() {
- let document = String::from("(hello 'world' 1 2 3)");
- match lex(&document) {
+ let document: &str = "(hello 'world' 1 2 3)";
+ match lex(document) {
Ok(tree) => {
- assert_eq!(tree.to_string(), document);
+ assert_eq!(tree, document);
}
Err(s) => {
print!("{}\n", s);
@@ -31,10 +31,10 @@ mod lex_tests {
#[test]
fn test_lex_complex_list() {
- let document = String::from("(hello 'world' (1 2 (1 2 3)) 1 2 3)");
- match lex(&document) {
+ let document: &str = "(hello 'world' (1 2 (1 2 3)) 1 2 3)";
+ match lex(document) {
Ok(tree) => {
- assert_eq!(tree.to_string(), document);
+ assert_eq!(tree, document);
}
Err(s) => {
print!("{}\n", s);
@@ -45,11 +45,11 @@ mod lex_tests {
#[test]
fn test_bad_symbol() {
- let document = String::from("(as;dd)");
+ let document: &str = "(as;dd)";
let output: &str = "Problem lexing document: \"Unparsable token: as;dd\"";
- match lex(&document) {
+ match lex(document) {
Ok(tree) => {
- print!("Bad token yielded: {}\n", tree.to_string());
+ print!("Bad token yielded: {}\n", tree);
assert!(false);
}
Err(s) => {
@@ -60,10 +60,10 @@ mod lex_tests {
#[test]
fn test_list_delim_in_str() {
- let document = String::from("('(')");
- match lex(&document) {
+ let document: &str = "('(')";
+ match lex(document) {
Ok(tree) => {
- assert_eq!(tree.to_string(), document);
+ assert_eq!(tree, document);
}
Err(s) => {
print!("{}\n", s);
@@ -74,10 +74,10 @@ mod lex_tests {
#[test]
fn test_empty_string() {
- let document = String::from("('')");
- match lex(&document) {
+ let document: &str = "('')";
+ match lex(document) {
Ok(tree) => {
- assert_eq!(tree.to_string(), document);
+ assert_eq!(tree, document);
}
Err(s) => {
print!("{}\n", s);
@@ -88,11 +88,11 @@ mod lex_tests {
#[test]
fn test_unmatched_list_delim_flat() {
- let document = String::from("(one two");
+ let document: &str = "(one two";
let output: &str = "Problem lexing document: \"Unmatched list delimiter in input\"";
- match lex(&document) {
+ match lex(document) {
Ok(tree) => {
- print!("Bad token yielded: {}\n", tree.to_string());
+ print!("Bad token yielded: {}\n", tree);
assert!(false);
}
Err(s) => {
@@ -103,9 +103,9 @@ mod lex_tests {
#[test]
fn test_unmatched_list_delim_complex() {
- let document = String::from("(one two (three)");
+ let document: &str = "(one two (three)";
let output: &str = "Problem lexing document: \"Unmatched list delimiter in input\"";
- match lex(&document) {
+ match lex(document) {
Ok(tree) => {
print!("Bad token yielded: {}\n", tree);
assert!(false);
@@ -118,11 +118,11 @@ mod lex_tests {
#[test]
fn test_comment() {
- let document = String::from("#!/bin/relish\n(one two)");
+ let document: &str = "#!/bin/relish\n(one two)";
let output: &str = "(one two)";
- match lex(&document) {
+ match lex(document) {
Ok(tree) => {
- assert_eq!(tree.to_string(), output);
+ assert_eq!(tree, output);
}
Err(s) => {
print!("{}\n", s);
@@ -133,11 +133,11 @@ mod lex_tests {
#[test]
fn test_postline_comment() {
- let document = String::from("#!/bin/relish\n((one two)# another doc comment\n(three four))");
+ let document: &str = "#!/bin/relish\n((one two)# another doc comment\n(three four))";
let output: &str = "((one two) (three four))";
- match lex(&document) {
+ match lex(document) {
Ok(tree) => {
- assert_eq!(tree.to_string(), output.to_string());
+ assert_eq!(tree, output.to_string());
}
Err(s) => {
print!("{}\n", s);
@@ -148,11 +148,11 @@ mod lex_tests {
#[test]
fn test_inline_comment() {
- let document = String::from("#!/bin/relish\n((one two)\n# another doc comment\nthree)");
+ let document: &str = "#!/bin/relish\n((one two)\n# another doc comment\nthree)";
let output: &str = "((one two) three)";
- match lex(&document) {
+ match lex(document) {
Ok(tree) => {
- assert_eq!(tree.to_string(), output.to_string());
+ assert_eq!(tree, output);
}
Err(s) => {
print!("{}\n", s);
@@ -163,9 +163,9 @@ mod lex_tests {
#[test]
fn test_bad_token_list() {
- let document = String::from("(one t(wo)");
+ let document: &str = "(one t(wo)";
let output: &str = "Problem lexing document: \"list started in middle of another token\"";
- match lex(&document) {
+ match lex(document) {
Ok(tree) => {
print!("Bad token yielded: {}\n", tree);
assert!(false);
diff --git a/tests/test_lib_append.rs b/legacy_snippets/initial_rough_spaghetti_implementation/spaghetti_tests/test_lib_append.rs
similarity index 100%
rename from tests/test_lib_append.rs
rename to legacy_snippets/initial_rough_spaghetti_implementation/spaghetti_tests/test_lib_append.rs
diff --git a/tests/test_lib_str.rs b/legacy_snippets/initial_rough_spaghetti_implementation/spaghetti_tests/test_lib_str.rs
similarity index 100%
rename from tests/test_lib_str.rs
rename to legacy_snippets/initial_rough_spaghetti_implementation/spaghetti_tests/test_lib_str.rs
diff --git a/tests/test_lib_vars.rs b/legacy_snippets/initial_rough_spaghetti_implementation/spaghetti_tests/test_lib_vars.rs
similarity index 100%
rename from tests/test_lib_vars.rs
rename to legacy_snippets/initial_rough_spaghetti_implementation/spaghetti_tests/test_lib_vars.rs
diff --git a/src/stl.rs b/legacy_snippets/initial_rough_spaghetti_implementation/stl.rs
similarity index 100%
rename from src/stl.rs
rename to legacy_snippets/initial_rough_spaghetti_implementation/stl.rs
diff --git a/src/str.rs b/legacy_snippets/initial_rough_spaghetti_implementation/str.rs
similarity index 100%
rename from src/str.rs
rename to legacy_snippets/initial_rough_spaghetti_implementation/str.rs
diff --git a/src/vars.rs b/legacy_snippets/initial_rough_spaghetti_implementation/vars.rs
similarity index 100%
rename from src/vars.rs
rename to legacy_snippets/initial_rough_spaghetti_implementation/vars.rs
diff --git a/the_rewrite/src/func.rs b/legacy_snippets/seperate_tables/func.rs
similarity index 82%
rename from the_rewrite/src/func.rs
rename to legacy_snippets/seperate_tables/func.rs
index 38811b7..c38156e 100644
--- a/the_rewrite/src/func.rs
+++ b/legacy_snippets/seperate_tables/func.rs
@@ -17,14 +17,24 @@
use crate::eval::eval;
use crate::segment::{Seg, Ctr, Type};
-use crate::vars::VTable;
+use crate::vars::{VAR_TABLE, VTable, LIB_EXPORT};
use std::collections::HashMap;
use std::convert::TryInto;
+use lazy_static::lazy_static;
-pub struct FTable<'a>(HashMap>>);
+lazy_static! {
+ pub static ref FUNC_TABLE: FTable<'static> = {
+ let mut tab = FTable::new();
+ tab.declare(LIB_EXPORT);
+ tab
+ };
+}
-impl<'a> FTable<'a> {
- pub fn declare(&mut self, func: Box>) -> Option {
+
+pub struct FTable<'a> (HashMap);
+
+impl FTable<'_> {
+ pub fn declare(&mut self, func: Box) -> Option {
if let Operation::External(ref fun) = &func.function {
if let Args::Lazy(ref i) = func.args {
if fun.arg_syms.len() != i.clone().try_into().unwrap() {
@@ -42,7 +52,7 @@ impl<'a> FTable<'a> {
None
}
- pub fn get(&self, id: String) -> Option<&Box>> {
+ pub fn get(&self, id: String) -> Option<&Box> {
self.0.get(&id)
}
@@ -50,7 +60,7 @@ impl<'a> FTable<'a> {
self.0.remove(&id);
}
- pub fn new() -> FTable<'a> {
+ pub fn new() -> FTable<'static> {
FTable{0: HashMap::>::new()}
}
}
@@ -74,7 +84,7 @@ pub struct ExternalOperation<'a> {
* or a syntax tree to eval with the arguments
*/
pub enum Operation<'a> {
- Internal(Box Ctr<'a>>),
+ Internal(Box Ctr<'a>>),
External(ExternalOperation<'a>),
}
@@ -113,11 +123,9 @@ impl Args {
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;
@@ -140,7 +148,6 @@ impl Args {
arg_types[idx].to_string()
));
}
-
if idx == (arg_types.len() - 1) {
return Err("too many arguments".to_string());
}
@@ -152,8 +159,8 @@ impl Args {
}
}
-pub struct Function<'a> {
- pub function: Operation<'a>,
+pub struct Function {
+ pub function: Operation,
pub name: String,
pub args: Args,
@@ -164,19 +171,17 @@ pub struct Function<'a> {
pub eval_lazy: bool,
}
-impl<'a, 'b, 'c> Function<'a> {
+impl<'b, 'c> Function {
/* call
* routine is called by eval when a function call is detected
*/
pub fn func_call(
&self,
args: &'b Seg<'b>,
- vars: &'a mut VTable<'a>,
- funcs: &'a mut FTable<'a>,
) -> Result>, String> {
// put args in simplest desired form
let evaluated_args;
- match eval(args, vars, funcs, self.loose_syms, self.eval_lazy) {
+ match eval(args, self.loose_syms, self.eval_lazy) {
Ok(arg_data) => {
if let Ctr::Seg(ast) = *arg_data {
evaluated_args = *
@@ -201,18 +206,26 @@ impl<'a, 'b, 'c> Function<'a> {
* result of the final body is returned.
*/
match &self.function {
- Operation::Internal(ref f) => return Ok(Box::new(f(evaluated_args, vars, funcs))),
+ 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];
- vars.insert(f.arg_syms[n].clone(), Box::new(iter_arg));
+ 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, vars, funcs, self.loose_syms, true) {
+ match eval(data, self.loose_syms, true) {
Ok(ctr) => result = ctr,
Err(e) => return Err(e),
}
@@ -226,8 +239,13 @@ impl<'a, 'b, 'c> Function<'a> {
_ => panic!("function body not in standard form!"),
}
}
+
+ // clear local vars and restore previous values
for n in 0..f.arg_syms.len() {
- vars.remove(f.arg_syms[n]);
+ 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);
diff --git a/the_rewrite/src/vars.rs b/legacy_snippets/seperate_tables/vars.rs
similarity index 70%
rename from the_rewrite/src/vars.rs
rename to legacy_snippets/seperate_tables/vars.rs
index f143566..47196d5 100644
--- a/the_rewrite/src/vars.rs
+++ b/legacy_snippets/seperate_tables/vars.rs
@@ -16,10 +16,18 @@
*/
use crate::eval::eval;
-use crate::func::{Args, FTable, Function, Operation};
+use crate::func::{Args, Function, Operation};
use crate::segment::{Seg, Ctr};
use std::collections::HashMap;
use std::env;
+use lazy_static::lazy_static;
+
+lazy_static! {
+ pub static ref VAR_TABLE: VTable<'static> = {
+ VTable::new()
+ };
+}
+
/* Mapping between a string token and a tree of Segments
* The string token can be found in any Ctr::Symbol value
* it is expected that the trees stored are already evaluated
@@ -50,29 +58,45 @@ impl<'a, 'b> VTable<'a> {
}
}
-// returns a callback for the stdlib var export function with env_sync on or off
-pub fn get_export<'a>(env_cfg: bool) -> Function<'a>{
- return Function {
+// 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, vars: &mut VTable, funcs: &mut FTable| -> Ctr {
- _export_callback(ast, vars, funcs, env_cfg)
+ 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, vars: &mut VTable, funcs: &mut FTable, env_cfg: bool) -> Ctr<'a> {
+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), vars, funcs, false, true) {
+ Ctr::Seg(data_tree) => match eval(&Box::new(data_tree), false, true) {
Ok(seg) => match *seg {
Ctr::Seg(val) => {
- vars.insert(identifier.clone(), val.car);
+ SYM_TABLE.declare(Symbol {
+ value: UserVar(val.car),
+ name: identifier.clone(),
+ });
if env_cfg {
env::set_var(identifier.clone(), val.car.to_string())
}
@@ -82,7 +106,7 @@ fn _export_callback<'a> (ast: &Seg, vars: &mut VTable, funcs: &mut FTable, env_c
Err(e) => eprintln!("couldnt eval symbol: {}", e),
},
Ctr::None => {
- vars.remove(identifier.to_string());
+ VAR_TABLE.remove(identifier.to_string());
if env_cfg {
env::remove_var(identifier.to_string());
}
diff --git a/src/eval.rs b/src/eval.rs
index 23fbfc7..c09b6b8 100644
--- a/src/eval.rs
+++ b/src/eval.rs
@@ -15,130 +15,129 @@
* along with this program. If not, see .
*/
-use crate::func::{func_call, FTable};
-use crate::segment::{new_ast, Ast, Ctr};
-use crate::vars::VTable;
-use std::cell::RefCell;
-use std::rc::Rc;
+
+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(
- ast: Ast,
- vars: Rc>,
- funcs: Rc>,
+ ast: &Seg,
sym_loose: bool,
-) -> Result {
- let mut car = ast.borrow().clone().car;
- let mut cdr = ast.borrow().clone().cdr;
- let ret = new_ast(Ctr::None, Ctr::None);
- let mut iter = ret.clone();
+ call_lazy: bool,
+) -> Result, String> {
+ // data to return
+ let mut ret = Box::from(Ctr::None);
+
+ // to be assigned from cloned/evaled data
+ let mut car;
+ let mut cdr = Box::from(Ctr::None);
+
+ // lets me redirect the input
+ 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(ref tok) = car {
- if let Some(val) = vars.borrow().get(tok) {
- car = (**val).clone();
+ 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;
}
}
- // another check to detect if we may have a function call
- if let Ctr::Symbol(ref tok) = car {
- match cdr.clone() {
- Ctr::Seg(ast) => {
- if let Some(func) = funcs.borrow().get(tok) {
- return func_call(func.clone(), ast.clone(), vars.clone(), funcs.clone());
- } else if !sym_loose {
- return Err(format!("Couldnt find definition of {}.", tok));
+ // 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(func) = funcs.borrow().get(tok) {
- return func_call(
- func.clone(),
- new_ast(Ctr::None, Ctr::None),
- vars.clone(),
- funcs.clone(),
- );
- } 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!")),
}
- _ => return Err(format!("Arguments to function not a list!")),
}
}
+ // iterate over ast and build out ret
let mut none = false;
while !none {
- match car {
- // if LIST: call eval inner on it with first_item=true
+ match &**arg_car {
Ctr::Seg(ref inner) => {
- match eval(inner.clone(), vars.clone(), funcs.clone(), sym_loose) {
- Ok(res) => (*iter).borrow_mut().car = res,
+ match eval(inner, vars, funcs, sym_loose, call_lazy) {
+ Ok(res) => car = res,
Err(e) => return Err(format!("Evaluation error: {}", e)),
}
}
- // if SYMBOL: unwrap naively
Ctr::Symbol(ref tok) => {
- if let Some(val) = vars.borrow().get(&tok.clone()) {
- (*iter).borrow_mut().car = (**val).clone();
+ binding_for_vtable_get = vars.get(tok.clone());
+ if let Some(ref val) = binding_for_vtable_get {
+ car = val.clone();
} else if sym_loose {
- (*iter).borrow_mut().car = Ctr::Symbol(tok.to_string());
+ car = arg_car.clone()
} else {
- return Err(format!("Undefined variable: {}", tok));
+ return Err(format!("Undefined variable: {}", tok.clone()));
}
}
- // if OTHER: clone and set
_ => {
- (*iter).borrow_mut().car = car.clone();
+ car = arg_car.clone();
}
}
- match cdr {
- // if SYMBOL: unwrap naively, then end
+ match &**arg_cdr {
Ctr::Symbol(ref tok) => {
- if let Some(val) = vars.borrow().get(&tok.clone()) {
- (*iter).borrow_mut().car = (**val).clone();
+ if let Some(val) = vars.get(tok.clone()) {
+ cdr = val.clone();
} else if sym_loose {
- (*iter).borrow_mut().car = Ctr::Symbol(tok.to_string());
+ cdr = ast.cdr.clone()
} else {
- return Err(format!("Undefined variable: {}", tok));
+ return Err(format!("Undefined variable: {}", tok.clone()));
}
none = true;
}
- // if LIST:
- // - iter.cdr = new_ast(None, None)
- // - iter = iter.cdr
- // - car = cdr.car
- // - cdr = cdr.cdr
- // - LOOP
- Ctr::Seg(next) => {
- let n = new_ast(Ctr::None, Ctr::None);
- iter.borrow_mut().cdr = Ctr::Seg(n.clone());
- iter = n;
- car = next.borrow().clone().car;
- cdr = next.borrow().clone().cdr;
+ Ctr::Seg(ref next) => {
+ if let Ctr::None = *ret {
+ *ret = Ctr::Seg(Seg::from(car, cdr.clone()))
+ } else if let Ctr::Seg(ref mut s) = *ret {
+ s.append(Box::from(Ctr::Seg(Seg::from(car, cdr.clone()))))
+ }
+ arg_car = &next.car;
+ arg_cdr = &next.cdr
}
// if OTHER: clone and set, and then end
_ => {
- (*iter).borrow_mut().cdr = cdr.clone();
+ cdr = ast.cdr.clone();
none = true;
}
}
- if let Ctr::None = car {
- if let Ctr::None = cdr {
+ if let Ctr::None = **arg_car {
+ if let Ctr::None = **arg_cdr {
none = true;
}
}
}
- return Ok(Ctr::Seg(ret));
+ return Ok(ret);
}
diff --git a/src/lex.rs b/src/lex.rs
index 87700c8..337849b 100644
--- a/src/lex.rs
+++ b/src/lex.rs
@@ -15,7 +15,7 @@
* along with this program. If not, see .
*/
-use crate::segment::{list_append, Ctr, Seg};
+use crate::segment::{Ctr, Seg};
const UNMATCHED_STR_DELIM: &str = "Unmatched string delimiter in input";
const UNMATCHED_LIST_DELIM: &str = "Unmatched list delimiter in input";
@@ -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: String) -> Result>, String> {
+pub fn lex<'a>(document: &'a 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: 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<'a>(document: &'a String) -> Result, String> {
let doc_len = document.len();
if doc_len == 0 {
@@ -120,8 +120,7 @@ fn process<'a>(document: &'a String) -> Result>, String> {
return Err("list started in middle of another token".to_string());
}
- ref_stack.push(Box::new(Seg::new()));
-
+ ref_stack.push(Seg::new());
delim_stack.push(')');
}
// begin parsing a string
@@ -152,42 +151,47 @@ fn process<'a>(document: &'a String) -> Result>, String> {
return Err("Empty token".to_string());
}
- let mut current_seg = ref_stack.pop();
- let mut obj;
+ let mut current_seg = ref_stack.pop().unwrap();
+ let obj;
if is_str {
- obj = Ctr::String(token);
+ obj = Box::from(Ctr::String(token));
is_str = false;
token = String::new();
+ current_seg.append(obj);
} else if token.len() > 0 {
if token == "true" {
- obj = Ctr::Bool(true);
+ obj = Box::from(Ctr::Bool(true));
} else if token == "false" {
- obj = Ctr::Bool(false);
+ obj = Box::from(Ctr::Bool(false));
} else if let Ok(i) = token.parse::() {
- obj = Ctr::Integer(i);
+ obj = Box::from(Ctr::Integer(i));
} else if let Ok(f) = token.parse::() {
- obj = Ctr::Float(f);
+ obj = Box::from(Ctr::Float(f));
} else if let Some(s) = tok_is_symbol(&token) {
- obj = Ctr::Symbol(s);
+ obj = Box::from(Ctr::Symbol(s));
} else {
return Err(format!("Unparsable token: {}", token));
}
token = String::new();
+ current_seg.append(obj.clone());
}
- list_append(current_seg, obj);
-
if alloc_list {
// return if we have finished the document
if ref_stack.len() == 0 {
- return Ok(current_seg);
+ return Ok(Box::new(current_seg));
}
- // shortening this will lead to naught but pain
- obj = Ctr::Seg(current_seg.into_raw());
- current_seg = ref_stack.pop();
- list_append(current_seg, obj);
+ let t = current_seg;
+ current_seg = ref_stack.pop().unwrap();
+ /* TODO: is there a way to do this that doesnt
+ * involve needlessly copying heap data? I am
+ * not sure what optimizations rustc performs
+ * but I assume this should not end up copying
+ * contained segments around.
+ */
+ current_seg.append(Box::from(Ctr::Seg(t)));
}
ref_stack.push(current_seg);
diff --git a/src/lib.rs b/src/lib.rs
index 6a48eef..f2f1862 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -15,35 +15,37 @@
* along with this program. If not, see .
*/
-#![feature(derive_default_enum)]
-
-mod append;
+/*mod append;
mod config;
+ */
mod eval;
mod func;
mod lex;
mod segment;
-mod stl;
-mod str;
mod vars;
+/*mod stl;
+mod str;*/
+
+extern crate lazy_static;
pub mod ast {
pub use crate::eval::eval;
- pub use crate::func::{
- func_call, func_declare, Args, ExternalOperation, FTable, Function, Operation,
- };
pub use crate::lex::lex;
pub use crate::segment::{Ctr, Seg, Type};
- pub use crate::vars::{define, VTable};
+ pub use crate::sym::{
+ SYM_TABLE, SymTable, Symbol,
+ UserFn, ValueType, Args,
+ LIB_EXPORT_ENV, LIB_EXPORT_NO_ENV
+ };
}
-pub mod stdlib {
+/*pub mod stdlib {
pub use crate::append::get_append;
pub use crate::stl::get_stdlib;
pub use crate::str::{get_concat, get_echo};
pub use crate::vars::get_export;
-}
+}*/
-pub mod aux {
+/*pub mod aux {
pub use crate::config::configure;
-}
+}*/
diff --git a/src/segment.rs b/src/segment.rs
index 3535aa0..1b463ae 100644
--- a/src/segment.rs
+++ b/src/segment.rs
@@ -15,11 +15,12 @@
* along with this program. If not, see .
*/
use std::fmt;
-
+use std::marker::PhantomData;
+use std::ops::Index;
// Container
-#[derive(Debug, Clone, Default)]
-pub enum Ctr <'a> {
+#[derive(Debug, Default)]
+pub enum Ctr<'a> {
Symbol(String),
String(String),
Integer(i128),
@@ -45,20 +46,31 @@ pub enum Type {
/* Segment
* Holds two Containers.
* Basic building block for more complex data structures.
+ * I was going to call it Cell and then I learned about
+ * how important RefCells were in Rust
*/
-#[derive(Clone, Debug, Default)]
-pub struct Seg <'a> {
+#[derive(Debug)]
+pub struct Seg<'a> {
/* "Contents of Address Register"
* Historical way of referring to the first value in a cell.
*/
- pub car: &mut Ctr<'a>,
+ pub car: Box>,
/* "Contents of Decrement Register"
* Historical way of referring to the second value in a cell.
*/
- pub cdr: &mut Ctr<'a>,
+ 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 ()>
}
+static NOTHING: Ctr = Ctr::None;
+
impl Ctr<'_> {
pub fn to_type(&self) -> Type {
match self {
@@ -74,25 +86,140 @@ impl Ctr<'_> {
}
-fn seg_to_string(s: &Seg, parens: bool) -> String {
- let mut string = String::new();
- match s.car {
- Ctr::None => string.push_str(""),
- _ => string.push_str(s.car),
- }
- string.push(' ');
- match s.cdr {
- Ctr::Seg(inner) => string.push_str(seg_to_string(inner, false)),
- Ctr::None => {},
- _ => string.push_str(s.cdr),
+impl<'a> Seg<'a> {
+ /* 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>) {
+ if let Ctr::None = &*(self.car) {
+ self.car = obj;
+ return
+ }
+
+ if let Ctr::Seg(s) = &mut *(self.cdr) {
+ s.append(obj);
+ return
+ }
+
+ if let Ctr::None = &mut *(self.cdr) {
+ self.cdr = Box::new(Ctr::Seg(Seg::from_mono(obj)));
+ // pray for memory lost to the void
+ }
}
- if parens {
- String::from("(" + string + ")")
+ /* applies a function across a list in standard form
+ * function must take a Ctr and return a bool
+ * short circuits on the first false returned.
+ * also returns false on a non standard form list
+ */
+ pub fn circuit bool>(&self, func: &mut F) -> bool {
+ if func(&self.car) {
+ match &*(self.cdr) {
+ Ctr::None => true,
+ Ctr::Seg(l) => l.circuit(func),
+ _ => false,
+ }
+ } else {
+ false
+ }
+ }
+
+ pub fn from_mono(arg: Box>) -> Seg<'a> {
+ return Seg{
+ car: arg,
+ cdr: Box::new(Ctr::None),
+ _lifetime_variance_determinant: PhantomData,
+ }
+ }
+
+ pub fn from(car: Box>, cdr: Box>) -> Seg<'a> {
+ return Seg{
+ car,
+ cdr,
+ _lifetime_variance_determinant: PhantomData,
+ }
+ }
+
+ /* recurs over ast assumed to be list in standard form
+ * returns length
+ */
+ pub fn len(&self) -> u128 {
+ let mut len = 0;
+ self.circuit(&mut |_c: &Ctr| -> bool { len += 1; true });
+ len
+ }
+
+ pub fn new() -> Seg<'a> {
+ return Seg{
+ car: Box::new(Ctr::None),
+ cdr: Box::new(Ctr::None),
+ _lifetime_variance_determinant: PhantomData,
+ }
}
}
-impl fmt::Display for Ctr <'_> {
+fn seg_to_string(s: &Seg, parens: bool) -> String {
+ let mut string = String::new();
+ if parens { string.push('('); }
+ match *(s.car) {
+ Ctr::None => string.push_str(""),
+ _ => string.push_str(&s.car.to_string()),
+ }
+ string.push(' ');
+ match &*(s.cdr) {
+ Ctr::Seg(inner) => string.push_str(&seg_to_string(&inner, false)),
+ Ctr::None => {string.pop();},
+ _ => string.push_str(&s.cdr.to_string()),
+ }
+ if parens { string.push(')'); }
+
+ string
+}
+
+impl<'a> Clone for Seg<'a> {
+ fn clone(&self) -> Seg<'a> {
+ return Seg{
+ car: self.car.clone(),
+ cdr: self.cdr.clone(),
+ _lifetime_variance_determinant: PhantomData,
+ }
+ }
+}
+
+impl<'a> Index for Seg<'a> {
+ type Output = Ctr<'a>;
+
+ fn index(&self, idx: usize) -> &Self::Output {
+ if idx == 0 {
+ return &self.car;
+ }
+
+ if let Ctr::Seg(ref s) = *self.cdr {
+ return s.index(idx - 1)
+ }
+
+ return &NOTHING;
+ }
+}
+
+impl<'a> Clone for Ctr<'a> {
+ fn clone(&self) -> Ctr<'a> {
+ 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::Seg(s) => Ctr::Seg(s.clone()),
+ Ctr::None => Ctr::None,
+ }
+ }
+}
+
+impl fmt::Display for Ctr<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Ctr::Symbol(s) => write!(f, "{}", s),
@@ -100,14 +227,14 @@ impl fmt::Display for Ctr <'_> {
Ctr::Integer(s) => write!(f, "{}", s),
Ctr::Float(s) => write!(f, "{}", s),
Ctr::Bool(s) => {
- if s {
+ if *s {
write!(f, "T")
} else {
write!(f, "F")
}
},
Ctr::Seg(s) => write!(f, "{}", s),
- Ctr::None => Ok(),
+ Ctr::None => Ok(()),
}
}
}
@@ -118,16 +245,6 @@ impl fmt::Display for Seg<'_> {
}
}
-impl Iterator for Seg<'_> {
- fn next(&self) -> Option<&Seg> {
- if let Ctr::Seg(s) = self.cdr {
- Ok(s)
- } else {
- None()
- }
- }
-}
-
impl Type {
pub fn to_string(&self) -> String {
let ret: &str;
@@ -144,67 +261,3 @@ impl Type {
ret.to_owned()
}
}
-
-/* applies a function across a list in standard form
- * function must take a Ctr and return a bool
- * short circuits on the first false returned.
- * also returns false on a non standard form list
- */
-pub fn circuit(list: &Seg, func: &mut F) -> bool {
- if func(&list.car) {
- match list.cdr {
- Ctr::None => true,
- Ctr::Seg(l) => circuit(l, func),
- _ => false,
- }
- } else {
- false
- }
-}
-
-/* recurs over ast assumed to be list in standard form
- * returns length
- */
-pub fn list_len(list: &Seg) -> u128 {
- let mut len = 0;
- circuit(list, &circuit(&mut |c: &Ctr| -> bool { len += 1; true }))
-}
-
-/* recurs over tree assumed to be list in standard form
- * returns clone of ctr at index provided
- *
- * TODO: return result (or option?)
- */
-pub fn list_idx<'a>(list: &Seg, idx: u128) -> Ctr<'a> {
- if idx > 0 {
- if let Ctr::Seg(s) = list.car {
- list_idx(s, idx - 1)
- } else if idx == 1 {
- list.cdr
- } else {
- Ctr::None
- }
- } else {
- list.car
- }
-}
-
-/* recurs over tree assumed to be list in standard form
- * appends object to end of list
- *
- * TODO: return result
- */
-pub fn list_append<'a>(list: &Seg, obj: Ctr) {
- if let Ctr::None = list.car {
- list.car = obj;
- return
- }
-
- if let Ctr::Seg(s) = list.cdr {
- list_append(s, obj)
- }
-
- if let Ctr::None = list.cdr {
- list.cdr = Ctr::Seg(&Seg{car:obj, cdr:Ctr::None})
- }
-}
diff --git a/sym.rs b/sym.rs
new file mode 100644
index 0000000..fc6a442
--- /dev/null
+++ b/sym.rs
@@ -0,0 +1,294 @@
+/* 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;
+}
diff --git a/tests/test_lex.rs b/tests/test_lex.rs
index ff3d946..3469b4a 100644
--- a/tests/test_lex.rs
+++ b/tests/test_lex.rs
@@ -3,10 +3,10 @@ mod lex_tests {
#[test]
fn test_lex_basic_pair() {
- let document: &str = "(hello 'world')";
- match lex(document) {
+ let document = String::from("(hello 'world')");
+ match lex(&document) {
Ok(tree) => {
- assert_eq!(tree, document);
+ assert_eq!(tree.to_string(), document);
}
Err(s) => {
print!("{}\n", s);
@@ -17,10 +17,10 @@ mod lex_tests {
#[test]
fn test_lex_basic_list() {
- let document: &str = "(hello 'world' 1 2 3)";
- match lex(document) {
+ let document = String::from("(hello 'world' 1 2 3)");
+ match lex(&document) {
Ok(tree) => {
- assert_eq!(tree, document);
+ assert_eq!(tree.to_string(), document);
}
Err(s) => {
print!("{}\n", s);
@@ -31,10 +31,10 @@ mod lex_tests {
#[test]
fn test_lex_complex_list() {
- let document: &str = "(hello 'world' (1 2 (1 2 3)) 1 2 3)";
- match lex(document) {
+ let document = String::from("(hello 'world' (1 2 (1 2 3)) 1 2 3)");
+ match lex(&document) {
Ok(tree) => {
- assert_eq!(tree, document);
+ assert_eq!(tree.to_string(), document);
}
Err(s) => {
print!("{}\n", s);
@@ -45,11 +45,11 @@ mod lex_tests {
#[test]
fn test_bad_symbol() {
- let document: &str = "(as;dd)";
+ let document = String::from("(as;dd)");
let output: &str = "Problem lexing document: \"Unparsable token: as;dd\"";
- match lex(document) {
+ match lex(&document) {
Ok(tree) => {
- print!("Bad token yielded: {}\n", tree);
+ print!("Bad token yielded: {}\n", tree.to_string());
assert!(false);
}
Err(s) => {
@@ -60,10 +60,10 @@ mod lex_tests {
#[test]
fn test_list_delim_in_str() {
- let document: &str = "('(')";
- match lex(document) {
+ let document = String::from("('(')");
+ match lex(&document) {
Ok(tree) => {
- assert_eq!(tree, document);
+ assert_eq!(tree.to_string(), document);
}
Err(s) => {
print!("{}\n", s);
@@ -74,10 +74,10 @@ mod lex_tests {
#[test]
fn test_empty_string() {
- let document: &str = "('')";
- match lex(document) {
+ let document = String::from("('')");
+ match lex(&document) {
Ok(tree) => {
- assert_eq!(tree, document);
+ assert_eq!(tree.to_string(), document);
}
Err(s) => {
print!("{}\n", s);
@@ -88,11 +88,11 @@ mod lex_tests {
#[test]
fn test_unmatched_list_delim_flat() {
- let document: &str = "(one two";
+ let document = String::from("(one two");
let output: &str = "Problem lexing document: \"Unmatched list delimiter in input\"";
- match lex(document) {
+ match lex(&document) {
Ok(tree) => {
- print!("Bad token yielded: {}\n", tree);
+ print!("Bad token yielded: {}\n", tree.to_string());
assert!(false);
}
Err(s) => {
@@ -103,9 +103,9 @@ mod lex_tests {
#[test]
fn test_unmatched_list_delim_complex() {
- let document: &str = "(one two (three)";
+ let document = String::from("(one two (three)");
let output: &str = "Problem lexing document: \"Unmatched list delimiter in input\"";
- match lex(document) {
+ match lex(&document) {
Ok(tree) => {
print!("Bad token yielded: {}\n", tree);
assert!(false);
@@ -118,11 +118,11 @@ mod lex_tests {
#[test]
fn test_comment() {
- let document: &str = "#!/bin/relish\n(one two)";
+ let document = String::from("#!/bin/relish\n(one two)");
let output: &str = "(one two)";
- match lex(document) {
+ match lex(&document) {
Ok(tree) => {
- assert_eq!(tree, output);
+ assert_eq!(tree.to_string(), output);
}
Err(s) => {
print!("{}\n", s);
@@ -133,11 +133,11 @@ mod lex_tests {
#[test]
fn test_postline_comment() {
- let document: &str = "#!/bin/relish\n((one two)# another doc comment\n(three four))";
+ let document = String::from("#!/bin/relish\n((one two)# another doc comment\n(three four))");
let output: &str = "((one two) (three four))";
- match lex(document) {
+ match lex(&document) {
Ok(tree) => {
- assert_eq!(tree, output.to_string());
+ assert_eq!(tree.to_string(), output.to_string());
}
Err(s) => {
print!("{}\n", s);
@@ -148,11 +148,11 @@ mod lex_tests {
#[test]
fn test_inline_comment() {
- let document: &str = "#!/bin/relish\n((one two)\n# another doc comment\nthree)";
+ let document = String::from("#!/bin/relish\n((one two)\n# another doc comment\nthree)");
let output: &str = "((one two) three)";
- match lex(document) {
+ match lex(&document) {
Ok(tree) => {
- assert_eq!(tree, output);
+ assert_eq!(tree.to_string(), output.to_string());
}
Err(s) => {
print!("{}\n", s);
@@ -163,9 +163,9 @@ mod lex_tests {
#[test]
fn test_bad_token_list() {
- let document: &str = "(one t(wo)";
+ let document = String::from("(one t(wo)");
let output: &str = "Problem lexing document: \"list started in middle of another token\"";
- match lex(document) {
+ match lex(&document) {
Ok(tree) => {
print!("Bad token yielded: {}\n", tree);
assert!(false);
diff --git a/the_rewrite/tests/test_vars.rs b/tests/test_vars.rs
similarity index 100%
rename from the_rewrite/tests/test_vars.rs
rename to tests/test_vars.rs
diff --git a/the_rewrite/Cargo.toml b/the_rewrite/Cargo.toml
deleted file mode 100644
index 7214a92..0000000
--- a/the_rewrite/Cargo.toml
+++ /dev/null
@@ -1,11 +0,0 @@
-[package]
-name = "relish"
-version = "0.1.0"
-authors = ["Aidan "]
-edition = "2018"
-
-# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
-
-[dependencies]
-dirs = "3.0"
-rustyline = "8.2.0"
\ No newline at end of file
diff --git a/the_rewrite/src/segment.rs b/the_rewrite/src/segment.rs
deleted file mode 100644
index 1b463ae..0000000
--- a/the_rewrite/src/segment.rs
+++ /dev/null
@@ -1,263 +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 std::fmt;
-use std::marker::PhantomData;
-use std::ops::Index;
-
-// Container
-#[derive(Debug, Default)]
-pub enum Ctr<'a> {
- Symbol(String),
- String(String),
- Integer(i128),
- Float(f64),
- Bool(bool),
- Seg(Seg<'a>),
- #[default]
- None,
-}
-
-// Type of Container
-#[derive(PartialEq, Clone)]
-pub enum Type {
- Symbol,
- String,
- Integer,
- Float,
- Bool,
- Seg,
- None,
-}
-
-/* Segment
- * Holds two Containers.
- * Basic building block for more complex data structures.
- * I was going to call it Cell and then I learned about
- * how important RefCells were in Rust
- */
-#[derive(Debug)]
-pub struct Seg<'a> {
- /* "Contents of Address Register"
- * Historical way of referring to the first value in a cell.
- */
- pub car: Box>,
-
- /* "Contents of Decrement Register"
- * Historical way of referring to the second value in a cell.
- */
- 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 ()>
-}
-
-static NOTHING: Ctr = Ctr::None;
-
-impl Ctr<'_> {
- pub fn to_type(&self) -> Type {
- match self {
- Ctr::Symbol(_s) => Type::Symbol,
- Ctr::String(_s) => Type::String,
- Ctr::Integer(_s) => Type::Integer,
- Ctr::Float(_s) => Type::Float,
- Ctr::Bool(_s) => Type::Bool,
- Ctr::Seg(_s) => Type::Seg,
- Ctr::None => Type::None,
- }
- }
-
-}
-
-impl<'a> Seg<'a> {
- /* 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>) {
- if let Ctr::None = &*(self.car) {
- self.car = obj;
- return
- }
-
- if let Ctr::Seg(s) = &mut *(self.cdr) {
- s.append(obj);
- return
- }
-
- if let Ctr::None = &mut *(self.cdr) {
- self.cdr = Box::new(Ctr::Seg(Seg::from_mono(obj)));
- // pray for memory lost to the void
- }
- }
-
- /* applies a function across a list in standard form
- * function must take a Ctr and return a bool
- * short circuits on the first false returned.
- * also returns false on a non standard form list
- */
- pub fn circuit bool>(&self, func: &mut F) -> bool {
- if func(&self.car) {
- match &*(self.cdr) {
- Ctr::None => true,
- Ctr::Seg(l) => l.circuit(func),
- _ => false,
- }
- } else {
- false
- }
- }
-
- pub fn from_mono(arg: Box>) -> Seg<'a> {
- return Seg{
- car: arg,
- cdr: Box::new(Ctr::None),
- _lifetime_variance_determinant: PhantomData,
- }
- }
-
- pub fn from(car: Box>, cdr: Box>) -> Seg<'a> {
- return Seg{
- car,
- cdr,
- _lifetime_variance_determinant: PhantomData,
- }
- }
-
- /* recurs over ast assumed to be list in standard form
- * returns length
- */
- pub fn len(&self) -> u128 {
- let mut len = 0;
- self.circuit(&mut |_c: &Ctr| -> bool { len += 1; true });
- len
- }
-
- pub fn new() -> Seg<'a> {
- return Seg{
- car: Box::new(Ctr::None),
- cdr: Box::new(Ctr::None),
- _lifetime_variance_determinant: PhantomData,
- }
- }
-}
-
-fn seg_to_string(s: &Seg, parens: bool) -> String {
- let mut string = String::new();
- if parens { string.push('('); }
- match *(s.car) {
- Ctr::None => string.push_str(""),
- _ => string.push_str(&s.car.to_string()),
- }
- string.push(' ');
- match &*(s.cdr) {
- Ctr::Seg(inner) => string.push_str(&seg_to_string(&inner, false)),
- Ctr::None => {string.pop();},
- _ => string.push_str(&s.cdr.to_string()),
- }
- if parens { string.push(')'); }
-
- string
-}
-
-impl<'a> Clone for Seg<'a> {
- fn clone(&self) -> Seg<'a> {
- return Seg{
- car: self.car.clone(),
- cdr: self.cdr.clone(),
- _lifetime_variance_determinant: PhantomData,
- }
- }
-}
-
-impl<'a> Index for Seg<'a> {
- type Output = Ctr<'a>;
-
- fn index(&self, idx: usize) -> &Self::Output {
- if idx == 0 {
- return &self.car;
- }
-
- if let Ctr::Seg(ref s) = *self.cdr {
- return s.index(idx - 1)
- }
-
- return &NOTHING;
- }
-}
-
-impl<'a> Clone for Ctr<'a> {
- fn clone(&self) -> Ctr<'a> {
- 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::Seg(s) => Ctr::Seg(s.clone()),
- Ctr::None => Ctr::None,
- }
- }
-}
-
-impl fmt::Display for Ctr<'_> {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- match self {
- Ctr::Symbol(s) => write!(f, "{}", s),
- Ctr::String(s) => write!(f, "\'{}\'", s),
- Ctr::Integer(s) => write!(f, "{}", s),
- Ctr::Float(s) => write!(f, "{}", s),
- Ctr::Bool(s) => {
- if *s {
- write!(f, "T")
- } else {
- write!(f, "F")
- }
- },
- Ctr::Seg(s) => write!(f, "{}", s),
- Ctr::None => Ok(()),
- }
- }
-}
-
-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",
- }
-
- ret.to_owned()
- }
-}