Skip to content

Commit 4d4ef75

Browse files
committed
feat(draft): 2.0.0-draft.6 support (#92)
This just adds support for the new multiline string syntax. There's no other necessary changes to support draft.6.
1 parent 71bd07b commit 4d4ef75

21 files changed

+98
-57
lines changed

README.md

+3
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@ check out [`knuffel`](https://crates.io/crates/knuffel) or
1515
[`kaydle`](https://crates.io/crates/kaydle) instead for serde (or
1616
serde-like) parsing.
1717

18+
This crate supports parsing [KDL
19+
2.0.0-draft.6](https://github.com/kdl-org/kdl/releases/tag/2.0.0-draft.6)
20+
1821
### Example
1922

2023
```rust

examples/ci.kdl

+2-2
Original file line numberDiff line numberDiff line change
@@ -42,11 +42,11 @@ jobs {
4242
}
4343
step Clippy { run cargo clippy --all -- -D warnings }
4444
step "Run tests" { run cargo test --all --verbose }
45-
step "Other Stuff" run="
45+
step "Other Stuff" run="""
4646
echo foo
4747
echo bar
4848
echo baz
49-
"
49+
"""
5050
}
5151
}
5252
}

src/lib.rs

+3
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@
1313
//! [`kaydle`](https://crates.io/crates/kaydle) instead for serde (or
1414
//! serde-like) parsing.
1515
//!
16+
//! This crate supports parsing [KDL
17+
//! 2.0.0-draft.6](https://github.com/kdl-org/kdl/releases/tag/2.0.0-draft.6)
18+
//!
1619
//! ## Example
1720
//!
1821
//! ```rust

src/v2_parser.rs

+55-32
Original file line numberDiff line numberDiff line change
@@ -766,13 +766,13 @@ fn equals_sign(input: &mut Input<'_>) -> PResult<()> {
766766
}
767767

768768
/// ```text
769-
/// quoted-string := '"' (single-line-string-body | newline multi-line-string-body newline unicode-space*) '"'
769+
/// quoted-string := '"' single-line-string-body '"' | '"""' newline multi-line-string-body newline unicode-space*) '"""'
770770
/// single-line-string-body := (string-character - newline)*
771771
/// multi-line-string-body := string-character*
772772
/// ```
773773
fn quoted_string<'s>(input: &mut Input<'s>) -> PResult<Option<KdlValue>> {
774-
"\"".parse_next(input)?;
775-
let is_multiline = opt(newline).parse_next(input)?.is_some();
774+
let quotes = alt((("\"\"\"", newline).take(), "\"")).parse_next(input)?;
775+
let is_multiline = quotes.len() > 1;
776776
let ml_prefix: Option<String> = if is_multiline {
777777
Some(
778778
peek(preceded(
@@ -782,10 +782,13 @@ fn quoted_string<'s>(input: &mut Input<'s>) -> PResult<Option<KdlValue>> {
782782
repeat(0.., (not(newline), opt(ws_escape), string_char)).map(|()| ()),
783783
newline,
784784
),
785-
peek(terminated(repeat(0.., unicode_space).map(|()| ()), "\"")),
785+
peek(terminated(
786+
repeat(0.., unicode_space).map(|()| ()),
787+
"\"\"\"",
788+
)),
786789
)
787790
.map(|((), ())| ()),
788-
terminated(repeat(0.., unicode_space).map(|()| ()).take(), "\""),
791+
terminated(repeat(0.., unicode_space).map(|()| ()).take(), "\"\"\""),
789792
))
790793
.parse_next(input)?
791794
.to_string(),
@@ -814,7 +817,7 @@ fn quoted_string<'s>(input: &mut Input<'s>) -> PResult<Option<KdlValue>> {
814817
(
815818
&prefix[..],
816819
repeat(0.., unicode_space).map(|()| ()).take(),
817-
peek("\""),
820+
peek("\"\"\""),
818821
),
819822
)
820823
.map(|(s, _): (Vec<String>, (_, _, _))| {
@@ -836,9 +839,12 @@ fn quoted_string<'s>(input: &mut Input<'s>) -> PResult<Option<KdlValue>> {
836839
.resume_after(quoted_string_badval)
837840
.parse_next(input)?
838841
};
839-
cut_err("\"")
840-
.context(lbl("closing quote"))
841-
.parse_next(input)?;
842+
let closing_quotes = if is_multiline {
843+
"\"\"\"".context(lbl("multiline string closing quotes"))
844+
} else {
845+
"\"".context(lbl("string closing quote"))
846+
};
847+
cut_err(closing_quotes).parse_next(input)?;
842848
Ok(body.map(KdlValue::String))
843849
}
844850

@@ -903,13 +909,15 @@ fn escaped_char(input: &mut Input<'_>) -> PResult<char> {
903909
}
904910

905911
/// `raw-string := '#' raw-string-quotes '#' | '#' raw-string '#'`
906-
/// `raw-string-quotes := '"' (single-line-raw-string-body | newline multi-line-raw-string-body newline unicode-space*) '"'`
912+
/// `raw-string-quotes := '"' single-line-raw-string-body '"' | '"""' newline multi-line-raw-string-body newline unicode-space*) '"""'`
907913
/// `single-line-raw-string-body := (unicode - newline - disallowed-literal-code-points)*`
908914
/// `multi-line-raw-string-body := (unicode - disallowed-literal-code-points)`
909915
fn raw_string(input: &mut Input<'_>) -> PResult<Option<KdlValue>> {
910916
let hashes: String = repeat(1.., "#").parse_next(input)?;
911-
"\"".parse_next(input)?;
912-
let is_multiline = opt(newline).parse_next(input)?.is_some();
917+
let quotes = alt((("\"\"\"", newline).take(), "\"")).parse_next(input)?;
918+
let is_multiline = quotes.len() > 1;
919+
dbg!(&quotes);
920+
dbg!(is_multiline);
913921
let ml_prefix: Option<String> = if is_multiline {
914922
Some(
915923
peek(preceded(
@@ -921,7 +929,7 @@ fn raw_string(input: &mut Input<'_>) -> PResult<Option<KdlValue>> {
921929
(
922930
not(newline),
923931
not(disallowed_unicode),
924-
not(("\"", &hashes[..])),
932+
not(("\"\"\"", &hashes[..])),
925933
any,
926934
),
927935
)
@@ -930,13 +938,13 @@ fn raw_string(input: &mut Input<'_>) -> PResult<Option<KdlValue>> {
930938
),
931939
peek(terminated(
932940
repeat(0.., unicode_space).map(|()| ()),
933-
("\"", &hashes[..]),
941+
("\"\"\"", &hashes[..]),
934942
)),
935943
)
936944
.map(|((), ())| ()),
937945
terminated(
938946
repeat(0.., unicode_space).map(|()| ()).take(),
939-
("\"", &hashes[..]),
947+
("\"\"\"", &hashes[..]),
940948
),
941949
))
942950
.parse_next(input)?
@@ -945,6 +953,7 @@ fn raw_string(input: &mut Input<'_>) -> PResult<Option<KdlValue>> {
945953
} else {
946954
None
947955
};
956+
dbg!(&ml_prefix);
948957
let body: Option<String> = if let Some(prefix) = ml_prefix {
949958
repeat_till(
950959
0..,
@@ -955,7 +964,7 @@ fn raw_string(input: &mut Input<'_>) -> PResult<Option<KdlValue>> {
955964
newline.take().map(|_| "\n".to_string()),
956965
repeat_till(
957966
0..,
958-
(not(newline), not(("\"", &hashes[..])), any)
967+
(not(newline), not(("\"\"\"", &hashes[..])), any)
959968
.map(|((), (), _)| ())
960969
.take(),
961970
newline,
@@ -968,7 +977,7 @@ fn raw_string(input: &mut Input<'_>) -> PResult<Option<KdlValue>> {
968977
(
969978
&prefix[..],
970979
repeat(0.., unicode_space).map(|()| ()).take(),
971-
peek(("\"", &hashes[..])),
980+
peek(("\"\"\"", &hashes[..])),
972981
),
973982
)
974983
.map(|(s, _): (Vec<String>, (_, _, _))| {
@@ -996,9 +1005,12 @@ fn raw_string(input: &mut Input<'_>) -> PResult<Option<KdlValue>> {
9961005
.resume_after(raw_string_badval)
9971006
.parse_next(input)?
9981007
};
999-
cut_err(("\"", &hashes[..]))
1000-
.context(lbl("closing quote"))
1001-
.parse_next(input)?;
1008+
let closing_quotes = if is_multiline {
1009+
"\"\"\"".context(lbl("multiline raw string closing quotes"))
1010+
} else {
1011+
"\"".context(lbl("raw string closing quotes"))
1012+
};
1013+
cut_err((closing_quotes, &hashes[..])).parse_next(input)?;
10021014
Ok(body.map(KdlValue::String))
10031015
}
10041016

@@ -1044,40 +1056,46 @@ mod string_tests {
10441056
#[test]
10451057
fn multiline_quoted_string() {
10461058
assert_eq!(
1047-
string.parse(new_input("\"\nfoo\nbar\nbaz\n\"")).unwrap(),
1059+
string
1060+
.parse(new_input("\"\"\"\nfoo\nbar\nbaz\n\"\"\""))
1061+
.unwrap(),
10481062
Some(KdlValue::String("foo\nbar\nbaz".into()))
10491063
);
10501064
assert_eq!(
10511065
string
1052-
.parse(new_input("\"\n foo\n bar\n baz\n \""))
1066+
.parse(new_input("\"\"\"\n foo\n bar\n baz\n \"\"\""))
10531067
.unwrap(),
10541068
Some(KdlValue::String("foo\n bar\nbaz".into()))
10551069
);
10561070
assert_eq!(
1057-
string.parse(new_input("\"\nfoo\r\nbar\nbaz\n\"")).unwrap(),
1071+
string
1072+
.parse(new_input("\"\"\"\nfoo\r\nbar\nbaz\n\"\"\""))
1073+
.unwrap(),
10581074
Some(KdlValue::String("foo\nbar\nbaz".into()))
10591075
);
10601076
assert_eq!(
10611077
string
1062-
.parse(new_input("\"\n foo\n bar\n baz\n \""))
1078+
.parse(new_input("\"\"\"\n foo\n bar\n baz\n \"\"\""))
10631079
.unwrap(),
10641080
Some(KdlValue::String("foo\n bar\n baz".into()))
10651081
);
10661082
assert_eq!(
10671083
string
1068-
.parse(new_input("\"\n \\ foo\n \\ bar\n \\ baz\n \""))
1084+
.parse(new_input(
1085+
"\"\"\"\n \\ foo\n \\ bar\n \\ baz\n \"\"\""
1086+
))
10691087
.unwrap(),
10701088
Some(KdlValue::String("foo\n bar\n baz".into()))
10711089
);
10721090
assert_eq!(
10731091
string
1074-
.parse(new_input("\"\n\n string\t\n \""))
1092+
.parse(new_input("\"\"\"\n\n string\t\n \"\"\""))
10751093
.unwrap(),
10761094
Some(KdlValue::String("\nstring\t".into())),
10771095
"Empty line without any indentation"
10781096
);
10791097
assert!(string
1080-
.parse(new_input("\"\nfoo\n bar\n baz\n \""))
1098+
.parse(new_input("\"\"\"\nfoo\n bar\n baz\n \"\"\""))
10811099
.is_err());
10821100
}
10831101

@@ -1092,30 +1110,35 @@ mod string_tests {
10921110
#[test]
10931111
fn multiline_raw_string() {
10941112
assert_eq!(
1095-
string.parse(new_input("#\"\nfoo\nbar\nbaz\n\"#")).unwrap(),
1113+
string
1114+
.parse(new_input("#\"\"\"\nfoo\nbar\nbaz\n\"\"\"#"))
1115+
.unwrap(),
10961116
Some(KdlValue::String("foo\nbar\nbaz".into()))
10971117
);
10981118
assert_eq!(
10991119
string
1100-
.parse(new_input("#\"\nfoo\r\nbar\nbaz\n\"#"))
1120+
.parse(new_input("#\"\"\"\nfoo\r\nbar\nbaz\n\"\"\"#"))
11011121
.unwrap(),
11021122
Some(KdlValue::String("foo\nbar\nbaz".into()))
11031123
);
11041124
assert_eq!(
11051125
string
1106-
.parse(new_input("##\"\n foo\n bar\n baz\n \"##"))
1126+
.parse(new_input("##\"\"\"\n foo\n bar\n baz\n \"\"\"##"))
11071127
.unwrap(),
11081128
Some(KdlValue::String("foo\n bar\nbaz".into()))
11091129
);
11101130
assert_eq!(
11111131
string
1112-
.parse(new_input("#\"\n foo\n \\nbar\n baz\n \"#"))
1132+
.parse(new_input("#\"\"\"\n foo\n \\nbar\n baz\n \"\"\"#"))
11131133
.unwrap(),
11141134
Some(KdlValue::String("foo\n \\nbar\n baz".into()))
11151135
);
11161136
assert!(string
1117-
.parse(new_input("#\"\nfoo\n bar\n baz\n \"#"))
1137+
.parse(new_input("#\"\"\"\nfoo\n bar\n baz\n \"\"\"#"))
11181138
.is_err());
1139+
1140+
assert!(string.parse(new_input("#\"\nfoo\nbar\nbaz\n\"#")).is_err());
1141+
assert!(string.parse(new_input("\"\nfoo\nbar\nbaz\n\"")).is_err());
11191142
}
11201143

11211144
#[test]
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
ノード お名前=ฅ^•ﻌ•^
1+
ノード お名前=ฅ^•ﻌ•^

tests/test_cases/input/escaped_whitespace.kdl

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
// All of these strings are the same
22
node \
33
"Hello\n\tWorld" \
4-
"
4+
"""
55
Hello
66
World
7-
" \
7+
""" \
88
"Hello\n\ \tWorld" \
99
"Hello\n\
1010
\tWorld" \
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
node #"
1+
node #"""
22
hey
33
everyone
44
how goes?
5-
"#
5+
"""#
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
node #"
1+
node #"""
22
hey
33
everyone
44
how goes?
5-
"#
5+
"""#
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
node #"
1+
node #"""
22
hey
33
everyone
44
how goes?
5-
"#
5+
"""#
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
node #"
1+
node #"""
22
hey
33
everyone
44
how goes?
5-
"#
5+
"""#
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
node #"""one line"""#
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
node #"
2+
hey
3+
everyone
4+
how goes?
5+
"#
+2-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
node "
1+
node """
22
hey
33
everyone
44
how goes?
5-
"
5+
"""
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
node "
1+
node """
22
hey
33
everyone
44
how goes?
5-
"
5+
"""
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
node "
1+
node """
22
hey
33
everyone
44
how goes?
5-
"
5+
"""
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
node "
1+
node """
22
hey
33
everyone
44
how goes?
5-
"
5+
"""
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
node """one line"""
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
node "
2+
hey
3+
everyone
4+
how goes?
5+
"
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
node #"
1+
node #"""
22
hello
33
world
4-
"#
4+
"""#
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
1-
/- node 1.0 "a" b="
1+
/- node 1.0 "a" b="""
22
b
3-
"
3+
"""

0 commit comments

Comments
 (0)