Нежадный анализ списка с помощью pyparsing

У меня есть строка, состоящая из списка слов, которые я пытаюсь проанализировать с помощью pyparsing.

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

"one two three four"

следует разбирать на что-то похожее:

["one two"], "three", "four"

Я могу сделать это с помощью Regex:

import pyparsing as pp
data = "one two three four"
grammar = pp.Regex(r"(?P<first>(\w+\W?)+)\s(?P<penultimate>\w+) (?P<ultimate>\w+)")
print(grammar.parseString(data).dump())

который дает:

['one two three four']
- first: one two
- penultimate: three
- ultimate: four

Моя проблема в том, что я не могу получить тот же результат с не-Regex ParserElement из-за жадного характера pyparsing, например следующего:

import pyparsing as pp
data = "one two three four"
word = pp.Word(pp.alphas)
grammar = pp.Group(pp.OneOrMore(word))("first") + word("penultimate") + word("ultimate")
grammar.parseString(data)

не работает с трассировкой:

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/lib/python2.7/site-packages/pyparsing.py", line 1125, in parseString
    raise exc
pyparsing.ParseException: Expected W:(abcd...) (at char 18), (line:1, col:19)

потому что OneOrMore проглатывает все слова в списке. Мои попытки предотвратить это жадное поведение с помощью FollowedBy или NotAny пока не увенчались успехом - какие-либо предложения относительно того, как я могу добиться желаемого поведения?


person Jonathan Barber    schedule 18.06.2015    source источник


Ответы (1)


Что ж, ваше выражение OneOrMore просто нужно немного подтянуть - вы на правильном пути с FollowedBy. На самом деле вам не нужен только OneOrMore (слово), вам нужно «OneOrMore (слово, за которым следует еще как минимум 2 слова)». Чтобы добавить такой вид опережающего просмотра в процесс pyparsing, вы даже можете использовать новый оператор умножения '*', чтобы указать счетчик опережающего просмотра:

grammar = pp.Group(pp.OneOrMore(word + pp.FollowedBy(word*2)))("first") + word("penultimate") + word("ultimate")

Теперь выгрузка дает желаемое:

[['one', 'two'], 'three', 'four']
- first: ['one', 'two']
- penultimate: three
- ultimate: four
person PaulMcG    schedule 18.06.2015
comment
Большое спасибо за ваш ответ, я потерпел неудачу, потому что добавил FollowedBy вне OneOrMore, например: pp.OneOrMore (word) pp.FollowedBy (word + word) - person Jonathan Barber; 19.06.2015