From 1f5c3214b2855659079bd65dd6a45bb5762d8dd7 Mon Sep 17 00:00:00 2001 From: Akshay Date: Sat, 27 Mar 2021 13:16:01 +0530 Subject: add arity struct for primitives --- src/lisp/expr.rs | 94 ++++++++++++++++++++++++++++++++++++++++++++++++++---- src/lisp/number.rs | 21 ++++++++++++ 2 files changed, 108 insertions(+), 7 deletions(-) diff --git a/src/lisp/expr.rs b/src/lisp/expr.rs index acd3365..40420a1 100644 --- a/src/lisp/expr.rs +++ b/src/lisp/expr.rs @@ -3,21 +3,45 @@ use std::{convert::TryFrom, fmt}; use crate::app::AppState; use crate::lisp::{ error::{EvalError, LispError}, + eval::lookup_extended, number::LispNumber, + EnvList, Environment, }; +#[derive(Debug, Copy, PartialEq, Clone)] +pub enum Arity { + Exact(usize), + Atleast(usize), + Atmost(usize), + Range(usize, usize), + None, +} + +impl Arity { + pub fn is_valid(self, args: &[T]) -> bool { + match self { + Arity::Exact(a) => args.len() == a, + Arity::Atleast(a) => args.len() >= a, + Arity::Atmost(a) => args.len() <= a, + Arity::Range(low, high) => args.len() >= low && args.len() <= high, + Arity::None => true, + } + } + pub fn to_error(self) -> LispError { + LispError::Eval(EvalError::ArgumentCount(self)) + } +} + #[derive(Clone)] pub struct PrimitiveFunc { - pub arity: Option, // minimim arity + pub arity: Arity, // 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()); - } + if !self.arity.is_valid(args) { + return Err(EvalError::ArgumentCount(self.arity).into()); } (self.closure)(args, app) } @@ -42,6 +66,7 @@ pub enum LispExpr { Ident(Ident), PrimitiveFunc(PrimitiveFunc), Function(LispFunction), + Char(char), // none of these depths should be zero Quasiquote(Box, u32), @@ -83,6 +108,40 @@ impl LispExpr { v => LispExpr::Quasiquote(Box::new(v), n), } } + + pub fn compare(&self, other: &Self, envs: &EnvList) -> Result { + match (self, other) { + (LispExpr::Unit, LispExpr::Unit) => Ok(true), + (LispExpr::Number(s), LispExpr::Number(o)) => Ok(s == o), + (LispExpr::List(s), LispExpr::List(o)) => Ok(s + .iter() + .zip(o) + .all(|(a, b)| matches!(a.compare(b, envs), Ok(true)))), + (LispExpr::StringLit(s), LispExpr::StringLit(o)) => Ok(s == o), + (LispExpr::Char(s), LispExpr::Char(o)) => Ok(s == o), + (LispExpr::BoolLit(s), LispExpr::BoolLit(o)) => Ok(s == o), + (LispExpr::Ident(s), LispExpr::Ident(o)) => { + let s = lookup_extended(envs, s)?; + let o = lookup_extended(envs, o)?; + s.compare(&o, envs) + } + (LispExpr::PrimitiveFunc(s), LispExpr::PrimitiveFunc(o)) => { + let PrimitiveFunc { closure: s, .. } = s; + let PrimitiveFunc { closure: o, .. } = o; + Ok(*s as usize == *o as usize) + } + (LispExpr::Function(_), LispExpr::Function(_)) => Err(EvalError::BadForm.into()), + (LispExpr::Quasiquote(s, sd), LispExpr::Quasiquote(o, od)) => { + Ok(s.compare(o, envs)? && sd == od) + } + (LispExpr::Comma(s, sd), LispExpr::Comma(o, od)) => Ok(s.compare(o, envs)? && sd == od), + (LispExpr::CommaAt(s, sd), LispExpr::CommaAt(o, od)) => { + Ok(s.compare(o, envs)? && sd == od) + } + (LispExpr::Quote(s, sd), LispExpr::Quote(o, od)) => Ok(s.compare(o, envs)? && sd == od), + _ => Err(EvalError::TypeMismatch.into()), + } + } } impl fmt::Display for LispExpr { @@ -96,6 +155,7 @@ impl fmt::Display for LispExpr { } } LispExpr::StringLit(s) => write!(f, "{}", s)?, + LispExpr::Char(c) => write!(f, "{}", c)?, LispExpr::BoolLit(b) => { if *b { write!(f, "#t")? @@ -137,16 +197,36 @@ impl<'a> TryFrom<&'a LispExpr> for &'a LispNumber { } } -impl TryFrom for Ident { +//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 String { type Error = LispError; fn try_from(value: LispExpr) -> Result { match value { - LispExpr::Ident(i) => Ok(i), + LispExpr::StringLit(i) => Ok(i), _ => Err(LispError::Eval(EvalError::TypeMismatch)), } } } +impl AsRef for LispExpr { + fn as_ref(&self) -> &str { + match self { + LispExpr::StringLit(i) => &i, + _ => panic!("invalid downcast!"), + } + } +} + impl TryFrom for BoolLit { type Error = LispError; fn try_from(value: LispExpr) -> Result { diff --git a/src/lisp/number.rs b/src/lisp/number.rs index 23d7997..0ce5ac0 100644 --- a/src/lisp/number.rs +++ b/src/lisp/number.rs @@ -1,4 +1,5 @@ use std::{ + cmp::{Eq, Ord, Ordering, PartialEq, PartialOrd}, fmt, ops::{Add, Mul, Sub}, }; @@ -78,6 +79,26 @@ impl PartialEq for LispNumber { } } +impl Eq for LispNumber {} + +impl Ord for LispNumber { + fn cmp(&self, other: &Self) -> Ordering { + use LispNumber::*; + match (*self, *other) { + (Integer(a), Integer(b)) => a.cmp(&b), + (Float(a), Integer(b)) => (a as i64).cmp(&b), + (Integer(a), Float(b)) => a.cmp(&(b as i64)), + (Float(a), Float(b)) => (a as i64).cmp(&(b as i64)), + } + } +} + +impl PartialOrd for LispNumber { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + impl fmt::Display for LispNumber { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { -- cgit v1.2.3