Float parsing
diff --git a/src/lit.rs b/src/lit.rs
index 6b36315..a427ae8 100644
--- a/src/lit.rs
+++ b/src/lit.rs
@@ -132,6 +132,7 @@
     use escape::{cooked_char, cooked_string, raw_string};
     use space::whitespace;
     use nom::IResult;
+    use unicode_xid::UnicodeXID;
 
     named!(pub lit -> Lit, alt!(
         string
@@ -142,8 +143,9 @@
         |
         character
         |
+        float // must be before int
+        |
         int => { |(value, ty)| Lit::Int(value, ty) }
-    // TODO: Float
         |
         boolean
     ));
@@ -191,6 +193,19 @@
         (Lit::Char(ch))
     ));
 
+    named!(float -> Lit, do_parse!(
+        option!(whitespace) >>
+        value: float_string >>
+        suffix: alt!(
+            tag!("f32") => { |_| FloatTy::F32 }
+            |
+            tag!("f64") => { |_| FloatTy::F64 }
+            |
+            epsilon!() => { |_| FloatTy::Unsuffixed }
+        ) >>
+        (Lit::Float(value, suffix))
+    ));
+
     named!(pub int -> (u64, IntTy), tuple!(
         preceded!(
             option!(whitespace),
@@ -227,6 +242,81 @@
         keyword!("false") => { |_| Lit::Bool(false) }
     ));
 
+    fn float_string(input: &str) -> IResult<&str, String> {
+        let mut chars = input.chars().peekable();
+        match chars.next() {
+            Some(ch) if ch >= '0' && ch <= '9' => {}
+            _ => return IResult::Error,
+        }
+
+        let mut len = 1;
+        let mut has_dot = false;
+        let mut has_exp = false;
+        while let Some(&ch) = chars.peek() {
+            match ch {
+                '0'...'9' | '_' => {
+                    chars.next();
+                    len += 1;
+                }
+                '.' => {
+                    if has_dot {
+                        break;
+                    }
+                    chars.next();
+                    if chars.peek()
+                        .map(|&ch| ch == '.' || UnicodeXID::is_xid_start(ch))
+                        .unwrap_or(false) {
+                        return IResult::Error;
+                    }
+                    len += 1;
+                    has_dot = true;
+                }
+                'e' | 'E' => {
+                    chars.next();
+                    len += 1;
+                    has_exp = true;
+                    break;
+                }
+                _ => break,
+            }
+        }
+
+        let rest = &input[len..];
+        if !(has_dot || has_exp || rest.starts_with("f32") || rest.starts_with("f64")) {
+            return IResult::Error;
+        }
+
+        if has_exp {
+            let mut has_exp_value = false;
+            while let Some(&ch) = chars.peek() {
+                match ch {
+                    '+' | '-' => {
+                        if has_exp_value {
+                            break;
+                        }
+                        chars.next();
+                        len += 1;
+                    }
+                    '0'...'9' => {
+                        chars.next();
+                        len += 1;
+                        has_exp_value = true;
+                    }
+                    '_' => {
+                        chars.next();
+                        len += 1;
+                    }
+                    _ => break,
+                }
+            }
+            if !has_exp_value {
+                return IResult::Error;
+            }
+        }
+
+        IResult::Done(&input[len..], input[..len].into())
+    }
+
     pub fn digits(input: &str) -> IResult<&str, u64> {
         let mut value = 0u64;
         let mut len = 0;