/* 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}; use crate::sym::SymTable; /* iterates over a syntax tree * returns a NEW LIST of values * representing the simplest possible form of the input */ pub fn eval(ast: &Seg, syms: &mut SymTable) -> 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; let mut cdr; // lets me redirect the input let mut arg_car = &ast.car; let mut arg_cdr = &ast.cdr; // iterate over ast and build out ret let mut none = false; while !none { match &**arg_car { Ctr::Seg(ref inner) => car = eval(inner, syms)?, Ctr::Symbol(ref tok) => { let outer_scope_seg_holder: Seg; let args: &Seg; if let Ctr::Seg(ref candidate_args) = **arg_cdr { args = candidate_args; } else { outer_scope_seg_holder = Seg::from_mono(arg_cdr.clone()); args = &outer_scope_seg_holder; } match syms.call_symbol(tok, args, first) { Ok(s) => car = s, Err(s) => return Err(format!("error in call to {}: {}", tok, s)), } if let Some(b) = syms.is_function_type(tok) { if b { return Ok(car); } } } _ => { car = arg_car.clone(); } } match **arg_cdr { Ctr::Symbol(ref tok) => { let blank = Seg::new(); match syms.call_symbol(tok, &blank, false) { Ok(res) => cdr = res, Err(s) => return Err(format!("error fetching {}: {}", tok, s)), } none = true; } Ctr::Seg(ref next) => { cdr = Box::from(Ctr::None); arg_car = &next.car; arg_cdr = &next.cdr } _ => { cdr = arg_cdr.clone(); none = true; } } if let Ctr::None = *ret { *ret = Ctr::Seg(Seg::from(car, cdr)) } else if let Ctr::Seg(ref mut s) = *ret { s.append(car); if let Ctr::None = *cdr { } else { s.append(cdr); } } first = false; } Ok(ret) }