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
определение соответственно), это анализирует правильно.
Как я могу заставить этот синтаксический анализатор быть немного более ленивым и ожидать, пока он не соответствует Ключевому слову (в противоположность прихлебыванию всей строки как Идентификатор и ожидание _
, который не существует.
Спасибо.
[Примечание: Это - полное, переписывают моего вопроса; я не понял то, чем настоящая проблема была]
Я основал свой ответ на на этот , так как вы пытаетесь получить не жадное совпадение. Кажется, что это сложно сделать при 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
на Буквальный
, потому что действия первых слишком тонки и быстры, чтобы вызвать гнев. Я не доверяю ключевым словам ,
и не могу найти с ними соответствие.
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)
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.
Это ответ на вопрос, который вы, вероятно, также задавали себе: «Что такое реальное приложение для 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 + '_')
, он будет продолжать сопоставление символов, пока не найдет тот, который не является буквой , число или знак подчеркивания.