use std::fmt; use crate::app::AppState; use crate::lisp::{error::LispError, number::LispNumber, Environment}; #[derive(Clone)] pub struct PrimitiveFunc { pub arity: Option, // minimim arity pub closure: fn(&[LispExpr], &mut AppState) -> Result, } impl PrimitiveFunc { pub fn call(&self, args: &[LispExpr], app: &mut AppState) -> Result { if let Some(arity) = self.arity { if args.len() < arity { return Err(LispError::EvalError); } } (self.closure)(args, app) } } #[derive(Clone)] pub struct LispFunction { pub params: Vec, pub body: Vec, } #[derive(Clone)] pub enum LispExpr { Unit, Number(LispNumber), List(Vec), StringLit(String), BoolLit(bool), Ident(String), PrimitiveFunc(PrimitiveFunc), Function(LispFunction), // none of these depths should be zero Quasiquote(Box, u32), Comma(Box, u32), CommaAt(Box, u32), Quote(Box, u32), } impl LispExpr { pub fn comma(self, n: u32) -> LispExpr { match self { LispExpr::Comma(v, i) => LispExpr::Comma(v, i.checked_add(n).expect("comma overflow")), LispExpr::CommaAt(v, i) => LispExpr::CommaAt(v, i + n), v => LispExpr::Comma(Box::new(v), n), } } pub fn comma_at(self, n: u32) -> LispExpr { match self { LispExpr::CommaAt(v, i) => { LispExpr::CommaAt(v, i.checked_add(n).expect("comma_at overflow")) } v => LispExpr::CommaAt(Box::new(v), n), } } pub fn quote(self, n: u32) -> LispExpr { match self { LispExpr::Quote(v, i) => LispExpr::Quote(v, i.checked_add(n).expect("quote overflow")), v => LispExpr::Quote(Box::new(v), n), } } pub fn quasiquote(self, n: u32) -> LispExpr { match self { LispExpr::Quasiquote(v, i) => { LispExpr::Quasiquote(v, i.checked_add(n).expect("quasiquote overflow")) } v => LispExpr::Quasiquote(Box::new(v), n), } } } impl fmt::Display for LispExpr { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { LispExpr::Unit => write!(f, "()")?, LispExpr::Number(n) => write!(f, "{}", n)?, LispExpr::List(l) => { for expr in l.iter() { write!(f, " {} ", expr)? } } LispExpr::StringLit(s) => write!(f, "{}", s)?, LispExpr::BoolLit(b) => { if *b { write!(f, "#t")? } else { write!(f, "#f")? } } LispExpr::Ident(s) => write!(f, "{}", s)?, LispExpr::PrimitiveFunc(_) => write!(f, "<#primitive>")?, LispExpr::Function(func) => write!(f, "<#lambda {}>", func.params.join(" "))?, LispExpr::Quasiquote(val, depth) => { write!(f, "{}{}", "`".repeat(*depth as usize), val)? } LispExpr::Comma(val, depth) => write!(f, "{}{}", ",".repeat(*depth as usize), val)?, LispExpr::CommaAt(val, depth) => write!(f, "{}@{}", ",".repeat(*depth as usize), val)?, LispExpr::Quote(val, depth) => write!(f, "{}{}", "'".repeat(*depth as usize), val)?, }; Ok(()) } }