Skip to content

Commit b1ce477

Browse files
Merge pull request #5532 from dotty-staging/add-f-interpolator-proto
Add minimal prototype for f interpolator macro
2 parents f0fbf4b + b2b7aad commit b1ce477

File tree

2 files changed

+68
-0
lines changed

2 files changed

+68
-0
lines changed
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
import scala.quoted._
2+
import scala.tasty.Reflection
3+
4+
import scala.language.implicitConversions
5+
6+
object FQuote {
7+
8+
implicit class SCOps(ctx: StringContext) {
9+
inline def ff(args: => Any*): String = ~impl('(this), '(args))
10+
}
11+
12+
/*private*/ def impl(receiver: Expr[SCOps], args: Expr[Seq[Any]])(implicit reflect: Reflection): Expr[String] = {
13+
import reflect._
14+
15+
def liftListOfAny(lst: List[Term]): Expr[List[Any]] = lst match {
16+
case x :: xs =>
17+
val head = x.seal[Any]
18+
val tail = liftListOfAny(xs)
19+
'{ ~head :: ~tail }
20+
case Nil => '(Nil)
21+
}
22+
23+
def isStringConstant(tree: Term) = tree match {
24+
case Term.Literal(_) => true
25+
case _ => false
26+
}
27+
28+
def isSCOpsConversion(tree: Term) =
29+
tree.symbol.fullName == "FQuote$.SCOps"
30+
31+
def isStringContextApply(tree: Term) =
32+
tree.symbol.fullName == "scala.StringContext$.apply"
33+
34+
// FQuote.SCOps(StringContext.apply([p0, ...]: String*)
35+
val parts = receiver.unseal.underlyingArgument match {
36+
case Term.Apply(conv, List(Term.Apply(fun, List(Term.Typed(Term.Repeated(values), _)))))
37+
if isSCOpsConversion(conv) &&
38+
isStringContextApply(fun) &&
39+
values.forall(isStringConstant) =>
40+
values.collect { case Term.Literal(Constant.String(value)) => value }
41+
case tree =>
42+
throw new QuoteError(s"String literal expected, but ${tree.show} found")
43+
}
44+
45+
// [a0, ...]: Any*
46+
val Term.Typed(Term.Repeated(allArgs), _) = args.unseal.underlyingArgument
47+
48+
for ((arg, part) <- allArgs.zip(parts.tail)) {
49+
if (part.startsWith("%d") && !(arg.tpe <:< definitions.IntType)) {
50+
return '(s"`${~arg.showCode.toExpr}` is not of type Int")
51+
}
52+
53+
}
54+
55+
val string = parts.mkString("")
56+
'{ new collection.immutable.StringOps(~string.toExpr).format(~args: _*) }
57+
}
58+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import FQuote._
2+
3+
object Test {
4+
def main(args: Array[String]): Unit = {
5+
val one: Int = 1
6+
assert(ff"Hello $one%d!" == "Hello 1!")
7+
val world: String = "world"
8+
assert(ff"Hello $world%d!" == "`world` is not of type Int")
9+
}
10+
}

0 commit comments

Comments
 (0)