自从我询问并回答了 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
trait 的抽象类型成员:
type Elem
这意味着解析器的子类和子特征需要将类实例化为Elem
正在解析的输入元素的类型。例如,RegexParsers
并JavaTokenParsers
固定Elem
为等于Char
。
SoElem
被词法分析器使用,它负责将输入流分割成解析器想要处理的最小可能的标记。由于您要处理正则表达式,因此您的Elem
is Char
。
但别担心。仅仅因为你的词法分析器给了你Char
s 并不意味着你的解析器也被它们困住了。给你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
^