4

This is a simplified version of the problem I am facing but the underlying issue remains. After calling a macro, I want to generate case classes dynamically. I am able to retrieve parameters from macro call etc. The issue I am having is trying to use a string variable within quasiquotes. I essentially want to have the following:

def expand_impl(c: Context)(annottees: c.Expr[Any]*): c.Expr[Any] = {
    import c.universe._

    val toGen = "case class Foo()"

    val toReturn = c.Expr[Any](
        q"$toGen"
    )
    toReturn
}

However, the case class is not generated. Now I know that if I change toGen to q"case class Foo()" it will work, however toGen is a string I will generate after some other processing which returns a string, so I can't do that. Compiling it like this and manually looking at the the value of toReturn I get the following:

Expr[Any]("case class Foo()")

The string toGen is simply pasted in, along the quotes, meaning the case class is not generated.

I have looked for similar issues but can't find this example anywhere. How can I unquote the double quotes of a string variable within quasiquotes?

brioche
  • 43
  • 4
  • If you want to use quasiquotes, you have to use them for every nested expression, otherwise you'll just lift string expressions. Why is it not possible to use quasiquotes when constructing the case class declaration? – devkat Jul 01 '16 at 14:10
  • The idea is to have something like this: `val toGen = someMethod()`. The string that is returned by `someMethod()` will be something like "case class Foo()". If understand you correctly you are suggesting I should use quasiquotes when returning the case class? However this is where my problem lies, because `someMethod()`, will not always return the same case class, it dynamically generates them based on the input etc. and returns a string. – brioche Jul 01 '16 at 14:57
  • The problem is that quasiquotes don't parse strings, so whenever the expression you want to lift is expressed as a string, you can't use them. In this case you have to parse the string, like Régis Jean-Gilles demonstrates in his answer. – devkat Jul 01 '16 at 15:03

1 Answers1

3

There is a parse method defined on Context. It returns a Tree, and because trees can be interpolated in quasiquotes, you can very easily mix and match parsing with quasiquoting. By example:

scala> :paste
// Entering paste mode (ctrl-D to finish)

import scala.reflect.macros.whitebox.Context
import scala.language.experimental.macros

def test_impl(c: Context)(): c.Tree = {
  import c.universe._
  val tree = c.parse("""println(2)""")
  q"println(1); $tree; println(3)"
}
def test(): Unit = macro test_impl

// Exiting paste mode, now interpreting.

import scala.reflect.macros.whitebox.Context
import scala.language.experimental.macros
test_impl: (c: scala.reflect.macros.whitebox.Context)()c.Tree
defined term macro test: ()Unit

scala> test()
1
2
3

In this example I defined a def macro, but it should work just as well with macro annotations (as in your case).

Régis Jean-Gilles
  • 32,541
  • 5
  • 83
  • 97