我正在尝试创建一个结合了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
的类型不同,我显然会遇到类型错误。如何组合这些解析器?
答案 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
实例化为正在解析的输入元素的类型。例如,RegexParsers
和JavaTokenParsers
修正Elem
等于Char
。
因此词法分析器使用Elem
,它负责将输入流切换为解析器想要处理的最小可能标记。由于您要处理正则表达式,因此Elem
为Char
。
但别担心。仅仅因为你的词法分析器给你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
^