Support demangling symbols with dot-delimited words at the end

This accounts for values like LLVM IR branch labels.

Closes #15
diff --git a/src/lib.rs b/src/lib.rs
index b1156f4..2d0d7c8 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -36,6 +36,7 @@
 pub struct Demangle<'a> {
     original: &'a str,
     inner: &'a str,
+    suffix: &'a str,
     valid: bool,
     /// The number of ::-separated elements in the original name.
     elements: usize,
@@ -98,6 +99,18 @@
         }
     }
 
+    // Output like LLVM IR adds extra period-delimited words. See if
+    // we are in that case and save the trailing words if so.
+    let mut suffix = "";
+    if let Some(i) = s.rfind("E.") {
+        let (head, tail) = s.split_at(i + 1); // After the E, before the period
+
+        if is_symbol_like(tail) {
+            s = head;
+            suffix = tail;
+        }
+    }
+
     // First validate the symbol. If it doesn't look like anything we're
     // expecting, we just print it literally. Note that we must handle non-Rust
     // symbols because we could have any function in the backtrace.
@@ -155,6 +168,7 @@
 
     Demangle {
         inner: inner,
+        suffix: suffix,
         valid: valid,
         elements: elements,
         original: s,
@@ -202,6 +216,35 @@
     s.starts_with('h') && s[1..].chars().all(|c| c.is_digit(16))
 }
 
+fn is_symbol_like(s: &str) -> bool {
+    s.chars().all(|c| {
+        // Once `char::is_ascii_punctuation` and `char::is_ascii_alphanumeric`
+        // have been stable for long enough, use those instead for clarity
+        is_ascii_alphanumeric(c) || is_ascii_punctuation(c)
+    })
+}
+
+// Copied from the documentation of `char::is_ascii_alphanumeric`
+fn is_ascii_alphanumeric(c: char) -> bool {
+    match c {
+        '\u{0041}' ... '\u{005A}' |
+        '\u{0061}' ... '\u{007A}' |
+        '\u{0030}' ... '\u{0039}' => true,
+        _ => false,
+    }
+}
+
+// Copied from the documentation of `char::is_ascii_punctuation`
+fn is_ascii_punctuation(c: char) -> bool {
+    match c {
+        '\u{0021}' ... '\u{002F}' |
+        '\u{003A}' ... '\u{0040}' |
+        '\u{005B}' ... '\u{0060}' |
+        '\u{007B}' ... '\u{007E}' => true,
+        _ => false,
+    }
+}
+
 impl<'a> fmt::Display for Demangle<'a> {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
         // Alright, let's do this.
@@ -288,6 +331,8 @@
             }
         }
 
+        try!(f.write_str(self.suffix));
+
         Ok(())
     }
 }
@@ -399,6 +444,17 @@
     }
 
     #[test]
+    fn demangle_llvm_ir_branch_labels() {
+        t!("_ZN4core5slice77_$LT$impl$u20$core..ops..index..IndexMut$LT$I$GT$$u20$for$u20$$u5b$T$u5d$$GT$9index_mut17haf9727c2edfbc47bE.exit.i.i", "core::slice::<impl core::ops::index::IndexMut<I> for [T]>::index_mut::haf9727c2edfbc47b.exit.i.i");
+        t_nohash!("_ZN4core5slice77_$LT$impl$u20$core..ops..index..IndexMut$LT$I$GT$$u20$for$u20$$u5b$T$u5d$$GT$9index_mut17haf9727c2edfbc47bE.exit.i.i", "core::slice::<impl core::ops::index::IndexMut<I> for [T]>::index_mut.exit.i.i");
+    }
+
+    #[test]
+    fn demangle_ignores_suffix_that_doesnt_look_like_a_symbol() {
+        t!("_ZN3fooE.llvm moocow", "_ZN3fooE.llvm moocow");
+    }
+
+    #[test]
     fn dont_panic() {
         super::demangle("_ZN2222222222222222222222EE").to_string();
         super::demangle("_ZN5*70527e27.ll34csaғE").to_string();