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?

3
  • 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? Commented Jul 1, 2016 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. Commented Jul 1, 2016 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. Commented Jul 1, 2016 at 15:03

1 Answer 1

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).

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.