@@ -21,6 +21,7 @@ import util.Spans._
21
21
import Constants ._
22
22
import ScriptParsers ._
23
23
import Decorators ._
24
+ import scala .tasty .util .Chars .isIdentifierStart
24
25
import scala .annotation .{tailrec , switch }
25
26
import rewrites .Rewrites .patch
26
27
@@ -48,6 +49,13 @@ object Parsers {
48
49
val Class, Type, TypeParam, Def : Value = Value
49
50
}
50
51
52
+ type StageKind = Int
53
+ object StageKind {
54
+ val None = 0
55
+ val Quoted = 1
56
+ val Spliced = 2
57
+ }
58
+
51
59
private implicit class AddDeco (val buf : ListBuffer [Tree ]) extends AnyVal {
52
60
def +++= (x : Tree ) = x match {
53
61
case x : Thicket => buf ++= x.trees
@@ -199,6 +207,16 @@ object Parsers {
199
207
def isStatSep : Boolean =
200
208
in.token == NEWLINE || in.token == NEWLINES || in.token == SEMI
201
209
210
+ /** A '$' identifier is treated as a splice if followed by a `{`.
211
+ * A longer identifier starting with `$` is treated as a splice/id combination
212
+ * in a quoted block '{...'
213
+ */
214
+ def isSplice : Boolean =
215
+ in.token == IDENTIFIER && in.name(0 ) == '$' && {
216
+ if (in.name.length == 1 ) in.lookaheadIn(BitSet (LBRACE ))
217
+ else (staged & StageKind .Quoted ) != 0
218
+ }
219
+
202
220
/* ------------- ERROR HANDLING ------------------------------------------- */
203
221
204
222
/** The offset of the last time when a statement on a new line was definitely
@@ -354,6 +372,14 @@ object Parsers {
354
372
finally inEnum = saved
355
373
}
356
374
375
+ private [this ] var staged = StageKind .None
376
+ def withinStaged [T ](kind : StageKind )(op : => T ): T = {
377
+ val saved = staged
378
+ staged |= kind
379
+ try op
380
+ finally staged = saved
381
+ }
382
+
357
383
def migrationWarningOrError (msg : String , offset : Int = in.offset): Unit =
358
384
if (in.isScala2Mode)
359
385
ctx.migrationWarning(msg, source.atSpan(Span (offset)))
@@ -690,14 +716,18 @@ object Parsers {
690
716
}
691
717
val isNegated = negOffset < in.offset
692
718
atSpan(negOffset) {
693
- if (in.token == SYMBOLLIT ) {
694
- migrationWarningOrError(em """ symbol literal ' ${in.name} is no longer supported,
695
- |use a string literal " ${in.name}" or an application Symbol(" ${in.name}") instead. """ )
696
- if (in.isScala2Mode) {
697
- patch(source, Span (in.offset, in.offset + 1 ), " Symbol(\" " )
698
- patch(source, Span (in.charOffset - 1 ), " \" )" )
719
+ if (in.token == QUOTEID ) {
720
+ if ((staged & StageKind .Spliced ) != 0 && isIdentifierStart(in.name(1 )))
721
+ Quote (atSpan(in.offset + 1 )(Ident (in.name.drop(1 ))))
722
+ else {
723
+ migrationWarningOrError(em """ symbol literal ' ${in.name} is no longer supported,
724
+ |use a string literal " ${in.name}" or an application Symbol(" ${in.name}") instead. """ )
725
+ if (in.isScala2Mode) {
726
+ patch(source, Span (in.offset, in.offset + 1 ), " Symbol(\" " )
727
+ patch(source, Span (in.charOffset - 1 ), " \" )" )
728
+ }
729
+ atSpan(in.skipToken()) { SymbolLit (in.strVal) }
699
730
}
700
- atSpan(in.skipToken()) { SymbolLit (in.strVal) }
701
731
}
702
732
else if (in.token == INTERPOLATIONID ) interpolatedString(inPattern)
703
733
else finish(in.token match {
@@ -919,26 +949,27 @@ object Parsers {
919
949
else t
920
950
921
951
/** The block in a quote or splice */
922
- def stagedBlock (isQuote : Boolean ) = {
923
- val saved = in.inQuote
924
- in.inQuote = isQuote
925
- inDefScopeBraces {
926
- try
927
- block() match {
928
- case t @ Block (Nil , expr) =>
929
- if (expr.isEmpty) t else expr
930
- case t => t
931
- }
932
- finally in.inQuote = saved
952
+ def stagedBlock () =
953
+ inDefScopeBraces(block()) match {
954
+ case t @ Block (Nil , expr) if ! expr.isEmpty => expr
955
+ case t => t
933
956
}
934
- }
935
957
936
- /** SimpleEpxr ::= ‘$’ (id | ‘{’ Block ‘}’)
937
- * SimpleType ::= ‘$’ (id | ‘{’ Block ‘}’)
958
+ /** SimpleEpxr ::= spliceId | ‘$’ ‘{’ Block ‘}’)
959
+ * SimpleType ::= spliceId | ‘$’ ‘{’ Block ‘}’)
938
960
*/
939
961
def splice (isType : Boolean ): Tree =
940
- atSpan(in.skipToken()) {
941
- val expr = if (isIdent) termIdent() else stagedBlock(isQuote = false )
962
+ atSpan(in.offset) {
963
+ val expr =
964
+ if (in.name.length == 1 ) {
965
+ in.nextToken()
966
+ withinStaged(StageKind .Spliced )(stagedBlock())
967
+ }
968
+ else atSpan(in.offset + 1 ) {
969
+ val id = Ident (in.name.drop(1 ))
970
+ in.nextToken()
971
+ id
972
+ }
942
973
if (isType) TypSplice (expr) else Splice (expr)
943
974
}
944
975
@@ -950,7 +981,7 @@ object Parsers {
950
981
* | `_' TypeBounds
951
982
* | Refinement
952
983
* | Literal
953
- * | ‘$’ (id | ‘{’ Block ‘}’)
984
+ * | ‘$’ ‘{’ Block ‘}’
954
985
*/
955
986
def simpleType (): Tree = simpleTypeRest {
956
987
if (in.token == LPAREN )
@@ -964,7 +995,7 @@ object Parsers {
964
995
val start = in.skipToken()
965
996
typeBounds().withSpan(Span (start, in.lastOffset, start))
966
997
}
967
- else if (in.token == SPLICE )
998
+ else if (isSplice )
968
999
splice(isType = true )
969
1000
else path(thisOK = false , handleSingletonType) match {
970
1001
case r @ SingletonTypeTree (_) => r
@@ -1426,9 +1457,10 @@ object Parsers {
1426
1457
1427
1458
/** SimpleExpr ::= ‘new’ (ConstrApp [TemplateBody] | TemplateBody)
1428
1459
* | BlockExpr
1429
- * | ‘'’ (id | ‘{’ Block ‘}’)
1460
+ * | ‘'’ ‘{’ Block ‘}’
1430
1461
* | ‘'’ ‘[’ Type ‘]’
1431
- * | ‘$’ (id | ‘{’ Block ‘}’)
1462
+ * | ‘$’ ‘{’ Block ‘}’
1463
+ * | quoteId
1432
1464
* | SimpleExpr1 [`_']
1433
1465
* SimpleExpr1 ::= literal
1434
1466
* | xmlLiteral
@@ -1443,7 +1475,10 @@ object Parsers {
1443
1475
val t = in.token match {
1444
1476
case XMLSTART =>
1445
1477
xmlLiteral()
1446
- case IDENTIFIER | BACKQUOTED_IDENT | THIS | SUPER =>
1478
+ case IDENTIFIER =>
1479
+ if (isSplice) splice(isType = false )
1480
+ else path(thisOK = true )
1481
+ case BACKQUOTED_IDENT | THIS | SUPER =>
1447
1482
path(thisOK = true )
1448
1483
case USCORE =>
1449
1484
val start = in.skipToken()
@@ -1459,19 +1494,13 @@ object Parsers {
1459
1494
blockExpr()
1460
1495
case QUOTE =>
1461
1496
atSpan(in.skipToken()) {
1462
- Quote {
1463
- if (in.token == LBRACKET ) {
1464
- val saved = in.inQuote
1465
- in.inQuote = true
1466
- inBrackets {
1467
- try typ() finally in.inQuote = saved
1468
- }
1497
+ withinStaged(StageKind .Quoted ) {
1498
+ Quote {
1499
+ if (in.token == LBRACKET ) inBrackets(typ())
1500
+ else stagedBlock()
1469
1501
}
1470
- else stagedBlock(isQuote = true )
1471
1502
}
1472
1503
}
1473
- case SPLICE =>
1474
- splice(isType = false )
1475
1504
case NEW =>
1476
1505
canApply = false
1477
1506
newExpr()
0 commit comments