/* 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::FTable; use crate::segment::{Seg, Ctr}; 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<'a>( ast: &'a Seg<'a>, vars: &'a mut VTable<'a>, funcs: &'a mut FTable<'a>, sym_loose: bool, ) -> Result>, String> { let mut ret = Box::new(Ctr::Seg(Seg::new())); let mut iter: &mut Seg; if let Ctr::Seg(ref mut s) = *ret { iter = s; } else { return Err("Critfail: no fuckin clue whats up here".to_string()) } let mut car = &ast.car; // doing an initial variable check here allows us // to find functions passed in as variables if let Ctr::Symbol(tok) = **car { if let Some(val) = vars.get(tok.clone()) { car = &(val.clone()); } } // another check to detect if we may have a function call if let Ctr::Symbol(ref tok) = **car { match *ast.cdr { Ctr::Seg(ref ast) => { if let Some(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.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 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, vars, funcs, sym_loose) { Ok(res) => (*iter).car = res, Err(e) => return Err(format!("Evaluation error: {}", e)), } } // if SYMBOL: unwrap naively Ctr::Symbol(ref tok) => { if let Some(val) = vars.get(tok.clone()) { iter.car = val.clone(); } else if sym_loose { iter.car = car.clone() } else { return Err(format!("Undefined variable: {}", tok.clone())); } } // if OTHER: clone and set _ => { iter.car = car.clone(); } } match *ast.cdr { // if SYMBOL: unwrap naively, then end Ctr::Symbol(ref tok) => { if let Some(val) = vars.get(tok.clone()) { iter.cdr = val.clone(); } else if sym_loose { iter.cdr = ast.cdr.clone() } else { 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) => { iter.cdr = Box::new(Ctr::Seg(Seg::new())); ast = &next; } // if OTHER: clone and set, and then end _ => { iter.cdr = ast.cdr.clone(); none = true; } } if let Ctr::None = **car { if let Ctr::None = *ast.cdr { none = true; } } } return Ok(ret); }