From c1f76ed64f35aa5c811a9e61f0c1340711274596 Mon Sep 17 00:00:00 2001 From: Akshay Date: Sun, 28 Mar 2021 20:51:29 +0530 Subject: add Debug & PartialEq impls for LispExpr; add eval tests --- src/lisp/eval.rs | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ src/lisp/expr.rs | 54 ++++++++++++++++++++++++++++++++++++++++++------------ src/lisp/parse.rs | 53 +++++++++++++++++++++++++++-------------------------- 3 files changed, 117 insertions(+), 38 deletions(-) (limited to 'src') diff --git a/src/lisp/eval.rs b/src/lisp/eval.rs index f7f918a..f49efb9 100644 --- a/src/lisp/eval.rs +++ b/src/lisp/eval.rs @@ -222,3 +222,51 @@ pub fn unwrap_ident(i: LispExpr) -> String { _ => panic!("unwrap_ident expected string"), } } + +#[cfg(test)] +mod tests { + use super::*; + + use crate::lisp::{expr::LispExpr, lex::Lexer, number::LispNumber, parse::Parser}; + + fn run(code: &str, app: &mut AppState) -> LispExpr { + let mut parser = Parser::new(Lexer::new(code, 0)); + eval(&parser.parse_single_expr().unwrap(), app).unwrap() + } + + #[test] + fn eval_all() { + let sdl_context = sdl2::init().unwrap(); + let ttf_context = sdl2::ttf::init().unwrap(); + let mut app = AppState::init(100, 100, &sdl_context, &ttf_context, None, None); + eval_arithmetic(&mut app); + eval_logical(&mut app); + } + + fn eval_arithmetic(app: &mut AppState) { + assert_eq!( + run("(+ 1 2 3)", app), + LispExpr::Number(LispNumber::Integer(6)) + ); + assert_eq!( + run("(+ 1.1 2.2 3.3)", app), + LispExpr::Number(LispNumber::Float(6.6)) + ); + assert_eq!( + run("(* 1 2 3 4 5)", app), + LispExpr::Number(LispNumber::Integer(120)) + ); + assert_eq!(run("(< 1 2)", app), LispExpr::BoolLit(true)); + assert_eq!(run("(> 6 5 4 3 2 1)", app), LispExpr::BoolLit(true)); + assert_eq!(run("(< 1 2 3 4 5 6)", app), LispExpr::BoolLit(true)); + assert_eq!(run("(>= 5 5 4 3 2 1)", app), LispExpr::BoolLit(true)); + assert_eq!(run("(<= 2 2 3 4 5 6)", app), LispExpr::BoolLit(true)); + } + + fn eval_logical(app: &mut AppState) { + assert_eq!(run("(and #t #t)", app), LispExpr::BoolLit(true)); + assert_eq!(run("(or #f #t)", app), LispExpr::BoolLit(true)); + assert_eq!(run("(not #t)", app), LispExpr::BoolLit(false)); + assert_eq!(run("(not #f)", app), run("(not (not #t))", app)); + } +} diff --git a/src/lisp/expr.rs b/src/lisp/expr.rs index 8fd794e..059113b 100644 --- a/src/lisp/expr.rs +++ b/src/lisp/expr.rs @@ -1,4 +1,4 @@ -use std::{convert::TryFrom, fmt}; +use std::{cmp::PartialEq, convert::TryFrom, fmt}; use crate::app::AppState; use crate::lisp::{ @@ -177,6 +177,47 @@ impl fmt::Display for LispExpr { } } +impl fmt::Debug for LispExpr { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + LispExpr::Unit => f.debug_tuple("Unit").finish(), + LispExpr::Number(n) => write!(f, "Number({:?})", n), + LispExpr::List(l) => f.debug_list().entries(l.iter()).finish(), + LispExpr::StringLit(s) => write!(f, "String({:?})", s), + LispExpr::Char(c) => write!(f, "Char({:?})", c), + LispExpr::BoolLit(b) => write!(f, "Bool({:?})", b), + LispExpr::Ident(s) => write!(f, "Ident({})", 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), + } + } +} + +impl PartialEq for LispExpr { + fn eq(&self, other: &Self) -> bool { + match (self, other) { + (LispExpr::Unit, LispExpr::Unit) => true, + (LispExpr::Number(s), LispExpr::Number(o)) => s == o, + (LispExpr::List(s), LispExpr::List(o)) => s.iter().zip(o).all(|(a, b)| a == b), + (LispExpr::StringLit(s), LispExpr::StringLit(o)) => s == o, + (LispExpr::Char(s), LispExpr::Char(o)) => s == o, + (LispExpr::BoolLit(s), LispExpr::BoolLit(o)) => s == o, + (LispExpr::Ident(s), LispExpr::Ident(o)) => s == o, + (LispExpr::PrimitiveFunc(_), LispExpr::PrimitiveFunc(_)) => false, + (LispExpr::Function(_), LispExpr::Function(_)) => false, + (LispExpr::Quasiquote(s, sd), LispExpr::Quasiquote(o, od)) => (s, sd) == (o, od), + (LispExpr::Comma(s, sd), LispExpr::Comma(o, od)) => (s, sd) == (o, od), + (LispExpr::CommaAt(s, sd), LispExpr::CommaAt(o, od)) => (s, sd) == (o, od), + (LispExpr::Quote(s, sd), LispExpr::Quote(o, od)) => (s, sd) == (o, od), + _ => false, + } + } +} + impl TryFrom for LispNumber { type Error = LispError; fn try_from(value: LispExpr) -> Result { @@ -197,17 +238,6 @@ impl<'a> TryFrom<&'a LispExpr> for &'a LispNumber { } } -//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 { diff --git a/src/lisp/parse.rs b/src/lisp/parse.rs index 7cca434..6a8de9f 100644 --- a/src/lisp/parse.rs +++ b/src/lisp/parse.rs @@ -210,32 +210,33 @@ mod tests { } #[test] - // fn parse_lisp_expr() { - // assert_eq!( - // parse("1.5").unwrap(), - // LispExpr::Number(LispNumber::Float(1.5)) - // ); - - // assert_eq!( - // parse(r#""hello""#).unwrap(), - // LispExpr::StringLit(r#""hello""#.into()) - // ); - - // assert_eq!(parse("foo").unwrap(), LispExpr::Ident("foo".into())); - - // let items = (1..=5) - // .map(LispNumber::Integer) - // .map(LispExpr::Number) - // .collect::>(); - // assert_eq!(parse("(1 2 3 4 5)").unwrap(), LispExpr::List(items)); - - // let foo = LispExpr::Ident("foo".into()); - // let bar = LispExpr::Comma(Box::new(LispExpr::Ident("bar".into())), 1); - // assert_eq!( - // parse("`(foo ,bar)").unwrap(), - // LispExpr::Quasiquote(Box::new(LispExpr::List(vec![foo, bar])), 1) - // ) - // } + fn parse_lisp_expr() { + assert_eq!( + parse("1.5").unwrap(), + LispExpr::Number(LispNumber::Float(1.5)) + ); + + assert_eq!( + parse(r#""hello""#).unwrap(), + LispExpr::StringLit("hello".into()) + ); + + assert_eq!(parse("foo").unwrap(), LispExpr::Ident("foo".into())); + + let items = (1..=5) + .map(LispNumber::Integer) + .map(LispExpr::Number) + .collect::>(); + assert_eq!(parse("(1 2 3 4 5)").unwrap(), LispExpr::List(items)); + + let foo = LispExpr::Ident("foo".into()); + let bar = LispExpr::Comma(Box::new(LispExpr::Ident("bar".into())), 1); + assert_eq!( + parse("`(foo ,bar)").unwrap(), + LispExpr::Quasiquote(Box::new(LispExpr::List(vec![foo, bar])), 1) + ) + } + #[should_panic] #[test] fn unbalanced_comma() { -- cgit v1.2.3