/* relish: versatile lisp shell * Copyright (C) 2021 Ava Affine * * 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 std::fmt; // Container #[derive(Debug, Clone, Default)] pub enum Ctr <'a> { Symbol(String), String(String), Integer(i128), Float(f64), Bool(bool), Seg(Seg<'a>), #[default] None, } // Type of Container #[derive(PartialEq, Clone)] pub enum Type { Symbol, String, Integer, Float, Bool, Seg, None, } /* Segment * Holds two Containers. * Basic building block for more complex data structures. */ #[derive(Clone, Debug, Default)] pub struct Seg <'a> { /* "Contents of Address Register" * Historical way of referring to the first value in a cell. */ pub car: &mut Ctr<'a>, /* "Contents of Decrement Register" * Historical way of referring to the second value in a cell. */ pub cdr: &mut Ctr<'a>, } impl Ctr<'_> { pub fn to_type(&self) -> Type { match self { Ctr::Symbol(_s) => Type::Symbol, Ctr::String(_s) => Type::String, Ctr::Integer(_s) => Type::Integer, Ctr::Float(_s) => Type::Float, Ctr::Bool(_s) => Type::Bool, Ctr::Seg(_s) => Type::Seg, Ctr::None => Type::None, } } } fn seg_to_string(s: &Seg, parens: bool) -> String { let mut string = String::new(); match s.car { Ctr::None => string.push_str(""), _ => string.push_str(s.car), } string.push(' '); match s.cdr { Ctr::Seg(inner) => string.push_str(seg_to_string(inner, false)), Ctr::None => {}, _ => string.push_str(s.cdr), } if parens { String::from("(" + string + ")") } } impl fmt::Display for Ctr <'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Ctr::Symbol(s) => write!(f, "{}", s), Ctr::String(s) => write!(f, "\'{}\'", s), Ctr::Integer(s) => write!(f, "{}", s), Ctr::Float(s) => write!(f, "{}", s), Ctr::Bool(s) => { if s { write!(f, "T") } else { write!(f, "F") } }, Ctr::Seg(s) => write!(f, "{}", s), Ctr::None => Ok(), } } } impl fmt::Display for Seg<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}", seg_to_string(self, true)) } } impl Iterator for Seg<'_> { fn next(&self) -> Option<&Seg> { if let Ctr::Seg(s) = self.cdr { Ok(s) } else { None() } } } impl Type { pub fn to_string(&self) -> String { let ret: &str; match self { Type::Symbol => ret = "symbol", Type::String => ret = "string", Type::Integer => ret = "integer", Type::Float => ret = "float", Type::Bool => ret = "bool", Type::Seg => ret = "segment", Type::None => ret = "none", } ret.to_owned() } } /* applies a function across a list in standard form * function must take a Ctr and return a bool * short circuits on the first false returned. * also returns false on a non standard form list */ pub fn circuit(list: &Seg, func: &mut F) -> bool { if func(&list.car) { match list.cdr { Ctr::None => true, Ctr::Seg(l) => circuit(l, func), _ => false, } } else { false } } /* recurs over ast assumed to be list in standard form * returns length */ pub fn list_len(list: &Seg) -> u128 { let mut len = 0; circuit(list, &circuit(&mut |c: &Ctr| -> bool { len += 1; true })) } /* recurs over tree assumed to be list in standard form * returns clone of ctr at index provided * * TODO: return result (or option?) */ pub fn list_idx<'a>(list: &Seg, idx: u128) -> Ctr<'a> { if idx > 0 { if let Ctr::Seg(s) = list.car { list_idx(s, idx - 1) } else if idx == 1 { list.cdr } else { Ctr::None } } else { list.car } } /* recurs over tree assumed to be list in standard form * appends object to end of list * * TODO: return result */ pub fn list_append<'a>(list: &Seg, obj: Ctr) { if let Ctr::None = list.car { list.car = obj; return } if let Ctr::Seg(s) = list.cdr { list_append(s, obj) } if let Ctr::None = list.cdr { list.cdr = Ctr::Seg(&Seg{car:obj, cdr:Ctr::None}) } }