aboutsummaryrefslogtreecommitdiff
path: root/src/parser/document.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/parser/document.rs')
-rw-r--r--src/parser/document.rs141
1 files changed, 141 insertions, 0 deletions
diff --git a/src/parser/document.rs b/src/parser/document.rs
new file mode 100644
index 0000000..aa8fb11
--- /dev/null
+++ b/src/parser/document.rs
@@ -0,0 +1,141 @@
+use std::cell::RefCell;
+
+use winnow::combinator::cut_err;
+use winnow::combinator::eof;
+use winnow::combinator::opt;
+use winnow::combinator::peek;
+use winnow::combinator::repeat;
+use winnow::token::any;
+use winnow::token::one_of;
+use winnow::trace::trace;
+
+use crate::document::Document;
+use crate::key::Key;
+use crate::parser::inline_table::KEYVAL_SEP;
+use crate::parser::key::key;
+use crate::parser::prelude::*;
+use crate::parser::state::ParseState;
+use crate::parser::table::table;
+use crate::parser::trivia::{comment, line_ending, line_trailing, newline, ws};
+use crate::parser::value::value;
+use crate::table::TableKeyValue;
+use crate::Item;
+use crate::RawString;
+
+// ;; TOML
+
+// toml = expression *( newline expression )
+
+// expression = ( ( ws comment ) /
+// ( ws keyval ws [ comment ] ) /
+// ( ws table ws [ comment ] ) /
+// ws )
+pub(crate) fn document(input: &mut Input<'_>) -> PResult<Document> {
+ let state = RefCell::new(ParseState::default());
+ let state_ref = &state;
+
+ let _o = (
+ // Remove BOM if present
+ opt(b"\xEF\xBB\xBF"),
+ parse_ws(state_ref),
+ repeat(0.., (
+ dispatch! {peek(any);
+ crate::parser::trivia::COMMENT_START_SYMBOL => cut_err(parse_comment(state_ref)),
+ crate::parser::table::STD_TABLE_OPEN => cut_err(table(state_ref)),
+ crate::parser::trivia::LF |
+ crate::parser::trivia::CR => parse_newline(state_ref),
+ _ => cut_err(keyval(state_ref)),
+ },
+ parse_ws(state_ref),
+ ))
+ .map(|()| ()),
+ eof,
+ )
+ .parse_next(input)?;
+ state.into_inner().into_document().map_err(|err| {
+ winnow::error::ErrMode::from_external_error(input, winnow::error::ErrorKind::Verify, err)
+ })
+}
+
+pub(crate) fn parse_comment<'s, 'i>(
+ state: &'s RefCell<ParseState>,
+) -> impl Parser<Input<'i>, (), ContextError> + 's {
+ move |i: &mut Input<'i>| {
+ (comment, line_ending)
+ .span()
+ .map(|span| {
+ state.borrow_mut().on_comment(span);
+ })
+ .parse_next(i)
+ }
+}
+
+pub(crate) fn parse_ws<'s, 'i>(
+ state: &'s RefCell<ParseState>,
+) -> impl Parser<Input<'i>, (), ContextError> + 's {
+ move |i: &mut Input<'i>| {
+ ws.span()
+ .map(|span| state.borrow_mut().on_ws(span))
+ .parse_next(i)
+ }
+}
+
+pub(crate) fn parse_newline<'s, 'i>(
+ state: &'s RefCell<ParseState>,
+) -> impl Parser<Input<'i>, (), ContextError> + 's {
+ move |i: &mut Input<'i>| {
+ newline
+ .span()
+ .map(|span| state.borrow_mut().on_ws(span))
+ .parse_next(i)
+ }
+}
+
+pub(crate) fn keyval<'s, 'i>(
+ state: &'s RefCell<ParseState>,
+) -> impl Parser<Input<'i>, (), ContextError> + 's {
+ move |i: &mut Input<'i>| {
+ parse_keyval
+ .try_map(|(p, kv)| state.borrow_mut().on_keyval(p, kv))
+ .parse_next(i)
+ }
+}
+
+// keyval = key keyval-sep val
+pub(crate) fn parse_keyval(input: &mut Input<'_>) -> PResult<(Vec<Key>, TableKeyValue)> {
+ trace(
+ "keyval",
+ (
+ key,
+ cut_err((
+ one_of(KEYVAL_SEP)
+ .context(StrContext::Expected(StrContextValue::CharLiteral('.')))
+ .context(StrContext::Expected(StrContextValue::CharLiteral('='))),
+ (
+ ws.span(),
+ value(RecursionCheck::default()),
+ line_trailing
+ .context(StrContext::Expected(StrContextValue::CharLiteral('\n')))
+ .context(StrContext::Expected(StrContextValue::CharLiteral('#'))),
+ ),
+ )),
+ )
+ .try_map::<_, _, std::str::Utf8Error>(|(key, (_, v))| {
+ let mut path = key;
+ let key = path.pop().expect("grammar ensures at least 1");
+
+ let (pre, v, suf) = v;
+ let pre = RawString::with_span(pre);
+ let suf = RawString::with_span(suf);
+ let v = v.decorated(pre, suf);
+ Ok((
+ path,
+ TableKeyValue {
+ key,
+ value: Item::Value(v),
+ },
+ ))
+ }),
+ )
+ .parse_next(input)
+}