Skip to content

Commit 5ea35db

Browse files
committed
Define the escapes lexically
1 parent 6951d32 commit 5ea35db

File tree

2 files changed

+50
-31
lines changed

2 files changed

+50
-31
lines changed

spec/fluent.ebnf

+16-13
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ InlineExpression ::= StringLiteral
5757
| inline_placeable
5858

5959
/* Literals */
60-
StringLiteral ::= "\"" quoted_text_char* "\""
60+
StringLiteral ::= "\"" quoted_char* "\""
6161
NumberLiteral ::= "-"? digit+ ("." digit+)?
6262

6363
/* Inline Expressions */
@@ -85,23 +85,26 @@ Function ::= [A-Z] [A-Z_?-]*
8585

8686
/* Characters */
8787
/* Any Unicode character excluding C0 control characters (but including tab),
88-
* space, surrogate blocks and non-characters (U+FFFE, U+FFFF).
88+
* surrogate blocks and non-characters (U+FFFE, U+FFFF).
8989
* Cf. https://www.w3.org/TR/REC-xml/#NT-Char */
90-
regular_char ::= [\\u{9}\\u{21}-\\u{D7FF}\\u{E000}-\\u{FFFD}]
90+
regular_char ::= [\\u{9}\\u{20}-\\u{D7FF}\\u{E000}-\\u{FFFD}]
9191
| [\\u{10000}-\\u{10FFFF}]
9292
/* The opening brace in text starts a placeable. */
93-
text_char ::= (regular_char - "{")
94-
| "\u0020"
93+
special_text_char ::= "{"
94+
/* Double quote and backslash need to be escaped in string literals. */
95+
special_quoted_char ::= "\""
96+
| "\\"
97+
text_char ::= regular_char - special_text_char
9598
/* Indented text may not start with characters which mark its end. */
9699
indented_char ::= text_char - "}" - "[" - "*" - "."
97-
/* Backslash can be used to escape the double quote and the backslash itself.
98-
* The literal opening brace { is allowed because StringLiterals may not have
99-
* placeables. \uXXXX Unicode escape sequences are recognized, too. */
100-
quoted_text_char ::= (text_char - "\"" - "\\")
101-
| /\\u[0-9a-fA-F]{4}/
102-
| "{"
103-
| "\\\\"
104-
| "\\\""
100+
literal_escape ::= "\\" special_quoted_char
101+
unicode_escape ::= "\\u" /[0-9a-fA-F]{4}/
102+
/* The literal opening brace { is allowed in string literals because they may
103+
* not have placeables. */
104+
quoted_char ::= (text_char - special_quoted_char)
105+
| special_text_char
106+
| literal_escape
107+
| unicode_escape
105108
digit ::= [0-9]
106109

107110
/* Whitespace */

syntax/grammar.mjs

+34-18
Original file line numberDiff line numberDiff line change
@@ -200,7 +200,7 @@ let InlineExpression = defer(() =>
200200
let StringLiteral = defer(() =>
201201
sequence(
202202
string("\""),
203-
repeat(quoted_text_char),
203+
repeat(quoted_char),
204204
string("\""))
205205
.map(element_at(1))
206206
.map(join)
@@ -374,20 +374,27 @@ let Function =
374374
/* Characters */
375375

376376
/* Any Unicode character excluding C0 control characters (but including tab),
377-
* space, surrogate blocks and non-characters (U+FFFE, U+FFFF).
377+
* surrogate blocks and non-characters (U+FFFE, U+FFFF).
378378
* Cf. https://www.w3.org/TR/REC-xml/#NT-Char */
379379
let regular_char =
380380
either(
381-
charset("\\u{9}\\u{21}-\\u{D7FF}\\u{E000}-\\u{FFFD}"),
381+
charset("\\u{9}\\u{20}-\\u{D7FF}\\u{E000}-\\u{FFFD}"),
382382
charset("\\u{10000}-\\u{10FFFF}"));
383383

384384
/* The opening brace in text starts a placeable. */
385-
let text_char =
385+
let special_text_char =
386+
string("{");
387+
388+
/* Double quote and backslash need to be escaped in string literals. */
389+
let special_quoted_char =
386390
either(
387-
and(
388-
not(string("{")),
389-
regular_char),
390-
string("\u0020"));
391+
string("\""),
392+
string("\\"));
393+
394+
let text_char =
395+
and(
396+
not(special_text_char),
397+
regular_char);
391398

392399
/* Indented text may not start with characters which mark its end. */
393400
let indented_char =
@@ -398,19 +405,28 @@ let indented_char =
398405
not(string("}")),
399406
text_char);
400407

401-
/* Backslash can be used to escape the double quote and the backslash itself.
402-
* The literal opening brace { is allowed because StringLiterals may not have
403-
* placeables. \uXXXX Unicode escape sequences are recognized, too. */
404-
let quoted_text_char =
408+
let literal_escape =
409+
sequence(
410+
string("\\"),
411+
special_quoted_char)
412+
.map(join);
413+
414+
let unicode_escape =
415+
sequence(
416+
string("\\u"),
417+
regex(/[0-9a-fA-F]{4}/))
418+
.map(join);
419+
420+
/* The literal opening brace { is allowed in string literals because they may
421+
* not have placeables. */
422+
let quoted_char =
405423
either(
406424
and(
407-
not(string("\\")),
408-
not(string("\"")),
425+
not(special_quoted_char),
409426
text_char),
410-
regex(/\\u[0-9a-fA-F]{4}/),
411-
string("{"),
412-
string("\\\\"),
413-
string("\\\""));
427+
special_text_char,
428+
literal_escape,
429+
unicode_escape);
414430

415431
let digit = charset("0-9");
416432

0 commit comments

Comments
 (0)