/* Mycelium Scheme * Copyright (C) 2025 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 core::fmt::{self, Formatter}; use core::ops::Index; use core::cell::RefCell; use alloc::format; use alloc::rc::Rc; use alloc::vec::Vec; use alloc::string::String; use organelle::Number; #[derive(Default, Clone, PartialEq)] pub enum Datum { Number(Number), Bool(bool), List(Rc), Symbol(String), Char(u8), String(Vec), Vector(RefCell>>), ByteVector(RefCell>), #[default] None, } fn byte_to_escaped_char(b: u8) -> String { // alarm, backspace, delete match b { _ if b > 31 && b < 127 => String::from(b as char), _ => format!("x{:x}", b), } } fn fmt_vec(ve: &RefCell>) -> String { let v = ve.borrow(); if v.len() == 0 { return String::new() } let mut s = format!("{}", v[0]); let mut i = v.iter(); i.next(); // discard i.for_each(|e| { s = format!("{} {}", s, e); }); s } impl fmt::Display for Datum { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { match self { Datum::Number(n) => write!(f, "{}", Into::::into(*n)), Datum::Bool(n) => write!(f, "{}", if *n {"#t"} else {"#f"}), Datum::List(n) => write!(f, "{n}"), Datum::Symbol(n) => write!(f, "{n}"), Datum::Char(n) => write!(f, "#\\{}", byte_to_escaped_char(*n)), Datum::String(n) => write!(f, "\"{}\"", String::from_utf8_lossy(&*n)), Datum::Vector(n) => write!(f, "#({})", fmt_vec(n)), Datum::ByteVector(n) => write!(f, "#u8({})", fmt_vec(n)), Datum::None => Ok(()) } } } /* WARNING * This is in a sense overloaded. * Instead of using this to print debugging information for the * Rust code, I have instead overloaded it to print the most * maximal expanded valid syntax for this Datum */ impl fmt::Debug for Datum { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { match self { Datum::Number(n) => write!(f, "{}", Into::::into(*n)), Datum::Bool(n) => write!(f, "{}", if *n {"#t"} else {"#f"}), Datum::List(n) => write!(f, "{n}"), Datum::Char(n) => write!(f, "{}", byte_to_escaped_char(*n)), Datum::Symbol(n) => write!(f, "{n}"), Datum::String(n) => write!(f, "\"{}\"", String::from_utf8_lossy(&*n)), Datum::Vector(n) => write!(f, "#({n:?})"), Datum::ByteVector(n) => write!(f, "#u8({n:?})"), Datum::None => Ok(()) } } } #[derive(Default, Clone, PartialEq)] pub struct Ast(pub Rc, pub Rc); impl Ast { pub fn subsl(&self, start: isize, end: isize) -> Ast { if end - start == 1 { return Ast(Rc::from(self[start as usize].clone()), Rc::from(Datum::None)) } if end == 0 { return Ast( Rc::from((*(self.0)).clone()), Rc::from(Datum::None) ) } let Datum::List(ref next) = *self.1 else { panic!("index into improper list form") }; if start <= 0 { Ast( Rc::from((*(self.0)).clone()), Rc::from(Datum::List( Rc::from(next.subsl(start - 1, end - 1)))) ) } else { next.subsl(start - 1, end - 1) } } pub fn len(&self) -> usize { let Datum::List(ref next) = *self.1 else { return 1 }; 1 + next.len() } } impl Iterator for Ast { type Item = Rc; fn next(&mut self) -> Option { if let Datum::List(n) = &*self.1 { let tmp_pair = n; self.0 = tmp_pair.0.clone(); self.1 = tmp_pair.1.clone(); return Some(self.0.clone()); } if let Datum::None = *self.1 { return None; } let tmp = self.1.clone(); self.0 = Rc::from(Datum::None); self.1 = Rc::from(Datum::None); return Some(tmp); } } impl Index for Ast { type Output = Datum; fn index(&self, index: usize) -> &Self::Output { if index == 0 { if let Datum::None = *self.0 { panic!("out of bounds indexing into AST") } else { self.0.as_ref() } } else { let Datum::List(ref next) = *self.1 else { panic!("out of bounds indexing into AST") }; next.index(index - 1) } } } impl fmt::Display for Ast { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "({}", self.0)?; let mut cur = self; while let Datum::List(next) = &*cur.1 { cur = &next; write!(f, " {}", cur.0)?; } if let Datum::None = &*cur.1 { write!(f, ")") } else { write!(f, " . {})", cur.1) } } } impl fmt::Debug for Ast { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "({}", self.0)?; let mut cur = self; let mut end = 1; while let Datum::List(next) = &*cur.1 { cur = &next; end += 1; write!(f, "({} . ", cur.0)? } write!(f, "{}{}", cur.1, ")".repeat(end)) } }