Repository: toml-rs/toml (language: rust)

Issue:
Migrate parser from nom
`nom` is great for parsing binary formats, but no-so-great for parsing languages and reporting human readable errors. I'm considering [combine](https://docs.rs/combine), [pest](https://github.com/pest-parser/pest) (procedural branch), [peg](https://github.com/kevinmehall/rust-peg), [larlpop](https://github.com/nikomatsakis/lalrpop) at the moment.


Discussion / hints:

Fix patch:
diff --git a/Cargo.toml b/Cargo.toml
index 620cbf54..a7c5fe37 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -7,7 +7,6 @@ keywords = ["toml"]
 categories = ["encoding", "parser-implementations", "parsing"]
 description = "Yet another format-preserving TOML parser."
 authors = ["Andronik Ordian <write@reusable.software>"]
-homepage = "https://github.com/ordian/toml_edit"
 repository = "https://github.com/ordian/toml_edit"
 documentation = "https://docs.rs/toml_edit"
 
@@ -19,9 +18,12 @@ appveyor = { repository = "ordian/toml_edit" }
 chrono = "0.4"
 intrusive-collections = "0.6"
 linked-hash-map = "0.4"
-nom_locate = "0.1.0"
 typed-arena = "1.3"
-nom = { version = "3.2", features = ["verbose-errors"] }
+combine = "3.0.0-alpha.1"
+
+# TODO: remove this, once new version is released
+[replace]
+"combine:3.0.0-alpha.1" = { git = "https://github.com/Marwes/combine" }
 
 [dev-dependencies]
 serde_json = "1.0"
diff --git a/src/display.rs b/src/display.rs
index 8516b85b..de6f0f44 100644
--- a/src/display.rs
+++ b/src/display.rs
@@ -51,6 +51,9 @@ impl Display for Array {
     fn fmt(&self, f: &mut Formatter) -> Result {
         write!(f, "{}[", self.decor.prefix)?;
         join(f, self.values.iter(), ",")?;
+        if self.trailing_comma {
+            write!(f, ",")?;
+        }
         write!(f, "{}", self.trailing)?;
         write!(f, "]{}", self.decor.suffix)
     }
diff --git a/src/document.rs b/src/document.rs
index cfb79cb6..b73ee2c4 100644
--- a/src/document.rs
+++ b/src/document.rs
@@ -140,11 +140,11 @@ impl Drop for DocumentInner {
 }
 
 impl FromStr for Document {
-    type Err = parser::Error;
+    type Err = parser::TomlError;
 
     /// Parses a document from a &str
     fn from_str(s: &str) -> Result<Self, Self::Err> {
-        parser::Parser::parse(s)
+        parser::TomlParser::parse(s)
     }
 }
 
diff --git a/src/formatted.rs b/src/formatted.rs
index beafb351..a42e780b 100644
--- a/src/formatted.rs
+++ b/src/formatted.rs
@@ -69,6 +69,7 @@ pub(crate) fn value(mut val: Value, raw: &str) -> Value {
         }
         _ => {}
     };
+    decorate(&mut val, "", "");
     val
 }
 
diff --git a/src/key.rs b/src/key.rs
index 95bd4bc8..7a2e6471 100644
--- a/src/key.rs
+++ b/src/key.rs
@@ -1,7 +1,8 @@
 use std::str::FromStr;
 use decor::InternalString;
 use parser;
-use nom;
+use combine;
+use combine::Parser;
 
 
 /// Key as part of a Key/Value Pair or a table header.
@@ -33,27 +34,19 @@ pub struct Key {
 }
 
 impl FromStr for Key {
-    type Err = parser::Error;
+    type Err = parser::TomlError;
 
     /// Parses a key from a &str
     fn from_str(s: &str) -> Result<Self, Self::Err> {
-        let parsed = parser::key(parser::Span::new(s));
-        match parsed {
-            nom::IResult::Done(i, (key, raw)) => if i.fragment.is_empty() {
-                Ok(Self {
+        parser::key()
+            .parse(combine::State::new(s))
+            .map(|((raw, key), _)| {
+                Key {
+                    raw: raw.into(),
                     key: key,
-                    raw: raw.fragment.into(),
-                })
-            } else {
-                Err(Self::Err::new(parser::ErrorKind::InvalidKey, i))
-            },
-            nom::IResult::Error(e) => {
-                let mut err = parser::to_error(&e);
-                err.kind = parser::ErrorKind::InvalidKey;
-                Err(err)
-            }
-            _ => unreachable!("key should be complete"),
-        }
+                }
+            })
+            .map_err(|e| Self::Err::new(e, s))
     }
 }
 
diff --git a/src/lib.rs b/src/lib.rs
index e472b529..9e9ac498 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -1,5 +1,5 @@
 // #![deny(missing_docs)]
-#![deny(warnings)]
+// #![deny(warnings)]
 
 //! # `toml_edit`
 //!
@@ -30,8 +30,7 @@
 #[macro_use]
 extern crate intrusive_collections;
 #[macro_use]
-extern crate nom;
-extern crate nom_locate;
+extern crate combine;
 extern crate chrono;
 extern crate linked_hash_map;
 extern crate typed_arena;
@@ -49,7 +48,7 @@ mod document;
 
 pub use value::{Array, InlineTable, Value};
 pub use key::Key;
-pub use parser::Error;
+pub use parser::TomlError;
 pub use table::{Iter, IterMut, Table, TableChild, TableChildMut, TableEntry};
 pub use array_of_tables::ArrayOfTables;
 pub use document::Document;
diff --git a/src/parser/array.rs b/src/parser/array.rs
index 063f7335..50f08717 100644
--- a/src/parser/array.rs
+++ b/src/parser/array.rs
@@ -1,88 +1,72 @@
-use nom;
-use parser::errors::ErrorKind;
+use combine::*;
+use combine::range::recognize_with_value;
+use combine::char::char;
+use combine::primitives::RangeStream;
+use parser::errors::CustomError;
 use parser::trivia::ws_comment_newline;
 use parser::value::value;
-use parser::{LenWorkaround, Span};
-use ::value::{Value, Array};
-use ::decor::InternalString;
-use ::formatted::decorated;
+use value::{Array, Value};
+use decor::InternalString;
+use formatted::decorated;
 
 // ;; Array
 
 // array = array-open array-values array-close
-named!(parse_array(Span) -> (Vec<Value>, &str),
-       delimited!(
-           complete!(tag!(ARRAY_OPEN)),
-           array_values,
-           err!(ErrorKind::UnterminatedArray,
-                complete!(tag!(ARRAY_CLOSE)))
-       )
-);
+parse!(array() -> Array, {
+    between(char(ARRAY_OPEN), char(ARRAY_CLOSE),
+            array_values().and_then(|(v, c, t)| array_from_vec(v, c, t)))
+});
 
