python: замена регулярного выражения на BNF или pyparsing - PullRequest
1 голос
/ 09 сентября 2010

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

class Unit:
    # rules is an ordered dictionary of tagged regex that is intended to be applied in the given order
    # the group named V would correspond to the value (if any) for that particular tag
    rules = (
        ('Level', r'Lv. (?P<V>\d+)'),
        ('DPS', r'DPS: (?P<V>\d+)'),
        ('Type', r'(?P<V>Tank|Infantry|Artillery'),
        #the XXX will be expanded into a list of valid traits
        #note: (XXX| )* wouldn't work; it will match the first space it finds,
        #and stop at that if it's in front of something other than a trait
        ('Traits', r'(?P<V>(XXX)(XXX| )*)'),
        # flavor text, if any, ends with a dot
        ('FlavorText', r'(?P<V>.*\."?$)'),
        )
    rules = collections.OrderedDict(rules)
    traits = '|'.join('All-Terrain', 'Armored', 'Anti-Aircraft', 'Motorized')
    rules['Traits'] = re.sub('XXX', effects, rules['Traits'])

    for x in rules:
        rules[x] = re.sub('<V>', '<'+x+'>', rules[x])
        rules[x] = re.compile(rules[x])

    def __init__(self, data)
        # data looks like this:
        # Lv. 5 Tank DPS: 55 Motorized Armored
        for field, regex in Item.rules.items():
            data = regex.sub(self.parse, data, 1)
        if data:
            raise ParserError('Could not parse part of the input: ' + data)

    def parse(self, m):
        if len(m.groupdict()) != 1:
            Exception('Expected a single named group')
        field, value = m.groupdict().popitem()
        setattr(self, field, value)
        return ''

Работает нормально, но я чувствую, что достиг предела мощности регулярных выражений. В частности, в случае с Traits значение в итоге становится строкой, которую мне нужно разделить и преобразовать в список на более позднем этапе: например, в этом коде obj.Traits будет установлен на «Motorized Armored», но в более поздняя функция изменилась на («Моторизованный», «Бронированный»).

Я думаю о том, чтобы преобразовать этот код для использования либо EBNF, либо грамматики переноса, либо чего-то в этом роде. Мои цели:

  • делает этот код более аккуратным и менее подверженным ошибкам
  • избегать безобразного обращения к регистру со списком значений (где мне нужно сначала выполнить замену внутри регулярного выражения, а затем обработать результат для преобразования строки в список)

Что бы вы посоветовали о том, что использовать и как переписать код?

P.S. Я пропустил некоторые части кода, чтобы избежать беспорядка; если я внес какие-либо ошибки в процесс, извините - оригинальный код работает:)

1 Ответ

4 голосов
/ 09 сентября 2010

Я начал писать руководство по коучингу для pyparsing, но, посмотрев на ваши правила, они довольно легко переводятся в сами элементы pyparsing, не имея дело с EBNF, поэтому я просто приготовил быстрый пример:1003 * Я включил пример Regex, чтобы вы могли видеть, как регулярное выражение может быть вставлено в существующую грамматику преобразования.Состав rule с использованием операторов «&» означает, что отдельные элементы могут быть найдены в любом порядке (поэтому грамматика заботится об итерации по всем правилам вместо того, чтобы вы делали это в своем собственном коде).Pyparsing использует перегрузку операторов для создания сложных парсеров из простых: '+' for sequence, '|'и '^' для альтернатив (первое совпадение или самое длинное совпадение) и т. д.

Вот как будет выглядеть проанализированный результат - обратите внимание, что я добавил имена результатов так же, как вы использовали именованные группы вregexen:

data = "Lv. 5 Tank DPS: 55 Motorized Armored"

parsed_data = rule.parseString(data)
print parsed_data.dump()
print parsed_data.DPS
print parsed_data.Type
print ' '.join(parsed_data.Traits)

печатает:

['Lv.', '5', 'Tank', 'DPS:', '55', ['Motorized', 'Armored']]
- DPS: 55
- Level: 5
- Traits: ['Motorized', 'Armored']
- Type: Tank
55
Tank
Motorized Armored

Пожалуйста, зайдите в вики и посмотрите другие примеры.Вы можете easy_install установить pyparsing, но если вы загрузите исходный дистрибутив из SourceForge, появится много дополнительной документации.

...