/* 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::VTable;
use std::collections::HashMap;
use std::convert::TryInto;
pub struct FTable<'a>(HashMap>>);
impl<'a> FTable<'a> {
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() {
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());
}
}
self.0.insert(func.name, func);
None
}
pub fn get(&self, id: String) -> Option<&Box>> {
self.0.get(&id)
}
pub fn remove(&self, id: String) {
self.0.remove(&id);
}
pub fn new() -> FTable<'a> {
FTable{0: HashMap::>::new()}
}
}
// Standardized function signature for stdlib functions
//pub type InternalOperation = impl Fn(Box, Box, Box) -> Box;
#[derive(Debug)]
pub struct ExternalOperation<'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 stored function may either be a pointer to a function
* or a syntax tree to eval with the arguments
*/
pub enum Operation<'a> {
Internal(Box Ctr<'a>>),
External(ExternalOperation<'a>),
}
/* 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),
}
pub struct Function<'a> {
pub function: Operation<'a>,
pub name: String,
pub args: Args,
// dont fail on undefined symbol (passed to eval)
pub loose_syms: bool,
// dont evaluate args at all. leave that to the function
pub eval_lazy: bool,
}
impl<'a> Function<'a> {
/* call
* routine is called by eval when a function call is detected
*/
pub fn func_call(
&self,
args: &'a Seg<'a>,
vars: &'a mut VTable<'a>,
funcs: &'a mut FTable<'a>,
) -> Result>, String> {
let mut evaluated_args = args;
if !self.eval_lazy {
match eval(args, vars, funcs, self.loose_syms) {
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
))
}
}
}
match self.args {
Args::Lazy(ref num) => {
let called_arg_count = evaluated_args.len() as i128;
if *num == 0 {
if let Ctr::None = *evaluated_args.car {
//pass
} else {
return Err(format!(
"expected 0 args in call to {}. Got one or more.",
self.name,
));
}
} else if *num > -1 && (*num != called_arg_count) {
return Err(format!(
"expected {} args in call to {}. Got {}.",
num, self.name, called_arg_count
));
}
}
Args::Strict(ref arg_types) => {
let mut idx: usize = 0;
let passes = evaluated_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 little arguments in call to {}",
arg_types.len() - (idx + 1),
self.name
));
}
if !passes {
if idx < (arg_types.len() - 1) {
return Err(format!(
"argument {} in call to {} is of wrong type (expected {})",
idx + 1,
self.name,
arg_types[idx].to_string()
));
}
if idx == (arg_types.len() - 1) {
return Err(format!(
"too many arguments in call to {}",
self.name
));
}
}
}
}
/* 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, vars, funcs))),
Operation::External(ref f) => {
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));
}
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) {
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!"),
}
}
for n in 0..f.arg_syms.len() {
vars.remove(f.arg_syms[n]);
}
return Ok(result);
}
}
}
}