Scala Tree Case

I am new to Scala and very new to writing macros and need a little help / advice. I have the following code ...

trait ValidationRule case class Required() extends ValidationRule case class HasLength(l: Int) extends ValidationRule case class Person(name: String) myMacro[Person] { p => p.name.is(Required(), HasLength(255)) } 

Obviously there is missing code here, but it's just pseudo to get the question.

So, for a tree representing p => p.name.is(Required(), HasLength(255)) , I'm trying to write match/case to select all the expressions representing the ValidationRule . Sort of:

 case TypeApply(Select(_, .... 

Can anyone suggest a better match case to be able to retrieve a list of trees that represent each "all" ValidationRules from the "is" method?

+5
source share
1 answer

You should definitely see Quasiquotes .

Quasiquadrats are used to perform two actions: to create trees and to compare with tree patterns. They allow you to express the tree you want to work with in terms of Scala equivalent code. You allow the quasiquaile library to work with how the Scala code maps to the tree graph, and that's good!

You can play with them in REPL, although the results may vary slightly in the macro universe:

 scala> import scala.reflect.runtime.universe._ scala> showRaw(cq"p => p.name.is(Required(), HasLength(255))") res0: String = CaseDef( Bind( TermName("p"), Ident(termNames.WILDCARD)), EmptyTree, Apply( Select( Select( Ident(TermName("p")), TermName("name")), TermName("is")), List( Apply( Ident(TermName("Required")), List()), Apply( Ident(TermName("HasLength")), List(Literal(Constant(255))))))) 

Another thing you can do with Quasiquotes is to actually use them for pattern matching.

 scala> val fromTree = cq"p => p.name.is(Required(), HasLength(255))" scala> val cq"p => p.name.is($x, $y)" = fromTree x: reflect.runtime.universe.Tree = Required() y: reflect.runtime.universe.Tree = HasLength(255) 

Now you have to be careful, because this template ONLY matches if the user named his template variable p .

 scala> val fromTree = cq"x => x.name.is(Required(), HasLength(255))" scala> val cq"p => p.name.is($x, $y)" = fromTree scala.MatchError: case (x @ _) => x.name.is(Required(), HasLength(255)) (of class scala.reflect.internal.Trees$CaseDef) ... 33 elided 

Instead, you'll want to be more general:

 scala> val cq"${p1:TermName} => ${p2:TermName}.name.is($x, $y)" = fromTree p1: reflect.runtime.universe.TermName = x p2: reflect.runtime.universe.TermName = x x: reflect.runtime.universe.Tree = Required() y: reflect.runtime.universe.Tree = HasLength(255) scala> p1 == p2 res2: Boolean = true 

And of course, if you did this as part of the pattern matching, you could do:

 case cq"${p1:TermName} => ${p2:TermName}.name.is($x, $y)" if p1 == p2 => ??? 

Keep in mind that Macros are a deep dark hole. If you are just starting out, expect to spend a lot of time getting your macro right. After this, we expect to spend a lot of time on edge cases.

+2
source

All Articles