Skip to content

Commit e7b5ec6

Browse files
authored
unambiguous multiline strings (#414)
Fixes: #413
1 parent e477f32 commit e7b5ec6

14 files changed

+86
-51
lines changed

README.md

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,15 @@ package {
2525
2626
scripts {
2727
// "Raw" and dedented multi-line strings are supported.
28-
build #"
28+
message """
29+
hello
30+
world
31+
"""
32+
build #"""
2933
echo "foo"
3034
node -c "console.log('hello, world!');"
3135
echo "foo" > some-file.txt
32-
"#
36+
"""#
3337
}
3438
3539
// `\` breaks up a single node across multiple lines.

SPEC.md

Lines changed: 52 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -440,14 +440,14 @@ The string contains the literal characters `hello\n\r\asd"#world`
440440

441441
### Multi-line Strings
442442

443-
When a Quoted or Raw String spans multiple lines with literal, non-escaped
444-
Newlines, it follows a special multi-line syntax that automatically "dedents"
445-
the string, allowing its value to be indented to a visually matching level if
446-
desired.
443+
Quoted and Raw Strings support multiple lines with literal, non-escaped
444+
Newlines. They must use a special multi-line syntax, and they automatically
445+
"dedent" the string, allowing its value to be indented to a visually matching
446+
level as desired.
447447

448448
A Multi-line string _MUST_ start with a [Newline](#newline) immediately
449-
following its opening `"`. Its final line _MUST_ contain only whitespace,
450-
followed by a single closing `"`. All in-between lines that contain
449+
following its opening `"""` (whether Quoted or Raw). Its final line _MUST_ contain only whitespace,
450+
followed by a closing `"""`. All in-between lines that contain
451451
non-newline characters _MUST_ start with _at least_ the exact same whitespace
452452
as the final line (precisely matching codepoints, not merely counting characters).
453453
They may contain additional whitespace following this prefix.
@@ -457,13 +457,14 @@ Whitespace of the last line, and the matching Whitespace prefix on all
457457
intermediate lines. The first and last Newline can be the same character (that
458458
is, empty multi-line strings are legal).
459459

460-
Strings with literal Newlines that do not immediately start with a Newline and
461-
whose final `"` is not preceeded by optional whitespace and a Newline are
462-
illegal.
463-
464460
In other words, the final line specifies the whitespace prefix that will be
465461
removed from all other lines.
466462

463+
Multi-line Strings that do not immediately start with a Newline and whose final
464+
`"""` is not preceeded by optional whitespace and a Newline are illegal. This
465+
also means that `"""` may not be used for a single-line String (e.g.
466+
`"""foo"""`).
467+
467468
It is a syntax error for any body lines of the multi-line string to not match
468469
the whitespace prefix of the last line with the final quote.
469470

@@ -474,19 +475,32 @@ Literal Newline sequences in Multi-line Strings must be normalized to a single
474475
becomes a single `LF` during parsing.
475476

476477
This normalization does not apply to non-literal Newlines entered using escape
477-
sequences.
478+
sequences. That is:
479+
480+
```kdl
481+
multi-line """
482+
\r\n[CRLF]
483+
foo[CRLF]
484+
"""
485+
```
486+
487+
becomes:
488+
489+
```kdl
490+
"\r\n\nfoo"
491+
```
478492

479493
For clarity: this normalization is for individual sequences. That is, the
480494
literal sequence `CRLF CRLF` becomes `LF LF`, not `LF`.
481495

482496
#### Example
483497

484498
```kdl
485-
multi-line "
499+
multi-line """
486500
foo
487501
This is the base indentation
488502
bar
489-
"
503+
"""
490504
```
491505

492506
This example's string value will be:
@@ -506,11 +520,11 @@ If the last line wasn't indented as far,
506520
it won't dedent the rest of the lines as much:
507521

508522
```kdl
509-
multi-line "
523+
multi-line """
510524
foo
511525
This is no longer on the left edge
512526
bar
513-
"
527+
"""
514528
```
515529

516530
This example's string value will be:
@@ -528,11 +542,11 @@ Equivalent to `" foo\n This is no longer on the left edge\n bar"`
528542
Empty lines can contain any whitespace, or none at all, and will be reflected as empty in the value:
529543

530544
```kdl
531-
multi-line "
545+
multi-line """
532546
Indented a bit
533547
534548
A second indented paragraph.
535-
"
549+
"""
536550
```
537551

538552
This example's string value will be:
@@ -547,25 +561,29 @@ Equivalent to `"Indented a bit.\n\nA second indented paragraph."`
547561

548562
-----------
549563

550-
The following yield syntax errors:
564+
The following yield **syntax errors**:
551565

552566
```kdl
553-
multi-line "
554-
closing quote with non-whitespace prefix"
567+
multi-line """can't be single line"""
555568
```
556569

557570
```kdl
558-
multi-line "stuff
559-
"
571+
multi-line """
572+
closing quote with non-whitespace prefix"""
573+
```
574+
575+
```kdl
576+
multi-line """stuff
577+
"""
560578
```
561579

562580
```kdl
563581
// Every line must share the exact same prefix as the closing line.
564-
multi-line "[\n]
582+
multi-line """[\n]
565583
[tab]a[\n]
566584
[space][space]b[\n]
567585
[space][tab][\n]
568-
[tab]"
586+
[tab]"""
569587
```
570588

571589
#### Interaction with Whitespace Escapes
@@ -581,24 +599,25 @@ For example, the following example is illegal:
581599

582600
```kdl
583601
// Equivalent to trying to write a string containing `foo\nbar\`.
584-
"
602+
"""
585603
foo
586604
bar\
587-
"
605+
"""
588606
```
589607

590608
while the following example is allowed
591609
```kdl
592-
"
610+
"""
593611
foo \
594612
bar
595613
baz
596-
\ "
597-
// this is equivalent to
598-
"
614+
\ """
615+
616+
// equivalent to
617+
"""
599618
foo bar
600619
baz
601-
"
620+
"""
602621
```
603622

604623
### Number
@@ -800,15 +819,15 @@ dotted-ident := sign? '.' ((identifier-char - digit) identifier-char*)?
800819
identifier-char := unicode - unicode-space - newline - [\\/(){};\[\]"#=] - disallowed-literal-code-points - equals-sign
801820
disallowed-keyword-identifiers := 'true' - 'false' - 'null' - 'inf' - '-inf' - 'nan'
802821
803-
quoted-string := '"' (single-line-string-body | newline multi-line-string-body newline unicode-space*) '"'
822+
quoted-string := '"' single-line-string-body '"' | '"""' newline multi-line-string-body newline unicode-space*) '"""'
804823
single-line-string-body := (string-character - newline)*
805824
multi-line-string-body := string-character*
806825
string-character := '\' escape | [^\\"] - disallowed-literal-code-points
807826
escape := ["\\bfnrts] | 'u{' hex-digit{1, 6} '}' | (unicode-space | newline)+
808827
hex-digit := [0-9a-fA-F]
809828
810829
raw-string := '#' raw-string-quotes '#' | '#' raw-string '#'
811-
raw-string-quotes := '"' (single-line-raw-string-body | newline multi-line-raw-string-body newline unicode-space*) '"'
830+
raw-string-quotes := '"' single-line-raw-string-body '"' | '"""' newline multi-line-raw-string-body newline unicode-space*) '"""'
812831
single-line-raw-string-body := (unicode - newline - disallowed-literal-code-points)*
813832
multi-line-raw-string-body := (unicode - disallowed-literal-code-points)*
814833

examples/ci.kdl

Lines changed: 2 additions & 2 deletions
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
}
Lines changed: 2 additions & 2 deletions
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+
"""#
Lines changed: 2 additions & 2 deletions
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+
"""#
Lines changed: 2 additions & 2 deletions
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+
"""#
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
node #"""one line"""#
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
node #"""
2+
hey
3+
everyone
4+
how goes?
5+
"""#
Lines changed: 2 additions & 2 deletions
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+
"""
Lines changed: 2 additions & 2 deletions
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+
"""

0 commit comments

Comments
 (0)