/* 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::{SYM_TABLE, Symbol, ValueType}; 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, expect_all_symbols_defined: bool, simplify_function_branches: bool, ) -> 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 table_handle = SYM_TABLE.read().unwrap(); 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 { let mut prefetched_function: Option<&Symbol> = None; while let Ctr::Symbol(ref tok) = **arg_car { prefetched_function = table_handle.get(tok); if let Some(sym_ref) = prefetched_function { if let ValueType::VarForm(ref value) = sym_ref.value { arg_car = value; } else { break; } } else if !expect_all_symbols_defined { return Err(format!("evaluation error: undefined symbol {}", tok)) } else { break } } match **arg_car { Ctr::Seg(ref inner) => { match eval(inner, expect_all_symbols_defined, simplify_function_branches) { Ok(res) => car = res, Err(e) => return Err(format!("evaluation error: {}", e)), } } Ctr::Symbol(_) => { if simplify_function_branches && first { if let Some(func) = prefetched_function { if let Ctr::Seg(ref candidates) = **arg_cdr { let fc: Result, String>; match eval(candidates, expect_all_symbols_defined, false) { Ok(res) => { match *res { Ctr::Seg(ref args) => { fc = func.call(args); }, _ => { fc = func.call(&Seg::from_mono(res.clone())) } } match fc { Ok(datum) => car = datum, Err(e) => return Err(format!("call to {} failed: {}", func.name, e)) } } Err(e) => return Err(format!("evaluation error: {}", e)) } } else { match func.call(&Seg::new()) { Ok(res) => car = res, Err(e) => return Err(format!("call to {} failed: {}", func.name, e)) } } } else { car = arg_car.clone(); } } else { car = arg_car.clone(); } } _ => { car = arg_car.clone(); } } // weird tree but okay while let Ctr::Symbol(ref tok) = **arg_cdr { prefetched_function = table_handle.get(tok); if let Some(sym_ref) = prefetched_function { if let ValueType::VarForm(ref value) = sym_ref.value { arg_cdr = value; } else { break; } } else if !expect_all_symbols_defined { return Err(format!("evaluation error: undefined symbol {}", tok)) } else { break } } match **arg_cdr { Ctr::Symbol(_) => { cdr = arg_cdr.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 } _ => { cdr = ast.cdr.clone(); none = true; } } if let Ctr::None = **arg_car { if let Ctr::None = **arg_cdr { none = true; } } first = false; } Ok(ret) }