开发者

Scala Parser, why doesn't "pat <~ pat ~> pat" work?

开发者 https://www.devze.com 2023-02-28 01:59 出处:网络
Trying out a simple parser combinator, I\'m running into compilation errors. I would like to parse -- \"Smith, Joe\" into its Name object like Name(Joe, Smith). Simple enough, I guess.

Trying out a simple parser combinator, I'm running into compilation errors.

I would like to parse -- "Smith, Joe" into its Name object like Name(Joe, Smith). Simple enough, I guess.

Here is the code related with that:

    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)
开发者_运维技巧

And I'm testing it via

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

Getting a compilation error:

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

What is that I am missing here?


In this line here:

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

you don't want to use both <~ and ~>. You're creating a parser that matches "," and firstName and keeps only ",", and then you're creating a parser that matches lastName and the previous parser and keeps only lastName.

You can replace it with this:

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

However, although this will compile and combine the way you want, it won't parse what you want it to. I got this output when I tried:

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

Schmo, Joe
^

stringLiteral expects something that looks like a string literal in code (something in quotation marks). (JavaTokenParsers is meant to parse stuff that looks like Java.) This works:

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

You should probably replace it with a regex that specifies what kind of strings you will accept for names. If you look at the documentation here, you'll see:

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

A parser that matches a regex string

So you can just put a Regex object there and it will be converted into a parser that matches it.


the ~> combinator ignores the left side and the <~ combinator ignores the right side. So the result of lastName <~ "," ~> firstName can never include the results of both firstName and lastName. Actually it is only the parse result of lastName because "," ~> firstName is ignored. You need to use sequential composition here:

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

Or if you want a prettier pattern match:

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


The code

lastName <~ "," ~> firstName

will end up throwing away the result of parsing firstName. Because of the operator precedence rules in Scala, the statement is parsed as if it were parenthesized like so:

lastName <~ ("," ~> firstName)

but even if it were grouped differently you are still only dealing with three parsers and throwing away the result of two of them.

So you end up with a String being passed into your mapping function, which is written to expect a ~[String, String] instead. That's why you get the compiler error you do.

One helpful technique for troubleshooting this sort of thing is to add ascriptions to subexpressions:

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

which can help you to determine where exactly reality and your expectations diverge.

0

精彩评论

暂无评论...
验证码 换一张
取 消