Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Scala parser-combinators: how to invert matches?

Is it possible to invert matches with Scala parser combinators? I am trying to match lines with a parser that do not start with a set of keywords. I could do this with an annoying zero width negative lookahead regular expression (e.g. "(?!h1|h2).*"), but I'd rather do it with a Scala parser. The best I've been able to come up with is this:

def keyword = "h1." | "h2."
def alwaysfails = "(?=a)b".r
def linenotstartingwithkeyword = keyword ~! alwaysfails | ".*".r

The idea is here that I use ~! to forbid backtracking to the all-matching regexp, and then continue with a regex "(?=a)b".r that matches nothing. (By the way, is there a predefined parser that always fails?) That way the line would not be matched if a keyword is found but would be matched if keyword does not match.

I am wondering if there is a better way to do this. Is there?

like image 616
Hans-Peter Störr Avatar asked Oct 31 '12 19:10

Hans-Peter Störr


1 Answers

You can use not here:

import scala.util.parsing.combinator._

object MyParser extends RegexParsers {
  val keyword = "h1." | "h2."
  val lineNotStartingWithKeyword = not(keyword) ~> ".*".r

  def apply(s: String) = parseAll(lineNotStartingWithKeyword, s)
}

Now:

scala> MyParser("h1. test")
res0: MyParser.ParseResult[String] = 
[1.1] failure: Expected failure

h1. test
^

scala> MyParser("h1 test")
res1: MyParser.ParseResult[String] = [1.8] parsed: h1 test

Note that there is also a failure method on Parsers, so you could just as well have written your version with keyword ~! failure("keyword!"). But not's a lot nicer, anyway.

like image 115
Travis Brown Avatar answered Sep 28 '22 14:09

Travis Brown