/* 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::sym::SymTable; use crate::segment::{Seg, Ctr}; /* 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 = Box::from(Ctr::None); // 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) => { match eval(inner, syms) { Ok(res) => car = res, Err(e) => return Err(format!("evaluation error: {}", e)), } } 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)), } } _ => { 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) => { 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 } _ => { cdr = ast.cdr.clone(); none = true; } } if let Ctr::None = **arg_car { if let Ctr::None = **arg_cdr { none = true; } } first = false; } Ok(ret) }