简体   繁体   English

在Scala宏中匹配XML文字

[英]Matching XML Literals in Scala Macros

I want to transform Scala XML literals with a macro. 我想用一个宏转换Scala XML文字。 (Not a string literal with XML but actual XML literals). (不是带XML的字符串文字,而是实际的XML文字)。 As far as I understand, XML literals are not actually built into the language on the AST level but are desugared in the parser. 据我了解,XML文字实际上并没有内置在AST级别的语言中,而是在解析器中使用了。 Interestingly though, this does work: 有趣的是,这确实有效:

case q"<specificTag></specificTag>" => ... // succeeds for specificTag with no
                                           // attributes and children

But obviously, this is totally useless because it is impossible to match arbitrary xml that way. 但是显然,这是完全没有用的,因为不可能以这种方式匹配任意xml。 Something like 就像是

case q"<$prefix:$label ..$attrs>$children</$prefix:$label>" => ...

can not work because we would have to bind the same variable twice in a pattern. 无法工作,因为我们必须在一个模式中两次绑定相同的变量。

Printing out the tree of such an xml literal expression actually gives the desugared version. 打印出这样的xml文字表达式的树实际上给出了已废止的版本。 For example. 例如。

new _root_.scala.xml.Elem(null,"specificTag",_root_.scala.xml.Null,$scope,false)

But trying to match this fails: 但是尝试匹配此失败:

case q"new _root_.scala.xml.Elem(..$params)" => ... // never succeeds

I am confused! 我很困惑! My question is: Is there a way to reliably match arbitrary xml litarals in scala macros? 我的问题是:有没有办法可靠地在scala宏中匹配任意xml litarals? Additionally: Why are they supported in quasiquotes for constant xml and not for the desugared value after all? 另外:为什么为什么在准报价中为常量xml支持它们,而对于最终删除的值却不支持呢?

The xml is wrapped in blocks, macro invoked as rename( <top><bottom>hello</bottom></top> ) . xml封装在块中,宏作为rename( <top><bottom>hello</bottom></top> )调用。 I noticed that by looking at the incoming tree, not what's constructed by quasiquotes. 我注意到通过查看传入的树,而不是由准引用构造的树。

I had lodged this issue when I looked at your question previously; 我以前看您的问题时曾提出过这个问题 I don't know whether my SO is that; 我不知道我那是不是。 I tried bumping SS in sbt. 我尝试在sbt中提高SS There's another SO issue that's probably unrelated. 还有另一个SO问题可能与之无关。

  class Normalizer(val c: Context) {
    import c.universe._ 
    def impl(e: c.Tree) = e match {
      case Block(List(), Block(List(), x)) => x match {
        case q"new scala.xml.Elem($prefix, $label, $attrs, $scope, $min, $t)" =>
          Console println s"Childed tree is ${showRaw(e)}" 
          val b = t match {
            case Typed(b, z) => c.untypecheck(b.duplicate)
            case _           => EmptyTree
          } 
          val Literal(Constant(tag: String)) = label
          val x = c.eval(c.Expr[NodeBuffer](b))
          //q"""<${tag.reverse}>..$x</${tag.reverse}>"""  // SO
          e
        case q"new scala.xml.Elem($prefix, $label, $attrs, $scope, $min)" =>
          Console println s"Childless tree is ${showRaw(e)}" ; e
        case _ => Console println s"Tree is ${showRaw(e)}" ; e
      }
      case _ => Console println s"Nonblock is ${showRaw(e)}" ; e
    }
  }

Unfortunately quasiquotes don't natively support matching of xml literals and until today the only way to do it was to match on desugared tree as demonstrated by @som-snytt . 不幸的是,准引用本身并不支持xml文字的匹配,直到今天,唯一的方法是在@ som-snytt演示的脱枝树上进行匹配。 But it's very easy to get it wrong and such manipulations may require so many AST nodes that they will blow up the pattern matcher . 但是很容易弄错它,这种操作可能需要太多的AST节点,以至于它们会破坏模式匹配器

To address this weakness we've just released a first milestone of scalamacros/xml , a library that turns this problem around: instead of working with AST of XML it lets you work with pure XML nodes instead: 为了解决这个弱点,我们刚刚发布了scalamacros / xml的第一个里程碑,该库可以解决此问题:与使用XML的AST相比,它不使用XML的AST,而是让您使用纯XML节点:

scala> val q"${elem: xml.Elem}" = q"<foo><bar/></foo>"
elem: scala.xml.Elem = <foo><bar/></foo>

Here we use unlifting to convert code to value and than we can just process it as xml. 在这里,我们使用重载将代码转换为值,然后我们可以将其作为xml处理。 In the end after processing you will probably want to convert it back to AST through lifting ): 最后,在处理之后,您可能需要通过提升 )将其转换回AST:

scala> q"$elem"
res4: org.scalamacros.xml.RuntimeLiftables.__universe.Tree =
new _root_.scala.xml.Elem(null, "foo", _root_.scala.xml.Null, $scope, false, ({
  val $buf = new _root_.scala.xml.NodeBuffer();
  $buf.$amp$plus(new _root_.scala.xml.Elem(null, "bar", _root_.scala.xml.Null, $scope, true));
  $buf
}: _*))

In case your original AST case some code snippets they will be converted to special Unquote node that contains such snippets: 如果您的原始AST案例有一些代码片段,它们将被转换为包含此类片段的特殊Unquote节点:

scala> val q"${elem: xml.Elem}" = q"<foo>{x + y}</foo>"
elem: scala.xml.Elem = <foo>{x.+(y)}</foo>

scala> val <foo>{Unquote(q"x + y")}</foo> = elem
// matches 

It's also easy to filter all unquote nodes through projection: 通过投影过滤所有未引用的节点也很容易:

scala> elem \ "#UNQUOTE"
res6: scala.xml.NodeSeq = NodeSeq({x.+(y)})

You might also be interested in checking out example sbt project with simple macro that uses this library or have a deeper look at our test suite . 您可能还想通过使用此库的简单宏签出示例sbt项目 ,或者更深入地了解我们的测试套件

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM