/* 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::{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: &Seg, sym_loose: bool, 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(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!")), } } } // iterate over ast and build out ret let mut none = false; while !none { match &**arg_car { Ctr::Seg(ref inner) => { match eval(inner, vars, funcs, sym_loose, call_lazy) { Ok(res) => car = res, 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() } else { return Err(format!("Undefined variable: {}", tok.clone())); } } _ => { car = arg_car.clone(); } } 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() } else { return Err(format!("Undefined variable: {}", tok.clone())); } none = true; } 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 _ => { cdr = ast.cdr.clone(); none = true; } } if let Ctr::None = **arg_car { if let Ctr::None = **arg_cdr { none = true; } } } return Ok(ret); }