Нежадное сопоставление Word в pyparsing?

Я хотел бы сопоставить слово, которое заканчивается на _foo или _bar. Я написал это:

identifier = Word(alphanums + '_')
string     = identifier + Suppress('_') + oneOf('foo bar')

К сожалению, я понял, что identifier жаден и потребляет все ключевое слово.

Как заставить identifier не быть жадным?

$ string.parseString('a_keyword_foo')
ParseException: Expected "_" (at char 13), (line:1, col:14)

Некоторые допустимые ключевые слова:

a_keyword_foo          # ['a_keyword', 'foo']
foo_bar_foo            # ['foo_bar',   'foo']
bar_bar                # ['bar',       'bar']

Некоторые недопустимые ключевые слова:

keyword_foo_foobar
2keywords_bar          # The leading number is perhaps another question...
foo _bar 
_foo

person nowox    schedule 08.10.2016    source источник
comment
Нежадное сопоставление обсуждается по адресу: stackoverflow.com/questions/15938540/ pyparsing-non-greedy-match. Удачи!   -  person Jurgenfd    schedule 08.10.2016
comment
@Jurgenfd, я читал это. Что я пропустил?   -  person nowox    schedule 08.10.2016


Ответы (2)


Как только вы узнаете, что ищете, вы можете использовать pp.SkipTo:

In [38]: foo_or_bar = Literal('foo') | Literal('bar')

In [39]: string = SkipTo(Literal('_') + foo_or_bar) + Literal('_') + foo_or_bar

In [42]: string.parseString('frumpy _foo')
Out[42]: (['frumpy ', '_', 'foo'], {})

К сожалению, вы также получаете такое поведение:

In [44]: string.parseString('frumpy _foo _foo')
Out[44]: (['frumpy ', '_', 'foo'], {})

в случае, если шаблон может появиться более одного раза.

Проблема в том, что pyparsing не работает с опережением. Если вас беспокоит и второй случай, вам придется определить его как одну или несколько вещей, оканчивающихся на подчеркивание + foo или bar (как указано выше), а затем взять последний.

person Ami Tavory    schedule 08.10.2016
comment
Красиво, я не знал SkipTo, но frumpy _foo это не одно ключевое слово, а 2. - person nowox; 08.10.2016
comment
@nowox Я не понимаю вашего комментария (или вопроса). Можете ли вы, возможно, добавить к вашему вопросу входные данные и требуемые результаты? - person Ami Tavory; 08.10.2016
comment
Я добавил несколько примеров к вопросу. - person nowox; 08.10.2016
comment
Если здесь используется SkipTo, добавьте выражение failOn (что-то вроде failOn=White()), чтобы не пропускать слишком далеко. - person PaulMcG; 09.10.2016

Если вам нужно/можно переключиться на re api, вы можете использовать там нежадное сопоставление:

    import re
    p = re.compile (r"""([a-z_]+?)        # lazy matching identifier
                         _ (bar|foo)      # _ with foo or bar
       """, re.VERBOSE)
    subject_string = 'a_hello_foo'
    m = p.match( subject_string )
    print "groups:", m.groups()
    print "group 1:", m.group(1)

В pyparsing также есть возможность использовать регулярное выражение.

person Jurgenfd    schedule 08.10.2016
comment
Я бы, вероятно, использовал здесь pyparsing.Regex, хотя вы также можете использовать старый добрый Word и добавить условие, чтобы принимать только те слова, которые заканчиваются на «_bar» или «_foo», что-то вроде string = identifier().addCondition(lambda t: t[0].endswith('_foo') or t[0].endswith('_bar') - person PaulMcG; 09.10.2016
comment
да, а затем выполнить постобработку, чтобы обрезать конечные символы. Должно быть легко ;-) - person Jurgenfd; 10.10.2016
comment
??? - не требует постобработки. Выражение Word получит только тело идентификатора, а не какие-либо дополнительные завершающие символы. И я почти уверен, что ОП хотел окончания '_foo' или '_bar', поэтому их тоже не нужно обрезать. - person PaulMcG; 12.10.2016