diff --git a/src/cell.rs b/src/cell.rs index fd8e6a6..366a884 100644 --- a/src/cell.rs +++ b/src/cell.rs @@ -20,6 +20,7 @@ use std::boxed::Box; // Container +#[derive(Clone)] pub enum Ctr { Symbol(String), String(String), @@ -32,6 +33,7 @@ pub enum Ctr { // Type of Container #[derive(PartialEq)] +#[derive(Clone)] pub enum Type { Symbol, String, @@ -46,6 +48,7 @@ pub enum Type { * Holds two Containers. * Basic building block for more complex data structures. */ +#[derive(Clone)] pub struct Cell { /* "Cell Address Register" * Historical way of referring to the first value in a cell. @@ -165,7 +168,7 @@ impl Cell { * short circuits on the first false returned. * also returns false on a non standard form list */ - pub fn circuit bool>(&mut self, func: F) -> bool{ + pub fn circuit bool>(&self, func: F) -> bool{ if func(self.car) { match self.cdr { Ctr::None => true, @@ -213,4 +216,31 @@ impl Cell { _ => 1 } } + + /* iterates through a list. + * returns a COPY of the cell at the cooresponding index + * WARNING: DESTRUCTIVE? + */ + pub fn index(&self, idx: usize) -> Ctr { + if idx > 0 { + match self.cdr { + Ctr::None => Ctr::None, + Ctr::Cell(c) => c.index(idx-1), + _ => { + if idx == 1 { + self.cdr + } else { + Ctr::None + } + } + } + } else { + match self.car { + Ctr::None => Ctr::None, + _ => { + self.cdr + } + } + } + } } diff --git a/src/eval.rs b/src/eval.rs new file mode 100644 index 0000000..3fc355a --- /dev/null +++ b/src/eval.rs @@ -0,0 +1,34 @@ +/* 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::boxed::Box; +use crate::cell::{Cell}; +use crate::func::FTable; +use crate::vars::VTable; + +/* iterates over a syntax tree + * returns a NEW LIST of values + * representing the simplest possible form of the input + */ +pub fn eval( + _ast: Box, + _vars: Box, + _funcs: Box, + _sym_loose: bool +) -> Result, String> { + Err("Unimplemented".to_string()) +} diff --git a/src/func.rs b/src/func.rs index 416da33..698e34a 100644 --- a/src/func.rs +++ b/src/func.rs @@ -16,14 +16,33 @@ */ use std::boxed::Box; +use std::convert::TryInto; use std::collections::HashMap; use crate::cell::{Ctr, Cell, Type}; use crate::vars::{VTable}; use crate::eval::eval; +pub type FTable = HashMap>; + // Standardized function signature for stdlib functions -pub type Operation = fn(Box, Box, Box) -> Box; -pub type FTable = HashMap; +pub type InternalOperation = fn(Box, Box, Box) -> Box; +pub struct ExternalOperation { + // 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? + ast: Box, + // list of argument string tokens + arg_syms: Vec +} + +/* A stored function may either be a pointer to a function + * or a syntax tree to eval with the arguments + */ +pub enum Operation { + Internal(InternalOperation), + External(ExternalOperation) +} /* Function Args * If Lazy, is an integer denoting number of args @@ -39,7 +58,6 @@ pub enum Args { pub struct Function { pub function: Operation, pub name: String, - pub times_called: u128, pub args: Args, // dont fail on undefined symbol (passed to eval) @@ -53,9 +71,9 @@ impl Function { /* call * routine is called by eval when a function call is detected */ - pub fn call_function ( - &mut self, - args: Box, + pub fn call( + &self, + args: Box, vars: Box, funcs: Box ) -> Result, String> { @@ -66,33 +84,31 @@ impl Function { Err(s) => return Err( format!( "error evaluating args to {}: {}", - func.name, + self.name, s ) ) } } - let mut passes = false; match self.args { Args::Lazy(num) => { if num < 0 { - passes = true } - if !(num == (args.len() - 1)) { + if !(num == (n_args.len() - 1)) { return Err(format!("expected {} args in call to {}", num, self.name)) } }, Args::Strict(arg_types) => { let idx: usize = 0; - passes = args.circuit(|c: Ctr| { + let mut passes = args.circuit(|c: Ctr| { if idx >= arg_types.len() { return false; } - if let c = Ctr::None { + if let Ctr::None = c { return false; } @@ -104,7 +120,7 @@ impl Function { }); if passes && idx < (arg_types.len() - 1) { - Err(format!( + return Err(format!( "{} too little arguments in call to {}", arg_types.len() - (idx + 1), self.name @@ -113,7 +129,7 @@ impl Function { if !passes { if idx < (arg_types.len() - 1) { - Err(format!( + return Err(format!( "argument {} in call to {} is of wrong type (expected {})", idx + 1, self.name, @@ -122,7 +138,7 @@ impl Function { } if idx == (arg_types.len() - 1) { - Err(format!( + return Err(format!( "too many arguments in call to {}", self.name )); @@ -131,24 +147,54 @@ impl Function { } } - self.times_called += 1; - return Ok((self.function)(args, vars, funcs)); + match self.function { + Operation::Internal(f) => Ok((f)(n_args, vars, funcs)), + Operation::External(f) => { + // copy var table and add args + let temp = vars.clone(); + for n in 0..f.arg_syms.len() { + temp.insert( + f.arg_syms[n], + Box::new(n_args.index(n)) + ); + } + eval(f.ast, temp, funcs, self.loose_syms) + } + } } } -impl FTable { - pub fn declare( - &mut self, - f: Function - ) { - // memory leak here? where does Function go? maybe it should be boxed.... - self.insert(f.name, f); +pub fn declare( + ft: Box, + f: Box +) -> Option { + if let Operation::External(fun) = f.function { + if let Args::Lazy(i) = f.args { + if fun.arg_syms.len() != i.try_into().unwrap() { + return Some( + "external function must have lazy args equal to declared arg_syms length" + .to_string() + ); + } + } else { + return Some( + "external function must have lazy args" + .to_string() + ); + } } - pub fn get( - &mut self, - identifier: String - ) -> Result, String> { - self.get(identifier) + ft.insert(f.name, f); + None +} + +pub fn get( + ft: Box, + identifier: String +) -> Option> { + if let Some(f) = ft.get(&identifier) { + Some(*f) + } else { + None } } diff --git a/src/lex.rs b/src/lex.rs index dc8cbc2..8232e45 100644 --- a/src/lex.rs +++ b/src/lex.rs @@ -23,10 +23,6 @@ const UNMATCHED_LIST_DELIM: &str = "Unmatched list delimiter in input"; /* takes a line of user input * returns an unsimplified tree of tokens. - * - * WARNING: lex and process ONLY SUPPORT ASCII CHARACTERS. - * Unicode and other technology where one rune can take multiple indexes - * can cause havoc if part of a rune matches a whitespace or other operator */ pub fn lex(document: String) -> Result, String> { if !document.is_ascii() { @@ -46,8 +42,6 @@ pub fn lex(document: String) -> Result, String> { /* The logic used in lex * Returns Ok(Box) if lexing passes * Returns Err(String) if an error occurs - * - * WARNING: read docs for lex */ fn process(document: String) -> Result, String> { let doc_len = document.len(); diff --git a/src/lib.rs b/src/lib.rs index 7ee3609..5ca14df 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -19,10 +19,12 @@ mod cell; mod lex; mod func; mod eval; +mod vars; pub mod ast { pub use crate::cell::{Cell, Ctr, cons, cell_as_string}; pub use crate::lex::{lex}; pub use crate::func::{Function, Operation, FTable}; + pub use crate::vars::VTable; pub use crate::eval::{eval}; } diff --git a/src/vars.rs b/src/vars.rs index e7c5f37..24c8f87 100644 --- a/src/vars.rs +++ b/src/vars.rs @@ -17,7 +17,7 @@ use std::boxed::Box; use std::collections::HashMap; -use crate::cell::{Ctr, Cell}; +use crate::cell::{Ctr}; /* Mapping between a string token and a tree of Cells * The string token can be found in any Ctr::Symbol value @@ -25,23 +25,25 @@ use crate::cell::{Ctr, Cell}; * Considerations: should I use a structure of cells for this? * Current thoughts: if this was a language without proper data types, yes. */ -pub type VTable = HashMap; +pub type VTable = HashMap>; -impl VTable { - pub fn define( - &mut self, - identifier: String, - var_tree: Box - ) { - if let Some(boxed_cell) = self.insert(identifier, var_tree) { - drop(boxed_cell); - } - } - - pub fn get( - &self, - identifier: String - ) -> Option> { - self.get(identifier) +pub fn define( + vt: Box, + identifier: String, + var_tree: Box +) { + if let Some(boxed_cell) = vt.insert(identifier, var_tree) { + drop(boxed_cell); + } +} + +pub fn get( + vt: Box, + identifier: String +) -> Option> { + if let Some(c) = vt.get(&identifier) { + Some(*c) + } else { + None } }