@@ 1,7 1,9 @@
+mod matching;
+
use std::{
borrow::Cow,
cmp::{max, min},
- fmt, str,
+ str,
};
const SYMBOL_CHARS: &str = "\0abcdefghijklmnopqrstuvxyz+-*?0123456789";
@@ 94,7 96,7 @@ fn parse_(src: &[u8]) -> PResult<Lisp, &'static str> {
}
[b, ..] if SYMBOL_CHARS.contains(*b as char) => {
let s = src[i..].split(|b| !SYMBOL_CHARS.contains(*b as char)).next().unwrap();
- match Symbol::pack(s) {
+ match Symbol::pack_slice(s) {
Some(symbol) => Ok((i + s.len(), Lisp::Symbol(symbol))),
None => Err((i, "valid symbol")),
}
@@ 168,23 170,31 @@ fn map_ok<T, U, E>(r: PResult<T, E>, f: impl FnOnce(T) -> U) -> PResult<U, E> {
impl Symbol {
pub fn pack_str(s: &str) -> Option<Self> {
- Self::pack(s.as_bytes())
+ Self::pack(s.bytes())
+ }
+
+ pub fn pack_slice(s: &[u8]) -> Option<Self> {
+ Self::pack(s.iter().cloned())
}
- pub fn pack(s: &[u8]) -> Option<Self> {
- if s.is_empty() || s.len() > SYMBOL_MAX_LEN || s[0].is_ascii_digit() || s[0] == 0 {
- None
- } else {
- let mut acc = SYMBOL_CHARS.find(s[0] as char)? as u128;
- let mut multiplier = (SYMBOL_CHARS.len() - 10) as u128;
- for &c in &s[1..] {
- if c == 0 {
- return None;
+ pub fn pack(s: impl IntoIterator<Item = u8>) -> Option<Self> {
+ let mut s = s.into_iter();
+ match s.next() {
+ None | Some(b'0'..=b'9' | 0) => None,
+ Some(c0) => {
+ let mut i = 1;
+ let mut acc = SYMBOL_CHARS.find(c0 as char)? as u128;
+ let mut multiplier = (SYMBOL_CHARS.len() - 10) as u128;
+ for c in s {
+ if i == SYMBOL_MAX_LEN || c == 0 {
+ return None;
+ }
+ acc += multiplier * SYMBOL_CHARS.find(c as char)? as u128;
+ multiplier *= SYMBOL_CHARS.len() as u128;
+ i += 1;
}
- acc += multiplier * SYMBOL_CHARS.find(c as char)? as u128;
- multiplier *= SYMBOL_CHARS.len() as u128;
+ Some(Symbol(acc))
}
- Some(Symbol(acc))
}
}
@@ 226,12 236,11 @@ impl Lisp {
}
}
-impl fmt::Display for Lisp {
- fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
+impl PartialEq<i64> for Lisp {
+ fn eq(&self, rhs: &i64) -> bool {
match self {
- Lisp::Int(x) => x.fmt(f),
- Lisp::Float(x) => x.fmt(f),
- _ => todo!(),
+ Lisp::Int(lhs) => lhs == rhs,
+ _ => false,
}
}
}
@@ 250,7 259,7 @@ mod test {
assert_eq!(Symbol::pack_str("a"), Some(Symbol(1)));
assert_eq!(Symbol::pack_str(""), None);
assert_eq!(Symbol::pack_str("0"), None);
- assert_eq!(Symbol::pack(&[0]), None);
+ assert_eq!(Symbol::pack_slice(&[0]), None);
}
#[test]
@@ 0,0 1,129 @@
+#[allow(unused_macros)]
+macro_rules! match_case {
+ // ==== GO! ====
+ ($e:expr, @go {_}, $body:expr, $else:expr) => {
+ { let _ = $e; $body }
+ };
+ ($e:expr, @go {$lhs:ident}, $body:expr, $else:expr) => {{
+ let $lhs = $e;
+ $body
+ }};
+ ($e:expr, @go {$lhs:literal}, $body:expr, $else:expr) => {
+ if $e == &$lhs { $body } else { $else }
+ };
+ ($e:expr, @go {i($lhs:pat)}, $body:expr, $else:expr) => {
+ match $e {
+ $crate::Lisp::Int($lhs) => $body,
+ _ => $else
+ }
+ };
+ ($e:expr, @go {$symbol:lifetime}, $body:expr, $else:expr) => {
+ match $e {
+ $crate::Lisp::Symbol(ref s) if s == &$crate::Symbol::pack(stringify!($symbol)[1..].bytes().map(|c| if c == b'_' { b'-' } else { c })).unwrap() =>
+ $body,
+ _ => $else,
+ }
+ };
+ ($e:expr, @go {sy($lhs:pat)}, $body:expr, $else:expr) => {
+ match $e {
+ $crate::Lisp::Symbol($lhs) => $body,
+ _ => $else
+ }
+ };
+ ($e:expr, @go {($($lhs:tt)*)}, $body:expr, $else:expr) => {{
+ let r = match $e {
+ $crate::Lisp::List(xs) => match_case!(xs, @munch {} {$($lhs)*}, $body, None),
+ _ => None,
+ };
+ if r.is_some() {
+ r
+ } else {
+ $else
+ }
+ }};
+
+ // ==== MUNCH! ====
+ ($es:expr, @munch {} {}, $body:expr, $else:expr) => {
+ if $es.is_empty() { $body } else { $else }
+ };
+ ($es:expr, @munch {$($lhs:tt)+} {}, $body:expr, $else:expr) => {
+ match_case!($es, @munch {$($lhs)+} {,}, $body, $else)
+ };
+ ($es:expr, @munch {$($lhs:tt)+} {, $($lhss:tt)*}, $body:expr, $else:expr) => {{
+ let r = if let Some((first, rest)) = $es.split_first() {
+ match_case!(first, @go {$($lhs)+}, match_case!(rest, @munch {} {$($lhss)*}, $body, None), None)
+ } else {
+ None
+ };
+ if r.is_some() {
+ r
+ } else {
+ $else
+ }
+ }};
+ ($es:expr, @munch {$($lhs:tt)*} {$t:tt $($tts:tt)*}, $body:expr, $else:expr) => {
+ match_case!($es, @munch {$($lhs)* $t} {$($tts)*}, $body, $else)
+ };
+}
+
+#[macro_export]
+macro_rules! match_expr {
+ // ==== MUNCH! ====
+ ($e:expr, @munch {$($lhs:tt)*} => $body:expr, $($clauses:tt)*) => {
+ match_case!($e, @go {$($lhs)*}, Some($body), match_expr!($e, @munch {} $($clauses)*))
+ };
+ ($e:expr, @munch {$($lhs:tt)*} => $body:expr) => {
+ match_expr!($e, @munch {$($lhs)*} => $body ,)
+ };
+ ($e:expr, @munch {}) => {
+ None
+ };
+ ($e:expr, @munch {$($lhs:tt)*} $t:tt $($tts:tt)*) => {
+ match_expr!($e, @munch {$($lhs)* $t} $($tts)*)
+ };
+
+ // ==== ENTRY ====
+ ($e:expr, { $($clauses:tt)* }) => {{
+ let e = &$e;
+ match_expr!(e, @munch {} $($clauses)*)
+ }};
+}
+
+#[cfg(test)]
+mod test {
+ use crate::*;
+
+ #[test]
+ fn match_expr() {
+ assert_eq!(match_expr!((), { _ => "hello" }), Some("hello"));
+
+ assert_eq!(match_expr!(Lisp::List(vec![]), { () => "yo", _ => "hello" }), Some("yo"));
+
+ assert_eq!(
+ match_expr!(parse(r#" ("hello" "there") "#.as_bytes()).unwrap(), { (_, s) => s.clone() }),
+ Some(Lisp::String("there".to_owned()))
+ );
+
+ assert_eq!(match_expr!(Lisp::Int(0), { 0 => 666, 123 => 456 }), Some(666));
+ assert_eq!(match_expr!(Lisp::Int(123), { 0 => 666, 123 => 456 }), Some(456));
+ assert_eq!(match_expr!(Lisp::Int(123), { i(x) => *x }), Some(123));
+
+ assert_eq!(match_expr!(parse(b"foo").unwrap(), { sy(s) => *s }), Symbol::pack_str("foo"));
+ assert_eq!(match_expr!(parse(b"foo").unwrap(), { sy(_) => () }), Some(()));
+ assert_eq!(match_expr!(parse(b"foo-bar").unwrap(), { 'foo_bar => () }), Some(()));
+
+ assert_eq!(match_expr!(parse(b"(foo 123)").unwrap(), { ('foo, i(x)) => *x }), Some(123));
+
+ #[derive(Debug, PartialEq)]
+ enum Update {
+ Version(i64, i64, i64),
+ Force,
+ }
+ use Update::*;
+ let v = match_expr!(parse(b"(version 1 2 3)").unwrap(), {
+ ('version, i(major), i(minor), i(patch)) => Version(*major, *minor, *patch),
+ 'force => Force
+ });
+ assert_eq!(v, Some(Version(1, 2, 3)));
+ }
+}