Scala Parser,为什么不“轻拍< ~pat~> pat”工作?

时间:2011-04-23 15:54:17

标签: scala parser-combinators

试用一个简单的解析器组合器,我遇到了编译错误。

我想解析 - “Smith,Joe”进入Name对象,如Name(Joe,Smith)。很简单,我想。

以下是与此相关的代码:

    import util.parsing.combinator._

    class NameParser extends JavaTokenParsers {
      lazy val name: Parser[Name] = 
        lastName <~ "," ~> firstName ^^ {case (l ~ f) => Name(f, l)}
      lazy val lastName = stringLiteral
      lazy val firstName = stringLiteral
    }

    case class Name(firstName:String, lastName: String)

我正在通过

进行测试
object NameParserTest {
  def main(args: Array[String]) {
    val parser = new NameParser()
    println(parser.parseAll(parser.name, "Schmo, Joe"))
  }
}

获取编译错误:

error: constructor cannot be instantiated to expected type;
found   : NameParser.this.~[a,b]
required: java.lang.String
lazy val name: Parser[Name] = lastName <~ "," ~> firstName ^^ {case (l ~ f) => Name(f, l)}

我在这里失踪的是什么?

3 个答案:

答案 0 :(得分:8)

在这一行:

  lazy val name: Parser[Name] = 
    lastName <~ "," ~> firstName ^^ {case (l ~ f) => Name(f, l)}

您不想同时使用<~~>。您正在创建一个与","firstName匹配且仅保留","的解析器,然后您创建一个与lastName和先前解析器匹配的解析器,并仅保留lastName

您可以将其替换为:

(lastName <~ ",") ~ firstName ^^ {case (l ~ f) => Name(f, l)}

但是,虽然这会按照您想要的方式进行编译和组合,但它不会解析您想要的内容。我尝试时得到了这个输出:

[1.1] failure: string matching regex `"([^"\p{Cntrl}\\]|\\[\\/bfnrt]|\\u[a-fA-F0-9]{4})*"' expected but `S' found

Schmo, Joe
^

stringLiteral期望在代码中看起来像字符串文字(引号中的某些内容)。 (JavaTokenParsers旨在解析看起来像Java的东西。)这有效:

scala> val x = new NameParser
x: NameParser = NameParser@1ea8dbd

scala> x.parseAll(x.name, "\"Schmo\", \"Joe\"")
res0: x.ParseResult[Name] = [1.15] parsed: Name("Joe","Schmo")

您应该用正则表达式替换它,该正则表达式指定您将接受名称的字符串类型。如果您查看文档here,您会看到:

implicit def regex (r: Regex) : Parser[String]

A parser that matches a regex string

所以你可以在那里放一个Regex对象,它将被转换成一个匹配它的解析器。

答案 1 :(得分:7)

~>组合子忽略左侧,<~组合子忽略右侧。因此,lastName <~ "," ~> firstName的结果永远不会包含firstNamelastName的结果。实际上它只是lastName的解析结果,因为"," ~> firstName被忽略了。您需要在此处使用顺序合成:

lazy val name: Parser[Name] =         
  lastName ~ "," ~ firstName ^^ {case (l ~_~ f) => Name(f, l)}

或者如果你想要更漂亮的模式匹配:

lazy val name: Parser[Name] =         
  lastName ~ ("," ~> firstName) ^^ {case (l ~ f) => Name(f, l)}

答案 2 :(得分:4)

代码

lastName <~ "," ~> firstName

最终将丢弃解析firstName的结果。由于Scala中的运算符优先级规则,语句被解析为好像它是括号一样:

lastName <~ ("," ~> firstName)

但即使它被分组不同,你仍然只处理三个解析器并丢弃其中两个解析器的结果。

所以你最终会将一个String传递给你的映射函数,这个函数被编写为期待~[String, String]。这就是你遇到编译错误的原因。

对此类问题进行故障排除的一种有用技巧是向子表达式添加归属:

lazy val name: Parser[Name] =
  ((lastName <~ "," ~> firstName): Parser[String ~ String]) ^^ { case l ~ f => Name(f, l) }

可以帮助您确定现实和您期望的确切位置。