объединить `Tree` и `List[Tree]` в отражении времени компиляции scala

У меня есть макрос scala, который создает конструктор для класса на лету.
Так, например, если у нас есть класс case class PersonConfig(name: String, age: Int, isFemale: Boolean). У меня есть древовидная структура для имени класса и аргументы, переданные классу, как показано ниже.

@ val className = q"PersonConfig"
className: Ident = Ident(PersonConfig)

@ val args = List(q""""Jyn Erso"""", q"26", q"true")
args: List[Literal] = List(Literal(Constant("Jyn Erso")), Literal(Constant(26)), Literal(Constant(true)))

Теперь, чтобы создать структуру AST, которая создала бы экземпляр PersonConfig (т.е. PersonConfig("Jyn Erso", 26, true)) мне нужно будет объединить значения className и args. Проблема здесь в том, что args может быть любого размера, поскольку этот макрос можно использовать для создания конструкторов для многих разных классов.

в настоящее время очевидным, но менее СУХИМ и подробным решением является сопоставление шаблона для параметра args и создание структуры AST, как показано ниже.

import scala.reflect.runtime.universe
def makeExpr(className: universe.Tree, args: List[universe.Tree]): universe.Tree = {
  args.reverse match {
    case node1 :: Nil => q"$className($node1)"
    case arg1 :: arg2 :: Nil => q"$className($arg1, $arg2)"
    case arg1 :: arg2 :: arg3 :: Nil => q"$className($arg1, $arg2, $arg3)"
    case arg1 :: arg2 :: arg3 :: arg4 :: Nil => q"$className($arg1, $arg2, $arg3, $arg4)"
    case arg1 :: arg2 :: arg3 :: arg4 :: arg5 :: Nil => q"$className($arg1, $arg2, $arg3, $arg4, $arg5)"
    case Nil => throw new Exception(s"argument list for class ${className.toString} cannot be empty")
    case _ => throw new Exception(s"argument list for class ${className.toString} is too long")
  }

}

Но есть ли лучший способ справиться с этим эффективно и что более СУХОЕ? например, используя foldLeft или другой эквивалентный метод для достижения того, что делает функция makeExpr?


person rogue-one    schedule 16.07.2018    source источник


Ответы (1)


Мне удалось сделать это с помощью foldLeft, как показано ниже.

  def makeExpr(c: blackbox.Context)(className: c.Tree, args: List[c.Tree]): c.universe.Tree = {
    import c.universe._
    args.reverse match {
      case head :: tail => tail.foldLeft(q"$className($head)")({
       case (q"$_(..$params)", node) => q"$className(..${params :+ node})"          })
      case Nil => throw new MacroException(s"argument list for class ${className.toString} cannot be empty")
    }
  }
person rogue-one    schedule 16.07.2018