3

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

我想将“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)}

我在这里想念什么?

4

3 回答 3

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")

您可能应该用正则表达式替换它,该正则表达式指定您将接受什么样的字符串作为名称。如果您查看此处的文档,您将看到:

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

A parser that matches a regex string

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

于 2011-04-23T19:08:35.457 回答
7

~>组合器忽略左侧,组合<~器忽略右侧。所以 的结果lastName <~ "," ~> firstName永远不能包括 和 的firstName结果lastName。实际上它只是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)}
于 2011-04-23T18:56:19.227 回答
4

编码

lastName <~ "," ~> firstName

最终会丢弃解析的结果firstName。由于 Scala 中的运算符优先级规则,该语句被解析为好像它被括号括起来,如下所示:

lastName <~ ("," ~> firstName)

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

因此,您最终将 aString传递给您的映射函数,该函数被编写为期望 a ~[String, String]。这就是你得到编译器错误的原因。

解决此类问题的一种有用技术是向子表达式添加归属:

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

这可以帮助您确定现实和您的期望究竟在哪里分歧。

于 2011-04-23T19:00:52.100 回答