Skip to content

Commit fd37e32

Browse files
authored
Merge pull request #307 from stasm/zeroeight-part1
Implement Syntax 0.8, part 1 (items 1-5)
2 parents 6edf276 + eb0493b commit fd37e32

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

50 files changed

+1377
-266
lines changed

eslint_src.json

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -32,17 +32,6 @@
3232
],
3333
"no-implied-eval": 2,
3434
"no-loop-func": 2,
35-
"no-magic-numbers": [
36-
1,
37-
{
38-
"ignore": [
39-
-1,
40-
0,
41-
1,
42-
2
43-
]
44-
}
45-
],
4635
"no-useless-call": 2,
4736
"no-useless-concat": 2,
4837
"no-delete-var": 2,

fluent-syntax/.gitattributes

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
test/fixtures_reference/crlf.ftl eol=crlf
2+
test/fixtures_reference/cr.ftl eol=cr
23
test/fixtures_structure/crlf.ftl eol=crlf

fluent-syntax/src/errors.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ function getErrorMessage(code, args) {
2828
}
2929
case "E0006": {
3030
const [id] = args;
31-
return `Expected term "${id}" to have a value`;
31+
return `Expected term "-${id}" to have a value`;
3232
}
3333
case "E0007":
3434
return "Keyword cannot end with a whitespace";
@@ -74,6 +74,8 @@ function getErrorMessage(code, args) {
7474
const [char] = args;
7575
return `Invalid Unicode escape sequence: \\u${char}.`;
7676
}
77+
case "E0027":
78+
return "Unbalanced closing brace in TextElement.";
7779
default:
7880
return code;
7981
}

fluent-syntax/src/parser.js

Lines changed: 19 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@ import { ParseError } from "./errors";
66

77

88
const trailingWSRe = /[ \t\n\r]+$/;
9+
// The Fluent Syntax spec uses /.*/ to parse comment lines. It matches all
10+
// characters except the following ones, which are considered line endings by
11+
// the regex engine.
12+
const COMMENT_EOL = ["\n", "\r", "\u2028", "\u2029"];
913

1014

