Skip to content

Commit 02f9cf2

Browse files
committed
consolidate interpolator parsing
1 parent 606e37a commit 02f9cf2

File tree

1 file changed

+53
-77
lines changed

1 file changed

+53
-77
lines changed

compiler/src/dotty/tools/dotc/parsing/Scanners.scala

Lines changed: 53 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -1338,47 +1338,9 @@ object Scanners {
13381338
}
13391339
}
13401340
else if (isInterpolated && ch == '$') {
1341-
// Handle interpolation
1342-
def getInterpolatedIdentRest(hasSupplement: Boolean): Unit =
1343-
@tailrec def loopRest(): Unit =
1344-
if ch != SU && isUnicodeIdentifierPart(ch) then
1345-
putChar(ch) ; nextRawChar()
1346-
loopRest()
1347-
else if atSupplementary(ch, isUnicodeIdentifierPart) then
1348-
putChar(ch) ; nextRawChar()
1349-
putChar(ch) ; nextRawChar()
1350-
loopRest()
1351-
else
1352-
finishNamedToken(IDENTIFIER, target = next)
1353-
end loopRest
1354-
setStrVal()
1355-
token = STRINGPART
1356-
next.lastOffset = charOffset - 1
1357-
next.offset = charOffset - 1
1358-
putChar(ch) ; nextRawChar()
1359-
if hasSupplement then
1360-
putChar(ch) ; nextRawChar()
1361-
loopRest()
1362-
end getInterpolatedIdentRest
1363-
1364-
nextRawChar()
1365-
if (ch == '$' || ch == '\'') {
1366-
putChar(ch)
1367-
nextRawChar()
1341+
if (handleStringInterpolation('\'')) {
13681342
getDedentedStringPartWithDelimiter(quoteCount, isInterpolated)
13691343
}
1370-
else if (ch == '{') {
1371-
setStrVal()
1372-
token = STRINGPART
1373-
}
1374-
else if isUnicodeIdentifierStart(ch) || ch == '_' then
1375-
getInterpolatedIdentRest(hasSupplement = false)
1376-
else if atSupplementary(ch, isUnicodeIdentifierStart) then
1377-
getInterpolatedIdentRest(hasSupplement = true)
1378-
else
1379-
error("invalid string interpolation: `$$`, `$'`, `$`ident or `$`BlockExpr expected".toMessage, off = charOffset - 2)
1380-
putChar('$')
1381-
getDedentedStringPartWithDelimiter(quoteCount, isInterpolated)
13821344
}
13831345
else {
13841346
val isUnclosedLiteral = !isUnicodeEscape && ch == SU
@@ -1391,6 +1353,57 @@ object Scanners {
13911353
}
13921354
end getDedentedStringPartWithDelimiter
13931355

1356+
/** Handle `$` in string interpolations. This is shared between regular strings and dedented strings.
1357+
* @param escapeChar The character that can be escaped with `$` (either `"` or `'`)
1358+
* @return true if the caller should continue parsing the rest of the string, false otherwise
1359+
*/
1360+
private def handleStringInterpolation(escapeChar: Char): Boolean = {
1361+
def getInterpolatedIdentRest(hasSupplement: Boolean): Unit =
1362+
@tailrec def loopRest(): Unit =
1363+
if ch != SU && isUnicodeIdentifierPart(ch) then
1364+
putChar(ch) ; nextRawChar()
1365+
loopRest()
1366+
else if atSupplementary(ch, isUnicodeIdentifierPart) then
1367+
putChar(ch) ; nextRawChar()
1368+
putChar(ch) ; nextRawChar()
1369+
loopRest()
1370+
else
1371+
finishNamedToken(IDENTIFIER, target = next)
1372+
end loopRest
1373+
setStrVal()
1374+
token = STRINGPART
1375+
next.lastOffset = charOffset - 1
1376+
next.offset = charOffset - 1
1377+
putChar(ch) ; nextRawChar()
1378+
if hasSupplement then
1379+
putChar(ch) ; nextRawChar()
1380+
loopRest()
1381+
end getInterpolatedIdentRest
1382+
1383+
nextRawChar()
1384+
if (ch == '$' || ch == escapeChar) {
1385+
putChar(ch)
1386+
nextRawChar()
1387+
true // continue parsing
1388+
}
1389+
else if (ch == '{') {
1390+
setStrVal()
1391+
token = STRINGPART
1392+
false // don't continue, we're done with this string part
1393+
}
1394+
else if isUnicodeIdentifierStart(ch) || ch == '_' then
1395+
getInterpolatedIdentRest(hasSupplement = false)
1396+
false // don't continue, identifier rest handles it
1397+
else if atSupplementary(ch, isUnicodeIdentifierStart) then
1398+
getInterpolatedIdentRest(hasSupplement = true)
1399+
false // don't continue, identifier rest handles it
1400+
else
1401+
val escapeDesc = if escapeChar == '"' then "`$\"\"`, " else "`$'`, "
1402+
error(s"invalid string interpolation: `$$$$`, $escapeDesc`$$`ident or `$$`BlockExpr expected".toMessage, off = charOffset - 2)
1403+
putChar('$')
1404+
true // continue parsing after error
1405+
}
1406+
13941407
private def getRawStringLit(): Unit =
13951408
if (ch == '\"') {
13961409
nextRawChar()
@@ -1435,46 +1448,9 @@ object Scanners {
14351448
getStringPart(multiLine)
14361449
}
14371450
else if (ch == '$') {
1438-
def getInterpolatedIdentRest(hasSupplement: Boolean): Unit =
1439-
@tailrec def loopRest(): Unit =
1440-
if ch != SU && isUnicodeIdentifierPart(ch) then
1441-
putChar(ch) ; nextRawChar()
1442-
loopRest()
1443-
else if atSupplementary(ch, isUnicodeIdentifierPart) then
1444-
putChar(ch) ; nextRawChar()
1445-
putChar(ch) ; nextRawChar()
1446-
loopRest()
1447-
else
1448-
finishNamedToken(IDENTIFIER, target = next)
1449-
end loopRest
1450-
setStrVal()
1451-
token = STRINGPART
1452-
next.lastOffset = charOffset - 1
1453-
next.offset = charOffset - 1
1454-
putChar(ch) ; nextRawChar()
1455-
if hasSupplement then
1456-
putChar(ch) ; nextRawChar()
1457-
loopRest()
1458-
end getInterpolatedIdentRest
1459-
1460-
nextRawChar()
1461-
if (ch == '$' || ch == '"') {
1462-
putChar(ch)
1463-
nextRawChar()
1451+
if (handleStringInterpolation('"')) {
14641452
getStringPart(multiLine)
14651453
}
1466-
else if (ch == '{') {
1467-
setStrVal()
1468-
token = STRINGPART
1469-
}
1470-
else if isUnicodeIdentifierStart(ch) || ch == '_' then
1471-
getInterpolatedIdentRest(hasSupplement = false)
1472-
else if atSupplementary(ch, isUnicodeIdentifierStart) then
1473-
getInterpolatedIdentRest(hasSupplement = true)
1474-
else
1475-
error("invalid string interpolation: `$$`, `$\"`, `$`ident or `$`BlockExpr expected".toMessage, off = charOffset - 2)
1476-
putChar('$')
1477-
getStringPart(multiLine)
14781454
}
14791455
else {
14801456
val isUnclosedLiteral = !isUnicodeEscape && (ch == SU || (!multiLine && (ch == CR || ch == LF)))

0 commit comments

Comments
 (0)