Question

Is it possible to store a Scala XML Pattern in a variable? Or at least patterns which search for a particular subtree structure with wildcards? I'd like to load patterns at runtime, e.g.,

import scala.xml._

// Is there something like XMLPattern?
def matchMyPattern(xml: Elem, p: XMLPattern): Option[Seq[Node]] = {
  xml match {
    case p(save, throwAway) => Some(save)
    case _ => None
  }
}

val patternsFromFile = Array("<a><b>{ save @ _* }</b>{ throwAway @ _  *}</a>", 
                             "<a>{ throwAway @ _* }<c>{ save @ _* }</c></a>")

val xml = <a><b>x</b><c>y</c></a>

for(pString <- patternsFromFile) {
  val p = new XMLPattern(pString)
  val extractedSeqNode = matchMyPattern(xml, p)
  ...
}

I am looking for a way that takes advantage of Scala's great pattern matching abilities. In particular, I would like to avoid:

  1. Writing my own string parsing and subtree search algorithm.

  2. Using a runtime interpreter along the lines of an Eval(myCode: String).

If it's not possible to access the Scala compiler's internal XML Pattern logic, can the pattern matching be simulated using something like parser combinators for trees?

Was it helpful?

Solution

It would be best to avoid loading code in at runtime.

Here is an example solution using Scala's 2.11's JSR-223 support:

scala> import javax.script._
import javax.script._

scala> val e = new ScriptEngineManager().getEngineByName("scala");
e: javax.script.ScriptEngine = scala.tools.nsc.interpreter.IMain@26969ee5

scala> val pf = e.eval("""{case <red>{scala.xml.Text(txt)}</red> => txt }: PartialFunction[scala.xml.Elem, String]""").asInstanceOf[PartialFunction[scala.xml.Elem, String]]
pf: PartialFunction[scala.xml.Elem,String] = <function1>

scala> pf(<yellow/>)
scala.MatchError: <yellow/> (of class scala.xml.Elem)
  at scala.PartialFunction$$anon$1.apply(PartialFunction.scala:248)
    at scala.PartialFunction$$anon$1.apply(PartialFunction.scala:246)
    at $anonfun$1.applyOrElse(<console>:8)
    at $anonfun$1.applyOrElse(<console>:8)
    at scala.runtime.AbstractPartialFunction.apply(AbstractPartialFunction.scala:36)
    ... 32 elided

scala> pf(<red>RED</red>)
: String = RED

You can convert the pf into an extractor if you like. Relevant reading:

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top