2013-08-27 4 views
1

Как часть макроса, я хочу манипулировать определениями частичной функции.Как сопоставить шаблон определения частичной функции в макросе Scala?

Для этого я использую Transformer манипулировать определения случае частичной функции и Traverser, чтобы проверить образцы определений случая:

def myMatchImpl[A: c.WeakTypeTag, B: c.WeakTypeTag](c: Context) 
    (expr: c.Expr[A])(patterns: c.Expr[PartialFunction[A, B]]): c.Expr[B] = { 
    import c.universe._ 

    val transformer = new Transformer { 
    override def transformCaseDefs(trees: List[CaseDef]) = trees map { 
     case caseDef @ CaseDef(pattern, guard , body) => { 
     // println(show(pattern)) 
     val traverser = new Traverser { 
      override def traverse(tree: Tree) = tree match { 
      // match against a specific pattern 
      } 
     } 
     traverser.traverse(pattern) 
     } 
    } 
    } 

    val transformedPartialFunction = transformer.transform(patterns.tree) 

    c.Expr[B](q"$transformedPartialFunction($expr)") 
} 

Теперь давайте предположим, интересные данные я хочу чтобы соответствовать против представлен класс данных (который является частью объекта примера):

case class Data(x: Int, y: String) 

При вызове Теперь макрокоманду на примере ниже

abstract class Foo 
case class Bar(data: Data) extends Foo 
case class Baz(string: String, data: Data) extends Foo 

def test(foo: Foo) = myMatch(foo){ 
    case Bar(Data(x,y)) => y 
    case Baz(_, Data(x,y)) => y 
}  

паттерны определений случае частичной функции преобразуются компилятором следующим образом (классы Foo, Bar и Baz являются членами объекта примера, тоже):

(data: Example.Data)Example.Bar((x: Int, y: String)Example.Data((x @ _), (y @ _))) 
(string: String, data: Example.Data)Example.Baz(_, (x: Int, y: String)Example.Data((x @ _), (y @ _))) 

Это результат печати лекал, как намекнули в макросе выше (с использованием шоу), сырые абстрактные синтаксические деревья (распечатаны с помощью showRaw) выглядит следующим образом:

Apply(TypeTree().setOriginal(Select(This(newTypeName("Example")), Example.Bar)), List(Apply(TypeTree().setOriginal(Select(This(newTypeName("Example")), Example.Data)), List(Bind(newTermName("x"), Ident(nme.WILDCARD)), Bind(newTermName("y"), Ident(nme.WILDCARD)))))) 

Apply(TypeTree().setOriginal(Select(This(newTypeName("Example")), Example.Baz)), List(Ident(nme.WILDCARD), Apply(TypeTree().setOriginal(Select(This(newTypeName("Example")), Example.Data)), List(Bind(newTermName("x"), Ident(nme.WILDCARD)), Bind(newTermName("y"), Ident(nme.WILDCARD)))))) 

Как написать шаблон-цитату, которая соответствует против них деревья?

+0

Попробуйте распечатать деревья с помощью 'showRaw', он покажет вам точную структуру дерева (а не только код-подобное представление). – ghik

+0

@ghik Я добавил вывод 'shawRaw' к вопросу. К сожалению, это тоже не очень помогает, так как я не знаю, как написать шаблон для такого дерева. Я соответствующим образом обновил этот вопрос. –

ответ

2

Прежде всего, существует специальный аромат quasiquotes специально для CaseDefs называется cq:

override def transformCaseDefs(trees: List[CaseDef]) = trees map { 
    case caseDef @ cq"$pattern if $guard => $body" => ... 
} 

Во-вторых, вы должны использовать pq деконструкции модели:

pattern match { 
    case pq"$name @ $nested" => ... 
    case pq"$extractor($arg1, $arg2: _*)" => ... 
    ... 
} 

Если вы заинтересованы в внутренности деревьев, которые используются для сопоставления с образцом, они создаются по patvarTransformer, определенным в TreeBuilder.scala

С другой стороны, если вы работаете с деревьями UnApply (которые производятся после проверки типов) У меня плохие новости для вас: квазикоторые в настоящее время не поддерживают их. Следуйте за SI-7789, чтобы получить уведомление, когда это исправлено.

+0

Спасибо за это намеки на квазивоты. Мне удалось создать желаемый шаблон «вручную» и поместить решение в ответ ниже. –

0

После того, как Ден Шабалин указал, что квазиквасоты не могут использоваться в этой конкретной настройке, мне удалось найти шаблон, который соответствует шаблонам определений случаев частичной функции.

Основная проблема заключается в том, что конструктор, который мы хотим сопоставить (в нашем примере Data), хранится в TypeTree узла Apply. Совпадение с деревом, завернутым в TypeTree, немного сложно, так как единственный экстрактор этого класса (TypeTree()) не очень полезен для этой конкретной задачи. Вместо этого мы должны выбрать завернутое дерево, используя original метод:

override def transform(tree: Tree) = tree match { 
    case Apply(constructor @ TypeTree(), args) => constructor.original match { 
    case Select(_, sym) if (sym == newTermName("Data")) => ... 
    } 
} 

В нашем случае использования обернутого дерево является Select узлом, и теперь мы можем проверить, является ли символ этого узла является один мы ищем для.

Смежные вопросы