Add parsing for characters
authorCaleb Jones <code@calebjones.net>
Wed, 14 Dec 2016 07:06:23 +0000 (02:06 -0500)
committerCaleb Jones <code@calebjones.net>
Wed, 14 Dec 2016 07:06:23 +0000 (02:06 -0500)
src/lib.rs

index bd8aa71f1259e6e9540b625f155a157692a4c76a..461a2e95095f27da18e55b16eeed44f1888416b4 100644 (file)
@@ -14,6 +14,8 @@ pub enum Atom {
     Sym(String),
     /// A value representing a string literal.
     Str(String),
+    /// A value representing a single character.
+    Char(char),
     /// A value representing an integer. Any number containing no decimal point
     /// will be parsed as an `Int`.
     Int(i64),
@@ -59,7 +61,7 @@ named!(list<&str, Vec<Sexp> >,
   )
 );
 
-named!(atom<&str, Atom>, alt!(string | symbol | number));
+named!(atom<&str, Atom>, alt!(string | symbol | number | character));
 
 named!(string<&str, Atom>,
   do_parse!(
@@ -83,7 +85,7 @@ named!(symbol<&str, Atom>,
 
 fn valid_ident_prefix(ident: &str) -> IResult<&str, ()>  {
     match ident.chars().next() {
-        Some(c) if !c.is_digit(10) && valid_ident_char(c) =>
+        Some(c) if c != '#' && !c.is_digit(10) && valid_ident_char(c) =>
             IResult::Done(&ident[1..], ()),
         None => IResult::Incomplete(nom::Needed::Unknown),
         _ => IResult::Error(nom::ErrorKind::Custom(0)),
@@ -103,6 +105,15 @@ named!(number<&str, Atom>,
   )
 );
 
+named!(character<&str, Atom>,
+  do_parse!(
+    opt!(multispace) >>
+    tag_s!("#\\") >>
+    character: take_s!(1) >>
+    (Atom::Char(character.chars().next().unwrap()))
+  )
+);
+
 #[cfg(test)]
 #[test]
 fn test_parse_number() {
@@ -127,7 +138,21 @@ fn test_parse_ident() {
     assert_eq!(symbol(" ->socket"), IResult::Done("", Atom::Sym("->socket".into())));
     assert_eq!(symbol("fib("), IResult::Done("(", Atom::Sym("fib".into())));
 
+    // We reserve #foo for the implementation to do as it wishes
+    assert!(symbol("#hi").is_err());
+
     assert!(symbol("0").is_err());
     assert!(symbol("()").is_err());
     assert!(symbol("").is_incomplete());
 }
+
+#[cfg(test)]
+#[test]
+fn test_parse_char() {
+    assert_eq!(character("#\\\""), IResult::Done("", Atom::Char('"')));
+    assert_eq!(character("#\\ "), IResult::Done("", Atom::Char(' ')));
+    assert_eq!(character("  #\\\\"), IResult::Done("", Atom::Char('\\')));
+
+    assert!(character("#").is_incomplete());
+    assert!(character("a").is_err());
+}