How to pattern match on Scala's parser combinator result
Asked Answered
B

3

5

We have a multithreaded RPC server that parses input strings. We've run into an issue where Scala's parser combinator library is not multithreaded safe: the var lastNoSuccess in Parsers.scala is used by any parsing. We get a NullPointerException in this line

if (!(lastNoSuccess != null && next.pos < lastNoSuccess.next.pos))

The default way to implement the parser by making an object that extends one of the Parsers, but I want to construct a parser on demand so each has its own internal state, so I'm using a class instead of an object. However, I can't get it to compile since I need to pattern match on the result:

import scala.util.parsing.combinator.RegexParsers

class SqlParserImpl
  extends RegexParsers
{
  val term: Parser[String] = """(?i)term\b""".r
}

object Test
{
  def main(args: Array[String]): Unit =
  {
    val parser = new SqlParserImpl
    parser.parseAll(parser.term, "term") match {
      // How do I match?
      case SqlParserImpl#Success(result, _) => true
      case SqlParserImpl#NoSuccess => false
    }
  }
}

Fails with

t.scala:16: error: '=>' expected but '#' found.
          case SqlParserImpl#Success(result, _) => true
                            ^
t.scala:17: error: '=>' expected but '#' found.
          case SqlParserImpl#NoSuccess => false
                            ^
two errors found
Brougham answered 6/5, 2011 at 3:22 Comment(0)
B
11

Use this:

val parser = new SqlParserImpl
parser.parseAll(parser.term, "term") match {
  case parser.Success(result, _) => true
  case parser.NoSuccess(_, _) => false
}

The # sign is used to designate a type member. In your case it's using a constructor or an extractor pattern which needs to reference to an object or something that looks like a constructor.

Hmm. I don't have a 2.7 handy. Try this:

parser.parseAll(parser.term, "term") match {
  case parser.Success(result, _) => true
  case parser.Failure(_, _) => false
  case parser.Error(_, _) => false
}
Burack answered 6/5, 2011 at 4:35 Comment(1)
Thanks. I'm using Scala 2.7.7 still (yes, we've been too busy to upgrade) and I'm getting this error: y.scala:17: error: value NoSuccess is not a member of SqlParserImpl case parser.NoSuccess(_, _) => falseBrougham
I
4

I was able to compile the following:

object Test {  
  def main(args: Array[String]): Unit = {
    val parser = new SqlParserImpl

    println(parser.parseAll(parser.term, "term") match {
      case x: parser.Success[_] => true
      case x: parser.NoSuccess => false
    })
  }
}
Irretrievable answered 6/5, 2011 at 3:40 Comment(0)
D
4

The NoSuccess object (with extractor) was added back in 2009, at a time when no code was being backported to 2.7 anymore It's implementation, however, is pretty simple:

object NoSuccess {
  def unapply[T](x: ParseResult[T]) = x match {
    case Failure(msg, next)   => Some(msg, next)
    case Error(msg, next)     => Some(msg, next)
    case _                    => None
  }
}

So you can replace the parser.NoSuccess(_, _) match with one parser.Failure(_, _) and one parser.Error(_, _) match. But if you are not interested in what is being returned, then it's simpler to match against the type:

case _: parser.Success[_] => true
case _: parser.NoSuccess  => false

Like suggested by Eugene.

Daffi answered 6/5, 2011 at 12:11 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.