Парсер комбинатор не заканчивается - как записать, что происходит? - PullRequest
7 голосов
/ 05 марта 2010

Я экспериментирую с комбинаторами синтаксического анализа и часто сталкиваюсь с тем, что кажется бесконечными рекурсиями.Вот первый, с которым я столкнулся:

import util.parsing.combinator.Parsers
import util.parsing.input.CharSequenceReader

class CombinatorParserTest extends Parsers {

  type Elem = Char

  def notComma = elem("not comma", _ != ',')

  def notEndLine = elem("not end line", x => x != '\r' && x != '\n')

  def text = rep(notComma | notEndLine)

}

object CombinatorParserTest {

  def main(args:Array[String]): Unit = {
    val p = new CombinatorParserTest()
    val r = p.text(new CharSequenceReader(","))
    // does not get here
    println(r)
  }

}

Как мне распечатать то, что происходит?И почему это не заканчивается?

Ответы [ 3 ]

4 голосов
/ 05 марта 2010

Регистрация попыток синтаксического анализа notComma и notEndLine показывает, что это конец файла (отображается как CTRL-Z в выходных данных журнала (...) ("mesg")) неоднократно разбирался. Вот как я изменил ваш парсер для этой цели:

def text = rep(log(notComma)("notComma") | log(notEndLine)("notEndLine"))

Я не совсем уверен, что происходит (я пробовал много вариантов вашей грамматики), но я думаю, что это что-то вроде этого: EOF на самом деле не персонаж, искусственно введенный в поток ввода, а скорее некий вечный условие в конце ввода. Таким образом, этот никогда не потребляемый псевдосимвол EOF неоднократно анализируется как «либо не запятая, либо не конец строки».

2 голосов
/ 06 марта 2010

Хорошо, я думаю, что понял это. CharSequenceReader возвращает \ 032 в качестве маркера для конца ввода. Так что, если я изменю свой ввод таким образом, он будет работать:

import util.parsing.combinator.Parsers
import util.parsing.input.CharSequenceReader

class CombinatorParserTest extends Parsers {

  type Elem = Char

  import CharSequenceReader.EofCh

  def notComma = elem("not comma", x => x != ',' && x!=EofCh)

  def notEndLine = elem("not end line", x => x != '\r' && x != '\n' && x!=EofCh)

  //def text = rep(notComma | notEndLine)
  def text = rep(log(notComma)("notComma") | log(notEndLine)("notEndLine"))

}

object CombinatorParserTest {

  def main(args:Array[String]): Unit = {
    val p = new CombinatorParserTest()
    val r = p.text(new CharSequenceReader(","))
    println(r)
  }

}

См. Исходный код для CharSequenceReader здесь . Если бы scaladoc упомянул об этом, это сэкономило бы мне много времени.

0 голосов
/ 28 апреля 2017

Я считаю, что функция регистрации крайне неудобна для ввода. Например, почему я должен делать log(parser)("string")? Почему бы не иметь что-то столь же простое как parser.log("string") ?. В любом случае, чтобы преодолеть это, я сделал это вместо:

trait Logging { self: Parsers =>

    // Used to turn logging on or off
    val debug: Boolean

    // Much easier than having to wrap a parser with a log function and type a message
    // i.e. log(someParser)("Message") vs someParser.log("Message")
    implicit class Logged[+A](parser: Parser[A]) {
        def log(msg: String): Parser[A] =
            if (debug) self.log(parser)(msg) else parser
    }
}

Теперь в вашем парсере вы можете добавить эту черту следующим образом:

import scala.util.parsing.combinator.Parsers
import scala.util.parsing.input.CharSequenceReader


object CombinatorParserTest extends App with Parsers with Logging {

    type Elem = Char

    override val debug: Boolean = true

    def notComma: Parser[Char] = elem("not comma", _ != ',')
    def notEndLine: Parser[Char] = elem("not end line", x => x != '\r' && x != '\n')
    def text: Parser[List[Char]] = rep(notComma.log("notComma") | notEndLine.log("notEndLine"))

    val r = text(new CharSequenceReader(","))

    println(r)
}

Вы также можете переопределить поле debug, чтобы отключить ведение журнала, если это необходимо.

Выполнение этого также показывает, что второй парсер правильно проанализировал запятую:

trying notComma at scala.util.parsing.input.CharSequenceReader@506e6d5e
notComma --> [1.1] failure: not comma expected

,
^
trying notEndLine at scala.util.parsing.input.CharSequenceReader@506e6d5e
notEndLine --> [1.2] parsed: ,
trying notComma at scala.util.parsing.input.CharSequenceReader@15975490
notComma --> [1.2] failure: end of input

,
 ^
trying notEndLine at scala.util.parsing.input.CharSequenceReader@15975490
notEndLine --> [1.2] failure: end of input

,
 ^
The result is List(,)

Process finished with exit code 0
...