Skip to content

Commit 516cf8e

Browse files
authored
Merge pull request #253 from projectfluent/feature/syntax-0.6
Implement Syntax 0.6 in the tooling parser Spec changelog: https://github.com/projectfluent/fluent/releases/tag/v0.6.0
2 parents 3e05317 + 36f9438 commit 516cf8e

File tree

161 files changed

+6105
-1712
lines changed

Some content is hidden

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

161 files changed

+6105
-1712
lines changed

fluent-syntax/src/ast.js

+52-34
Original file line numberDiff line numberDiff line change
@@ -26,17 +26,10 @@ export class Resource extends SyntaxNode {
2626
}
2727
}
2828

29-
export class Entry extends SyntaxNode {
30-
constructor() {
31-
super();
32-
this.type = "Entry";
33-
this.annotations = [];
34-
}
35-
36-
addAnnotation(annot) {
37-
this.annotations.push(annot);
38-
}
39-
}
29+
/*
30+
* An abstract base class for useful elements of Resource.body.
31+
*/
32+
export class Entry extends SyntaxNode {}
4033

4134
export class Message extends Entry {
4235
constructor(id, value = null, attributes = [], comment = null) {
@@ -60,6 +53,14 @@ export class Term extends Entry {
6053
}
6154
}
6255

56+
export class VariantList extends SyntaxNode {
57+
constructor(variants) {
58+
super();
59+
this.type = "VariantList";
60+
this.variants = variants;
61+
}
62+
}
63+
6364
export class Pattern extends SyntaxNode {
6465
constructor(elements) {
6566
super();
@@ -68,41 +69,44 @@ export class Pattern extends SyntaxNode {
6869
}
6970
}
7071

71-
export class TextElement extends SyntaxNode {
72+
/*
73+
* An abstract base class for elements of Patterns.
74+
*/
75+
export class PatternElement extends SyntaxNode {}
76+
77+
export class TextElement extends PatternElement {
7278
constructor(value) {
7379
super();
7480
this.type = "TextElement";
7581
this.value = value;
7682
}
7783
}
7884

79-
export class Placeable extends SyntaxNode {
85+
export class Placeable extends PatternElement {
8086
constructor(expression) {
8187
super();
8288
this.type = "Placeable";
8389
this.expression = expression;
8490
}
8591
}
8692

87-
export class Expression extends SyntaxNode {
88-
constructor() {
89-
super();
90-
this.type = "Expression";
91-
}
92-
}
93+
/*
94+
* An abstract base class for expressions.
95+
*/
96+
export class Expression extends SyntaxNode {}
9397

94-
export class StringExpression extends Expression {
98+
export class StringLiteral extends Expression {
9599
constructor(value) {
96100
super();
97-
this.type = "StringExpression";
101+
this.type = "StringLiteral";
98102
this.value = value;
99103
}
100104
}
101105

102-
export class NumberExpression extends Expression {
106+
export class NumberLiteral extends Expression {
103107
constructor(value) {
104108
super();
105-
this.type = "NumberExpression";
109+
this.type = "NumberLiteral";
106110
this.value = value;
107111
}
108112
}
@@ -115,28 +119,36 @@ export class MessageReference extends Expression {
115119
}
116120
}
117121

118-
export class ExternalArgument extends Expression {
122+
export class TermReference extends Expression {
123+
constructor(id) {
124+
super();
125+
this.type = "TermReference";
126+
this.id = id;
127+
}
128+
}
129+
130+
export class VariableReference extends Expression {
119131
constructor(id) {
120132
super();
121-
this.type = "ExternalArgument";
133+
this.type = "VariableReference";
122134
this.id = id;
123135
}
124136
}
125137

126138
export class SelectExpression extends Expression {
127-
constructor(expression, variants) {
139+
constructor(selector, variants) {
128140
super();
129141
this.type = "SelectExpression";
130-
this.expression = expression;
142+
this.selector = selector;
131143
this.variants = variants;
132144
}
133145
}
134146

135147
export class AttributeExpression extends Expression {
136-
constructor(id, name) {
148+
constructor(ref, name) {
137149
super();
138150
this.type = "AttributeExpression";
139-
this.id = id;
151+
this.ref = ref;
140152
this.name = name;
141153
}
142154
}
@@ -151,11 +163,12 @@ export class VariantExpression extends Expression {
151163
}
152164

153165
export class CallExpression extends Expression {
154-
constructor(callee, args = []) {
166+
constructor(callee, positional = [], named = []) {
155167
super();
156168
this.type = "CallExpression";
157169
this.callee = callee;
158-
this.args = args;
170+
this.positional = positional;
171+
this.named = named;
159172
}
160173
}
161174

@@ -179,11 +192,11 @@ export class Variant extends SyntaxNode {
179192
}
180193

181194
export class NamedArgument extends SyntaxNode {
182-
constructor(name, val) {
195+
constructor(name, value) {
183196
super();
184197
this.type = "NamedArgument";
185198
this.name = name;
186-
this.val = val;
199+
this.value = value;
187200
}
188201
}
189202

@@ -237,12 +250,17 @@ export class Function extends Identifier {
237250
}
238251
}
239252

240-
export class Junk extends Entry {
253+
export class Junk extends SyntaxNode {
241254
constructor(content) {
242255
super();
243256
this.type = "Junk";
257+
this.annotations = [];
244258
this.content = content;
245259
}
260+
261+
addAnnotation(annot) {
262+
this.annotations.push(annot);
263+
}
246264
}
247265

248266
export class Span extends BaseNode {

fluent-syntax/src/errors.js

+16
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,22 @@ function getErrorMessage(code, args) {
5858
return "Attributes of terms cannot be used as placeables";
5959
case "E0020":
6060
return "Unterminated string expression";
61+
case "E0021":
62+
return "Positional arguments must not follow named arguments";
63+
case "E0022":
64+
return "Named arguments must be unique";
65+
case "E0023":
66+
return "VariantLists are only allowed inside of other VariantLists.";
67+
case "E0024":
68+
return "Cannot access variants of a message.";
69+
case "E0025": {
70+
const [char] = args;
71+
return `Unknown escape sequence: \\${char}.`;
72+
}
73+
case "E0026": {
74+
const [char] = args;
75+
return `Invalid Unicode escape sequence: \\u${char}.`;
76+
}
6177
default:
6278
return code;
6379
}

fluent-syntax/src/ftlstream.js

+29-45
Original file line numberDiff line numberDiff line change
@@ -28,15 +28,17 @@ export class FTLParserStream extends ParserStream {
2828
}
2929

3030
skipBlankLines() {
31+
let lineCount = 0;
3132
while (true) {
3233
this.peekInlineWS();
3334

3435
if (this.currentPeekIs("\n")) {
3536
this.skipToPeek();
3637
this.next();
38+
lineCount++;
3739
} else {
3840
this.resetPeek();
39-
break;
41+
return lineCount;
4042
}
4143
}
4244
}
@@ -82,12 +84,13 @@ export class FTLParserStream extends ParserStream {
8284
this.skipInlineWS();
8385
}
8486

85-
takeCharIf(ch) {
86-
if (this.ch === ch) {
87-
this.next();
87+
expectLineEnd() {
88+
if (this.ch === undefined) {
89+
// EOF is a valid line end in Fluent.
8890
return true;
8991
}
90-
return false;
92+
93+
return this.expectChar("\n");
9194
}
9295

9396
takeChar(f) {
@@ -109,11 +112,7 @@ export class FTLParserStream extends ParserStream {
109112
(cc >= 65 && cc <= 90); // A-Z
110113
}
111114

112-
isEntryIDStart() {
113-
if (this.currentIs("-")) {
114-
this.peek();
115-
}
116-
115+
isIdentifierStart() {
117116
const ch = this.currentPeek();
118117
const isID = this.isCharIDStart(ch);
119118
this.resetPeek();
@@ -139,7 +138,7 @@ export class FTLParserStream extends ParserStream {
139138
return !includes(SPECIAL_LINE_START_CHARS, ch);
140139
}
141140

142-
isPeekPatternStart() {
141+
isPeekValueStart() {
143142
this.peekInlineWS();
144143
const ch = this.currentPeek();
145144

@@ -148,26 +147,7 @@ export class FTLParserStream extends ParserStream {
148147
return true;
149148
}
150149

151-
return this.isPeekNextLinePatternStart();
152-
}
153-
154-
isPeekNextLineZeroFourStyleComment() {
155-
if (!this.currentPeekIs("\n")) {
156-
return false;
157-
}
158-
159-
this.peek();
160-
161-
if (this.currentPeekIs("/")) {
162-
this.peek();
163-
if (this.currentPeekIs("/")) {
164-
this.resetPeek();
165-
return true;
166-
}
167-
}
168-
169-
this.resetPeek();
170-
return false;
150+
return this.isPeekNextLineValue();
171151
}
172152

173153
// -1 - any
@@ -184,7 +164,7 @@ export class FTLParserStream extends ParserStream {
184164
while (i <= level || (level === -1 && i < 3)) {
185165
this.peek();
186166
if (!this.currentPeekIs("#")) {
187-
if (i !== level && level !== -1) {
167+
if (i <= level && level !== -1) {
188168
this.resetPeek();
189169
return false;
190170
}
@@ -260,7 +240,7 @@ export class FTLParserStream extends ParserStream {
260240
return false;
261241
}
262242

263-
isPeekNextLinePatternStart() {
243+
isPeekNextLineValue() {
264244
if (!this.currentPeekIs("\n")) {
265245
return false;
266246
}
@@ -292,31 +272,24 @@ export class FTLParserStream extends ParserStream {
292272
if (this.currentIs("\n") && !this.peekCharIs("\n")) {
293273
this.next();
294274
if (this.ch === undefined ||
295-
this.isEntryIDStart() ||
296-
this.currentIs("#") ||
297-
(this.currentIs("/") && this.peekCharIs("/")) ||
298-
(this.currentIs("[") && this.peekCharIs("["))) {
275+
this.isIdentifierStart() ||
276+
this.currentIs("-") ||
277+
this.currentIs("#")) {
299278
break;
300279
}
301280
}
302281
this.next();
303282
}
304283
}
305284

306-
takeIDStart(allowTerm) {
307-
if (allowTerm && this.currentIs("-")) {
308-
this.next();
309-
return "-";
310-
}
311-
285+
takeIDStart() {
312286
if (this.isCharIDStart(this.ch)) {
313287
const ret = this.ch;
314288
this.next();
315289
return ret;
316290
}
317291

318-
const allowedRange = allowTerm ? "a-zA-Z-" : "a-zA-Z";
319-
throw new ParseError("E0004", allowedRange);
292+
throw new ParseError("E0004", "a-zA-Z");
320293
}
321294

322295
takeIDChar() {
@@ -351,4 +324,15 @@ export class FTLParserStream extends ParserStream {
351324

352325
return this.takeChar(closure);
353326
}
327+
328+
takeHexDigit() {
329+
const closure = ch => {
330+
const cc = ch.charCodeAt(0);
331+
return (cc >= 48 && cc <= 57) // 0-9
332+
|| (cc >= 65 && cc <= 70) // A-F
333+
|| (cc >= 97 && cc <= 102); // a-f
334+
};
335+
336+
return this.takeChar(closure);
337+
}
354338
}

0 commit comments

Comments
 (0)