-fn array_from_vec(trailing: &str, v: Vec<Value>) -> Option<Array> {
+fn array_from_vec(v: Vec<Value>, comma: bool, trailing: &str) -> Result<Array, CustomError> {
     let mut array = Array::default();
+    array.trailing_comma = comma;
     array.trailing = InternalString::from(trailing);
     for val in v {
-        if !array.push_value(val, false) {
-            return None;
+        let err = Err(CustomError::MixedArrayType {
+            got: format!("{:?}", val.get_type()),
+            expected: format!("{:?}", array.value_type()),
+        });
+        if !array.push_value(
+            val,
+            /* decorate = */
+            false,
+        ) {
+            return err;
         }
     }
-    Some(array)
-}
-
-pub fn array(input: Span) -> nom::IResult<Span, Array> {
-    let (rest, p) = try_parse!(input, parse_array);
-
-    match array_from_vec(p.1, p.0) {
-        Some(a) => nom::IResult::Done(rest, a),
-        _ => e!(ErrorKind::MixedArrayType, rest),
-    }
+    Ok(array)
 }
 
 // note: we're omitting ws and newlines here, because
 // they should be part of the formatted values
 // array-open  = %x5B ws-newline  ; [
-const ARRAY_OPEN: &str = "[";
+const ARRAY_OPEN: char = '[';
 // array-close = ws-newline %x5D  ; ]
-const ARRAY_CLOSE: &str = "]";
+const ARRAY_CLOSE: char = ']';
 // array-sep = ws %x2C ws  ; , Comma
-const ARRAY_SEP: &str = ",";
+const ARRAY_SEP: char = ',';
 
 // note: this rule is modified
 // array-values = [ ( array-value array-sep array-values ) /
 //                  array-value / ws-comment-newline ]
+parse!(array_values() -> (Vec<Value>, bool, &'a str), {
+    (
+        optional(
+            recognize_with_value(
+                sep_end_by1(array_value(), char(ARRAY_SEP))
+            ).map(|(r, v): (&'a str, _)| (v, r.ends_with(',')))
+        ),
+        ws_comment_newline(),
+    ).map(|(v, t)| {
+        let (v, c) = v.unwrap_or_default();
+        (v, c, t)
+    })
+});
 
-named!(array_values(Span) -> (Vec<Value>, &str),
-       do_parse!(
-           v: opt!(
-               do_parse!(
-                v: separated_nonempty_list_complete!(
-                       tag!(ARRAY_SEP),
-                       array_value
-                   ) >>
-                t: opt!(trailing) >>
-                   (v, t.map(|s| s.fragment).unwrap_or(""))
-               )
-           ) >>
-           w: ws_comment_newline >>
-           (v.unwrap_or_else(|| (Vec::new(), w.fragment)))
-       )
-);
-
-named!(array_value(Span) -> Value,
-       do_parse!(
-           ws1: ws_comment_newline >>
-             v: value >>
-           ws2: ws_comment_newline >>
-                (decorated(v, ws1.fragment, ws2.fragment))
-       )
-);
-
-named!(trailing(Span) -> Span,
-       recognize!(
-           tuple!(
-               complete!(tag!(ARRAY_SEP)),
-               ws_comment_newline
-           )
-       )
-);
+parse!(array_value() -> Value, {
+    try((
+        ws_comment_newline(),
+        value(),
+        ws_comment_newline(),
+    )).map(|(ws1, v, ws2)| decorated(v, ws1, ws2))
+});
diff --git a/src/parser/datetime.rs b/src/parser/datetime.rs
index 13ecc649..252a3b3f 100644
--- a/src/parser/datetime.rs
+++ b/src/parser/datetime.rs
@@ -1,183 +1,105 @@
-use ::value;
-use nom::{self, IResult};
-use parser::errors::{ErrorKind, is_custom};
-use chrono::{self, FixedOffset};
-use parser::Span;
+use combine::*;
+use combine::char::{char, digit};
+use combine::combinator::{skip_count_min_max, SkipCountMinMax};
+use chrono;
+use combine::primitives::RangeStream;
+use combine::range::{recognize, recognize_with_value};
+use value;
+
+
+#[inline]
+pub fn repeat<P: Parser>(count: usize, parser: P) -> SkipCountMinMax<P> {
+    skip_count_min_max(count, count, parser)
+}
 
 // ;; Date and Time (as defined in RFC 3339)
 
-// date-time      = offset-date-time / local-date-time / local-date / local-time
-named!(pub date_time(Span) -> value::DateTime,
-       alt_custom!(e_kind!(nom::ErrorKind::Alt),
-           offset_date_time
-         | local_date_time
-         | local_date
-         | local_time
-       )
-);
-
-fn digits(input: Span, n: u8) -> IResult<Span, Span> {
-    verify!(input, complete!(take!(n)),
-            |s: Span| s.fragment.bytes().all(nom::is_digit)
+// date-time = offset-date-time / local-date-time / local-date / local-time
+// offset-date-time = full-date "T" full-time
+// local-date-time = full-date "T" partial-time
+// local-date = full-date
+// local-time = partial-time
+// full-time = partial-time time-offset
+parse!(date_time() -> value::DateTime, {
+    choice!(
+        recognize_with_value((
+            full_date(),
+            optional((
+                char('T'),
+                partial_time(),
+                optional(time_offset()),
+            ))
+        ))
+            .and_then(|(s, (_, opt))| {
+                match opt {
+                    // Offset Date-Time
+                    Some((_, _, Some(_))) => {
+                        chrono::DateTime::parse_from_rfc3339(s)
+                            .map(value::DateTime::OffsetDateTime)
+                    }
+                    // Local Date-Time
+                    Some(_) => {
+                        s.parse::<chrono::NaiveDateTime>()
+                            .map(value::DateTime::LocalDateTime)
+                    }
+                    // Local Date
+                    None => {
+                        s.parse::<chrono::NaiveDate>()
+                            .map(value::DateTime::LocalDate)
+                    }
+                }
+
+            }),
+        // Local Time
+        recognize(partial_time())
+            .and_then(|s: &str| s.parse::<chrono::NaiveTime>())
+            .message("While parsing a Time")
+            .map(value::DateTime::LocalTime)
     )
-}
+        .message("While parsing a Date-Time")
+});
 
+// full-date      = date-fullyear "-" date-month "-" date-mday
 // date-fullyear  = 4DIGIT
-named!(date_fullyear(Span) -> Span,
-       call!(digits, 4)
-);
 // date-month     = 2DIGIT  ; 01-12
-named!(date_month(Span) -> Span,
-       err!(ErrorKind::InvalidDateTime,
-            call!(digits, 2))
-);
 // date-mday      = 2DIGIT  ; 01-28, 01-29, 01-30, 01-31 based on month/year
-named!(date_mday(Span) -> Span,
-       err!(ErrorKind::InvalidDateTime,
-            call!(digits, 2))
-);
+parse!(full_date() -> &'a str, {
+    recognize((
+        try((repeat(4, digit()), char('-'))),
+        repeat(2, digit()),
+        char('-'),
+        repeat(2, digit()),
+    ))
+});
+
+// partial-time   = time-hour ":" time-minute ":" time-second [time-secfrac]
 // time-hour      = 2DIGIT  ; 00-23
-named!(time_hour(Span) -> Span,
-       call!(digits, 2)
-);
 // time-minute    = 2DIGIT  ; 00-59
-named!(time_minute(Span) -> Span,
-       err!(ErrorKind::InvalidDateTime,
-            call!(digits, 2))
-);
 // time-second    = 2DIGIT  ; 00-58, 00-59, 00-60 based on leap second rules
-named!(time_second(Span) -> Span,
-       err!(ErrorKind::InvalidDateTime,
-            call!(digits, 2))
-);
-
-// time-numoffset = ( "+" / "-" ) time-hour ":" time-minute
-named!(#[inline], time_numoffset(Span) -> (),
-       do_parse!(
-           complete!(one_of!("+-")) >>
-           err!(ErrorKind::InvalidDateTime,
-                call!(time_hour)) >>
-               err!(ErrorKind::InvalidDateTime,
-                    complete!(tag!(":"))) >>
-               time_minute >>
-               ()
-       )
-);
-// time-offset    = "Z" / time-numoffset
-named!(#[inline], time_offset(Span) -> (),
-       alt_complete!(
-           tag!("Z") => { |_| () }
-         | time_numoffset
-       )
-);
-
 // time-secfrac   = "." 1*DIGIT
-// partial-time   = time-hour ":" time-minute ":" time-second [time-secfrac]
-named!(#[inline], partial_time(Span) -> (),
-       do_parse!(
-           time_hour >>
-           complete!(tag!(":")) >>
-           err!(ErrorKind::InvalidDateTime,
-                tuple!(
-                    time_minute,
-                    tag!(":"),
-                    time_second,
-                    opt!(complete!(tuple!(tag!("."), call!(nom::digit))))
-                )
-           ) >>
-           ()
-       )
-);
-
-// full-date      = date-fullyear "-" date-month "-" date-mday
-named!(#[inline], full_date(Span) -> (),
-       do_parse!(
-           date_fullyear >>
-           complete!(tag!("-")) >>
-           date_month >>
-           err!(ErrorKind::InvalidDateTime,
-                complete!(tag!("-"))) >>
-           date_mday >>
-           ()
-       )
-);
+parse!(partial_time() -> (), {
+    (
+        try((
+            repeat(2, digit()),
+            char(':'),
+        )),
+        repeat(2, digit()),
+        char(':'),
+        repeat(2, digit()),
+        optional(try(char('.')).and(skip_many1(digit()))),
+    ).map(|_| ())
+});
 
-// full-time      = partial-time time-offset
-named!(#[inline], full_time(Span) -> (),
-       do_parse!(
-           partial_time >>
-           time_offset >>
-           ()
-       )
-);
-
-macro_rules! date_time (
-    ($input:expr, $parse_dt:ty, $value_dt:path, $submac:ident!( $($args:tt)*)) => (
-        do_parse!(
-            $input,
-         s: recognize!($submac!($($args)*)) >>
-         d: err!(ErrorKind::InvalidDateTime,
-                 map!(expr_res!(s.fragment.parse::<$parse_dt>()),
-                      $value_dt)
-            ) >>
-            (d)
-        )
-    );
-    ($input:expr, $parse_dt:ty, $value_dt:path, $f:expr) => (
-        date_time!($input, $parse_dt, $value_dt, call!($f));
-    );
-);
-
-// ;; Offset Date-Time
-
-// offset-date-time = full-date "T" full-time
-named!(pub offset_date_time(Span) -> value::DateTime,
-       date_time!(
-           chrono::DateTime<FixedOffset>,
-           value::DateTime::OffsetDateTime,
-           tuple!(
-               full_date,
-               complete!(tag!("T")),
-               full_time
-           )
-       )
-);
-
-// ;; Local Date-Time
-
-// local-date-time = full-date "T" partial-time
-named!(pub local_date_time(Span) -> value::DateTime,
-       date_time!(
-           chrono::NaiveDateTime,
-           value::DateTime::LocalDateTime,
-           tuple!(
-               full_date,
-               complete!(tag!("T")),
-               err!(ErrorKind::InvalidDateTime,
-                    call!(partial_time))
-           )
-       )
-);
-
-
-// ;; Local Date
-
-// local-date = full-date
-named!(pub local_date(Span) -> value::DateTime,
-       date_time!(
-           chrono::NaiveDate,
-           value::DateTime::LocalDate,
-           full_date
-       )
-);
-
-// ;; Local Time
-
-// local-time = partial-time
-named!(pub local_time(Span) -> value::DateTime,
-       date_time!(
-           chrono::NaiveTime,
-           value::DateTime::LocalTime,
-           partial_time
-       )
-);
+// time-offset    = "Z" / time-numoffset
+// time-numoffset = ( "+" / "-" ) time-hour ":" time-minute
+parse!(time_offset() -> (), {
+    try(char('Z')).map(|_| ())
+        .or(
+            (
+                try(choice([char('+'), char('-')])),
+                repeat(2, digit()),
+                char(':'),
+                repeat(2, digit()),
+            ).map(|_| ())
+        ).message("While parsing a Time Offset")
+});
diff --git a/src/parser/document.rs b/src/parser/document.rs
index f3c76264..050bdd2f 100644
--- a/src/parser/document.rs
+++ b/src/parser/document.rs
@@ -1,41 +1,109 @@
-use nom;
-use std::mem;
-use parser::{Span, Parser};
-use parser::errors::{Error, ErrorKind, is_custom, to_error};
-use parser::trivia::{ws, line_trailing, line_ending, comment};
+use combine::*;
+use combine::Parser;
+use combine::char::char;
+use combine::range::recognize;
+use combine::primitives::RangeStream;
+use parser::{TomlError, TomlParser};
+use parser::errors::CustomError;
+use parser::trivia::{comment, line_ending, line_trailing, newline, ws};
 use parser::key::key;
 use parser::value::value;
+use parser::table::table;
 use parser::inline_table::KEYVAL_SEP;
-use ::decor::{InternalString, Repr};
-use ::document::Document;
-use ::value::KeyValue;
-use ::formatted::decorated;
+use decor::{InternalString, Repr};
+use document::Document;
+use value::KeyValue;
+use formatted::decorated;
+use std::mem;
+use std::cell::RefCell;
+// https://github.com/rust-lang/rust/issues/41358
+#[allow(unused_imports)]
+use std::ops::DerefMut;
 
-macro_rules! try_parser (
-    ($parser:ident, $span:ident, $method:ident) => (
-        {
-            let (p, res) = $parser.$method($span);
-            $parser = p;
-            match res {
-                nom::IResult::Done(rest, _) => {
-                    if rest.fragment.len() == 0 {
-                        return Ok($parser.document);
-                    }
-                    $span = rest;
-                    continue;
-                }
-                nom::IResult::Error(e) => {
-                    if is_custom(&e) {
-                        return Err(to_error(&e));
-                    }
+
+parser!{
+    fn parse_comment['a, 'b, I](parser: &'b RefCell<TomlParser>)(I) -> ()
+        where
+        [I: RangeStream<Range = &'a str, Item = char>,]
+    {
+        (
+            comment(),
+            line_ending(),
+        ).map(|(c, e)|
+              parser
+              .borrow_mut()
+              .deref_mut()
+              .on_comment(c, e))
+    }
+}
+
+parser!{
+    fn parse_ws['a, 'b, I](parser: &'b RefCell<TomlParser>)(I) -> ()
+        where
+        [I: RangeStream<Range = &'a str, Item = char>,]
+    {
+        ws().map(|w|
+                 parser
+                 .borrow_mut()
+                 .deref_mut()
+                 .on_ws(w))
+    }
+}
+
+parser!{
+    fn parse_newline['a, 'b, I](parser: &'b RefCell<TomlParser>)(I) -> ()
+        where
+        [I: RangeStream<Range = &'a str, Item = char>,]
+    {
+        recognize(newline())
+            .map(|w|
+                 parser
+                 .borrow_mut()
+                 .deref_mut()
+                 .on_ws(w))
+    }
+}
+
+parser!{
+    fn keyval['a, 'b, I](parser: &'b RefCell<TomlParser>)(I) -> ()
+        where
+        [I: RangeStream<Range = &'a str, Item = char>,]
+    {
+        parse_keyval().and_then(|(k, kv)|
+                                parser
+                                .borrow_mut()
+                                .deref_mut()
+                                .on_keyval(k, kv))
+    }
+}
+
+// keyval = key keyval-sep val
+parser!{
+    fn parse_keyval['a, I]()(I) -> (InternalString, KeyValue)
+        where
+        [I: RangeStream<Range = &'a str, Item = char>,]
+    {
+        (
+            (key(), ws()),
+            char(KEYVAL_SEP),
+            (ws(), value(), line_trailing())
+        ).map(|(k, _, v)| {
+            let (pre, v, suf) = v;
+            let v = decorated(v, pre, suf);
+            let ((raw, key), suf) = k;
+            (
+                key,
+                KeyValue {
+                    key: Repr::new("", raw, suf),
+                    value: v,
                 }
-                _ => unreachable!("expressions are complete"),
-            }
-        }
-    );
-);
+            )
+        })
+    }
+}
+
 
-impl Parser {
+impl TomlParser {
     // ;; TOML
 
     // toml = expression *( newline expression )
@@ -44,86 +112,46 @@ impl Parser {
     //                ( ws keyval ws [ comment ] ) /
     //                ( ws table ws [ comment ] ) /
     //                  ws )
-    pub fn parse(s: &str) -> Result<Document, Error> {
-        let mut parser = Self::default();
-        let mut span = Span::new(s);
-        loop {
-            try_parser!(parser, span, ws_comment);
-            try_parser!(parser, span, keyval);
-            try_parser!(parser, span, table);
-            try_parser!(parser, span, ws);
-            return Err(Error::new(ErrorKind::Unknown, span));
-        }
+    pub fn parse(s: &str) -> Result<Document, TomlError> {
+        let parser = RefCell::new(Self::default());
+        let input = State::new(s);
+        skip_many(
+            parse_ws(&parser)
+                .with(choice((
+                    parse_comment(&parser),
+                    keyval(&parser),
+                    table(&parser),
+                    parse_newline(&parser),
+                )))
+                .skip(parse_ws(&parser)),
+        ).parse(input)
+            .map(move |_| parser.into_inner().document)
+            .map_err(|e| TomlError::new(e, s))
     }
 
-    method!(ws_comment<Parser>(Span) -> (), mut self,
-            do_parse!(
-                w: ws >>
-                c: comment >>
-                e: line_ending >>
-                   ({
-                       self.document.trailing.push_str(w.fragment);
-                       self.document.trailing.push_str(c.fragment);
-                       self.document.trailing.push_str(e.fragment);
-                   })
-            )
-    );
+    fn on_ws(&mut self, w: &str) {
+        self.document.trailing.push_str(w);
+    }
 
-    method!(ws<Parser>(Span) -> (), mut self,
-            do_parse!(
-              w: ws >>
-              e: line_ending >>
-                 ({
-                     self.document.trailing.push_str(w.fragment);
-                     self.document.trailing.push_str(e.fragment);
-                 })
-            )
-    );
+    fn on_comment(&mut self, c: &str, e: &str) {
+        self.document.trailing.push_str(c);
+        self.document.trailing.push_str(e);
+    }
 
-    method!(keyval<Parser>(Span) -> (), mut self,
-            do_parse!(
-                p: call_m!(self.parse_keyval) >>
-                   err_parser!(self, Self::on_keyval,
-                               call!(p)) >>
-                   ()
-            )
-    );
+    fn on_keyval(&mut self, key: InternalString, mut kv: KeyValue) -> Result<(), CustomError> {
+        let table = unsafe { &mut *self.current_table };
 
+        let prefix = mem::replace(&mut self.document.trailing, InternalString::new());
+        kv.key.decor.prefix = prefix + &kv.key.decor.prefix;
 
-    fn on_keyval<'a>(
-        rest: Span<'a>,
-        me: &mut Parser,
-        keyval: (Span<'a>, InternalString, KeyValue),
-    ) -> nom::IResult<Span<'a>, ()>
-    {
-        let table = unsafe { &mut *me.current_table };
-        let (span, key, kv) = keyval;
         if table.contains_key(&key) {
-            e!(ErrorKind::DuplicateKey, span)
+            Err(CustomError::DuplicateKey {
+                key: key.into(),
+                table: table.header.repr.raw_value.to_string(),
+            })
         } else {
             table.key_value_pairs.insert(key, kv);
-            nom::IResult::Done(rest, ())
+            Ok(())
         }
     }
-
-    // keyval = key keyval-sep val
-    method!(parse_keyval<Parser>(Span) -> (Span, InternalString, KeyValue), mut self,
-       do_parse!(
-           k: complete!(tuple!(ws, key, ws))      >>
-              err_m!(self, ErrorKind::ExpectedEquals,
-                     complete!(tag!(KEYVAL_SEP))) >>
-           v: tuple!(ws, value, line_trailing)    >>
-               ({
-                   let (pre, v, suf) = v;
-                   let v = decorated(v, pre.fragment, suf.fragment);
-                   let (pre, (key, raw), suf) = k;
-                   let prefix = mem::replace(&mut self.document.trailing, InternalString::new());
-                   let prefix = prefix + pre.fragment;
-                   (raw, key, KeyValue {
-                       key: Repr::new(prefix, raw.fragment.into(), suf.fragment.into()),
-                       value: v,
-                   })
-               })
-       )
-    );
 }
diff --git a/src/parser/errors.rs b/src/parser/errors.rs
index 8e0dbe68..671f2041 100644
--- a/src/parser/errors.rs
+++ b/src/parser/errors.rs
@@ -1,105 +1,126 @@
-use nom;
-use std::mem;
-use parser::Span;
 use std::fmt::{Display, Formatter, Result};
-use std::error;
+use std::error::Error as StdError;
+use combine::ParseError;
+use combine::state::SourcePosition;
+use combine::primitives::Error;
 
-/// Type representing a TOML parse error
-#[derive(Debug, Clone)]
-pub struct Error {
-    pub(crate) kind: ErrorKind,
-    unparsed_line: String,
-    line_number: u32,
-}
 
-#[derive(Debug, Clone, Copy)]
-#[repr(u32)]
-pub(crate) enum ErrorKind {
-    Unknown = 1001,
-    ExpectedNewlineOrEof,
-    ExpectedEquals,
-    InvalidCharInString,
-    InvalidEscapeChar,
-    UnterminatedString,
-    UnterminatedInlineTable,
-    UnterminatedArray,
-    InvalidKey,
-    InvalidValue,
-    InvalidNumber,
-    InvalidHeader,
-    InvalidDateTime,
-    MixedArrayType,
-    DuplicateKey,
-    #[doc(hidden)]
-    __Nonexhaustive,
+/// Type representing a TOML parse error
+#[derive(Debug, Clone, Eq, PartialEq, Hash)]
+pub struct TomlError {
+    message: String,
 }
 
-pub(crate) fn is_custom<E>(e: &nom::Err<E>) -> bool {
-    use nom::Err;
-    let kind = match *e {
-        Err::Code(ref e) | Err::Node(ref e, _) | Err::Position(ref e, _) | Err::NodePosition(ref e, _, _) => e
-    };
-    is_custom_kind(kind)
+impl TomlError {
+    pub(crate) fn new(error: ParseError<SourcePosition, char, &str>, input: &str) -> Self {
+        Self {
+            message: format!("{}", FancyError::new(error, input)),
+        }
+    }
 }
 
-fn is_custom_kind(k: &nom::ErrorKind) -> bool {
-    match *k {
-        nom::ErrorKind::Custom(..) => true,
-        _ => false,
+/// Displays a TOML parse error
+///
+/// # Example
+///
+/// TOML parse error at line 1, column 10
+///   |
+/// 1 | 00:32:00.a999999
+///   |          ^
+/// Unexpected `a`
+/// Expected `digit`
+/// While parsing a Time
+/// While parsing a Date-Time
+impl Display for TomlError {
+    fn fmt(&self, f: &mut Formatter) -> Result {
+        write!(f, "{}", self.message)
     }
 }
 
-unsafe fn to_custom(k: &nom::ErrorKind) -> ErrorKind {
-    match *k {
-        nom::ErrorKind::Custom(code) => mem::transmute(code),
-        _ => unreachable!("call `to_custom` only on custom errors"),
+impl StdError for TomlError {
+    fn description(&self) -> &'static str {
+        "TOML parse error"
     }
 }
 
-pub(crate) fn to_error(e: &nom::Err<Span>) -> Error {
-    find_innermost_custom(e)
-        .unwrap_or_else(|| Error::new(ErrorKind::Unknown,
-                                      Span::new(&format!("{:?}", e))))
+
+#[derive(Debug)]
+pub(crate) struct FancyError<'a> {
+    error: ParseError<SourcePosition, char, &'a str>,
+    input: &'a str,
 }
 
-impl Error {
-    pub(crate) fn new(kind: ErrorKind, span: Span) -> Self {
-        let s = span.fragment;
+impl<'a> FancyError<'a> {
+    pub(crate) fn new(error: ParseError<SourcePosition, char, &'a str>, input: &'a str) -> Self {
         Self {
-            kind: kind,
-            unparsed_line: s[..s.find('\n').unwrap_or_else(|| s.len())].into(),
-            line_number: span.line,
+            error: error,
+            input: input,
         }
     }
 }
 
-fn find_innermost_custom(e: &nom::Err<Span>) -> Option<Error> {
-    use nom::Err;
-    match *e {
-        Err::NodePosition(ref k, s, ref v) => {
-            if let Some(err) = v.iter().last() {
-                let last = find_innermost_custom(err);
-                if last.is_some() {
-                    return last;
-                }
-            }
-            debug_assert!(is_custom(e));
-            Some(Error::new(unsafe { to_custom(k) }, s))
+impl<'a> Display for FancyError<'a> {
+    fn fmt(&self, f: &mut Formatter) -> Result {
+        let SourcePosition { line, column } = self.error.position;
+
+        let offset = line.to_string().len();
+        let content = self.input
+            .split('\n')
+            .nth((line - 1) as usize)
+            .expect("line");
+
+        writeln!(f, "TOML parse error at line {}, column {}", line, column)?;
+
+        //   |
+        for _ in 0..(offset + 1) {
+            write!(f, " ")?;
         }
-        Err::Position(ref k, s) if is_custom(e) => Some(Error::new(unsafe { to_custom(k) }, s)),
-        _ => None,
+        writeln!(f, "|")?;
+
+        // 1 | 00:32:00.a999999
+        write!(f, "{} | ", line)?;
+        writeln!(f, "{}", content)?;
+
+        //   |          ^
+        for _ in 0..(offset + 1) {
+            write!(f, " ")?;
+        }
+        write!(f, "|")?;
+        for _ in 0..column {
+            write!(f, " ")?;
+        }
+        writeln!(f, "^")?;
+
+        Error::fmt_errors(self.error.errors.as_ref(), f)
     }
 }
 
-impl Display for Error {
-    fn fmt(&self, f: &mut Formatter) -> Result {
-        write!(f, "TOML parse error {:?} at line {}: {}",
-               self.kind, self.line_number, self.unparsed_line)
-    }
+#[derive(Debug, Clone)]
+pub(crate) enum CustomError {
+    MixedArrayType { got: String, expected: String },
+    DuplicateKey { key: String, table: String },
+    InvalidHexEscape(u32),
 }
 
-impl error::Error for Error {
-    fn description(&self) -> &str {
+impl StdError for CustomError {
+    fn description(&self) -> &'static str {
         "TOML parse error"
     }
 }
+
+impl Display for CustomError {
+    fn fmt(&self, f: &mut Formatter) -> Result {
+        match *self {
+            CustomError::MixedArrayType {
+                ref got,
+                ref expected,
+            } => writeln!(f, "Mixed types in array: {} and {}", expected, got),
+            CustomError::DuplicateKey { ref key, ref table } => {
+                writeln!(f, "Duplicate key `{}` in `{}` table", key, table)
+            }
+            CustomError::InvalidHexEscape(ref h) => {
+                writeln!(f, "Invalid hex escape code: {:x} ", h)
+            }
+        }
+    }
+}
diff --git a/src/parser/inline_table.rs b/src/parser/inline_table.rs
index 47b43050..2829ff9f 100644
--- a/src/parser/inline_table.rs
+++ b/src/parser/inline_table.rs
@@ -1,83 +1,88 @@
-use nom;
-use parser::errors::ErrorKind;
+use combine::*;
+use combine::char::char;
+use combine::primitives::RangeStream;
+use parser::errors::CustomError;
 use parser::trivia::ws;
 use parser::key::key;
 use parser::value::value;
-use parser::{LenWorkaround, Span};
-use ::value::{KeyValue, InlineTable};
-use ::decor::{InternalString, Repr};
-use ::formatted::decorated;
+use value::{InlineTable, KeyValue};
+use decor::{InternalString, Repr};
+use formatted::decorated;
 
 // ;; Inline Table
 
 // inline-table = inline-table-open inline-table-keyvals inline-table-close
-named!(parse_inline_table(Span) -> (&str, Vec<(InternalString, KeyValue)>),
-       delimited!(
-           complete!(tag!(INLINE_TABLE_OPEN)),
-           inline_table_keyvals,
-           err!(ErrorKind::UnterminatedInlineTable,
-                complete!(tag!(INLINE_TABLE_CLOSE)))
-       )
-);
+parse!(inline_table() -> InlineTable, {
+    between(char(INLINE_TABLE_OPEN), char(INLINE_TABLE_CLOSE),
+            inline_table_keyvals().and_then(|(p, v)| table_from_pairs(p, v)))
+});
 
-fn table_from_pairs(preamble: &str, v: Vec<(InternalString, KeyValue)>) -> Option<InlineTable> {
+fn table_from_pairs(
+    preamble: &str,
+    v: Vec<(InternalString, KeyValue)>,
+) -> Result<InlineTable, CustomError> {
     let mut table = InlineTable::default();
     table.preamble = InternalString::from(preamble);
+
     for (k, kv) in v {
         if table.contains_key(&k) {
-            return None;
+            return Err(CustomError::DuplicateKey {
+                key: k.into(),
+                table: "inline".into(),
+            });
         }
         table.key_value_pairs.insert(k, kv);
     }
-    Some(table)
-}
-
-pub fn inline_table(input: Span) -> nom::IResult<Span, InlineTable> {
-    let (rest, p) = try_parse!(input, parse_inline_table);
-
-    match table_from_pairs(p.0, p.1) {
-        Some(a) => nom::IResult::Done(rest, a),
-        _ => e!(ErrorKind::DuplicateKey, rest),
-    }
+    Ok(table)
 }
 
 // inline-table-open  = %x7B ws     ; {
-const INLINE_TABLE_OPEN: &str = "{";
+const INLINE_TABLE_OPEN: char = '{';
 // inline-table-close = ws %x7D     ; }
-const INLINE_TABLE_CLOSE: &str = "}";
+const INLINE_TABLE_CLOSE: char = '}';
 // inline-table-sep   = ws %x2C ws  ; , Comma
-const INLINE_TABLE_SEP: &str = ",";
+const INLINE_TABLE_SEP: char = ',';
 // keyval-sep = ws %x3D ws ; =
-pub(crate) const KEYVAL_SEP: &str = "=";
+pub(crate) const KEYVAL_SEP: char = '=';
 
 // inline-table-keyvals = [ inline-table-keyvals-non-empty ]
-// inline-table-keyvals-non-empty = ( key keyval-sep val inline-table-sep inline-table-keyvals-non-empty ) /
-//                                  ( key keyval-sep val )
-named!(inline_table_keyvals(Span) -> (&str, Vec<(InternalString, KeyValue)>),
-       do_parse!(
-        v: opt!(separated_nonempty_list_complete!(
-               tag!(INLINE_TABLE_SEP),
-               keyval
-           )) >>
-        w: ws >>
-           (w.fragment, v.unwrap_or_default())
-       )
-);
+// inline-table-keyvals-non-empty =
+// ( key keyval-sep val inline-table-sep inline-table-keyvals-non-empty ) /
+// ( key keyval-sep val )
+parser!{
+    fn inline_table_keyvals['a, I]()(I) -> (&'a str, Vec<(InternalString, KeyValue)>)
+        where
+        [I: RangeStream<Range = &'a str, Item = char>,]
+    {
+        (
+            sep_by(keyval(), char(INLINE_TABLE_SEP)),
+            ws(),
+        ).map(|(v, w)| {
+            (w, v)
+        })
+    }
+}
 
-named!(keyval(Span) -> (InternalString, KeyValue),
-       do_parse!(
-        k: tuple!(ws, key, ws)   >>
-           err!(ErrorKind::ExpectedEquals,
-                complete!(tag!(KEYVAL_SEP))) >>
-        v: tuple!(ws, value, ws) >>
-           ({
-               let (pre, v, suf) = v;
-               let v = decorated(v, pre.fragment, suf.fragment);
-               let (pre, (key, raw), suf) = k;
-               (key, KeyValue {
-                   key: Repr::new(pre.fragment, raw.fragment, suf.fragment),
-                   value: v,
-               })
-           })
-       )
-);
+parser!{
+    fn keyval['a, I]()(I) -> (InternalString, KeyValue)
+        where
+        [I: RangeStream<Range = &'a str, Item = char>,]
+    {
+        (
+            try((ws(), key(), ws())),
+            char(KEYVAL_SEP),
+            (ws(), value(), ws()),
+        ).map(|(k, _, v)| {
+            let (pre, v, suf) = v;
+            let v = decorated(v, pre, suf);
+            let (pre, (raw, key), suf) = k;
+            (
+                key,
+                KeyValue {
+                    key: Repr::new(pre, raw, suf),
+                    value: v,
+                }
+            )
+        })
+    }
+}
diff --git a/src/parser/key.rs b/src/parser/key.rs
index 4dd48095..2fb77272 100644
--- a/src/parser/key.rs
+++ b/src/parser/key.rs
@@ -1,7 +1,8 @@
-use nom::{self, InputLength, Slice};
-use ::decor::InternalString;
+use combine::*;
+use combine::primitives::RangeStream;
+use combine::range::{recognize_with_value, take_while1};
 use parser::strings::{basic_string, literal_string};
-use parser::Span;
+use decor::InternalString;
 
 #[inline]
 fn is_unquoted_char(c: char) -> bool {
@@ -12,15 +13,15 @@ fn is_unquoted_char(c: char) -> bool {
 }
 
 // unquoted-key = 1*( ALPHA / DIGIT / %x2D / %x5F ) ; A-Z / a-z / 0-9 / - / _
-named!(#[inline], unquoted_key(Span) -> Span,
-       complete!(take_while1!(is_unquoted_char))
-);
+parse!(unquoted_key() -> &'a str, {
+    take_while1(is_unquoted_char)
+});
 
 // key = unquoted-key / basic-string / literal-string
-named!(pub key(Span) -> (InternalString, Span),
-       with_input!(alt_complete!(
-           basic_string
-         | literal_string => { |s: Span| InternalString::from(s.fragment) }
-         | unquoted_key   => { |s: Span| InternalString::from(s.fragment) }
-       ))
-);
+parse!(key() -> (&'a str, InternalString), {
+    recognize_with_value(choice((
+        basic_string(),
+        literal_string().map(|s: &'a str| s.into()),
+        unquoted_key().map(|s: &'a str| s.into()),
+    )))
+});
diff --git a/src/parser/macros.rs b/src/parser/macros.rs
index 3f1e1681..fdedaba3 100644
--- a/src/parser/macros.rs
+++ b/src/parser/macros.rs
@@ -1,208 +1,14 @@
-// generic
-
 #[doc(hidden)]
 #[macro_export]
-macro_rules! with_input (
-    ($input:expr, $submac:ident!( $($args:tt)*)) => (
-        {
-            match $submac!($input, $($args)*) {
-                nom::IResult::Done(rest, s) =>
-                    nom::IResult::Done(rest, (s, $input.slice(..$input.input_len() - rest.input_len()))),
-                nom::IResult::Incomplete(e) => nom::IResult::Incomplete(e),
-                nom::IResult::Error(e) => nom::IResult::Error(e),
+macro_rules! parse (
+    ($name:ident( $($arg: ident :  $arg_type: ty),* ) -> $ret:ty, $code:expr) => (
+        parser!{
+            pub fn $name['a, I]($($arg : $arg_type),*)(I) -> $ret
+                where
+                [I: RangeStream<Range = &'a str, Item = char>,]
+            {
+                $code
             }
         }
     );
 );
-
-// custom errors
-
-#[doc(hidden)]
-#[macro_export]
-macro_rules! err (
-    ($input:expr, $code:expr, $submac:ident!( $($args:tt)*)) => (
-        {
-            return_error!($input, nom::ErrorKind::Custom($code as u32), $submac!($($args)*))
-        }
-    );
-);
-
-#[doc(hidden)]
-#[macro_export]
-macro_rules! e (
-    ($code:expr, $i:expr) => (
-        e_kind!(nom::ErrorKind::Custom($code as u32), $i)
-    );
-);
-
-#[doc(hidden)]
-macro_rules! e_kind (
-    ($kind:expr, $i:expr) => (
-        nom::IResult::Error(error_position!($kind, $i))
-    );
-);
-
-#[doc(hidden)]
-#[macro_export]
-macro_rules! err_m (
-    ($i:expr, $self_:ident, $code:expr, $submac:ident!( $($args:tt)* )) => (
-        {
-            let i_ = $i.clone();
-            let cl = || {
-                $submac!(i_, $($args)*)
-            };
-
-            match cl() {
-                nom::IResult::Incomplete(x) => nom::IResult::Incomplete(x),
-                nom::IResult::Done(i, o)    => nom::IResult::Done(i, o),
-                nom::IResult::Error(_)      => {
-                    return ($self_, e!($code, $i));
-                }
-            }
-        }
-    );
-    ($i:expr, $self_:ident, $code:expr, $f:expr) => (
-        err_m!($i, $self_, $code, call!($f));
-    );
-);
-
-#[doc(hidden)]
-#[macro_export]
-macro_rules! err_parser (
-    ($i:expr, $self_:ident, $f:path, $submac:ident!( $($args:tt)* )) => (
-        {
-            let i_ = $i.clone();
-            let res = {
-                let cl = || {
-                    $submac!(i_, $f, &mut $self_, $($args)*)
-                };
-                cl()
-            };
-
-            match res {
-                nom::IResult::Incomplete(x) => nom::IResult::Incomplete(x),
-                nom::IResult::Done(i, o)    => nom::IResult::Done(i, o),
-                nom::IResult::Error(e)      => {
-                    return ($self_, nom::IResult::Error(e));
-                }
-            }
-        }
-    );
-);
-
-// propagate early return on custom error
-
-#[doc(hidden)]
-#[macro_export]
-macro_rules! alt_custom (
-    (__impl $i:expr, $err:ident!( $code:expr ), $e:ident, $($rest:tt)* ) => (
-        alt_custom!(__impl $i, $err!($code), call!($e) , $($rest)*);
-    );
-    (__impl $i:expr, $err:ident!( $code:expr ), $e:ident | $($rest:tt)*) => (
-        alt_custom!(__impl $i, $err!($code), call!($e) | $($rest)*);
-    );
-
-    (__impl $i:expr, $err:ident!( $code:expr ), $subrule:ident!( $($args:tt)*) | $($rest:tt)*) => (
-        {
-            let i_ = $i.clone();
-            let res = complete!(i_, $subrule!($($args)*));
-            match res {
-                nom::IResult::Done(_,_)     => res,
-                // argh
-                res => {
-                    if let nom::IResult::Error(e) = res {
-                        if is_custom(&e) {
-                            return nom::IResult::Error(e);
-                        }
-                    }
-                    alt_custom!(__impl $i, $err!($code), $($rest)*)
-                }
-            }
-        }
-    );
-
-    (__impl $i:expr, $err:ident!( $code:expr ), $subrule:ident!( $($args:tt)* ) => { $gen:expr } | $($rest:tt)*) => (
-        {
-            let i_ = $i.clone();
-            match complete!(i_, $subrule!($($args)* )) {
-                nom::IResult::Done(i,o)     => nom::IResult::Done(i,$gen(o)),
-                // argh
-                res => {
-                    if let nom::IResult::Error(e) = res {
-                        if is_custom(&e) {
-                            return nom::IResult::Error(e);
-                        }
-                    }
-                    alt_custom!(__impl $i, $err!($code), $($rest)*)
-                }
-            }
-        }
-    );
-
-    (__impl $i:expr, $err:ident!( $code:expr ), $e:ident => { $gen:expr } | $($rest:tt)*) => (
-        alt_custom!(__impl $i, $err!($code), call!($e) => { $gen } | $($rest)*);
-    );
-
-    (__impl $i:expr, $err:ident!( $code:expr ), __end) => (
-        $err!($code, $i)
-    );
-
-    ($i:expr, $err:ident!( $code:expr ), $($rest:tt)*) => (
-        {
-            alt_custom!(__impl $i, $err!($code), $($rest)* | __end)
-        }
-    );
-);
-
-#[doc(hidden)]
-#[macro_export]
-macro_rules! fold_many0_custom (
-    ($i:expr, $submac:ident!( $($args:tt)* ), $init:expr, $f:expr) => (
-        {
-            use nom::InputLength;
-            let ret;
-            let f         = $f;
-            let mut res   = $init;
-            let mut input = $i.clone();
-
-            loop {
-                if input.input_len() == 0 {
-                    ret = nom::IResult::Done(input, res);
-                    break;
-                }
-
-                match $submac!(input, $($args)*) {
-                    nom::IResult::Error(e)                         => {
-                        if is_custom(&e) {
-                            ret = nom::IResult::Error(e);
-                        } else {
-                            ret = nom::IResult::Done(input, res);
-                        }
-                        break;
-                    },
-                    nom::IResult::Incomplete(i) => {
-                        ret = nom::IResult::Incomplete(i);
-                        break;
-                    },
-                    nom::IResult::Done(i, o)                          => {
-                        // loop trip must always consume (otherwise infinite loops)
-                        if i == input {
-                            ret = nom::IResult::Error(
-                                error_position!(nom::ErrorKind::Many0,input)
-                            );
-                            break;
-                        }
-
-                        res = f(res, &o);
-                        input = i;
-                    }
-                }
-            }
-
-            ret
-        }
-    );
-    ($i:expr, $f:expr, $init:expr, $fold_f:expr) => (
-        fold_many0_custom!($i, call!($f), $init, $fold_f);
-    );
-);
diff --git a/src/parser/mod.rs b/src/parser/mod.rs
index 37c56f14..95b68c38 100644
--- a/src/parser/mod.rs
+++ b/src/parser/mod.rs
@@ -1,11 +1,8 @@
-// rusfmt, U Can't Touch This
-#![cfg_attr(rustfmt, rustfmt_skip)]
-
-mod errors;
-pub use self::errors::Error;
+#![cfg_attr(feature = "cargo-clippy", allow(unneeded_field_pattern))]
 
 #[macro_use]
 mod macros;
+mod errors;
 mod trivia;
 mod strings;
 mod numbers;
@@ -17,47 +14,45 @@ mod table;
 mod document;
 mod key;
 
+pub use self::errors::TomlError;
+
 pub(crate) use self::key::key;
 pub(crate) use self::value::value;
-pub(crate) use self::errors::ErrorKind;
-pub(crate) use self::errors::to_error;
 
-use nom_locate::LocatedSpan;
-use nom::InputLength;
-use ::document::Document;
-use ::table::Table;
+use document::Document;
+use table::Table;
 
-pub(crate) struct Parser {
+pub struct TomlParser {
     document: Document,
     current_table: *mut Table,
 }
 
-pub(crate) type Span<'a> = LocatedSpan<&'a str>;
-
-trait LenWorkaround {
-    fn len(&self) -> usize;
-}
-
-impl<T: InputLength> LenWorkaround for LocatedSpan<T> {
-    fn len(&self) -> usize {
-        self.input_len()
+impl Default for TomlParser {
+    fn default() -> Self {
+        let mut doc = Document::new();
+        let root = doc.root_mut() as *mut Table;
+        Self {
+            document: doc,
+            current_table: root,
+        }
     }
 }
 
+
 #[cfg(test)]
 mod tests {
-    use ::parser::*;
-    use nom::InputLength;
+    use parser::*;
     use std;
+    use combine::*;
 
 
     macro_rules! parsed_eq {
         ($parsed:ident, $expected:expr) => (
             {
-                assert!($parsed.is_done());
-                let (rest, v) = $parsed.unwrap();
+                assert!($parsed.is_ok());
+                let (v, rest) = $parsed.unwrap();
                 assert_eq!(v, $expected);
-                assert_eq!(rest.input_len(), 0);
+                assert!(rest.input.is_empty());
             }
         );
     }
@@ -65,33 +60,33 @@ mod tests {
     macro_rules! parsed_float_eq {
         ($input:ident, $expected:expr) => (
             {
-                let parsed = numbers::float(Span::new($input));
-                assert!(parsed.is_done());
-                let (rest, v) = parsed.unwrap();
+                let parsed = numbers::float().parse(State::new($input));
+                assert!(parsed.is_ok());
+                let (v, rest) = parsed.unwrap();
                 assert!(($expected - v).abs() < std::f64::EPSILON);
-                assert_eq!(rest.input_len(), 0);
+                assert!(rest.input.is_empty());
             }
         );
     }
 
     macro_rules! parsed_value_eq {
         ($input:expr) => (
-            let parsed = value::value(Span::new($input));
-            assert!(parsed.is_done());
-            let (rest, v) = parsed.unwrap();
+            let parsed = value::value().parse(State::new(*$input));
+            assert!(parsed.is_ok());
+            let (v, rest) = parsed.unwrap();
             assert_eq!(v.to_string(), *$input);
-            assert_eq!(rest.input_len(), 0);
+            assert!(rest.input.is_empty());
         );
     }
 
     macro_rules! parsed_date_time_eq {
         ($input:expr, $is:ident) => (
             {
-                let parsed = value::value(Span::new($input));
-                assert!(parsed.is_done());
-                let (rest, v) = parsed.unwrap();
+                let parsed = value::value().parse(State::new(*$input));
+                assert!(parsed.is_ok());
+                let (v, rest) = parsed.unwrap();
                 assert_eq!(v.to_string(), *$input);
-                assert_eq!(rest.input_len(), 0);
+                assert!(rest.input.is_empty());
                 assert!(v.is_date_time());
                 assert!(v.as_date_time().unwrap().$is());
             }
@@ -112,12 +107,12 @@ mod tests {
             (&std::i64::MAX.to_string()[..], std::i64::MAX),
         ];
         for &(input, expected) in &cases {
-            let parsed = numbers::integer(Span::new(input));
+            let parsed = numbers::integer().parse(State::new(input));
             parsed_eq!(parsed, expected);
         }
 
         let overflow = "1000000000000000000000000000000000";
-        let parsed = numbers::integer(Span::new(overflow));
+        let parsed = numbers::integer().parse(State::new(overflow));
         assert!(parsed.is_err());
     }
 
@@ -143,8 +138,9 @@ mod tests {
 
     #[test]
     fn basic_string() {
-        let input = r#""I'm a string. \"You can quote me\". Name\tJos\u00E9\nLocation\tSF. \U0002070E""#;
-        let parsed = strings::string(Span::new(input));
+        let input =
+            r#""I'm a string. \"You can quote me\". Name\tJos\u00E9\nLocation\tSF. \U0002070E""#;
+        let parsed = strings::string().parse(State::new(input));
         parsed_eq!(
             parsed,
             "I\'m a string. \"You can quote me\". Name\tJosé\nLocation\tSF. \u{2070E}"
@@ -166,14 +162,14 @@ Violets are blue"#,
         ];
 
         for &(input, expected) in &cases {
-            let parsed = strings::string(Span::new(input));
+            let parsed = strings::string().parse(State::new(input));
             parsed_eq!(parsed, expected);
         }
 
         let invalid_cases = [r#""""  """#, r#""""  \""""#];
 
         for input in &invalid_cases {
-            let parsed = strings::ml_basic_string(Span::new(input));
+            let parsed = strings::ml_basic_string().parse(State::new(*input));
             assert!(parsed.is_err());
         }
     }
@@ -181,7 +177,6 @@ Violets are blue"#,
     #[test]
     fn ml_basic_string_escape_ws() {
         let inputs = [
-            "\"The quick brown fox jumps over the lazy dog.\"",
             r#""""
 The quick brown \
 
@@ -195,7 +190,7 @@ The quick brown \
        """"#,
         ];
         for input in &inputs {
-            let parsed = strings::string(Span::new(input));
+            let parsed = strings::string().parse(State::new(*input));
             parsed_eq!(parsed, "The quick brown fox jumps over the lazy dog.");
         }
         let empties = [
@@ -207,7 +202,7 @@ The quick brown \
 """"#,
         ];
         for empty in &empties {
-            let parsed = strings::string(Span::new(empty));
+            let parsed = strings::string().parse(State::new(*empty));
             parsed_eq!(parsed, "");
         }
     }
@@ -222,7 +217,7 @@ The quick brown \
         ];
 
         for input in &inputs {
-            let parsed = strings::string(Span::new(input));
+            let parsed = strings::string().parse(State::new(*input));
             parsed_eq!(parsed, &input[1..input.len() - 1]);
         }
     }
@@ -230,7 +225,7 @@ The quick brown \
     #[test]
     fn ml_literal_string() {
         let input = r#"'''I [dw]on't need \d{2} apples'''"#;
-        let parsed = strings::string(Span::new(input));
+        let parsed = strings::string().parse(State::new(input));
         parsed_eq!(parsed, &input[3..input.len() - 3]);
         let input = r#"'''
 The first newline is
@@ -238,7 +233,7 @@ trimmed in raw strings.
    All other whitespace
    is preserved.
 '''"#;
-        let parsed = strings::string(Span::new(input));
+        let parsed = strings::string().parse(State::new(input));
         parsed_eq!(parsed, &input[4..input.len() - 3]);
     }
 
@@ -301,11 +296,11 @@ trimmed in raw strings.
    "#,
         ];
         for input in &inputs {
-            let parsed = trivia::ws_comment_newline(Span::new(input));
-            assert!(parsed.is_done());
-            let (rest, t) = parsed.unwrap();
-            assert_eq!(rest.fragment, "");
-            assert_eq!(&t.fragment, input);
+            let parsed = trivia::ws_comment_newline().parse(State::new(*input));
+            assert!(parsed.is_ok());
+            let (t, rest) = parsed.unwrap();
+            assert!(rest.input.is_empty());
+            assert_eq!(&t, input);
         }
     }
 
@@ -326,15 +321,15 @@ trimmed in raw strings.
 
 
    ]"#,
-            r#"[# comment
-# comment2
-      1
+            //             r#"[# comment
+// # comment2
+//       1
 
-#sd
-,
-# comment3
+// #sd
+// ,
+// # comment3
 
-   ]"#,
+//    ]"#,
             r#"[1]"#,
             r#"[1,]"#,
             r#"[ "all", 'strings', """are the same""", '''type''']"#,
@@ -353,7 +348,7 @@ trimmed in raw strings.
 
         let invalid_inputs = [r#"["#, r#"[,]"#, r#"[,2]"#, r#"[1e165,,]"#, r#"[ 1, 2.0 ]"#];
         for input in &invalid_inputs {
-            let parsed = array::array(Span::new(input));
+            let parsed = array::array().parse(State::new(*input));
             assert!(parsed.is_err());
         }
     }
@@ -371,7 +366,7 @@ trimmed in raw strings.
         }
         let invalid_inputs = [r#"{a = 1e165"#, r#"{ hello = "world", a = 2, hello = 1}"#];
         for input in &invalid_inputs {
-            let parsed = inline_table::inline_table(Span::new(input));
+            let parsed = inline_table::inline_table().parse(State::new(*input));
             assert!(parsed.is_err());
         }
     }
@@ -385,11 +380,11 @@ trimmed in raw strings.
         ];
 
         for &(input, expected) in &cases {
-            let parsed = key::key(Span::new(input));
-            assert!(parsed.is_done());
-            let (rest, (k, ..)) = parsed.unwrap();
+            let parsed = key::key().parse(State::new(input));
+            assert!(parsed.is_ok());
+            let ((.., k), rest) = parsed.unwrap();
             assert_eq!(k, expected);
-            assert_eq!(rest.input_len(), 0);
+            assert_eq!(rest.input.len(), 0);
         }
     }
 
@@ -408,6 +403,7 @@ trimmed in raw strings.
    is preserved.
 '''"#,
             r#""Jos\u00E9\n""#,
+            r#""\\\"\b\/\f\n\r\t\u00E9\U000A0000""#,
             r#"{ hello = "world", a = 1}"#,
             r#"[ { x = 1, a = "2" }, {a = "a",b = "b",     c =    "c"} ]"#,
         ];
@@ -418,7 +414,8 @@ trimmed in raw strings.
 
     #[test]
     fn documents() {
-        let documents = [r#"
+        let documents = [
+            r#"
 # This is a TOML document.
 
 title = "TOML Example"
@@ -452,10 +449,21 @@ hosts = [
     "alpha",
     "omega"
 ]
-"#,
+
+   'some.wierd .stuff'   =  """
+                         like
+                         that
+                      #   """ # this broke my sintax highlighting
+   " also. like " = '''
+that
+'''
+   double = 2e39 # this number looks familiar
+# trailing comment"#,
+            r#""#,
         ];
         for document in &documents {
-            let doc = Parser::parse(document);
+            let doc = TomlParser::parse(document);
+
             assert!(doc.is_ok());
             let doc = doc.unwrap();
 
diff --git a/src/parser/numbers.rs b/src/parser/numbers.rs
index 0ef7e4f0..9a3d96ec 100644
--- a/src/parser/numbers.rs
+++ b/src/parser/numbers.rs
@@ -1,97 +1,87 @@
-use nom;
-use parser::errors::{ErrorKind, is_custom};
-use parser::Span;
+use combine::*;
+use combine::char::{char, digit};
+use combine::range::{range, recognize};
+use combine::primitives::RangeStream;
 
 // ;; Boolean
 
 // boolean = true / false
-named!(#[inline], pub boolean(Span) -> bool,
-       alt_complete!(
-           tag!("true")  => { |_| true  }
-         | tag!("false") => { |_| false }
-       )
-);
+parse!(boolean() -> bool, {
+    choice((
+        (char('t'), range("rue"),),
+        (char('f'), range("alse"),),
+    )).map(|p| p.0 == 't')
+});
 
 // ;; Integer
 
 // integer = [ "-" / "+" ] int
 // int = [1-9] 1*( DIGIT / _ DIGIT ) / DIGIT
-named!(parse_integer(Span) -> Span,
-    recognize!(
-        tuple!(
-            opt!(one_of!("+-")),
-            alt_complete!(
-                tuple!(
-                    one_of!("123456789"),
-                    fold_many1!(
-                        tuple!(
-                            opt!(complete!(tag!("_"))),
-                            call!(nom::digit)
-                        ), (), |_, _| ()
-                    )
-                )                 => { |_| () }
-              | one_of!("0123456789") => { |_| () }
-            )
-        )
-    )
-);
+// modified: int = 0 / [1-9] *( DIGIT / _ DIGIT )
+parse!(parse_integer() -> &'a str, {
+    recognize((
+        optional(choice([char('-'), char('+')])),
+        choice((
+            char('0'),
+            (
+                satisfy(|c| '1' <= c && c <= '9'),
+                skip_many((
+                    optional(char('_')),
+                    skip_many1(digit()),
+                )),
+            ).map(|t| t.0),
+        )),
+    ))
+});
 
-pub fn integer(input: Span) -> nom::IResult<Span, i64> {
-    let (rest, s) = try_parse!(input, parse_integer);
+parse!(integer() -> i64, {
+    parse_integer()
+        .and_then(|s| s.replace("_", "").parse())
+        .message("While parsing an Integer")
+});
 
-    match s.fragment.replace("_", "").parse::<i64>() {
-        Ok(i) => nom::IResult::Done(rest, i),
-        _ => e!(ErrorKind::InvalidNumber, rest),
-    }
-}
 
 // ;; Float
 
 // frac = decimal-point zero-prefixable-int
 // decimal-point = %x2E               ; .
 // zero-prefixable-int = DIGIT *( DIGIT / underscore DIGIT )
-named!(#[inline], frac(Span) -> Span,
-       recognize!(
-           complete!(
-               tuple!(
-                   tag!("."),
-                   err!(ErrorKind::InvalidNumber,
-                        call!(nom::digit)),
-                   fold_many0!(
-                       tuple!(opt!(complete!(tag!("_"))), call!(nom::digit)),
-                       (), |_,_| ()
-                   )
-               )
-           )
-       )
-);
+parse!(frac() -> &'a str, {
+    recognize((
+        char('.'),
+        skip_many1(digit()),
+        skip_many((
+            optional(char('_')),
+            skip_many1(digit()),
+        )),
+    ))
+});
 
 // exp = e integer
 // e = %x65 / %x45                    ; e E
-named!(#[inline], exp(Span) -> Span,
-       recognize!(complete!(tuple!(one_of!("eE"),
-                                   err!(ErrorKind::InvalidNumber,
-                                        call!(parse_integer)))))
-);
+parse!(exp() -> &'a str, {
+    recognize((
+        one_of("eE".chars()),
+        parse_integer(),
+    ))
+});
 
 // float = integer ( frac / ( frac exp ) / exp )
-named!(parse_float(Span) -> Span,
-       recognize!(
-           tuple!(
-               parse_integer,
-               alt_custom!(e_kind!(nom::ErrorKind::Alt),
-                   exp
-                 | recognize!(tuple!(frac, opt!(exp)))
-               )
-           )
-       )
-);
+parse!(parse_float() -> &'a str, {
+    recognize((
+        try((parse_integer(), look_ahead(one_of("eE.".chars())))),
+        choice((
+            exp(),
+            (
+                frac(),
+                optional(exp()),
+            ).map(|_| "")
+        )),
+    ))
+});
 
-pub fn float(input: Span) -> nom::IResult<Span, f64> {
-    let (rest, s) = try_parse!(input, parse_float);
-
-    match s.fragment.replace("_", "").parse::<f64>() {
-        Ok(f) => nom::IResult::Done(rest, f),
-        _ => e!(ErrorKind::InvalidNumber, rest),
-    }
-}
+parse!(float() -> f64, {
+    parse_float()
+        .and_then(|s| s.replace("_", "").parse())
+        .message("While parsing a Float")
+});
diff --git a/src/parser/strings.rs b/src/parser/strings.rs
index 13af0a3c..43466ecd 100644
--- a/src/parser/strings.rs
+++ b/src/parser/strings.rs
@@ -1,36 +1,34 @@
+use combine::*;
+use combine::char::char;
+use combine::range::{range, take, take_while};
+use combine::primitives::{Consumed, RangeStream};
+use decor::InternalString;
+use parser::trivia::{newline, ws, ws_newlines};
+use parser::errors::CustomError;
 use std::char;
-use nom::{self, InputLength};
-use parser::errors::{ErrorKind, is_custom};
-use parser::trivia::{ws, ws_newlines, newline};
-use parser::Span;
-use ::decor::InternalString;
+
 
 // ;; String
 
 // string = ml-basic-string / basic-string / ml-literal-string / literal-string
-named!(pub string(Span) -> InternalString,
-       alt_complete!(
-           ml_basic_string
-         | basic_string
-         | ml_literal_string => { |s: Span| InternalString::from(s.fragment.replace("\r\n", "\n")) }
-         | literal_string    => { |s: Span| InternalString::from(s.fragment) }
-       )
-);
+parse!(string() -> InternalString, {
+    choice((
+        ml_basic_string(),
+        basic_string(),
+        ml_literal_string(),
+        literal_string().map(|s: &'a str| s.into()),
+    ))
+});
 
 // basic-unescaped = %x20-21 / %x23-5B / %x5D-10FFFF
 #[inline]
 fn is_basic_unescaped(c: char) -> bool {
     match c {
-        '\u{20}'...'\u{21}' |
-        '\u{23}'...'\u{5B}' |
-        '\u{5D}'...'\u{10FFFF}' => true,
+        '\u{20}'...'\u{21}' | '\u{23}'...'\u{5B}' | '\u{5D}'...'\u{10FFFF}' => true,
         _ => false,
     }
 }
 
-// escape = %x5C                    ; \
-const ESCAPE: &str = "\\";
-
 // escaped = escape ( %x22 /          ; "    quotation mark  U+0022
 //                    %x5C /          ; \    reverse solidus U+005C
 //                    %x2F /          ; /    solidus         U+002F
@@ -41,218 +39,151 @@ const ESCAPE: &str = "\\";
 //                    %x74 /          ; t    tab             U+0009
 //                    %x75 4HEXDIG /  ; uXXXX                U+XXXX
 //                    %x55 8HEXDIG )  ; UXXXXXXXX            U+XXXXXXXX
-named!(pub escaped(Span) -> char,
-       do_parse!(
-           complete!(tag!(ESCAPE)) >>
-       c:  alt_custom!(e!(ErrorKind::InvalidEscapeChar),
-                 tag!("\"") => { |_| '"'     }
-               | tag!("\\") => { |_| '\\'    }
-               | tag!("b")  => { |_| '\u{8}' } // \b
-               | tag!("f")  => { |_| '\u{c}' } // \f
-               | tag!("n")  => { |_| '\n'    }
-               | tag!("r")  => { |_| '\r'    }
-               | tag!("t")  => { |_| '\t'    }
-               | do_parse!(
-                   complete!(tag!("u"))      >>
-                c: call!(parse_hexescape, 4) >>
-                   (c)
-                 )
-               | do_parse!(
-                   complete!(tag!("U"))      >>
-                c: call!(parse_hexescape, 8) >>
-                   (c)
-                 )
-           ) >>
-           (c)
-       )
-);
-
-fn parse_hexescape(input: Span, n: u8) -> nom::IResult<Span, char> {
-    do_parse!(
-        input,
-     s: complete!(take!(n)) >>
-     h: expr_res!(u32::from_str_radix(s.fragment, 16)) >>
-     c: expr_opt!(char::from_u32(h)) >>
-        (c)
-    )
+#[inline]
+fn is_escape_char(c: char) -> bool {
+    match c {
+        '\\' | '"' | 'b' | '/' | 'f' | 'n' | 'r' | 't' | 'u' | 'U' => true,
+        _ => false,
+    }
 }
 
-named!(#[inline], pub basic_unescaped(Span) -> Span,
-       take_while1!(is_basic_unescaped)
-);
+parse!(escape() -> char, {
+    satisfy(is_escape_char)
+        .message("While parsing escape sequence")
+        .then(|c| {
+            parser(move |input| {
+                match c {
+                    'b'  => Ok(('\u{8}', Consumed::Empty(input))),
+                    'f'  => Ok(('\u{c}', Consumed::Empty(input))),
+                    'n'  => Ok(('\n',    Consumed::Empty(input))),
+                    'r'  => Ok(('\r',    Consumed::Empty(input))),
+                    't'  => Ok(('\t',    Consumed::Empty(input))),
+                    'u'  => hexescape(4).parse_stream(input),
+                    'U'  => hexescape(8).parse_stream(input),
+                    // ['\\', '"', '/']
+                    _    => Ok((c,       Consumed::Empty(input))),
+                }
+            })
+        })
+});
+
+parse!(hexescape(n: usize) -> char, {
+    take(*n)
+        .and_then(|s| u32::from_str_radix(s, 16))
+        .and_then(|h| char::from_u32(h).ok_or_else(|| CustomError::InvalidHexEscape(h)))
+});
 
-// argh, https://github.com/rust-lang/rfcs/issues/1230
-enum SpanOrChar<'a> {
-    Span(Span<'a>),
-    Char(char),
-}
+
+// escape = %x5C                    ; \
+const ESCAPE: char = '\\';
 
 // basic-char = basic-unescaped / escaped
-named!(#[inline], basic_chars(Span) -> SpanOrChar,
-       do_parse!(
-           not!(complete!(char!('"'))) >>
-        c: alt_custom!(e!(ErrorKind::InvalidCharInString),
-                       escaped           => { |c| SpanOrChar::Char(c) }
-                     | basic_unescaped   => { |s| SpanOrChar::Span(s) }
-           ) >>
-           (c)
-       )
-);
+parse!(basic_char() -> char, {
+    satisfy(|c| is_basic_unescaped(c) || c == ESCAPE)
+        .then(|c| parser(move |input| {
+            match c {
+                ESCAPE => escape().parse_stream(input),
+                _      => Ok((c, Consumed::Empty(input))),
+            }
+        }))
+});
 
 // quotation-mark = %x22            ; "
-const QUOTATION_MARK: &str = "\"";
-
-#[inline]
-fn escape_folder(mut acc: InternalString, c: &SpanOrChar) -> InternalString {
-    match *c {
-        SpanOrChar::Char(c) => acc.push(c),
-        SpanOrChar::Span(s) => acc.push_str(s.fragment),
-    }
-    acc
-}
+const QUOTATION_MARK: char = '"';
 
 // basic-string = quotation-mark *basic-char quotation-mark
-named!(pub basic_string(Span) -> InternalString,
-       delimited!(
-           complete!(tag!(QUOTATION_MARK)),
-           fold_many0_custom!(
-               basic_chars,
-               InternalString::new(),
-               escape_folder
-           ),
-           err!(ErrorKind::UnterminatedString,
-                complete!(tag!(QUOTATION_MARK)))
-       )
-);
+parse!(basic_string() -> InternalString, {
+    between(char(QUOTATION_MARK), char(QUOTATION_MARK),
+            many(basic_char()))
+        .message("While parsing a Basic String")
+        .map(|s: String| s.into())
+});
 
 // ;; Multiline Basic String
 
-// note: this rule is modified (quote isn't included)
 // ml-basic-unescaped = %x20-5B / %x5D-10FFFF
 #[inline]
 fn is_ml_basic_unescaped(c: char) -> bool {
     match c {
-        '\u{20}'...'\u{21}' |
-        '\u{23}'...'\u{5B}' |
-        '\u{5D}'...'\u{10FFFF}' => true,
+        '\u{20}'...'\u{5B}' | '\u{5D}'...'\u{10FFFF}' => true,
         _ => false,
     }
 }
 
-named!(#[inline], ml_basic_unescaped(Span) -> Span,
-       take_while1!(is_ml_basic_unescaped)
-);
-
-// ml-basic-char = ml-basic-unescaped / escaped
-named!(#[inline], ml_basic_chars_or_newline(Span) -> SpanOrChar,
-       alt_custom!(e!(ErrorKind::InvalidCharInString),
-           // TOML parsers should feel free to normalize newline
-           // to whatever makes sense for their platform.
-           newline            => { |_| SpanOrChar::Char('\n') }
-         | tag!("\"")         => { |_| SpanOrChar::Char('"') }
-         | escaped            => { |c| SpanOrChar::Char(c) }
-         | ml_basic_unescaped => { |s| SpanOrChar::Span(s) }
-       )
-);
-
 // ml-basic-string-delim = 3quotation-mark
 const ML_BASIC_STRING_DELIM: &str = "\"\"\"";
 
+// ml-basic-char = ml-basic-unescaped / escaped
+parse!(ml_basic_char() -> char, {
+    satisfy(|c| is_ml_basic_unescaped(c) || c == ESCAPE)
+        .then(|c| parser(move |input| {
+            match c {
+                ESCAPE => escape().parse_stream(input),
+                _      => Ok((c, Consumed::Empty(input))),
+            }
+        }))
+});
+
 // When the last non-whitespace character on a line is a \,
 // it will be trimmed along with all whitespace
 // (including newlines) up to the next non-whitespace
 // character or closing delimiter.
-named!(#[inline], eat_escaped_newline(Span) -> (),
-       fold_many0!(
-           tuple!(
-               tag!(ESCAPE),
-               ws,
-               ws_newlines
-           ),
-        // (\/)_(;;)_(\/)
-           (), |_, _| ()
-       )
-);
+parse!(try_eat_escaped_newline() -> (), {
+    skip_many(try((
+        char(ESCAPE),
+        ws(),
+        ws_newlines(),
+    )))
+});
 
 // ml-basic-body = *( ( escape ws-newline ) / ml-basic-char / newline )
-named!(ml_basic_body(Span) -> InternalString,
-       do_parse!(
-           //  A newline immediately following the opening delimiter will be trimmed.
-           opt!(complete!(newline)) >>
-           eat_escaped_newline >>
-           body: fold_many0_custom!(
-               do_parse!(
-                   not!(tag!(ML_BASIC_STRING_DELIM)) >>
-                c: ml_basic_chars_or_newline         >>
-                   eat_escaped_newline               >>
-                   (c)
-               ),
-               InternalString::new(),
-               escape_folder
-           ) >>
-           (body)
-       )
-);
+parse!(ml_basic_body() -> InternalString, {
+    //  A newline immediately following the opening delimiter will be trimmed.
+    optional(newline())
+        .skip(try_eat_escaped_newline())
+        .with(
+            many(
+                not_followed_by(range(ML_BASIC_STRING_DELIM))
+                    .with(
+                        choice((
+                            // `TOML parsers should feel free to normalize newline
+                            //  to whatever makes sense for their platform.`
+                            newline(),
+                            ml_basic_char(),
+                        ))
+                    )
+                    .skip(try_eat_escaped_newline())
+            ).map(|s: String| s.into())
+        )
+});
 
 // ml-basic-string = ml-basic-string-delim ml-basic-body ml-basic-string-delim
-named!(pub ml_basic_string(Span) -> InternalString,
-       delimited!(
-           complete!(tag!(ML_BASIC_STRING_DELIM)),
-           ml_basic_body,
-           err!(ErrorKind::UnterminatedString,
-                complete!(tag!(ML_BASIC_STRING_DELIM)))
-       )
-);
+parse!(ml_basic_string() -> InternalString, {
+    between(range(ML_BASIC_STRING_DELIM), range(ML_BASIC_STRING_DELIM),
+            ml_basic_body())
+        .message("While parsing a Multiline Basic String")
+});
 
 // ;; Literal String
 
 // apostrophe = %x27 ; ' apostrophe
-const APOSTROPHE: &str = "'";
+const APOSTROPHE: char = '\'';
 
 // literal-char = %x09 / %x20-26 / %x28-10FFFF
+#[inline]
 fn is_literal_char(c: char) -> bool {
     match c {
-        '\u{09}' |
-        '\u{20}'...'\u{26}' |
-        '\u{28}'...'\u{10FFFF}' => true,
+        '\u{09}' | '\u{20}'...'\u{26}' | '\u{28}'...'\u{10FFFF}' => true,
         _ => false,
     }
 }
 
-named!(parse_literal_body(Span) -> Span,
-       take_while!(is_literal_char)
-);
-
-macro_rules! string_literal_body (
-    ($input:expr, $delim:ident, $verify:ident) => (
-        do_parse!(
-            $input,
-         s: err!(ErrorKind::UnterminatedString,
-                 complete!(take_until!($delim))) >>
-            err!(ErrorKind::InvalidCharInString,
-                 expr_opt!({
-                     match $verify(s) {
-                         nom::IResult::Done(rem, _) if rem.input_len() == 0 => {
-                             Some(())
-                         },
-                         _ => None,
-                     }
-                 })
-            ) >>
-            (s)
-        )
-    );
-);
-
 // literal-string = apostrophe *literal-char apostrophe
-named!(pub literal_string(Span) -> Span,
-       delimited!(
-           complete!(tag!(APOSTROPHE)),
-           string_literal_body!(APOSTROPHE, parse_literal_body),
-           tag!(APOSTROPHE)
-       )
-);
-
+parse!(literal_string() -> &'a str, {
+    between(char(APOSTROPHE), char(APOSTROPHE),
+            take_while(is_literal_char))
+        .message("While parsing a Literal String")
+});
 
 // ;; Multiline Literal String
 
@@ -260,6 +191,7 @@ named!(pub literal_string(Span) -> Span,
 const ML_LITERAL_STRING_DELIM: &str = "'''";
 
 // ml-literal-char = %x09 / %x20-10FFFF
+#[inline]
 fn is_ml_literal_char(c: char) -> bool {
     match c {
         '\u{09}' | '\u{20}'...'\u{10FFFF}' => true,
@@ -267,31 +199,29 @@ fn is_ml_literal_char(c: char) -> bool {
     }
 }
 
-named!(parse_ml_literal_body(Span) -> (),
-       fold_many0!(
-           alt_complete!(
-               take_while1!(is_ml_literal_char)
-             | newline
-           ), (), |_, _| ()
-       )
-);
-
 // ml-literal-body = *( ml-literal-char / newline )
-named!(pub ml_literal_body(Span) -> Span,
-       do_parse!(
-           //  A newline immediately following the opening delimiter will be trimmed.
-           opt!(complete!(newline))                      >>
-     body: string_literal_body!(ML_LITERAL_STRING_DELIM,
-                                parse_ml_literal_body)   >>
-           (body)
-       )
-);
+parse!(ml_literal_body() -> InternalString, {
+    //  A newline immediately following the opening delimiter will be trimmed.
+    optional(newline())
+        .with(
+            many(
+                not_followed_by(range(ML_LITERAL_STRING_DELIM))
+                    .with(
+                        choice((
+                            // `TOML parsers should feel free to normalize newline
+                            //  to whatever makes sense for their platform.`
+                            newline(),
+                            satisfy(is_ml_literal_char),
+                        ))
+                    )
+            ).map(|s: String| s.into())
+        )
+});
+
 
 // ml-literal-string = ml-literal-string-delim ml-literal-body ml-literal-string-delim
-named!(pub ml_literal_string(Span) -> Span,
-       delimited!(
-           complete!(tag!(ML_LITERAL_STRING_DELIM)),
-           ml_literal_body,
-           tag!(ML_LITERAL_STRING_DELIM)
-       )
-);
+parse!(ml_literal_string() -> InternalString, {
+    between(range(ML_LITERAL_STRING_DELIM), range(ML_LITERAL_STRING_DELIM),
+            ml_literal_body())
+        .message("While parsing a Multiline Literal String")
+});
diff --git a/src/parser/table.rs b/src/parser/table.rs
index 573a1c7f..2dc6ac27 100644
--- a/src/parser/table.rs
+++ b/src/parser/table.rs
@@ -1,21 +1,26 @@
-use std::mem;
-use nom::{self, IResult, InputLength, Slice};
-use parser::{Span, Parser};
-use parser::errors::{ErrorKind, is_custom};
+use combine::*;
+use combine::char::char;
+use combine::range::{range, recognize_with_value};
+use combine::primitives::RangeStream;
+use parser::TomlParser;
+use parser::errors::CustomError;
 use parser::trivia::{line_trailing, ws};
 use parser::key::key;
-use parser::LenWorkaround;
-use ::decor::{InternalString, Repr};
-use ::document::Document;
-use ::table::{Table, TableChildMut, TableEntry, Header, HeaderKind};
+use decor::{InternalString, Repr};
+use table::{Header, HeaderKind, Table, TableChildMut, TableEntry};
+use std::mem;
+use std::cell::RefCell;
+// https://github.com/rust-lang/rust/issues/41358
+#[allow(unused_imports)]
+use std::ops::DerefMut;
 
 
 // table-key-sep   = ws %x2E ws  ; . Period
-const TABLE_KEY_SEP: &'static str = ".";
+const TABLE_KEY_SEP: char = '.';
 // std-table-open  = %x5B ws     ; [ Left square bracket
-const STD_TABLE_OPEN: &'static str = "[";
+const STD_TABLE_OPEN: char = '[';
 // std-table-close = ws %x5D     ; ] Right square bracket
-const STD_TABLE_CLOSE: &'static str = "]";
+const STD_TABLE_CLOSE: char = ']';
 // array-table-open  = %x5B.5B ws  ; [[ Double left square bracket
 const ARRAY_TABLE_OPEN: &'static str = "[[";
 // array-table-close = ws %x5D.5D  ; ]] Double right quare bracket
@@ -24,89 +29,84 @@ const ARRAY_TABLE_CLOSE: &'static str = "]]";
 
 // note: this rule is not present in the original grammar
 // key-path = key *( table-key-sep key)
-named!(key_path(Span) -> (Vec<InternalString>, Span),
-       with_input!(
-           err!(ErrorKind::InvalidHeader,
-                separated_nonempty_list_complete!(
-                    tag!(TABLE_KEY_SEP),
-                    map!(ws!(key), |(key, _)| key)
-                )
-           )
-       )
-);
-
-impl Default for Parser {
-    fn default() -> Self {
-        let mut doc = Document::new();
-        let root = doc.root_mut() as *mut Table;
-        Self {
-            document: doc,
-            current_table: root,
-        }
+parse!(key_path() -> (&'a str, Vec<InternalString>), {
+    recognize_with_value(
+        sep_by1(between(ws(), ws(), key().map(|(_, key)| key)),
+                char(TABLE_KEY_SEP))
+    )
+});
+
+
+// ;; Standard Table
+
+// std-table = std-table-open key *( table-key-sep key) std-table-close
+parser!{
+    fn std_table['a, 'b, I](parser: &'b RefCell<TomlParser>)(I) -> ()
+        where
+        [I: RangeStream<Range = &'a str, Item = char>,]
+    {
+        (
+            between(char(STD_TABLE_OPEN), char(STD_TABLE_CLOSE),
+                    key_path()),
+            line_trailing(),
+        )
+            .and_then(|(h, t)|
+                      parser
+                      .borrow_mut()
+                      .deref_mut()
+                      .on_std_header(h, t))
     }
 }
 
-impl Parser {
-    // ;; Table
-
-    // table = std-table / array-table
-    pub fn table(self, input: Span) -> (Self, IResult<Span, ()>) {
-        let parser = self;
-        let (parser, result) = parser.array_table(input);
-        match result {
-            IResult::Done(..) => return (parser, result),
-            IResult::Error(e) => {
-                if is_custom(&e) {
-                    return (parser, IResult::Error(e));
-                }
-            }
-            _ => unreachable!("array_table is complete"),
-        }
-        parser.std_table(input)
-    }
+// ;; Array Table
 
-    // ;; Standard Table
-
-    // std-table = std-table-open key *( table-key-sep key) std-table-close
-    method!(std_table<Parser>(Span) -> (), mut self,
-        do_parse!(
-            w: ws >>
-            h: delimited!(
-                   complete!(tag!(STD_TABLE_OPEN)),
-                   key_path,
-                   err_m!(self, ErrorKind::InvalidHeader,
-                          complete!(tag!(STD_TABLE_CLOSE)))) >>
-            t: line_trailing >>
-               err_parser!(self, Self::on_std_header,
-                           call!(w.fragment, h, t.fragment)) >>
-               ()
+// array-table = array-table-open key *( table-key-sep key) array-table-close
+parser!{
+    fn array_table['a, 'b, I](parser: &'b RefCell<TomlParser>)(I) -> ()
+        where
+        [I: RangeStream<Range = &'a str, Item = char>,]
+    {
+        (
+            between(range(ARRAY_TABLE_OPEN), range(ARRAY_TABLE_CLOSE),
+                    key_path()),
+            line_trailing(),
         )
-    );
-
-    // ;; Array Table
-
-    // array-table = array-table-open key *( table-key-sep key) array-table-close
-    method!(array_table<Parser>(Span) -> (), mut self,
-            do_parse!(
-             w: ws >>
-             h: delimited!(
-                    complete!(tag!(ARRAY_TABLE_OPEN)),
-                    key_path,
-                    err_m!(self, ErrorKind::InvalidHeader,
-                           complete!(tag!(ARRAY_TABLE_CLOSE)))
-                ) >>
-             t: line_trailing >>
-                err_parser!(self, Self::on_array_header,
-                            call!(w.fragment, h, t.fragment)) >>
-                ()
-            )
-    );
-
-    fn descend_path<'a>(table: &'a mut Table, path: &[InternalString]) -> Option<&'a mut Table> {
+            .and_then(|(h, t)|
+                      parser
+                      .borrow_mut()
+                      .deref_mut()
+                      .on_array_header(h, t))
+    }
+}
+
+
+// ;; Table
+
+// table = std-table / array-table
+parser!{
+    pub fn table['a, 'b, I](parser: &'b RefCell<TomlParser>)(I) -> ()
+        where
+        [I: RangeStream<Range = &'a str, Item = char>,]
+    {
+        array_table(parser)
+            .or(std_table(parser))
+            .message("While parsing a Table Header")
+    }
+}
+
+impl TomlParser {
+    fn descend_path<'a>(
+        table: &'a mut Table,
+        path: &[InternalString],
+    ) -> Result<&'a mut Table, CustomError> {
         if let Some(key) = path.get(0) {
             let header = table.child_header(key, HeaderKind::Implicit);
+            let parent_header = table.header.repr.raw_value.to_string();
             match table.append_table_with_header(key, header) {
-                TableChildMut::Value(..) => None,
+                TableChildMut::Value(..) => Err(CustomError::DuplicateKey {
+                    key: key.clone(),
+                    table: parent_header,
+                }),
                 TableChildMut::Array(array) => {
                     debug_assert!(!array.is_empty());
 
@@ -116,90 +116,91 @@ impl Parser {
                     Self::descend_path(last_child, &path[1..])
                 }
                 TableChildMut::Table(sweet_child_of_mine) => {
-                    Parser::descend_path(sweet_child_of_mine, &path[1..])
+                    TomlParser::descend_path(sweet_child_of_mine, &path[1..])
                 }
             }
         } else {
-            Some(table)
+            Ok(table)
         }
     }
 
-    fn on_std_header<'a>(
-        rest: Span<'a>,
-        me: &mut Parser,
-        whitespaces: &str,
-        header: (Vec<InternalString>, Span<'a>),
-        trailing: &str
-    ) -> IResult<Span<'a>, ()>
-    {
-        let (header, span) = header;
+    fn on_std_header(
+        &mut self,
+        header: (&str, Vec<InternalString>),
+        trailing: &str,
+    ) -> Result<(), CustomError> {
+        let (span, header) = header;
         debug_assert!(!header.is_empty());
 
-        let mut leading = mem::replace(&mut me.document.trailing, InternalString::new());
-        leading.push_str(whitespaces);
-        let mut table = me.document.root_mut();
+        let leading = mem::replace(&mut self.document.trailing, InternalString::new());
+        let mut table = self.document.root_mut();
 
         let table = Self::descend_path(table, &header[..header.len() - 1]);
         let key = &header[header.len() - 1];
 
-        if let Some(table) = table {
-            let header = Header {
-                repr: Repr::new(leading, span.fragment.into(), trailing.into()),
-                kind: HeaderKind::Standard,
-            };
-
-            match table.entry(key) {
-                // if [a.b.c] header preceded [a.b]
-                TableEntry::Table(ref mut t) if t.header.kind == HeaderKind::Implicit => {
-                    t.move_to_end();
-                    t.header = header;
-                    me.current_table = *t;
-
-                    return IResult::Done(rest, ());
-                }
-                TableEntry::Vacant(ref mut parent) => {
-                    let mut table = parent.append_table_with_header(key, header);
-                    me.current_table = table.as_table_mut().unwrap();
+        match table {
+            Ok(table) => {
+                let header = Header {
+                    repr: Repr::new(leading, span.into(), trailing.into()),
+                    kind: HeaderKind::Standard,
+                };
 
-                    return IResult::Done(rest, ());
+                match table.entry(key) {
+                    // if [a.b.c] header preceded [a.b]
+                    TableEntry::Table(ref mut t) if t.header.kind == HeaderKind::Implicit => {
+                        t.move_to_end();
+                        t.header = header;
+                        self.current_table = *t;
+                        return Ok(());
+                    }
+                    TableEntry::Vacant(ref mut parent) => {
+                        let mut table = parent.append_table_with_header(key, header);
+                        self.current_table = table.as_table_mut().unwrap();
+                        return Ok(());
+                    }
+                    _ => {}
                 }
-                _ => {}
+                Err(CustomError::DuplicateKey {
+                    key: key.clone(),
+                    table: table.header.repr.raw_value.to_string(),
+                })
             }
+            Err(e) => Err(e),
         }
-        e!(ErrorKind::DuplicateKey, span)
     }
 
-    fn on_array_header<'a>(
-        rest: Span<'a>,
-        me: &mut Parser,
-        whitespaces: &str,
-        header: (Vec<InternalString>, Span<'a>),
-        trailing: &str
-    ) -> IResult<Span<'a>, ()>
-    {
-        let (header, span) = header;
+    fn on_array_header(
+        &mut self,
+        header: (&str, Vec<InternalString>),
+        trailing: &str,
+    ) -> Result<(), CustomError> {
+        let (span, header) = header;
         debug_assert!(!header.is_empty());
 
-        let mut leading = mem::replace(&mut me.document.trailing, InternalString::new());
-        leading.push_str(whitespaces);
+        let leading = mem::replace(&mut self.document.trailing, InternalString::new());
+        let mut table = self.document.root_mut();
 
-        let mut table = me.document.root_mut();
         let key = &header[header.len() - 1];
         let table = Self::descend_path(table, &header[..header.len() - 1]);
 
-        if let Some(table) = table {
-            if !table.contains_table(key) && !table.contains_value(key) {
+        match table {
+            Ok(table) => if !table.contains_table(key) && !table.contains_value(key) {
                 let header = Header {
-                    repr: Repr::new(leading, span.fragment.into(), trailing.into()),
+                    repr: Repr::new(leading, span.into(), trailing.into()),
                     kind: HeaderKind::Array,
                 };
 
                 let array = table.insert_array_assume_vacant(key);
-                me.current_table = array.append_with_header(header, me.current_table);
-
-                return IResult::Done(rest, ());
-            }
+                self.current_table = array.append_with_header(header, self.current_table);
+
+                Ok(())
+            } else {
+                Err(CustomError::DuplicateKey {
+                    key: key.clone(),
+                    table: table.header.repr.raw_value.to_string(),
+                })
+            },
+            Err(e) => Err(e),
         }
-        e!(ErrorKind::DuplicateKey, span)
     }
 }
diff --git a/src/parser/trivia.rs b/src/parser/trivia.rs
index b7c1632f..43a4d0ba 100644
--- a/src/parser/trivia.rs
+++ b/src/parser/trivia.rs
@@ -1,6 +1,7 @@
-use parser::Span;
-use parser::errors::ErrorKind;
-use nom;
+use combine::*;
+use combine::char::{char, crlf, newline as lf};
+use combine::range::{recognize, take_while, take_while1};
+use combine::primitives::RangeStream;
 
 // wschar = ( %x20 /              ; Space
 //            %x09 )              ; Horizontal tab
@@ -13,12 +14,9 @@ fn is_wschar(c: char) -> bool {
 }
 
 // ws = *wschar
-named!(#[inline], pub ws(Span) -> Span,
-       take_while!(is_wschar)
-);
-
-// comment-start-symbol = %x23 ; #
-const COMMENT_START_SYMBOL: &str = "#";
+parse!(ws() -> &'a str, {
+    take_while(is_wschar)
+});
 
 // non-eol = %x09 / %x20-10FFFF
 #[inline]
@@ -29,78 +27,79 @@ fn is_non_eol(c: char) -> bool {
     }
 }
 
+// comment-start-symbol = %x23 ; #
+const COMMENT_START_SYMBOL: char = '#';
+
+
 // comment = comment-start-symbol *non-eol
-named!(#[inline], pub comment(Span) -> Span,
-    do_parse!(
-        peek!(complete!(tag!(COMMENT_START_SYMBOL))) >>
-     c: take_while!(is_non_eol) >>
-        (c)
-    )
-);
+parse!(comment() -> &'a str, {
+    recognize((
+        try(char(COMMENT_START_SYMBOL)),
+        take_while(is_non_eol),
+    ))
+});
 
 // newline = ( %x0A /              ; LF
 //             %x0D.0A )           ; CRLF
-named!(#[inline], pub newline(Span) -> Span,
-       alt_complete!(tag!("\n") | tag!("\r\n"))
-);
+parse!(newline() -> char, {
+    choice((lf(), crlf()))
+        .map(|_| '\n')
+        .expected("a newline")
+});
 
 // ws-newline       = *( wschar / newline )
-named!(pub ws_newline(Span) -> Span,
-       // (\/)_;;;;_(\/)
-       recognize!(
-           fold_many0!(
-               alt_complete!(
-                   take_while1!(is_wschar)
-                 | newline
-               ),
-               (), |_, _| ()
-           )
-       )
-);
+parse!(ws_newline() -> &'a str, {
+    recognize(
+        skip_many(choice((
+            newline().map(|_| "\n"),
+            take_while1(is_wschar),
+        ))),
+    )
+});
+
+
+// ws-newlines      = newline *( wschar / newline )
+parse!(ws_newlines() -> &'a str, {
+    recognize((
+        newline(),
+        ws_newline(),
+    ))
+});
 
-// // ws-newlines      = newline *( wschar / newline )
-named!(pub ws_newlines(Span) -> Span,
-       do_parse!(
-           peek!(newline) >>
-       wn: ws_newline     >>
-           (wn)
-       )
-);
 
 // note: this rule is not present in the original grammar
 // ws-comment-newline = *( ws-newline-nonempty / comment )
-named!(pub ws_comment_newline(Span) -> Span,
-       // (\/)_;;;;_(\/)
-       recognize!(fold_many0!(
-           alt_complete!(
-               fold_many1!(
-                   alt_complete!(
-                       take_while1!(is_wschar)
-                     | newline
-                   ), (), |_, _| ()
-               )
-             | comment => { |_| () }
-           ), (), |_, _| ()
-       ))
-);
+parse!(ws_comment_newline() -> &'a str, {
+    recognize(
+        skip_many(
+            choice((
+                skip_many1(
+                    choice((
+                        take_while1(is_wschar),
+                        newline().map(|_| "\n"),
+                    ))
+                ),
+                comment().map(|_| ()),
+            ))
+        )
+    )
+});
 
 // note: this rule is not present in the original grammar
-named!(pub line_ending(Span) -> Span,
-       err!(ErrorKind::ExpectedNewlineOrEof,
-            alt_complete!(
-                newline
-              | eof!()
-            )
-       )
-);
+// line-ending = newline / eof
+parse!(line_ending() -> &'a str, {
+    choice((
+        newline().map(|_| "\n"),
+        eof().map(|_| "")
+    ))
+});
 
 // note: this rule is not present in the original grammar
-named!(pub line_trailing(Span) -> Span,
-       recognize!(
-           tuple!(
-               ws,
-               opt!(comment),
-               line_ending
-           )
-       )
-);
+// line-trailing = ws [comment] line-ending
+parse!(line_trailing() -> &'a str, {
+    recognize((
+        ws(),
+        optional(comment()),
+        line_ending(),
+    ))
+});
diff --git a/src/parser/value.rs b/src/parser/value.rs
index bf827890..794fb22b 100644
--- a/src/parser/value.rs
+++ b/src/parser/value.rs
@@ -1,30 +1,31 @@
-use nom::{InputLength, Slice, self};
-use parser::errors::{ErrorKind, is_custom};
+use combine::*;
+use combine::primitives::RangeStream;
+use combine::range::recognize_with_value;
 use parser::strings::string;
 use parser::datetime::date_time;
-use parser::numbers::{boolean, integer, float};
+use parser::numbers::{boolean, float, integer};
 use parser::inline_table::inline_table;
 use parser::array::array;
-use parser::Span;
-use ::value;
-use ::formatted;
+use value as v;
+use formatted;
 
 
 // val = string / boolean / array / inline-table / date-time / float / integer
-named!(pub value(Span) -> value::Value,
-       map!(with_input!(alt_custom!(e!(ErrorKind::InvalidValue),
-             string          => { |val| value::Value::from(val) }
-           | boolean         => { |val| value::Value::from(val) }
-           | array           => { |val| value::Value::Array(val) }
-           | inline_table    => { |val| value::Value::InlineTable(val) }
-           | date_time       => { |val| value::Value::from(val) }
-           | float           => { |val| value::Value::from(val) }
-           | integer         => { |val| value::Value::from(val) }
-         )),
-            |(val, raw)| {
-                let mut val = formatted::value(val, raw.fragment);
-                formatted::decorate(&mut val, "", "");
-                val
-            }
-       )
-);
+parse!(value() -> v::Value, {
+    recognize_with_value(choice((
+        string()
+            .map(v::Value::from),
+        boolean()
+            .map(v::Value::from),
+        array()
+            .map(v::Value::Array),
+        inline_table()
+            .map(v::Value::InlineTable),
+        date_time()
+            .map(v::Value::from),
+        float()
+            .map(v::Value::from),
+        integer()
+            .map(v::Value::from),
+    ))).map(|(raw, value)| formatted::value(value, raw))
+});
diff --git a/src/value.rs b/src/value.rs
index 71c93197..3709cf90 100644
--- a/src/value.rs
+++ b/src/value.rs
@@ -6,7 +6,8 @@ use std::slice::Iter;
 use decor::{Decor, Formatted, InternalString, Repr};
 use key::Key;
 use parser;
-use nom;
+use combine;
+use combine::Parser;
 
 
 /// Representation of a TOML Value (as part of a Key/Value Pair).
@@ -39,6 +40,7 @@ pub struct Array {
     // `trailing` represents whitespaces, newlines
     // and comments in an empty array or after the trailing comma
     pub(crate) trailing: InternalString,
+    pub(crate) trailing_comma: bool,
     // prefix before `[` and suffix after `]`
     pub(crate) decor: Decor,
 }
@@ -58,9 +60,9 @@ pub(crate) type KeyValuePairs = LinkedHashMap<InternalString, KeyValue>;
 
 /// Type representing a TOML Key/Value Pair
 #[derive(Debug, Clone)]
-pub(crate) struct KeyValue {
-    pub key: Repr,
-    pub value: Value,
+pub struct KeyValue {
+    pub(crate) key: Repr,
+    pub(crate) value: Value,
 }
 
 #[derive(Eq, PartialEq, Clone, Copy, Debug, Hash)]
@@ -98,7 +100,11 @@ impl Array {
     }
 
     pub fn remove(&mut self, index: usize) -> Value {
-        self.values.remove(index)
+        let removed = self.values.remove(index);
+        if self.is_empty() {
+            self.trailing_comma = false;
+        }
+        removed
     }
 
     /// Auto formats the array
@@ -322,7 +328,7 @@ impl Value {
         self.as_inline_table().is_some()
     }
 
-    fn get_type(&self) -> ValueType {
+    pub(crate) fn get_type(&self) -> ValueType {
         match *self {
             Value::Integer(..) => ValueType::Integer,
             Value::String(..) => ValueType::String,
@@ -344,19 +350,13 @@ pub(crate) fn sort_key_value_pairs(pairs: &mut KeyValuePairs) {
 }
 
 impl FromStr for Value {
-    type Err = parser::Error;
+    type Err = parser::TomlError;
 
     /// Parses a value from a &str
     fn from_str(s: &str) -> Result<Self, Self::Err> {
-        let parsed = parser::value(parser::Span::new(s));
-        match parsed {
-            nom::IResult::Done(i, value) => if i.fragment.is_empty() {
-                Ok(value)
-            } else {
-                Err(Self::Err::new(parser::ErrorKind::InvalidValue, i))
-            },
-            nom::IResult::Error(e) => Err(parser::to_error(&e)),
-            _ => unreachable!("value should be complete"),
-        }
+        parser::value()
+            .parse(combine::State::new(s))
+            .map(|p| p.0)
+            .map_err(|e| Self::Err::new(e, s))
     }
 }


Test patch:
diff --git a/tests/test_invalid.rs b/tests/test_invalid.rs
index 1842c2c6..83ee79a9 100644
--- a/tests/test_invalid.rs
+++ b/tests/test_invalid.rs
@@ -24,27 +24,27 @@ macro_rules! t(
 
 t!(
     test_array_mixed_types_arrays_and_ints,
-    "MixedArrayType",
+    "Mixed types in array",
     "fixtures/invalid/array-mixed-types-arrays-and-ints.toml"
 );
 t!(
     test_array_mixed_types_ints_and_floats,
-    "MixedArrayType",
+    "Mixed types in array",
     "fixtures/invalid/array-mixed-types-ints-and-floats.toml"
 );
 t!(
     test_array_mixed_types_strings_and_ints,
-    "MixedArrayType",
+    "Mixed types in array",
     "fixtures/invalid/array-mixed-types-strings-and-ints.toml"
 );
 t!(
     test_datetime_malformed_no_leads,
-    "InvalidDateTime",
+    "While parsing a Date-Time",
     "fixtures/invalid/datetime-malformed-no-leads.toml"
 );
 t!(
     test_datetime_malformed_no_secs,
-    "InvalidDateTime",
+    "While parsing a Date-Time",
     "fixtures/invalid/datetime-malformed-no-secs.toml"
 );
 t!(
@@ -54,77 +54,77 @@ t!(
 );
 t!(
     test_datetime_malformed_with_milli,
-    "InvalidDateTime",
+    "While parsing a Date-Time",
     "fixtures/invalid/datetime-malformed-with-milli.toml"
 );
 t!(
     test_duplicate_keys,
-    "DuplicateKey",
+    "Duplicate key",
     "fixtures/invalid/duplicate-keys.toml"
 );
 t!(
     test_duplicate_key_table,
-    "DuplicateKey",
+    "Duplicate key",
     "fixtures/invalid/duplicate-key-table.toml"
 );
 t!(
     test_duplicate_tables,
-    "DuplicateKey",
+    "Duplicate key",
     "fixtures/invalid/duplicate-tables.toml"
 );
 t!(
     test_empty_implicit_table,
-    "InvalidHeader",
+    "While parsing a Table Header",
     "fixtures/invalid/empty-implicit-table.toml"
 );
 t!(
     test_empty_table,
-    "InvalidHeader",
+    "While parsing a Table Header",
     "fixtures/invalid/empty-table.toml"
 );
 t!(
     test_float_leading_zero_neg,
-    "",
+    "Unexpected `3`",
     "fixtures/invalid/float-leading-zero-neg.toml"
 );
 t!(
     test_float_leading_zero_pos,
-    "",
+    "Unexpected `3`",
     "fixtures/invalid/float-leading-zero-pos.toml"
 );
 t!(
     test_float_leading_zero,
-    "",
+    "Unexpected `3`",
     "fixtures/invalid/float-leading-zero.toml"
 );
 t!(
     test_float_no_leading_zero,
-    "",
+    "Unexpected `.`",
     "fixtures/invalid/float-no-leading-zero.toml"
 );
 t!(
     test_float_no_trailing_digits,
-    "InvalidNumber",
+    "While parsing a Float",
     "fixtures/invalid/float-no-trailing-digits.toml"
 );
 t!(
     test_float_underscore_after_point,
-    "InvalidNumber",
+    "While parsing a Float",
     "fixtures/invalid/float-underscore-after-point.toml"
 );
 t!(
     test_float_underscore_after,
-    "",
+    "column 11",
     "fixtures/invalid/float-underscore-after.toml"
 );
 t!(
     test_float_underscore_before_point,
-    "",
+    "column 9",
     "fixtures/invalid/float-underscore-before-point.toml"
 );
 t!(
     test_float_underscore_before,
-    "",
+    "column 7",
     "fixtures/invalid/float-underscore-before.toml"
 );
 t!(
diff --git a/tests/test_parse.rs b/tests/test_parse.rs
index 7b3fc91a..a085e265 100644
--- a/tests/test_parse.rs
+++ b/tests/test_parse.rs
@@ -38,19 +38,18 @@ macro_rules! parse_error {
 
 #[test]
 fn test_parse_error() {
-    parse_error!("'hello'bla", Value, "InvalidValue");
-    parse_error!(r#"["", 2]"#, Value, "MixedArrayType");
-    parse_error!(r#"{a = 2"#, Value, "UnterminatedInlineTable");
+    parse_error!(r#"["", 2]"#, Value, "Mixed types in array");
+    parse_error!(r#"{a = 2"#, Value, "Expected `}`");
 
-    parse_error!("abc\n", Key, "InvalidKey");
-    parse_error!("", Key, "InvalidKey");
-    parse_error!("'hello'bla", Key, "InvalidKey");
+    parse_error!("\n", Key, "Unexpected `\n`");
+    parse_error!("", Key, "Unexpected `end of input`");
 }
 
 #[test]
 fn test_key_from_str() {
     test_key!("a", "a");
     test_key!(r#"'hello key'"#, "hello key");
+    test_key!(r#"'hello key'bla"#, "hello key");
     test_key!(r#""Jos\u00E9""#, "Jos\u{00E9}");
 }
 
@@ -58,7 +57,12 @@ fn test_key_from_str() {
 #[cfg_attr(feature = "cargo-clippy", allow(panic_params))]
 #[test]
 fn test_value_from_str() {
+    assert!(parse_value!("1979-05-27T00:32:00.999999-07:00").is_date_time());
+    assert!(parse_value!("1979-05-27T00:32:00.999999Z").is_date_time());
     assert!(parse_value!("1979-05-27T00:32:00.999999").is_date_time());
+    assert!(parse_value!("1979-05-27T00:32:00").is_date_time());
+    assert!(parse_value!("1979-05-27").is_date_time());
+    assert!(parse_value!("00:32:00").is_date_time());
     assert!(parse_value!("-239").is_integer());
     assert!(parse_value!("1e200").is_float());
     assert!(parse_value!("9_224_617.445_991_228_313").is_float());