/* 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 std::rc::Rc; use std::cell::RefCell; // Recursive data type for a tree of Segments pub type Ast = Rc>; // Container #[derive(Clone)] #[derive(Debug)] pub enum Ctr { Symbol(String), String(String), Integer(i128), Float(f64), Bool(bool), Seg(Ast), None } // Type of Container #[derive(PartialEq)] #[derive(Clone)] pub enum Type { Symbol, String, Integer, Float, Bool, Seg, None } /* Segment * Holds two Containers. * Basic building block for more complex data structures. * I was going to call it Cell and then I learned about * how important RefCells were in Rust */ #[derive(Clone)] #[derive(Debug)] pub struct Seg { /* "Contents of Address Register" * Historical way of referring to the first value in a cell. */ pub car: Ctr, /* "Contents of Decrement Register" * Historical way of referring to the second value in a cell. */ pub cdr: Ctr } 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 } } } impl Type { pub fn to_str(&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() } } /* Prints a Syntax Tree as a string */ pub fn ast_as_string(c: Ast, with_parens: bool) -> String { let mut string = String::new(); let mut prn_space = true; let seg = c.borrow(); match &seg.car { Ctr::Symbol(s) => string.push_str(&s), Ctr::String(s) => { string.push('\''); string.push_str(&s); string.push('\''); }, Ctr::Integer(i) => string = string + &i.to_string(), Ctr::Float(f) => string = string + &f.to_string(), Ctr::Bool(b) => string = string + &b.to_string(), Ctr::Seg(c) => string.push_str(ast_as_string(c.clone(), true).as_str()), Ctr::None => prn_space = false } if prn_space { string.push(' '); } match &seg.cdr { Ctr::Symbol(s) => string.push_str(&s), Ctr::String(s) => { string.push('\''); string.push_str(&s); string.push('\''); }, Ctr::Integer(i) => string = string + &i.to_string(), Ctr::Float(f) => string = string + &f.to_string(), Ctr::Bool(b) => string = string + &b.to_string(), Ctr::Seg(c) => string.push_str(ast_as_string(c.clone(), false).as_str()), Ctr::None => { if prn_space { string.pop(); } } } // TODO: maybe a better way to do this if with_parens { let mut extra = String::from("("); extra.push_str(&string); extra.push(')'); string = extra } return string } pub fn ast_to_string(c: Ast) -> String { ast_as_string(c.clone(), true) } /* NOTE: "Standard form" is used here to refer to a list of segments * that resembles a typical linked list. This means that Car may hold whatever, * but Cdr must either be Seg or None. */ /* Initializes a new ast node with segment car and cdr passed in */ pub fn new_ast(car: Ctr, cdr: Ctr) -> Ast { Rc::new(RefCell::new(Seg{ car: car, cdr: cdr })) } /* 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 bool>(tree: Ast, func: &mut F) -> bool{ let inner = tree.borrow(); if func(&inner.car) { match &inner.cdr { Ctr::None => true, Ctr::Seg(c) => circuit(c.clone(), func), _ => false } } else { false } } /* recurs over ast assumed to be list in standard form * returns length */ pub fn list_len(tree: Ast) -> u128 { match &tree.borrow().cdr { Ctr::Seg(c) => list_len(c.clone()) + 1, _ => 1 } } /* recurs over tree assumed to be list in standard form * returns clone of ctr at index provided */ pub fn list_idx(tree: Ast, idx: u128) -> Ctr { let inner = tree.borrow(); if idx > 0 { match &inner.cdr { Ctr::None => Ctr::None, Ctr::Seg(c) => list_idx(c.clone(), idx - 1), _ => { if idx == 1 { inner.cdr.clone() } else { Ctr::None } } } } else { match inner.car { Ctr::None => Ctr::None, _ => { inner.car.clone() } } } } /* recurs over tree assumed to be list in standard form * appends object to end of list */ pub fn list_append(tree: Ast, obj: Ctr) { let mut inner = tree.borrow_mut(); match &inner.car { Ctr::None => { inner.car = obj; }, _ => { match &inner.cdr { Ctr::None => { inner.cdr = Ctr::Seg(new_ast(obj, Ctr::None)); }, Ctr::Seg(tr) => { list_append(tr.clone(), obj); }, _ => () } } } }