@@ -43,7 +43,7 @@ export default class FluentParser {
43
43
// Poor man's decorators.
44
44
const methodNames = [
45
45
"getComment" , "getMessage" , "getTerm" , "getAttribute" , "getIdentifier" ,
46
- "getVariant" , "getNumber" , "getValue" , " getPattern", "getVariantList" ,
46
+ "getVariant" , "getNumber" , "getPattern" , "getVariantList" ,
47
47
"getTextElement" , "getPlaceable" , "getExpression" ,
48
48
"getSelectorExpression" , "getCallArg" , "getString" , "getLiteral"
49
49
] ;
@@ -231,7 +231,7 @@ export default class FluentParser {
231
231
ps . skipBlankInline ( ) ;
232
232
ps . expectChar ( "=" ) ;
233
233
234
- const value = this . getInlineOrBlock ( ps , this . getPattern ) ;
234
+ const value = this . getValue ( ps , { allowVariantList : false } ) ;
235
235
const attrs = this . getAttributes ( ps ) ;
236
236
237
237
if ( value === null && attrs . length === 0 ) {
@@ -248,7 +248,11 @@ export default class FluentParser {
248
248
ps . skipBlankInline ( ) ;
249
249
ps . expectChar ( "=" ) ;
250
250
251
- const value = this . getInlineOrBlock ( ps , this . getValue ) ;
251
+ // XXX Once https://github.com/projectfluent/fluent/pull/220 lands,
252
+ // getTerm will be the only place where VariantLists are still legal. Move
253
+ // the code currently present in getValueType up to here then, and remove
254
+ // the allowVariantList switch.
255
+ const value = this . getValue ( ps , { allowVariantList : true } ) ;
252
256
if ( value === null ) {
253
257
throw new ParseError ( "E0006" , id . name ) ;
254
258
}
@@ -257,22 +261,6 @@ export default class FluentParser {
257
261
return new AST . Term ( id , value , attrs ) ;
258
262
}
259
263
260
- getInlineOrBlock ( ps , method ) {
261
- ps . peekBlankInline ( ) ;
262
- if ( ps . isValueStart ( ) ) {
263
- ps . skipToPeek ( ) ;
264
- return method . call ( this , ps , { isBlock : false } ) ;
265
- }
266
-
267
- ps . peekBlankBlock ( ) ;
268
- if ( ps . isValueContinuation ( ) ) {
269
- ps . skipToPeek ( ) ;
270
- return method . call ( this , ps , { isBlock : true } ) ;
271
- }
272
-
273
- return null ;
274
- }
275
-
276
264
getAttribute ( ps ) {
277
265
ps . expectChar ( "." ) ;
278
266
@@ -281,7 +269,7 @@ export default class FluentParser {
281
269
ps . skipBlankInline ( ) ;
282
270
ps . expectChar ( "=" ) ;
283
271
284
- const value = this . getInlineOrBlock ( ps , this . getPattern ) ;
272
+ const value = this . getValue ( ps , { allowVariantList : false } ) ;
285
273
if ( value === null ) {
286
274
throw new ParseError ( "E0012" ) ;
287
275
}
@@ -328,7 +316,7 @@ export default class FluentParser {
328
316
return this . getIdentifier ( ps ) ;
329
317
}
330
318
331
- getVariant ( ps , hasDefault ) {
319
+ getVariant ( ps , { hasDefault, allowVariantList } ) {
332
320
let defaultIndex = false ;
333
321
334
322
if ( ps . currentChar === "*" ) {
@@ -349,21 +337,23 @@ export default class FluentParser {
349
337
ps . skipBlank ( ) ;
350
338
ps . expectChar ( "]" ) ;
351
339
352
- const value = this . getInlineOrBlock ( ps , this . getValue ) ;
340
+ // XXX We need to pass allowVariantList all the way down to here because
341
+ // nested VariantLists in Terms are legal for now.
342
+ const value = this . getValue ( ps , { allowVariantList} ) ;
353
343
if ( value === null ) {
354
344
throw new ParseError ( "E0012" ) ;
355
345
}
356
346
357
347
return new AST . Variant ( key , value , defaultIndex ) ;
358
348
}
359
349
360
- getVariants ( ps ) {
350
+ getVariants ( ps , { allowVariantList } ) {
361
351
const variants = [ ] ;
362
352
let hasDefault = false ;
363
353
364
354
ps . skipBlank ( ) ;
365
355
while ( ps . isVariantStart ( ) ) {
366
- const variant = this . getVariant ( ps , hasDefault ) ;
356
+ const variant = this . getVariant ( ps , { allowVariantList , hasDefault} ) ;
367
357
368
358
if ( variant . default ) {
369
359
hasDefault = true ;
@@ -419,29 +409,54 @@ export default class FluentParser {
419
409
return new AST . NumberLiteral ( num ) ;
420
410
}
421
411
422
- getValue ( ps , { isBlock} ) {
412
+ // getValue distinguished between patterns which start on the same line as the
413
+ // identifier (a.k.a. inline signleline patterns and inline multiline
414
+ // patterns) and patterns which start on a new line (a.k.a. block multiline
415
+ // patterns). The distinction is important for the dedentation logic: the
416
+ // indent of the first line of a block pattern must be taken into account when
417
+ // calculating the maximum common indent.
418
+ getValue ( ps , { allowVariantList} ) {
423
419
ps . peekBlankInline ( ) ;
424
- const peekOffset = ps . peekOffset ;
425
- if ( ps . currentPeek === "{" ) {
420
+ if ( ps . isValueStart ( ) ) {
421
+ ps . skipToPeek ( ) ;
422
+ return this . getValueType (
423
+ ps , { isBlock : false , allowVariantList} ) ;
424
+ }
425
+
426
+ ps . peekBlankBlock ( ) ;
427
+ if ( ps . isValueContinuation ( ) ) {
428
+ ps . skipToPeek ( ) ;
429
+ return this . getValueType ( ps , { isBlock : true , allowVariantList} ) ;
430
+ }
431
+
432
+ return null ;
433
+ }
434
+
435
+ // Parse a VariantList (if allowed) or a Pattern.
436
+ getValueType ( ps , { isBlock, allowVariantList} ) {
437
+ ps . peekBlankInline ( ) ;
438
+ if ( allowVariantList && ps . currentPeek === "{" ) {
439
+ const start = ps . peekOffset ;
426
440
ps . peek ( ) ;
427
- ps . peekBlank ( ) ;
428
- if ( ps . isVariantStart ( ) ) {
429
- ps . resetPeek ( peekOffset ) ;
430
- ps . skipToPeek ( ) ;
431
- return this . getVariantList ( ps ) ;
441
+ ps . peekBlankInline ( ) ;
442
+ if ( ps . currentPeek === EOL ) {
443
+ ps . peekBlank ( ) ;
444
+ if ( ps . isVariantStart ( ) ) {
445
+ ps . resetPeek ( start ) ;
446
+ ps . skipToPeek ( ) ;
447
+ return this . getVariantList ( ps , { allowVariantList} ) ;
448
+ }
432
449
}
433
450
}
434
451
435
452
ps . resetPeek ( ) ;
436
- return this . getPattern ( ps , { isBlock} ) ;
453
+ const pattern = this . getPattern ( ps , { isBlock} ) ;
454
+ return pattern ;
437
455
}
438
456
439
457
getVariantList ( ps ) {
440
458
ps . expectChar ( "{" ) ;
441
- ps . skipBlankInline ( ) ;
442
- ps . expectLineEnd ( ) ;
443
- const variants = this . getVariants ( ps ) ;
444
- ps . skipBlank ( ) ;
459
+ var variants = this . getVariants ( ps , { allowVariantList : true } ) ;
445
460
ps . expectChar ( "}" ) ;
446
461
return new AST . VariantList ( variants ) ;
447
462
}
@@ -490,6 +505,9 @@ export default class FluentParser {
490
505
return new AST . Pattern ( this . dedent ( elements , commonIndentLength ) ) ;
491
506
}
492
507
508
+ // Create a token representing an indent. It's not part of the AST and it will
509
+ // be trimmed and merged into adjacent TextElements, or turned into a new
510
+ // TextElement, if it's surrounded by two Placeables.
493
511
getIndent ( ps , value , start ) {
494
512
return {
495
513
type : "Indent" ,
@@ -498,6 +516,8 @@ export default class FluentParser {
498
516
} ;
499
517
}
500
518
519
+ // Dedent a list of elements by removing the maximum common indent from the
520
+ // beginning of text lines. The common indent is calculated in getPattern.
501
521
dedent ( elements , commonIndent ) {
502
522
const trimmed = [ ] ;
503
523
@@ -528,6 +548,8 @@ export default class FluentParser {
528
548
}
529
549
530
550
if ( element . type === "Indent" ) {
551
+ // Is the indent hasn't been merged into a preceding TextElement,
552
+ // convert it into a new TextElement.
531
553
const textElement = new AST . TextElement ( element . value ) ;
532
554
if ( this . withSpans ) {
533
555
textElement . addSpan ( element . span . start , element . span . end ) ;
@@ -538,7 +560,7 @@ export default class FluentParser {
538
560
trimmed . push ( element ) ;
539
561
}
540
562
541
- // Trim trailing whitespace.
563
+ // Trim trailing whitespace from the Pattern .
542
564
const lastElement = trimmed [ trimmed . length - 1 ] ;
543
565
if ( lastElement . type === "TextElement" ) {
544
566
lastElement . value = lastElement . value . replace ( trailingWSRe , "" ) ;
@@ -600,16 +622,14 @@ export default class FluentParser {
600
622
601
623
getPlaceable ( ps ) {
602
624
ps . expectChar ( "{" ) ;
625
+ ps . skipBlank ( ) ;
603
626
const expression = this . getExpression ( ps ) ;
604
627
ps . expectChar ( "}" ) ;
605
628
return new AST . Placeable ( expression ) ;
606
629
}
607
630
608
631
getExpression ( ps ) {
609
- ps . skipBlank ( ) ;
610
-
611
632
const selector = this . getSelectorExpression ( ps ) ;
612
-
613
633
ps . skipBlank ( ) ;
614
634
615
635
if ( ps . currentChar === "-" ) {
@@ -638,21 +658,13 @@ export default class FluentParser {
638
658
ps . skipBlankInline ( ) ;
639
659
ps . expectLineEnd ( ) ;
640
660
641
- const variants = this . getVariants ( ps ) ;
642
-
643
- // VariantLists are only allowed in other VariantLists.
644
- if ( variants . some ( v => v . value . type === "VariantList" ) ) {
645
- throw new ParseError ( "E0023" ) ;
646
- }
647
-
661
+ const variants = this . getVariants ( ps , { allowVariantList : false } ) ;
648
662
return new AST . SelectExpression ( selector , variants ) ;
649
663
} else if ( selector . type === "AttributeExpression" &&
650
664
selector . ref . type === "TermReference" ) {
651
665
throw new ParseError ( "E0019" ) ;
652
666
}
653
667
654
- ps . skipBlank ( ) ;
655
-
656
668
return selector ;
657
669
}
658
670
0 commit comments