如何将解析器与不同类型的Elem结合起来

时间:2013-03-17 07:52:54

标签: scala types parser-combinators

我正在尝试创建一个结合了Regex解析器和我自己的自定义解析器的解析器。我查看了Scala: How to combine parser combinators from different objects,但该问题和答案涉及具有相同类型Elem的解析器。

假设我有一些RegexParsers,还有一个解析字符串的解析器:

trait NumbersParsers extends RegexParsers {
  def number = """\d+""".r
}

trait LookupParsers extends Parsers {
  type Elem = String
  def word = elem("word", (potential:String) => dictionary.exists(_.equals(x))
}

如果我天真地结合这些解析器

object MyParser extends NumbersParsers with RegexParsers {
  def quantitive = number ~ word
}

由于Elem的类型不同,我显然会遇到类型错误。如何组合这些解析器?

1 个答案:

答案 0 :(得分:4)

自从我问及回答Scala: How to combine parser combinators from different objects后,我觉得有点负责回答这个问题。

答案很简单,你不能组合不同类型的Elem。解决此问题的另一种优雅方法是使用^?通过额外过滤来扩充正则表达式解析器。

阅读Scala编程中的Combinator Parsing可能会有所帮助:

  

分析器输入

     

有时,解析器会读取令牌流而不是原始字符序列。然后使用单独的词法分析器将原始字符流转换为令牌流。解析器输入的类型定义如下:

type Input = Reader[Elem]   
     

类Reader来自包scala.util.parsing.input。它类似于Stream,但也跟踪它读取的所有元素的位置。类型Elem表示各个输入元素。它是Parsers特征的抽象类型成员:

type Elem
     

这意味着Parsers的子类和子项需要将类Elem实例化为正在解析的输入元素的类型。例如,RegexParsersJavaTokenParsers修正Elem等于Char

因此词法分析器使用Elem,它负责将输入流切换为解析器想要处理的最小可能标记。由于您要处理正则表达式,因此ElemChar

但别担心。仅仅因为你的词法分析器给你Char并不意味着你的解析器也被它们困住了。 RegexParsers给出的是从正则表达式到Parser[String]的隐式转换器。您可以使用^^运算符(完全映射输入)和^?运算符(部分映射输入)进一步转换它们。

让我们将它们合并到您的解析器中:

import scala.util.parsing.combinator._

scala> val dictionary = Map("Foo" -> "x")
dictionary: scala.collection.immutable.Map[String,String] = Map(Foo -> x)

scala> trait NumbersParsers extends RegexParsers {
     |   def number: Parser[Int] = """\d+""".r ^^ { _.toInt }
     | }
defined trait NumbersParsers

scala> trait LookupParsers extends RegexParsers {
     |   def token: Parser[String] = """\w+""".r
     |   def word =
     |     token ^? ({
     |       case x if dictionary.contains(x) => x
     |     }, {
     |       case s => s + " is not found in the dictionary!"
     |     })
     | }
defined trait LookupParsers

scala> object MyParser extends NumbersParsers with LookupParsers {
     |   def quantitive = number ~ word
     |   
     |   def main(args: Array[String]) {
     |     println(parseAll(quantitive, args(0) ))
     |   }
     | }
defined module MyParser

scala> MyParser.main(Array("1 Foo"))
[1.6] parsed: (1~Foo)

scala> MyParser.main(Array("Foo"))
[1.1] failure: string matching regex `\d+' expected but `F' found

Foo
^

scala> MyParser.main(Array("2 Bar"))
[1.6] failure: Bar is not found in the dictionary!

2 Bar
     ^