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; -}