From 827e54124f03fd11d497caa8213ca583f480b982 Mon Sep 17 00:00:00 2001 From: Akshay Date: Fri, 2 Apr 2021 11:05:11 +0530 Subject: add DottedList type, fix `cons` finally --- src/lisp/expr.rs | 26 ++++++++++++++++++++++++-- src/lisp/prelude.rs | 46 ++++++++++++++++++++++++++++++++++------------ 2 files changed, 58 insertions(+), 14 deletions(-) (limited to 'src/lisp') diff --git a/src/lisp/expr.rs b/src/lisp/expr.rs index 31476c9..aeaa2d8 100644 --- a/src/lisp/expr.rs +++ b/src/lisp/expr.rs @@ -62,6 +62,7 @@ pub enum LispExpr { Unit, Number(LispNumber), List(Vec), + DottedList(Vec), StringLit(String), BoolLit(bool), Ident(Ident), @@ -157,7 +158,14 @@ impl LispExpr { pub fn unwrap_list(&self) -> Vec { match &self { LispExpr::List(p) => p.clone(), - _ => panic!("attempt to call `unwrap_number` on invalid type"), + _ => panic!("attempt to call `unwrap_list` on invalid type"), + } + } + + pub fn unwrap_dotted_list(&self) -> Vec { + match &self { + LispExpr::DottedList(p) => p.clone(), + _ => panic!("attempt to call `unwrap_dotted_list` on invalid type"), } } @@ -209,6 +217,13 @@ impl fmt::Display for LispExpr { &l.iter().map(|expr| format!("{}", expr)).collect::>()[..].join(" ") )?; } + LispExpr::DottedList(l) => { + write!(f, "(")?; + for item in &l[..l.len() - 1] { + write!(f, "{} ", item)?; + } + write!(f, ". {})", &l[l.len() - 1])?; + } LispExpr::StringLit(s) => write!(f, "{:?}", s)?, LispExpr::Char(c) => write!(f, "{}", c)?, LispExpr::BoolLit(b) => { @@ -237,7 +252,14 @@ impl fmt::Debug for LispExpr { 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::List(l) => { + write!(f, "List ")?; + f.debug_list().entries(l.iter()).finish() + }, + LispExpr::DottedList(l) => { + write!(f, "DottedList ")?; + 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), diff --git a/src/lisp/prelude.rs b/src/lisp/prelude.rs index 372bb00..add405c 100644 --- a/src/lisp/prelude.rs +++ b/src/lisp/prelude.rs @@ -153,8 +153,11 @@ pub fn new_env() -> Result { (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)| a == b)), + (LispExpr::DottedList(s), LispExpr::DottedList(o)) => Ok(s.iter().zip(o).all(|(a, b)| a == b)), (LispExpr::List(s), LispExpr::Unit) => Ok(s.len() == 0), (LispExpr::Unit, LispExpr::List(s)) => Ok(s.len() == 0), + (LispExpr::DottedList(_), LispExpr::Unit) => Ok(false), + (LispExpr::Unit, LispExpr::DottedList(_)) => Ok(false), (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), @@ -244,18 +247,6 @@ pub fn new_env() -> Result { } }); - primitive!(env, Arity::Exact(2), "cons", |args, _| { - if type_match!(args, 1 => LispExpr::Unit) { - return Ok(LispExpr::List(vec![args[0].clone()])); - } else if !type_match!(args, 1 => LispExpr::List(_)) { - return Ok(LispExpr::List(vec![args[0].clone(), args[1].clone()])); - } else { - let mut rest = args[1].unwrap_list(); - rest.insert(0, args[0].clone()); - return Ok(LispExpr::List(rest)); - } - }); - primitive!(env, Arity::Exact(0), "grid-enabled?", |_, app| { Ok(LispExpr::BoolLit(app.grid.enabled)) }); @@ -285,9 +276,30 @@ pub fn new_env() -> Result { return Ok(LispExpr::Unit); }); + primitive!(env, Arity::Exact(2), "cons", |args, _| { + if type_match!(args, 1 => LispExpr::Unit) { + return Ok(LispExpr::List(vec![args[0].clone()])); + } else if type_match!(args, 1 => LispExpr::DottedList(_)) { + // cons of anything to an improper list is an improper list + let mut rest = args[1].unwrap_dotted_list(); + rest.insert(0, args[0].clone()); + return Ok(LispExpr::DottedList(rest)); + } else if type_match!(args, 1 => LispExpr::List(_)) { + // cons of anything to a proper list is a proper list + let mut rest = args[1].unwrap_list(); + rest.insert(0, args[0].clone()); + return Ok(LispExpr::List(rest)); + } else { + // attempt to cons non-lists + return Ok(LispExpr::DottedList(vec![args[0].clone(), args[1].clone()])); + } + }); + primitive!(env, Arity::Exact(1), "car", |args, _| { if type_match!(args, 0 => LispExpr::List(_)) { return Ok(args[0].unwrap_list().swap_remove(0)); + } else if type_match!(args, 0 => LispExpr::DottedList(_)) { + return Ok(args[0].unwrap_dotted_list().swap_remove(0)); } else if type_match!(args, 0 => LispExpr::Unit) { return Err(EvalError::AccessEmptyList.into()); } else { @@ -297,6 +309,7 @@ pub fn new_env() -> Result { primitive!(env, Arity::Exact(1), "cdr", |args, _| { if type_match!(args, 0 => LispExpr::List(_)) { + // cdr of a proper list is a proper list let mut ls = args[0].unwrap_list(); if ls.len() == 0 { return Err(EvalError::AccessEmptyList.into()); @@ -306,6 +319,15 @@ pub fn new_env() -> Result { ls.remove(0); return Ok(LispExpr::List(ls)); } + } else if type_match!(args, 0 => LispExpr::DottedList(_)){ + // cdr of an improper list is an improper list or an atom + let ls = args[0].unwrap_dotted_list(); + if ls.len() == 2 { + return Ok(ls.into_iter().last().unwrap()); + } else { + // should be unreachable + return Err(EvalError::AccessEmptyList.into()); + } } else if type_match!(args, 0 => LispExpr::Unit) { return Err(EvalError::AccessEmptyList.into()); } else { -- cgit v1.2.3