Сопоставление ключевых слов в Pyparsing: не жадная лексема токенов - PullRequest
5 голосов
/ 15 декабря 2009

питонисты:

Предположим, вы хотите проанализировать следующую строку с помощью 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), он анализируется правильно.

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

Спасибо.

[Примечание: это полное переписывание моего вопроса; Я не понял, в чем реальная проблема]

Ответы [ 4 ]

7 голосов
/ 15 декабря 2009

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

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 может быть полон злых, отвратительных персонажей, а не просто красивых alphanums со случайным подчеркиванием.

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

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

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

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

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

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

1 голос
/ 15 декабря 2009

Вы также можете проанализировать идентификатор и параметр как один токен и разделить их в действии разбора:

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")

В приведенном выше примере предполагается, что идентификаторы и параметры всегда имеют формат XXX_YYY (содержащий одно подчеркивание).

Если это не так, вам нужно настроить метод split_ident_and_param().

1 голос
/ 15 декабря 2009

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

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']

Если это не так, но если длина идентификатора фиксирована, вы можете определить Идентификатор следующим образом:

Identifier = Word( alphanums + '_' , exact=7)
0 голосов
/ 15 декабря 2009

Это ответ на вопрос, который вы, вероятно, также задавали себе: «Что такое реальное приложение для 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 + '_'), он будет сохранять совпадающие символы, пока не найдет тот, который не является буквой, цифрой или подчеркиванием.

...