diff --git a/Cargo.toml b/Cargo.toml index 836200d..253a4cb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,12 +12,13 @@ rust-version = "1.56.0" edition = "2021" [features] -default = ["span"] +default = ["span", "serde"] span = [] [dependencies] miette = "7.2.0" num = "0.4.2" +serde = { version = "1.0.210", optional = true } thiserror = "1.0.40" winnow = { version = "0.6.20", features = ["alloc", "unstable-recover"] } diff --git a/src/de.rs b/src/de.rs new file mode 100644 index 0000000..a1a1d06 --- /dev/null +++ b/src/de.rs @@ -0,0 +1,235 @@ +use serde::{de, Deserialize}; +use thiserror::Error; +use winnow::{stream::Recoverable, Located}; + +use crate::{v2_parser::KdlParseError, KdlParseFailure}; + +/// serde deserializer for KDL documents +#[derive(Debug)] +pub struct Deserializer<'de> { + input: Recoverable, KdlParseError>, +} + +impl<'de> Deserializer<'de> { + /// Create a new deserializer from a string + pub fn from_str(input: &'de str) -> Self { + Self { + input: Recoverable::new(Located::new(input)), + } + } +} + +/// Deserialize a type from a KDL string +pub fn from_str<'a, T>(input: &'a str) -> Result +where + T: Deserialize<'a>, +{ +} + +#[derive(Debug, Error)] +struct DeError(String); + +impl std::fmt::Display for DeError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.0) + } +} + +impl de::Error for DeError { + fn custom(msg: T) -> Self { + DeError(msg.to_string()) + } +} + +struct KdlVisitor; + +impl<'de> de::Visitor<'de> for KdlVisitor { + type Value = (); + + fn expecting<'a>(&self, formatter: &mut std::fmt::Formatter<'a>) -> std::fmt::Result { + write!(formatter, "a KDL value") + } + + fn visit_map(self, mut map: A) -> Result + where + A: de::MapAccess<'de>, + { + while let Some(key) = map.next_key()? { + match key { + "type" => { + let value = map.next_value::()?; + println!("type: {}", value); + } + "value" => { + let value = map.next_value::()?; + println!("value: {}", value); + } + _ => { + map.next_value::()?; + } + } + } + + Ok(()) + } +} + +impl<'de, 'a> de::Deserializer<'de> for &'a mut Deserializer<'de> { + type Error = DeError; + + fn deserialize_any(self, visitor: V) -> Result + where + V: de::Visitor<'de>, + { + self.deserialize_map(visitor) + } + + fn deserialize_bool(self, visitor: V) -> Result + where + V: de::Visitor<'de>, + { + todo!() + } + + fn deserialize_char(self, visitor: V) -> Result + where + V: de::Visitor<'de>, + { + todo!() + } + + fn deserialize_str(self, visitor: V) -> Result + where + V: de::Visitor<'de>, + { + todo!() + } + + fn deserialize_string(self, visitor: V) -> Result + where + V: de::Visitor<'de>, + { + todo!() + } + + fn deserialize_bytes(self, visitor: V) -> Result + where + V: de::Visitor<'de>, + { + todo!() + } + + fn deserialize_byte_buf(self, visitor: V) -> Result + where + V: de::Visitor<'de>, + { + todo!() + } + + fn deserialize_option(self, visitor: V) -> Result + where + V: de::Visitor<'de>, + { + todo!() + } + + fn deserialize_unit(self, visitor: V) -> Result + where + V: de::Visitor<'de>, + { + todo!() + } + + fn deserialize_unit_struct( + self, + name: &'static str, + visitor: V, + ) -> Result + where + V: de::Visitor<'de>, + { + todo!() + } + + fn deserialize_newtype_struct( + self, + name: &'static str, + visitor: V, + ) -> Result + where + V: de::Visitor<'de>, + { + todo!() + } + + fn deserialize_seq(self, visitor: V) -> Result + where + V: de::Visitor<'de>, + { + todo!() + } + + fn deserialize_tuple(self, len: usize, visitor: V) -> Result + where + V: de::Visitor<'de>, + { + todo!() + } + + fn deserialize_tuple_struct( + self, + name: &'static str, + len: usize, + visitor: V, + ) -> Result + where + V: de::Visitor<'de>, + { + todo!() + } + + fn deserialize_map(self, visitor: V) -> Result + where + V: de::Visitor<'de>, + { + todo!() + } + + fn deserialize_struct( + self, + name: &'static str, + fields: &'static [&'static str], + visitor: V, + ) -> Result + where + V: de::Visitor<'de>, + { + todo!() + } + + fn deserialize_enum( + self, + name: &'static str, + variants: &'static [&'static str], + visitor: V, + ) -> Result + where + V: de::Visitor<'de>, + { + todo!() + } + + fn deserialize_identifier(self, visitor: V) -> Result + where + V: de::Visitor<'de>, + { + todo!() + } + + fn deserialize_ignored_any(self, visitor: V) -> Result + where + V: de::Visitor<'de>, + { + todo!() + } +} diff --git a/src/document.rs b/src/document.rs index 7287dd0..718afcf 100644 --- a/src/document.rs +++ b/src/document.rs @@ -436,7 +436,7 @@ second_node /* This time, the comment is here */ param=153 { /- comment /* block comment */ inline /*comment*/ here - another /-commend there + another /-comment there after some whitespace @@ -485,7 +485,7 @@ final;"; ); let foo = doc.get("foo").expect("expected a foo node"); - assert_eq!(foo.format().map(|f| &f.trailing[..]), Some("\n")); + assert_eq!(foo.format().map(|f| &f.terminator[..]), Some("\n")); assert_eq!(&foo[2], &"three".into()); assert_eq!(&foo["bar"], &"baz".into()); assert_eq!( @@ -527,6 +527,7 @@ final;"; // if you're making KdlEntries this way, you need to inject // your own whitespace (or format the node) node.push(" \"blah\"=0xDEADbeef".parse::()?); + dbg!(&node); doc.nodes_mut().push(node); assert_eq!( @@ -714,7 +715,8 @@ this { } // that's nice -inline { time; to; live "our" "dreams"; "y;all"; } +inline { time; to; live "our" "dreams"; "y;all" } + "####; let doc: KdlDocument = input.parse()?; @@ -724,8 +726,9 @@ inline { time; to; live "our" "dreams"; "y;all"; } // Now check some more interesting concrete spans - // The whole document should presumably be "the input" again? - check_span(input, doc.span(), &input); + // The whole document should be everything from the first node until the + // last before_terminator whitespace. + check_span(&input[1..(input.len() - 2)], doc.span(), &input); // This one-liner node should be the whole line without leading whitespace let is_node = doc @@ -772,13 +775,11 @@ inline { time; to; live "our" "dreams"; "y;all"; } ); // The child document is a little weird, it's the contents *inside* the braces - // with extra newlines on both ends. + // without the surrounding whitespace/comments. Just the actual contents. check_span( - r####"{ - "it" /*shh*/ "has"="πŸ’―" ##"the"## + r####""it" /*shh*/ "has"="πŸ’―" ##"the"## Best🎊est - "syntax ever" - }"####, + "syntax ever""####, and_node.children().unwrap().span(), &input, ); @@ -807,14 +808,14 @@ inline { time; to; live "our" "dreams"; "y;all"; } // Make sure inline nodes work ok let inline_node = doc.get("inline").unwrap(); check_span( - r#"inline { time; to; live "our" "dreams"; "y;all"; }"#, + r#"inline { time; to; live "our" "dreams"; "y;all" }"#, inline_node.span(), &input, ); let inline_children = inline_node.children().unwrap(); check_span( - r#"{ time; to; live "our" "dreams"; "y;all"; }"#, + r#"time; to; live "our" "dreams"; "y;all" "#, inline_children.span(), &input, ); diff --git a/src/lib.rs b/src/lib.rs index ea8e862..87cbdf8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -189,3 +189,8 @@ mod node; mod value; mod v2_parser; + +#[cfg(feature = "serde")] +pub mod de; +#[cfg(feature = "serde")] +pub mod se; diff --git a/src/node.rs b/src/node.rs index a5cb63f..1ea9532 100644 --- a/src/node.rs +++ b/src/node.rs @@ -515,28 +515,30 @@ impl KdlNode { pub(crate) fn autoformat_impl(&mut self, indent: usize, no_comments: bool) { if let Some(KdlNodeFormat { leading, + before_terminator, + terminator, trailing, before_children, .. }) = self.format_mut() { crate::fmt::autoformat_leading(leading, indent, no_comments); + crate::fmt::autoformat_trailing(before_terminator, no_comments); crate::fmt::autoformat_trailing(trailing, no_comments); *trailing = trailing.trim().into(); - if trailing.starts_with(';') { - trailing.remove(0); + if !terminator.starts_with('\n') { + *terminator = "\n".into(); } if let Some(c) = trailing.chars().next() { if !c.is_whitespace() { trailing.insert(0, ' '); } } - trailing.push('\n'); *before_children = " ".into(); } else { self.set_format(KdlNodeFormat { - trailing: "\n".into(), + terminator: "\n".into(), ..Default::default() }) } @@ -598,8 +600,14 @@ impl KdlNode { } write!(f, "}}")?; } - if let Some(KdlNodeFormat { trailing, .. }) = self.format() { - write!(f, "{}", trailing)?; + if let Some(KdlNodeFormat { + before_terminator, + terminator, + trailing, + .. + }) = self.format() + { + write!(f, "{before_terminator}{terminator}{trailing}")?; } Ok(()) } @@ -618,8 +626,11 @@ pub struct KdlNodeFormat { pub after_ty: String, /// Whitespace and comments preceding the node's children block. pub before_children: String, - /// Whitespace and comments following the node itself, including the - /// optional semicolon. + /// Whitespace and comments right before the node's terminator. + pub before_terminator: String, + /// The terminator for the node. + pub terminator: String, + /// Whitespace and comments following the node itself, after the terminator. pub trailing: String, } @@ -652,7 +663,9 @@ mod test { node.format(), Some(&KdlNodeFormat { leading: "\n\t ".into(), - trailing: ";\n".into(), + before_terminator: "".into(), + terminator: ";".into(), + trailing: "\n".into(), before_ty_name: "".into(), after_ty_name: "".into(), after_ty: "".into(), diff --git a/src/se.rs b/src/se.rs new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/src/se.rs @@ -0,0 +1 @@ + diff --git a/src/v2_parser.rs b/src/v2_parser.rs index 0aabeb9..d2ab421 100644 --- a/src/v2_parser.rs +++ b/src/v2_parser.rs @@ -9,7 +9,8 @@ use num::CheckedMul; use winnow::{ ascii::{digit1, hex_digit1, oct_digit1, Caseless}, combinator::{ - alt, cut_err, eof, fail, not, opt, peek, preceded, repeat, repeat_till, terminated, + alt, cut_err, delimited, eof, fail, not, opt, peek, preceded, repeat, repeat_till, + separated, terminated, }, error::{ AddContext, ContextError, ErrorKind, FromExternalError, FromRecoverableError, ParserError, @@ -226,12 +227,16 @@ pub(crate) fn document(input: &mut Input<'_>) -> PResult { /// `nodes := (line-space* node)* line-space*` fn nodes(input: &mut Input<'_>) -> PResult { - let ((leading, nodes, trailing), _span) = ( - repeat(0.., line_space).map(|()| ()).take(), - repeat(0.., node), - repeat(0.., line_space).map(|()| ()).take(), + let (leading, (nodes, _span), _final_terminator, trailing) = ( + repeat(0.., alt((line_space.void(), (slashdash, base_node).void()))) + .map(|()| ()) + .take(), + separated(0.., node, node_terminator).with_span(), + opt(node_terminator), + repeat(0.., alt((line_space.void(), (slashdash, base_node).void()))) + .map(|()| ()) + .take(), ) - .with_span() .parse_next(input)?; Ok(KdlDocument { nodes, @@ -244,35 +249,69 @@ fn nodes(input: &mut Input<'_>) -> PResult { }) } -/// `base-node := type? optional-node-space string (required-node-space node-prop-or-arg)* (required-node-space node-children)?` +/// base-node := slashdash? type? node-space* string +/// (node-space+ slashdash? node-prop-or-arg)* +/// (node-space+ slashdash node-children)* +/// (node-space+ node-children)? +/// (node-space+ slashdash node-children)* +/// node-space* +/// node := base-node node-space* node-terminator +/// final-node := base-node node-space* node-terminator? +fn node(input: &mut Input<'_>) -> PResult { + let leading = repeat(0.., alt((line_space.void(), (slashdash, base_node).void()))) + .map(|()| ()) + .take() + .parse_next(input)?; + let mut nd = base_node.parse_next(input)?; + if let Some(fmt) = nd.format_mut() { + fmt.leading = leading.into(); + } + Ok(nd) +} + fn base_node(input: &mut Input<'_>) -> PResult { let ((ty, after_ty, name, entries, children), _span) = ( opt(ty), - optional_node_space.take(), + node_space0.take(), identifier, repeat( 0.., - (peek(required_node_space), node_entry).map(|(_, e): ((), _)| e), + (peek(node_space1), node_entry).map(|(_, e): ((), _)| e), ) .map(|e: Vec>| e.into_iter().flatten().collect::>()), - opt((optional_node_space.take(), node_children)), + opt((before_node_children.take(), node_children)), ) .with_span() .parse_next(input)?; + let (before_terminator, terminator) = if children.is_some() { + ( + opt(slashdashed_children).take(), + peek(opt(node_terminator).take()), + ) + .parse_next(input)? + } else { + ( + before_node_children.take(), + peek(opt(node_terminator).take()), + ) + .parse_next(input)? + }; let (before_inner_ty, ty, after_inner_ty) = ty.unwrap_or_default(); let (before_children, children) = children - .map(|(before_children, children)| (before_children, Some(children))) - .unwrap_or(("", None)); + .map(|(before_children, children)| (before_children.into(), Some(children))) + .unwrap_or(("".into(), None)); Ok(KdlNode { ty, name, entries, children, format: Some(KdlNodeFormat { - after_ty: after_ty.into(), before_ty_name: before_inner_ty.into(), after_ty_name: after_inner_ty.into(), - before_children: before_children.into(), + after_ty: after_ty.into(), + before_children, + before_terminator: before_terminator.into(), + terminator: terminator.into(), ..Default::default() }), #[cfg(feature = "span")] @@ -280,25 +319,6 @@ fn base_node(input: &mut Input<'_>) -> PResult { }) } -/// `node := base-node optional-node-space node-terminator` -fn node(input: &mut Input<'_>) -> PResult { - let (leading, (mut node, _span), (trailing, terminator)) = ( - repeat(0.., line_space).map(|()| ()).take(), - base_node.with_span(), - (optional_node_space.take(), node_terminator.take()), - ) - .parse_next(input)?; - if let Some(fmt) = node.format_mut() { - fmt.leading = leading.into(); - fmt.trailing = format!("{trailing}{terminator}"); - } - #[cfg(feature = "span")] - { - node.span = _span.into(); - } - Ok(node) -} - #[cfg(test)] #[test] fn test_node() { @@ -313,20 +333,13 @@ fn test_node() { }, entries: vec![], children: None, - format: Some(KdlNodeFormat { - after_ty: "".into(), - before_ty_name: "".into(), - after_ty_name: "".into(), - before_children: "".into(), - leading: "".into(), - trailing: "".into() - }), + format: Some(Default::default()), span: (0..7).into() } ); assert_eq!( - base_node.parse(new_input("foo bar")).unwrap(), + node.parse(new_input("foo bar")).unwrap(), KdlNode { ty: None, name: KdlIdentifier { @@ -349,17 +362,15 @@ fn test_node() { format: Some(KdlNodeFormat { ..Default::default() }), - span: (0..7).into() + span: (0..8).into() } ); } pub(crate) fn padded_node(input: &mut Input<'_>) -> PResult { - let ((leading, mut node, trailing), _span) = ( - repeat(0.., alt((line_space, node_space))) - .map(|_: ()| ()) - .take(), + let ((mut node, _terminator, trailing), _span) = ( node, + opt(node_terminator), repeat(0.., alt((line_space, node_space))) .map(|_: ()| ()) .take(), @@ -367,8 +378,7 @@ pub(crate) fn padded_node(input: &mut Input<'_>) -> PResult { .with_span() .parse_next(input)?; if let Some(fmt) = node.format_mut() { - fmt.leading = format!("{leading}{}", fmt.leading); - fmt.trailing = format!("{}{trailing}", fmt.trailing); + fmt.trailing = trailing.into(); } #[cfg(feature = "span")] { @@ -377,14 +387,6 @@ pub(crate) fn padded_node(input: &mut Input<'_>) -> PResult { Ok(node) } -/// `final-node := base-node optional-node-space node-terminator?` -fn final_node(input: &mut Input<'_>) -> PResult { - let node = base_node.parse_next(input)?; - optional_node_space.parse_next(input)?; - opt(node_terminator).parse_next(input)?; - Ok(node) -} - pub(crate) fn padded_node_entry(input: &mut Input<'_>) -> PResult { let ((leading, entry, trailing), _span) = ( repeat(0.., line_space).map(|_: ()| ()).take(), @@ -414,8 +416,11 @@ pub(crate) fn padded_node_entry(input: &mut Input<'_>) -> PResult { /// `node-prop-or-arg := prop | value` fn node_entry(input: &mut Input<'_>) -> PResult> { - let (leading, mut entry) = - (optional_node_space.take(), alt((prop, value))).parse_next(input)?; + let (leading, mut entry) = ( + (node_space0, opt((slashdashed_entries, node_space1))).take(), + alt((prop, value)), + ) + .parse_next(input)?; entry = entry.map(|mut e| { if let Some(fmt) = e.format_mut() { fmt.leading = leading.into(); @@ -425,6 +430,15 @@ fn node_entry(input: &mut Input<'_>) -> PResult> { Ok(entry) } +fn slashdashed_entries(input: &mut Input<'_>) -> PResult<()> { + separated(1.., (slashdash, node_entry), node_space1) + .map(|()| ()) + .take() + .map(|x| x.to_string()) + .parse_next(input)?; + Ok(()) +} + #[cfg(test)] #[test] fn entry_test() { @@ -455,37 +469,127 @@ fn entry_test() { span: (0..3).into() }) ); + + assert_eq!( + node_entry.parse(new_input("/-foo bar")).unwrap(), + Some(KdlEntry { + ty: None, + value: KdlValue::String("bar".into()), + name: None, + format: Some(KdlEntryFormat { + value_repr: "bar".into(), + leading: "/-foo ".into(), + ..Default::default() + }), + span: (6..9).into() + }) + ); + + assert_eq!( + node_entry.parse(new_input("/- foo=1 bar = 2")).unwrap(), + Some(KdlEntry { + ty: None, + value: 2.into(), + name: Some(KdlIdentifier { + value: "bar".into(), + repr: Some("bar".into()), + span: (9..12).into(), + }), + format: Some(KdlEntryFormat { + value_repr: "2".into(), + leading: "/- foo=1 ".into(), + after_ty: " ".into(), + after_eq: " ".into(), + ..Default::default() + }), + span: (9..16).into() + }) + ); + + assert_eq!( + node_entry.parse(new_input("/- \nfoo = 1 bar = 2")).unwrap(), + Some(KdlEntry { + ty: None, + value: 2.into(), + name: Some(KdlIdentifier { + value: "bar".into(), + repr: Some("bar".into()), + span: (12..16).into(), + }), + format: Some(KdlEntryFormat { + value_repr: "2".into(), + leading: "/- \nfoo = 1 ".into(), + after_ty: " ".into(), + after_eq: " ".into(), + ..Default::default() + }), + span: (12..18).into() + }) + ); +} + +fn before_node_children(input: &mut Input<'_>) -> PResult<()> { + alt(( + ( + node_space1, + slashdashed_entries, + // This second one will fail if `node_entry_leading` is empty. + node_space1, + slashdashed_children, + ) + .take(), + (node_space1, slashdashed_entries).take(), + (node_space1, slashdashed_children).take(), + node_space0.take(), + )) + .void() + .parse_next(input)?; + node_space0.parse_next(input)?; + Ok(()) +} + +#[cfg(test)] +#[test] +fn before_node_children_test() { + assert!(before_node_children.parse(new_input(" /- { }")).is_ok()); + assert!(before_node_children.parse(new_input(" /- { bar }")).is_ok()); +} + +fn slashdashed_children(input: &mut Input<'_>) -> PResult<()> { + node_space0.parse_next(input)?; + separated( + 1.., + (slashdash.void(), node_children.void()).void(), + node_space1, + ) + .map(|()| ()) + .parse_next(input) +} + +#[cfg(test)] +#[test] +fn around_children_test() { + assert!(slashdashed_children.parse(new_input("/- { }")).is_ok()); + assert!(slashdashed_children.parse(new_input("/- { bar }")).is_ok()); } /// `node-children := '{' nodes final-node? '}'` fn node_children(input: &mut Input<'_>) -> PResult { - let _start = input.location(); - "{".parse_next(input)?; - let mut ns = nodes.parse_next(input)?; - let fin = opt(final_node).parse_next(input)?; - if let Some(fin) = fin { - ns.nodes.push(fin); - } - cut_err("}").parse_next(input)?; - #[cfg(feature = "span")] - { - ns.span = (_start..input.location()).into(); - } - Ok(ns) + delimited("{", nodes, cut_err("}")).parse_next(input) } /// `node-terminator := single-line-comment | newline | ';' | eof` fn node_terminator(input: &mut Input<'_>) -> PResult<()> { - alt((eof.void(), ";".void(), newline, single_line_comment)).parse_next(input) + alt((";".void(), newline, single_line_comment)).parse_next(input) } /// `prop := string optional-node-space equals-sign optional-node-space value` fn prop(input: &mut Input<'_>) -> PResult> { let ((key, after_key, _eqa, after_eq, value), _span) = ( identifier, - optional_node_space.take(), + node_space0.take(), equals_sign.take(), - optional_node_space.take(), + node_space0.take(), cut_err(value), ) .with_span() @@ -507,7 +611,7 @@ fn prop(input: &mut Input<'_>) -> PResult> { /// `value := type? optional-node-space (string | number | keyword)` fn value(input: &mut Input<'_>) -> PResult> { let ((ty, (value, raw)), _span) = ( - opt((ty, optional_node_space.take())), + opt((ty, node_space0.take())), alt((keyword.map(Some), number.map(Some), string)).with_taken(), ) .with_span() @@ -533,69 +637,32 @@ fn value(input: &mut Input<'_>) -> PResult> { fn ty<'s>(input: &mut Input<'s>) -> PResult<(&'s str, Option, &'s str)> { "(".parse_next(input)?; let (before_ty, ty, after_ty) = ( - optional_node_space.take(), + node_space0.take(), cut_err(identifier.context(lbl("type name"))) .resume_after((badval, peek(")").void(), badval).void()), - optional_node_space.take(), + node_space0.take(), ) .parse_next(input)?; cut_err(")").parse_next(input)?; Ok((before_ty, ty, after_ty)) } -/// `plain-line-space := newline | ws | single-line-comment` -fn plain_line_space(input: &mut Input<'_>) -> PResult<()> { - alt((newline, ws, single_line_comment)).parse_next(input) -} - -/// `plain-node-space := ws* escline ws* | ws+` -fn plain_node_space(input: &mut Input<'_>) -> PResult<()> { - alt(((wss, escline, wss).void(), wsp)).parse_next(input) -} - -/// `line-space := plain-line-space+ | '/-' plain-node-space* node` +/// `line-space := newline | ws | single-line-comment` fn line_space(input: &mut Input<'_>) -> PResult<()> { - alt(( - repeat(1.., plain_line_space).map(|_: ()| ()).void(), - ( - "/-", - repeat(0.., plain_node_space).map(|_: ()| ()), - cut_err(node), - ) - .void() - .context(lbl("slashdashed node")), - )) - .parse_next(input) + alt((newline, ws, single_line_comment)).parse_next(input) } -/// `node-space := plain-node-space+ ('/-' plain-node-space* (node-prop-or-arg | node-children))?` +/// `node-space := ws* escline ws* | ws+` fn node_space(input: &mut Input<'_>) -> PResult<()> { - repeat(1.., plain_node_space) - .map(|_: ()| ()) - .parse_next(input)?; - opt(( - "/-", - repeat(0.., plain_node_space).map(|_: ()| ()), - cut_err(alt(( - node_entry.void().context(lbl("slashdashed entry")), - node_children.void().context(lbl("slashdashed children")), - ))), - )) - .void() - .parse_next(input) + alt(((wss, escline, wss).void(), wsp)).parse_next(input) } -/// `required-node-space := node-space* plain-node-space+` -fn required_node_space(input: &mut Input<'_>) -> PResult<()> { - repeat(0.., (node_space, peek(plain_node_space))) - .map(|_: ()| ()) - .parse_next(input)?; - repeat(1.., plain_node_space).parse_next(input) +fn node_space0(input: &mut Input<'_>) -> PResult<()> { + repeat(0.., node_space).parse_next(input) } -/// `optional-node-space := node-space*` -fn optional_node_space(input: &mut Input<'_>) -> PResult<()> { - repeat(0.., node_space).parse_next(input) +fn node_space1(input: &mut Input<'_>) -> PResult<()> { + repeat(1.., node_space).parse_next(input) } /// `string := identifier-string | quoted-string | raw-string` @@ -1134,7 +1201,7 @@ fn escline(input: &mut Input<'_>) -> PResult<()> { #[cfg(test)] #[test] fn escline_test() { - let node = node.parse(new_input("foo bar\\\n baz\n")).unwrap(); + let node = node.parse(new_input("foo bar\\\n baz")).unwrap(); assert_eq!(node.entries().len(), 2); } @@ -1231,6 +1298,32 @@ fn multi_line_comment_test() { .is_ok()); } +/// slashdash := '/-' line-space* +fn slashdash(input: &mut Input<'_>) -> PResult<()> { + ("/-", repeat(0.., line_space).map(|()| ())) + .void() + .parse_next(input) +} + +#[cfg(test)] +#[test] +fn slashdash_tests() { + assert!(document.parse(new_input("/- foo bar")).is_ok()); + assert!(node.parse(new_input("/- foo\nbar baz")).is_ok()); + assert!(node_entry.parse(new_input("/-commented tada")).is_ok()); + assert!(node.parse(new_input("foo /- { }")).is_ok()); + assert!(node.parse(new_input("foo /- { bar }")).is_ok()); + assert!(node + .parse(new_input("/- foo bar\nnode /-1 2 { x }")) + .is_ok()); + assert!(node + .parse(new_input("/- foo bar\nnode 2 /-3 { x }")) + .is_ok()); + assert!(node + .parse(new_input("/- foo bar\nnode /-1 2 /-3 { x }")) + .is_ok()); +} + /// `number := keyword-number | hex | octal | binary | decimal` fn number(input: &mut Input<'_>) -> PResult { alt((float_value, integer_value)).parse_next(input) @@ -1384,7 +1477,7 @@ fn test_hex() { hex:: .parse(new_input("0xABCDEF0123456789abcdef0123456789")) .is_err(), - "i128 overflow" + "i128 overflow" ); assert!(hex::.parse(new_input("0x_deadbeef123")).is_err()); @@ -1477,11 +1570,7 @@ fn test_binary() { fn signum(input: &mut Input<'_>) -> PResult { let sign = opt(alt(('+', '-'))).parse_next(input)?; let mult = if let Some(sign) = sign { - if sign == '+' { - true - } else { - false - } + sign == '+' } else { true }; diff --git a/tests/compliance.rs b/tests/compliance.rs index 684ac26..d27456f 100644 --- a/tests/compliance.rs +++ b/tests/compliance.rs @@ -76,7 +76,7 @@ fn spec_compliance() -> miette::Result<()> { fn validate_res( res: Result, path: &Path, - src: &String, + src: &str, ) -> Result<(), ComplianceDiagnostic> { let file_name = path.file_name().unwrap(); let expected_dir = path @@ -94,9 +94,9 @@ fn validate_res( if actual != expected { return Err(ComplianceDiagnostic::ExpectationMismatch { file: path.into(), - original: src.clone(), - expected, - actual, + original: src.into(), + expected: expected.replace('\n', "\\n").replace(" ", "."), + actual: actual.replace('\n', "\\n").replace(" ", "."), }); } } else if underscored.exists() { diff --git a/tests/test_cases/expected_kdl/slashdash_multi_line_comment_entry.kdl b/tests/test_cases/expected_kdl/slashdash_multi_line_comment_entry.kdl new file mode 100644 index 0000000..0c7db5c --- /dev/null +++ b/tests/test_cases/expected_kdl/slashdash_multi_line_comment_entry.kdl @@ -0,0 +1 @@ +node 1 3 diff --git a/tests/test_cases/expected_kdl/slashdash_multi_line_comment_inline.kdl b/tests/test_cases/expected_kdl/slashdash_multi_line_comment_inline.kdl new file mode 100644 index 0000000..0c7db5c --- /dev/null +++ b/tests/test_cases/expected_kdl/slashdash_multi_line_comment_inline.kdl @@ -0,0 +1 @@ +node 1 3 diff --git a/tests/test_cases/expected_kdl/slashdash_multiple_child_blocks.kdl b/tests/test_cases/expected_kdl/slashdash_multiple_child_blocks.kdl new file mode 100644 index 0000000..6ff16cc --- /dev/null +++ b/tests/test_cases/expected_kdl/slashdash_multiple_child_blocks.kdl @@ -0,0 +1,3 @@ +node foo { + three +} diff --git a/tests/test_cases/expected_kdl/slashdash_newline_before_children.kdl b/tests/test_cases/expected_kdl/slashdash_newline_before_children.kdl new file mode 100644 index 0000000..3b77f56 --- /dev/null +++ b/tests/test_cases/expected_kdl/slashdash_newline_before_children.kdl @@ -0,0 +1 @@ +node 1 2 diff --git a/tests/test_cases/expected_kdl/slashdash_newline_before_entry.kdl b/tests/test_cases/expected_kdl/slashdash_newline_before_entry.kdl new file mode 100644 index 0000000..0c7db5c --- /dev/null +++ b/tests/test_cases/expected_kdl/slashdash_newline_before_entry.kdl @@ -0,0 +1 @@ +node 1 3 diff --git a/tests/test_cases/expected_kdl/slashdash_newline_before_node.kdl b/tests/test_cases/expected_kdl/slashdash_newline_before_node.kdl new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/tests/test_cases/expected_kdl/slashdash_newline_before_node.kdl @@ -0,0 +1 @@ + diff --git a/tests/test_cases/expected_kdl/slashdash_single_line_comment_entry.kdl b/tests/test_cases/expected_kdl/slashdash_single_line_comment_entry.kdl new file mode 100644 index 0000000..0c7db5c --- /dev/null +++ b/tests/test_cases/expected_kdl/slashdash_single_line_comment_entry.kdl @@ -0,0 +1 @@ +node 1 3 diff --git a/tests/test_cases/expected_kdl/slashdash_single_line_comment_node.kdl b/tests/test_cases/expected_kdl/slashdash_single_line_comment_node.kdl new file mode 100644 index 0000000..6810417 --- /dev/null +++ b/tests/test_cases/expected_kdl/slashdash_single_line_comment_node.kdl @@ -0,0 +1 @@ +node2 diff --git a/tests/test_cases/expected_kdl/unicode_silly.kdl b/tests/test_cases/expected_kdl/unicode_silly.kdl new file mode 100644 index 0000000..5fa566d --- /dev/null +++ b/tests/test_cases/expected_kdl/unicode_silly.kdl @@ -0,0 +1 @@ +γƒŽγƒΌγƒ‰γ€€γŠεε‰=ΰΈ…^β€’ο»Œβ€’^ΰΈ… diff --git a/tests/test_cases/input/slashdash_child_block_before_entry_err.kdl b/tests/test_cases/input/slashdash_child_block_before_entry_err.kdl new file mode 100644 index 0000000..b9edfc3 --- /dev/null +++ b/tests/test_cases/input/slashdash_child_block_before_entry_err.kdl @@ -0,0 +1,5 @@ +node /-{ + child +} foo { + bar +} diff --git a/tests/test_cases/input/slashdash_multi_line_comment_entry.kdl b/tests/test_cases/input/slashdash_multi_line_comment_entry.kdl new file mode 100644 index 0000000..97a41e7 --- /dev/null +++ b/tests/test_cases/input/slashdash_multi_line_comment_entry.kdl @@ -0,0 +1,6 @@ +node 1 /- /* +multi +line +comment +here +*/ 2 3 diff --git a/tests/test_cases/input/slashdash_multi_line_comment_inline.kdl b/tests/test_cases/input/slashdash_multi_line_comment_inline.kdl new file mode 100644 index 0000000..1fd93ce --- /dev/null +++ b/tests/test_cases/input/slashdash_multi_line_comment_inline.kdl @@ -0,0 +1 @@ +node 1 /-/*two*/2 3 diff --git a/tests/test_cases/input/slashdash_multiple_child_blocks.kdl b/tests/test_cases/input/slashdash_multiple_child_blocks.kdl new file mode 100644 index 0000000..2f85ce1 --- /dev/null +++ b/tests/test_cases/input/slashdash_multiple_child_blocks.kdl @@ -0,0 +1,10 @@ +node foo /-{ + one +} \ +/-{ + two +} { + three +} /-{ + four +} diff --git a/tests/test_cases/input/slashdash_newline_before_children.kdl b/tests/test_cases/input/slashdash_newline_before_children.kdl new file mode 100644 index 0000000..deefb7f --- /dev/null +++ b/tests/test_cases/input/slashdash_newline_before_children.kdl @@ -0,0 +1,4 @@ +node 1 2 /- +{ + child +} diff --git a/tests/test_cases/input/slashdash_newline_before_entry.kdl b/tests/test_cases/input/slashdash_newline_before_entry.kdl new file mode 100644 index 0000000..f6de9f9 --- /dev/null +++ b/tests/test_cases/input/slashdash_newline_before_entry.kdl @@ -0,0 +1,2 @@ +node 1 /- +2 3 diff --git a/tests/test_cases/input/slashdash_newline_before_node.kdl b/tests/test_cases/input/slashdash_newline_before_node.kdl new file mode 100644 index 0000000..545464f --- /dev/null +++ b/tests/test_cases/input/slashdash_newline_before_node.kdl @@ -0,0 +1,2 @@ +/- +node 1 2 3 diff --git a/tests/test_cases/input/slashdash_single_line_comment_entry.kdl b/tests/test_cases/input/slashdash_single_line_comment_entry.kdl new file mode 100644 index 0000000..2f807fc --- /dev/null +++ b/tests/test_cases/input/slashdash_single_line_comment_entry.kdl @@ -0,0 +1,2 @@ +node 1 /- // stuff +2 3 diff --git a/tests/test_cases/input/slashdash_single_line_comment_node.kdl b/tests/test_cases/input/slashdash_single_line_comment_node.kdl new file mode 100644 index 0000000..a378a18 --- /dev/null +++ b/tests/test_cases/input/slashdash_single_line_comment_node.kdl @@ -0,0 +1,3 @@ +/- // this is a comment +node1 +node2 diff --git a/tests/test_cases/input/unicode_silly.kd b/tests/test_cases/input/unicode_silly.kd new file mode 100644 index 0000000..5fa566d --- /dev/null +++ b/tests/test_cases/input/unicode_silly.kd @@ -0,0 +1 @@ +γƒŽγƒΌγƒ‰γ€€γŠεε‰=ΰΈ…^β€’ο»Œβ€’^ΰΈ… diff --git a/tests/test_cases/input/unterminated_empty_node.kdl b/tests/test_cases/input/unterminated_empty_node.kdl new file mode 100644 index 0000000..bdc79c5 --- /dev/null +++ b/tests/test_cases/input/unterminated_empty_node.kdl @@ -0,0 +1 @@ +node {