Scala комбинаторный парсер, что значит >> - PullRequest
5 голосов
/ 02 ноября 2011

Я немного запутался по поводу ">>" в scala. Даниэль сказал в Scala-синтаксических анализаторах, анализирующих xml? , что его можно использовать для параметризации базы синтаксического анализатора на основе результата предыдущего синтаксического анализатора. Может ли кто-нибудь дать мне пример / подсказку? Я уже читал скаляр, но все еще не понимаю.

спасибо

Ответы [ 2 ]

16 голосов
/ 02 ноября 2011

Как я уже сказал, он служит для параметризации парсера, но давайте разберем пример, чтобы прояснить ситуацию.

Давайте начнем с простого парсера, который разбирает число, за которым следует слово:

def numberAndWord = number ~ word
def number        = "\\d+".r
def word          = "\\w+".r

Под RegexParsers , это будет разбирать такие вещи, как "3 плода".

Теперь, допустим, вы также хотите получить список этих "n вещей". Например, «3 плода: банан, яблоко, апельсин». Давайте попробуем разобрать это, чтобы увидеть, как это происходит.

Во-первых, как мне разобрать "N" вещи? Как оказалось, есть метод repN:

def threeThings = repN(3, word)

Это будет анализировать "банановое яблоко-апельсин", но не "банан, яблоко, апельсин". Мне нужен разделитель. Это repsep обеспечивает это, но не позволяет мне указать, сколько повторений я хочу. Итак, давайте сами предоставим разделитель:

def threeThings = word ~ repN(2, "," ~> word)

Хорошо, эти слова. Теперь мы можем написать целый пример для трех вещей, например:

def listOfThings = "3" ~ word ~ ":" ~ threeThings
def word         = "\\w+".r
def threeThings  = word ~ repN(2, "," ~> word)

Это работает, за исключением того, что я исправляю "N" в 3. Я хочу позволить пользователю указать, сколько. И вот тут появляется >>, также известный как into (и, да, это flatMap для Parser). Во-первых, давайте изменим threeThings:

def things(n: Int) = n match {
  case 1          => word ^^ (List(_))
  case x if x > 1 => word ~ repN(x - 1, "," ~> word) ^^ { case w ~ l => w :: l }
  case x          => err("Invalid repetitions: "+x)
}

Это немного сложнее, чем вы могли ожидать, потому что я заставляю его возвращать Parser[List[String]]. Но как мне передать параметр вещам? Я имею в виду, это не сработает:

def listOfThings = number ~ word ~ ":" ~ things(/* what do I put here?*/)

Но мы можем переписать это так:

def listOfThings = (number ~ word <~ ":") >> {
  case n ~ what => things(n.toInt)
}

Это почти достаточно хорошо, за исключением того, что я теперь потерял n и what: он возвращает только «Список (банан, яблоко, апельсин)», не столько, сколько должно быть, а то, что они есть. Я могу сделать это так:

def listOfThings   = (number ~ word <~ ":") >> {
  case n ~ what => things(n.toInt) ^^ { list => new ~(n.toInt, new ~(what, list)) }
}
def number         = "\\d+".r
def word           = "\\w+".r
def things(n: Int) = n match {
  case 1          => word ^^ (List(_))
  case x if x > 1 => word ~ repN(x - 1, "," ~> word) ^^ { case w ~ l => w :: l }
  case x          => err("Invalid repetitions: "+x)
}

Просто последний комментарий. Вы, возможно, задавались вопросом: «что вы имеете в виду flatMap? Разве это не монада / вещь для понимания?» Почему, да и да! :-) Вот еще один способ записи listOfThings:

def listOfThings   = for {
  nOfWhat  <- number ~ word <~ ":"
  n ~ what = nOfWhat
  list     <- things(n.toInt)
}  yield new ~(n.toInt, new ~(what, list))

Я не делаю n ~ what <- number ~ word <~ ":", потому что он использует filter или withFilter в Scala, который не реализован Parsers. Но вот еще один способ написать это, который не имеет точно такой же семантики, но дает те же результаты:

def listOfThings   = for {
  n    <- number
  what <- word
  _    <- ":" : Parser[String]
  list <- things(n.toInt)
}  yield new ~(n.toInt, new ~(what, list))

Это может даже привести к мысли, что, возможно, утверждение, что "монады повсюду", может иметь к этому какое-то отношение : -)

5 голосов
/ 02 ноября 2011

Метод >> берет функцию, которая получает результат синтаксического анализатора, и использует его для создания нового синтаксического анализатора. Как уже говорилось, это можно использовать для параметризации парсера по результату предыдущего парсера.

Пример

Следующий анализатор анализирует строку с n + 1 целочисленными значениями. Первое значение n указывает количество следующих значений. Это первое целое число анализируется, а затем результат этого анализа используется для создания синтаксического анализатора, который анализирует n дальнейшие целые числа.

Определение парсера

В следующей строке предполагается, что вы можете проанализировать целое число с помощью parseInt: Parser[Int]. Сначала он анализирует целочисленное значение n, а затем использует >> для анализа n дополнительных целых чисел, которые формируют результат синтаксического анализатора. Поэтому начальный n не возвращается парсером (хотя это размер возвращаемого списка).

def intLine: Parser[Seq[Int]] = parseInt >> (n => repN(n,parseInt))

Допустимые значения

1 42
3 1 2 3
0

Неверные данные

0 1
1
3 42 42
...