@@ -21,6 +21,7 @@ import util.Spans._
2121import Constants ._
2222import ScriptParsers ._
2323import Decorators ._
24+ import scala .tasty .util .Chars .isIdentifierStart
2425import scala .annotation .{tailrec , switch }
2526import rewrites .Rewrites .patch
2627
@@ -48,6 +49,13 @@ object Parsers {
4849 val Class, Type, TypeParam, Def : Value = Value
4950 }
5051
52+ type StageKind = Int
53+ object StageKind {
54+ val None = 0
55+ val Quoted = 1
56+ val Spliced = 2
57+ }
58+
5159 private implicit class AddDeco (val buf : ListBuffer [Tree ]) extends AnyVal {
5260 def +++= (x : Tree ) = x match {
5361 case x : Thicket => buf ++= x.trees
@@ -199,6 +207,16 @@ object Parsers {
199207 def isStatSep : Boolean =
200208 in.token == NEWLINE || in.token == NEWLINES || in.token == SEMI
201209
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+
202220/* ------------- ERROR HANDLING ------------------------------------------- */
203221
204222 /** The offset of the last time when a statement on a new line was definitely
@@ -354,6 +372,14 @@ object Parsers {
354372 finally inEnum = saved
355373 }
356374
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+
357383 def migrationWarningOrError (msg : String , offset : Int = in.offset): Unit =
358384 if (in.isScala2Mode)
359385 ctx.migrationWarning(msg, source.atSpan(Span (offset)))
@@ -690,14 +716,18 @@ object Parsers {
690716 }
691717 val isNegated = negOffset < in.offset
692718 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) }
699730 }
700- atSpan(in.skipToken()) { SymbolLit (in.strVal) }
701731 }
702732 else if (in.token == INTERPOLATIONID ) interpolatedString(inPattern)
703733 else finish(in.token match {
@@ -919,26 +949,27 @@ object Parsers {
919949 else t
920950
921951 /** 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
933956 }
934- }
935957
936- /** SimpleEpxr ::= ‘$’ (id | ‘{’ Block ‘}’)
937- * SimpleType ::= ‘$’ (id | ‘{’ Block ‘}’)
958+ /** SimpleEpxr ::= spliceId | ‘$’ ‘{’ Block ‘}’)
959+ * SimpleType ::= spliceId | ‘$’ ‘{’ Block ‘}’)
938960 */
939961 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+ }
942973 if (isType) TypSplice (expr) else Splice (expr)
943974 }
944975
@@ -950,7 +981,7 @@ object Parsers {
950981 * | `_' TypeBounds
951982 * | Refinement
952983 * | Literal
953- * | ‘$’ (id | ‘{’ Block ‘}’)
984+ * | ‘$’ ‘{’ Block ‘}’
954985 */
955986 def simpleType (): Tree = simpleTypeRest {
956987 if (in.token == LPAREN )
@@ -964,7 +995,7 @@ object Parsers {
964995 val start = in.skipToken()
965996 typeBounds().withSpan(Span (start, in.lastOffset, start))
966997 }
967- else if (in.token == SPLICE )
998+ else if (isSplice )
968999 splice(isType = true )
9691000 else path(thisOK = false , handleSingletonType) match {
9701001 case r @ SingletonTypeTree (_) => r
@@ -1426,9 +1457,10 @@ object Parsers {
14261457
14271458 /** SimpleExpr ::= ‘new’ (ConstrApp [TemplateBody] | TemplateBody)
14281459 * | BlockExpr
1429- * | ‘'’ (id | ‘{’ Block ‘}’)
1460+ * | ‘'’ ‘{’ Block ‘}’
14301461 * | ‘'’ ‘[’ Type ‘]’
1431- * | ‘$’ (id | ‘{’ Block ‘}’)
1462+ * | ‘$’ ‘{’ Block ‘}’
1463+ * | quoteId
14321464 * | SimpleExpr1 [`_']
14331465 * SimpleExpr1 ::= literal
14341466 * | xmlLiteral
@@ -1443,7 +1475,10 @@ object Parsers {
14431475 val t = in.token match {
14441476 case XMLSTART =>
14451477 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 =>
14471482 path(thisOK = true )
14481483 case USCORE =>
14491484 val start = in.skipToken()
@@ -1459,19 +1494,13 @@ object Parsers {
14591494 blockExpr()
14601495 case QUOTE =>
14611496 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()
14691501 }
1470- else stagedBlock(isQuote = true )
14711502 }
14721503 }
1473- case SPLICE =>
1474- splice(isType = false )
14751504 case NEW =>
14761505 canApply = false
14771506 newExpr()
0 commit comments