Как пропустить пробелы, но использовать их как разделитель токенов в комбинаторе парсера

Я пытаюсь создать небольшой синтаксический анализатор, в котором токены (к счастью) никогда не содержат пробелов. Пробелы (пробелы, табуляции и символы новой строки) по сути являются разделителями токенов (за исключением случаев, когда есть скобки и т. Д.).

Я расширяю класс RegexParsers. Если я включаю skipWhitespace, синтаксический анализатор жадно объединяет токены, когда следующий токен совпадает с регулярным выражением предыдущего. С другой стороны, если я выключаю skipWhitespace, он жалуется на то, что пробелы не являются частью определения. Я пытаюсь максимально сопоставить BNF, и, учитывая, что пробел почти всегда является разделителем (кроме скобок или некоторых других случаев, когда разделитель явно определен в BNF), существует далеко, чтобы избежать размещения регулярного выражения с пробелами во всех мои определения?

ОБНОВЛЕНИЕ

Это небольшой тестовый пример, в котором токены соединяются вместе:

import scala.util.parsing.combinator.RegexParsers

object TestParser extends RegexParsers {
  def test  = "(test" ~> name <~ ")"

  def name : Parser[String] = (letter ~ (anyChar*)) ^^ { case first ~ rest => (first :: rest).mkString}

  def anyChar = letter | digit | "_".r | "-".r
  def letter = """[a-zA-Z]""".r
  def digit = """\d""".r

  def main(args: Array[String]) {

    val s = "(test hello these should not be joined and I should get an error)"

    val res = parseAll(test, s)
    res match {
      case Success(r, n) => println(r)
      case Failure(msg, n) => println(msg)
      case Error(msg, n) => println(msg)
    }

  }

}

В приведенном выше случае я просто соединяю струны вместе. Аналогичный эффект произойдет, если я изменю test на следующее, ожидая, что он выдаст мне список отдельных слов после теста, но вместо этого он объединяет их вместе и просто дает мне список из одного элемента с длинной строкой без пробелов посередине:

def test  = "(test" ~> (name+) <~ ")"

person jbx    schedule 27.12.2013    source источник
comment
IIRC, пропуск пробелов выполняется только в начале каждого токена до тех пор, пока не будет найден первый непробельный символ. Это не соответствует тому, что, по вашему мнению, происходит, не могли бы вы предоставить образец кода и контрольный пример?   -  person Daniel C. Sobral    schedule 27.12.2013
comment
@ DanielC.Sobral Добавил небольшой пример, показывающий, как это происходит.   -  person jbx    schedule 27.12.2013


Ответы (1)


Пробелы пропускаются непосредственно перед каждым производственным правилом. Итак, в этом фрагменте:

def name : Parser[String] = (letter ~ (anyChar*)) ^^ { case first ~ rest => (first :: rest).mkString}

Он будет пропускать пробелы перед каждой буквой и, что еще хуже, каждой пустой строкой для хорошей оценки (поскольку anyChar* может быть пустым).

Используйте регулярные выражения (или простые строки) для каждого токена, а не для каждого лексического элемента. Нравится:

object TestParser extends RegexParsers {
  def test  = "(test" ~> name <~ ")"
  def name : Parser[String] = """[a-zA-Z][a-zA-Z0-9_-]*""".r

  // ...
person Daniel C. Sobral    schedule 27.12.2013
comment
Не уверен, что у меня пустая строковая часть. Так это потому, что у меня есть отдельное правило anyChar? Итак, до этого пробелы снова пропускались, но anyChar все равно нужно было объединить в синтаксическом анализаторе name более высокого уровня? Значит, маленькие anyChars один за другим съедают больше токенов? - person jbx; 27.12.2013
comment
@jbx Да. Пустая строка - это отдельный вопрос, который не имеет отношения к рассматриваемой проблеме. Перед каждым парсером, и не только anyChar является парсером, но даже letter и digit неявно преобразуются в парсеры, пробелы пропускаются. - person Daniel C. Sobral; 27.12.2013
comment
Вы не можете поместить семантическое действие в регулярное выражение. Результатом регулярного выражения является простая неструктурированная строка. - person Valentin Tihomirov; 02.01.2016