1115
function withSpan(fn) {
@@ -39,10 +43,9 @@ export default class FluentParser {
3943
// Poor man's decorators.
4044
const methodNames = [
4145
"getComment", "getMessage", "getTerm", "getAttribute", "getIdentifier",
42-
"getTermIdentifier", "getVariant", "getNumber",
43-
"getValue", "getPattern", "getVariantList", "getTextElement",
44-
"getPlaceable", "getExpression", "getSelectorExpression", "getCallArg",
45-
"getString", "getLiteral"
46+
"getVariant", "getNumber", "getValue", "getPattern", "getVariantList",
47+
"getTextElement", "getPlaceable", "getExpression",
48+
"getSelectorExpression", "getCallArg", "getString", "getLiteral"
4649
];
4750
for (const name of methodNames) {
4851
this[name] = withSpan(this[name]);
@@ -189,10 +192,10 @@ export default class FluentParser {
189192
level = i;
190193
}
191194

192-
if (ps.currentChar !== EOL) {
195+
if (!COMMENT_EOL.includes(ps.currentChar)) {
193196
ps.expectChar(" ");
194197
let ch;
195-
while ((ch = ps.takeChar(x => x !== EOL))) {
198+
while ((ch = ps.takeChar(x => !COMMENT_EOL.includes(x)))) {
196199
content += ch;
197200
}
198201
}
@@ -242,7 +245,8 @@ export default class FluentParser {
242245
}
243246

244247
getTerm(ps) {
245-
const id = this.getTermIdentifier(ps);
248+
ps.expectChar("-");
249+
const id = this.getIdentifier(ps);
246250

247251
ps.skipBlankInline();
248252
ps.expectChar("=");
@@ -301,13 +305,6 @@ export default class FluentParser {
301305
return new AST.Identifier(name);
302306
}
303307

304-
getTermIdentifier(ps) {
305-
ps.expectChar("-");
306-
const id = this.getIdentifier(ps);
307-
return new AST.Identifier(`-${id.name}`);
308-
309-
}
310-
311308
getVariantKey(ps) {
312309
const ch = ps.currentChar;
313310

@@ -455,6 +452,8 @@ export default class FluentParser {
455452
if (ch === "{") {
456453
const element = this.getPlaceable(ps);
457454
elements.push(element);
455+
} else if (ch === "}") {
456+
throw new ParseError("E0027");
458457
} else {
459458
const element = this.getTextElement(ps);
460459
elements.push(element);
@@ -478,7 +477,7 @@ export default class FluentParser {
478477

479478
let ch;
480479
while ((ch = ps.currentChar)) {
481-
if (ch === "{") {
480+
if (ch === "{" || ch === "}") {
482481
return new AST.TextElement(buffer);
483482
}
484483

@@ -494,23 +493,17 @@ export default class FluentParser {
494493
continue;
495494
}
496495

497-
if (ch === "\\") {
498-
ps.next();
499-
buffer += this.getEscapeSequence(ps);
500-
continue;
501-
}
502-
503496
buffer += ch;
504497
ps.next();
505498
}
506499

507500
return new AST.TextElement(buffer);
508501
}
509502

510-
getEscapeSequence(ps, specials = ["{", "\\"]) {
503+
getEscapeSequence(ps) {
511504
const next = ps.currentChar;
512505

513-
if (specials.includes(next)) {
506+
if (next === "\\" || next === "\"") {
514507
ps.next();
515508
return `\\${next}`;
516509
}
@@ -738,7 +731,7 @@ export default class FluentParser {
738731
let ch;
739732
while ((ch = ps.takeChar(x => x !== '"' && x !== EOL))) {
740733
if (ch === "\\") {
741-
val += this.getEscapeSequence(ps, ["{", "\\", "\""]);
734+
val += this.getEscapeSequence(ps);
742735
} else {
743736
val += ch;
744737
}
@@ -777,7 +770,8 @@ export default class FluentParser {
777770
}
778771

779772
if (ch === "-") {
780-
const id = this.getTermIdentifier(ps);
773+
ps.next();
774+
const id = this.getIdentifier(ps);
781775
return new AST.TermReference(id);
782776
}
783777

fluent-syntax/src/serializer.js

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,9 @@ export default class FluentSerializer {
4444
serializeEntry(entry, state = 0) {
4545
switch (entry.type) {
4646
case "Message":
47-
case "Term":
4847
return serializeMessage(entry);
48+
case "Term":
49+
return serializeTerm(entry);
4950
case "Comment":
5051
if (state & HAS_ENTRIES) {
5152
return `\n${serializeComment(entry, "#")}\n`;
@@ -95,8 +96,7 @@ function serializeMessage(message) {
9596
parts.push(serializeComment(message.comment));
9697
}
9798

98-
parts.push(serializeIdentifier(message.id));
99-
parts.push(" =");
99+
parts.push(`${serializeIdentifier(message.id)} =`);
100100

101101
if (message.value) {
102102
parts.push(serializeValue(message.value));
@@ -111,6 +111,25 @@ function serializeMessage(message) {
111111
}
112112

113113

114+
function serializeTerm(term) {
115+
const parts = [];
116+
117+
if (term.comment) {
118+
parts.push(serializeComment(term.comment));
119+
}
120+
121+
parts.push(`-${serializeIdentifier(term.id)} =`);
122+
parts.push(serializeValue(term.value));
123+
124+
for (const attribute of term.attributes) {
125+
parts.push(serializeAttribute(attribute));
126+
}
127+
128+
parts.push("\n");
129+
return parts.join("");
130+
}
131+
132+
114133
function serializeAttribute(attribute) {
115134
const id = serializeIdentifier(attribute.id);
116135
const value = indent(serializeValue(attribute.value));
@@ -202,8 +221,9 @@ function serializeExpression(expr) {
202221
case "NumberLiteral":
203222
return serializeNumberLiteral(expr);
204223
case "MessageReference":
205-
case "TermReference":
206224
return serializeMessageReference(expr);
225+
case "TermReference":
226+
return serializeTermReference(expr);
207227
case "VariableReference":
208228
return serializeVariableReference(expr);
209229
case "AttributeExpression":
@@ -237,6 +257,11 @@ function serializeMessageReference(expr) {
237257
}
238258

239259

260+
function serializeTermReference(expr) {
261+
return `-${serializeIdentifier(expr.id)}`;
262+
}
263+
264+
240265
function serializeVariableReference(expr) {
241266
return `$${serializeIdentifier(expr.id)}`;
242267
}
Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,16 @@
1-
# ~ERROR E0025, pos 8, args "A"
2-
key1 = \A
1+
## Backslash is a regular character in text elements.
2+
key01 = \A
3+
key02 = \u0041
4+
key03 = \\u0041
5+
key04 = \u000z
6+
key05 = \{Value}
37
4-
# ~ERROR E0026, pos 23, args "000z"
5-
key2 = \u000z
8+
key06 = {"Escaped \" quote"}
9+
key07 = {"Escaped \\ backslash"}
10+
key08 = {"Escaped \u0041 A"}
611
7-
key3 = \{Escaped}
8-
key4 = {"Escaped \" quote"}
9-
key5 = \u0041
10-
key6 = \\u0041
12+
# ~ERROR E0025, pos 232, args "A"
13+
key09 = {"\A"}
14+
15+
# ~ERROR E0026, pos 252, args "000z"
16+
key10 = {"\u000z"}

fluent-syntax/test/fixtures_behavior/placeable_in_placeable.ftl

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,9 @@ key2 = { { foo } }
77
# { foo }
88
# }
99

10-
key4 = { { foo }
1110
# ~ERROR E0003, pos 96, args "}"
11+
key4 = { { foo }
1212
1313
14+
# ~ERROR E0027, pos 111
1415
key5 = { foo } }

fluent-syntax/test/fixtures_behavior/term.ftl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,8 @@ err4 = { -brand() }
2525
# ~ERROR E0008, pos 339
2626

2727
-err5 =
28-
# ~ERROR E0006, pos 351, args "-err5"
28+
# ~ERROR E0006, pos 351, args "err5"
2929

3030
-err6 =
3131
.attr = Attribute
32-
# ~ERROR E0006, pos 360, args "-err6"
32+
# ~ERROR E0006, pos 360, args "err6"
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
face-with-tears-of-joy = 😂
2+
tetragram-for-centre = 𝌆
3+
4+
surrogates-in-text = \uD83D\uDE02
5+
surrogates-in-string = {"\uD83D\uDE02"}
6+
surrogates-in-adjacent-strings = {"\uD83D"}{"\uDE02"}
7+
8+
emoji-in-text = A face 😂 with tears of joy.
9+
emoji-in-string = {"A face 😂 with tears of joy."}
10+
11+
# ERROR Invalid identifier
12+
err-😂 = Value
13+
14+
# ERROR Invalid expression
15+
err-invalid-expression = { 😂 }
16+
17+
# ERROR Invalid variant key
18+
err-invalid-variant-key = { $sel ->
19+
*[😂] Value
20+
}

0 commit comments

Comments
 (0)