Поиск по ключевым словам в Pyparsing: нежадное прихлебывание маркеров

Pythonistas:

Предположим, что Вы хотите проанализировать следующее строковое использование Pyparsing:

'ABC_123_SPEED_X 123'

были ABC_123 идентификатор; SPEED_X параметр, и 123 значение. Я думал о следующем BNF использование Pyparsing:

Identifier = Word( alphanums + '_' )
Parameter = Keyword('SPEED_X') or Keyword('SPEED_Y') or Keyword('SPEED_Z')
Value = # assume I already have an expression valid for any value
Entry = Identifier + Literal('_') + Parameter + Value
tokens = Entry.parseString('ABC_123_SPEED_X 123')
#Error: pyparsing.ParseException: Expected "_" (at char 16), (line:1, col:17)

Если я удаляю подчеркивание с середины (и корректируйтесь Entry определение соответственно), это анализирует правильно.

Как я могу заставить этот синтаксический анализатор быть немного более ленивым и ожидать, пока он не соответствует Ключевому слову (в противоположность прихлебыванию всей строки как Идентификатор и ожидание _, который не существует.

Спасибо.

[Примечание: Это - полное, переписывают моего вопроса; я не понял то, чем настоящая проблема была]

5
задан Escualo 15 December 2009 в 17:40
поделиться

4 ответа

Я основал свой ответ на на этот , так как вы пытаетесь получить не жадное совпадение. Кажется, что это сложно сделать при pyparsing, но возможно, если проявить некоторый ум и пойти на компромисс. Похоже, работает следующее:

from pyparsing import *
Parameter = Literal('SPEED_X') | Literal('SPEED_Y') | Literal('SPEED_Z')
UndParam = Suppress('_') + Parameter
Identifier = SkipTo(UndParam)
Value = Word(nums)
Entry = Identifier + UndParam + Value

Когда мы запускаем это из интерактивного интерпретатора, мы видим следующее:

>>> Entry.parseString('ABC_123_SPEED_X 123')
(['ABC_123', 'SPEED_X', '123'], {})

Обратите внимание, что это компромисс; поскольку я использую SkipTo , идентификатор может содержать злые, отвратительные символы, а не только красивые буквенные символы с редким подчеркиванием.

РЕДАКТИРОВАТЬ: Благодаря Полу МакГуайру мы можем придумать действительно элегантное решение, установив Идентификатор следующим образом:

Identifier = Combine(Word(alphanums) +
        ZeroOrMore('_' + ~Parameter + Word(alphanums)))

Давайте посмотрим, как это работает. Во-первых, игнорируйте внешний Combine ; мы вернемся к этому позже. Мы знаем, что, начиная с Word (alphanums) , мы получим 'ABC' часть ссылочной строки, 'ABC_123_SPEED_X 123' . Важно отметить, что в этом случае мы не позволяли «слову» содержать символы подчеркивания. Мы встраиваем это отдельно в логику.

Затем нам нужно захватить часть '_ 123' , не втягивая также '_ SPEED_X' . Давайте также пропустим здесь ZeroOrMore и вернемся к нему позже. Мы начинаем с символа подчеркивания Literal , но мы можем сократить его, используя только '_' , что даст нам ведущее подчеркивание, но не все '_ 123' . Интуитивно мы бы поместили еще одно слово (alphanums) , чтобы зафиксировать остальное, но это именно то, что доставит нам неприятности, потребляя все оставшиеся '_ 123_SPEED_X' . Вместо этого мы говорим: «Пока нижнее подчеркивание не параметр , проанализируйте его как часть моего идентификатора . Мы утверждаем, что в терминах pyparsing как '_' + ~ Parameter + Word (alphanums) . Поскольку мы предполагаем, что можем иметь произвольное количество повторений подчеркивания + WordButNotParameter, мы заключаем это выражение в конструкцию ZeroOrMore . (Если вы всегда ожидаете как минимум подчеркивания + WordButNotParameter после инициала, вы можете использовать OneOrMore .)

Наконец, нам нужно обернуть начальное слово, и специальное подчеркивание + слово повторяется вместе, чтобы было понятно, что они смежные, не разделенные пробелами, поэтому мы оборачиваем все выражение в конструкцию Combine . Таким образом, 'ABC _123_SPEED_X' вызовет ошибку синтаксического анализа, но 'ABC_123_SPEED_X' будет анализировать правильно.

Также обратите внимание, что мне пришлось изменить Keyword на Буквальный , потому что действия первых слишком тонки и быстры, чтобы вызвать гнев. Я не доверяю ключевым словам , и не могу найти с ними соответствие.

7
ответ дан 14 December 2019 в 04:39
поделиться

If you are sure that the identifier never ends with an underscore, you can enforce it in the definition:

from pyparsing import *

my_string = 'ABC_123_SPEED_X 123'

Identifier = Combine(Word(alphanums) + Literal('_') + Word(alphanums))
Parameter = Literal('SPEED_X') | Literal('SPEED_Y') | Literal('SPEED_Z')
Value = Word(nums)
Entry = Identifier + Literal('_').suppress() + Parameter  + Value
tokens = Entry.parseString(my_string)

print tokens # prints: ['ABC_123', 'SPEED_X', '123']

If it's not the case but if the identifier length is fixed you can define Identifier like this:

Identifier = Word( alphanums + '_' , exact=7)
1
ответ дан 14 December 2019 в 04:39
поделиться

You can also parse the identifier and parameter as one token, and split them in a parse action:

from pyparsing import *
import re

def split_ident_and_param(tokens):
    mo = re.match(r"^(.*?_.*?)_(.*?_.*?)$", tokens[0])
    return [mo.group(1), mo.group(2)]

ident_and_param = Word(alphanums + "_").setParseAction(split_ident_and_param)
value = Word(nums)
entry = ident_and_param + value

print entry.parseString("APC_123_SPEED_X 123")

The example above assumes that the identifiers and parameters always have the format XXX_YYY (containing one single underscore).

If this is not the case, you need to adjust the split_ident_and_param() method.

1
ответ дан 14 December 2019 в 04:39
поделиться

Это ответ на вопрос, который вы, вероятно, также задавали себе: «Что такое реальное приложение для reduce ?):

>>> keys = ['CAT', 'DOG', 'HORSE', 'DEER', 'RHINOCEROS']
>>> p = reduce(lambda x, y: x | y, [Keyword(x) for x in keys])
>>> p
{{{{"CAT" | "DOG"} | "HORSE"} | "DEER"} | "RHINOCEROS"}

Edit:

Это было довольно хороший ответ на исходный вопрос. Мне придется поработать над новым.

Дальнейшее редактирование:

Я почти уверен, что вы не можете делать то, что пытаетесь сделать. Парсер, который pyparsing создает не опережающий просмотр. Поэтому, если вы укажете ему, что он соответствует Word (alphanums + '_') , он будет продолжать сопоставление символов, пока не найдет тот, который не является буквой , число или знак подчеркивания.

-1
ответ дан 14 December 2019 в 04:39
поделиться
Другие вопросы по тегам:

Похожие вопросы: