use std::{convert::TryFrom, fmt}; use crate::app::AppState; use crate::lisp::{ error::{EvalError, LispError}, number::LispNumber, }; #[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(EvalError::ArgumentCount(self.arity.map(|u| u as u32)).into()); } } (self.closure)(args, app) } } pub type Ident = String; pub type BoolLit = bool; #[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(Ident), 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(()) } } impl TryFrom for LispNumber { type Error = LispError; fn try_from(value: LispExpr) -> Result { match value { LispExpr::Number(i) => Ok(i), _ => Err(LispError::Eval(EvalError::TypeMismatch)), } } } impl<'a> TryFrom<&'a LispExpr> for &'a LispNumber { type Error = LispError; fn try_from(value: &'a LispExpr) -> Result { match value { LispExpr::Number(i) => Ok(i), _ => Err(LispError::Eval(EvalError::TypeMismatch)), } } } impl TryFrom for Ident { type Error = LispError; fn try_from(value: LispExpr) -> Result { match value { LispExpr::Ident(i) => Ok(i), _ => Err(LispError::Eval(EvalError::TypeMismatch)), } } } impl TryFrom for BoolLit { type Error = LispError; fn try_from(value: LispExpr) -> Result { match value { LispExpr::BoolLit(i) => Ok(i), _ => Err(LispError::Eval(EvalError::TypeMismatch)), } } } pub fn is_ident>(expr: E) -> bool { matches!(expr.as_ref(), LispExpr::Ident(_)) } pub fn is_number>(expr: E) -> bool { matches!(expr.as_ref(), LispExpr::Number(_)) }