Понимание «не» в комбинаторах парсеров

Я написал следующий синтаксический анализатор с целью fail-ing по пробелам:

import scala.util.parsing.combinator._

object Foo extends JavaTokenParsers { 
  val wsTest = not(whiteSpace) // uses whitespace inherited from `RegexParsers`
}

Почему парсинг кучи пробелов успешен?

scala> Foo.parseAll(Foo.wsTest, "          ")
res5: Foo.ParseResult[Unit] = [1.11] parsed: ()

scala> res5.successful
res6: Boolean = true

Глядя на Parsers#not из проекта, я ожидал Failure для моего вышеприведенного теста.

  /** Wrap a parser so that its failures and errors become success and
   *  vice versa -- it never consumes any input.
   */
  def not[T](p: => Parser[T]): Parser[Unit] = Parser { in =>
    p(in) match {
      case Success(_, _)  => Failure("Expected failure", in)
      case _              => Success((), in)
    }
  }

person Kevin Meredith    schedule 01.09.2014    source источник
comment
not работает правильно. Я предполагаю, что синтаксический анализатор по умолчанию пропускает пробелы, и вы должны отключить это. Возможно, это поможет: stackoverflow.com/questions/ 3564094/   -  person Kigyo    schedule 01.09.2014
comment
My guess is that the parser skips white spaces by default — я наблюдал такое поведение с классом, расширяющим JavaTokenParsers. Однако я не ожидал, что Foo.parseAll(Foo.wsTest, " ") преуспеет.   -  person Kevin Meredith    schedule 01.09.2014


Ответы (1)


JavaTokenParsers расширяет RegexParsers, RegexParsers имеет:

 protected val whiteSpace = """\s+""".r

 def skipWhitespace = whiteSpace.toString.length > 0

 implicit def regex(r: Regex): Parser[String] = new Parser[String] {
    ... 
    val start = handleWhiteSpace(source, offset)
    ...
 }

 protected def handleWhiteSpace(source: java.lang.CharSequence, offset: Int): Int =
   if (skipWhitespace)
     (whiteSpace findPrefixMatchOf (source.subSequence(offset, source.length))) match {
       case Some(matched) => offset + matched.end
       case None => offset
     }
   else
     offset

поэтому он пропускает пробелы (вы можете переопределить это, переопределив def skipWhitespace = false)

поэтому для парсера " " равно ""

пробел пытается сопоставить "", но не удается ("""\s+""" требует по крайней мере один пробел), и не преобразует это в успех

person Siphor    schedule 01.09.2014
comment
Спасибо, @Siphor. На самом деле я задал этот вопрос как продолжение реализации этого ответа. Я добавил val nonWhitespaceRegex: Regex = "\\S+".r вместе с guard(nonWhitespaceRegex) ~> ..., чтобы убедиться, что Input, то есть токены, которые я анализировал, не были полностью пробелами. В противном случае проверка набора пробелов на EOF вернет false. - person Kevin Meredith; 02.09.2014