pyparsing Regex как Word - PullRequest
       6

pyparsing Regex как Word

5 голосов
/ 09 июня 2011

Я создаю синтаксический анализатор для выполнения простых действий с объектами, идентифицированными с помощью точечной нотации , что-то вроде этого:

DISABLE ALL;
ENABLE A.1 B.1.1 C

, но в DISABLE ALL ключевое слово ALLвместо этого соответствует 3 Regex(r'[a-zA-Z]') => 'A', 'L', 'L', который я использую для сопоставления аргументов.

Как я могу создать Word с помощью регулярных выражений?AFAIK Я не могу получить A.1.1 с помощью Word

пожалуйста, см. Пример ниже

import pyparsing as pp

def toggle_item_action(s, loc, tokens):
    'enable / disable a sequence of items'
    action = True if tokens[0].lower() == "enable" else False
    for token in tokens[1:]:
        print "it[%s].active = %s" % (token, action)

def toggle_all_items_action(s, loc, tokens):
    'enable / disable ALL items'
    action = True if tokens[0].lower() == "enable" else False
    print "it.enable_all(%s)" % action

expr_separator = pp.Suppress(';')

#match A
area = pp.Regex(r'[a-zA-Z]')
#match A.1
category = pp.Regex(r'[a-zA-Z]\.\d{1,2}')
#match A.1.1
criteria = pp.Regex(r'[a-zA-Z]\.\d{1,2}\.\d{1,2}')
#match any of the above
item = area ^ category ^ criteria
#keyword to perform action on ALL items
all_ = pp.CaselessLiteral("all")

#actions
enable = pp.CaselessKeyword('enable')
disable = pp.CaselessKeyword('disable')
toggle = enable | disable

#toggle item expression
toggle_item = (toggle + item + pp.ZeroOrMore(item)
    ).setParseAction(toggle_item_action)

#toggle ALL items expression
toggle_all_items = (toggle + all_).setParseAction(toggle_all_items_action)

#swapping order to `toggle_all_items ^ toggle_item` works
#but seems to weak to me and error prone for future maintenance
expr = toggle_item ^ toggle_all_items
#expr = toggle_all_items ^ toggle_item

more = expr + pp.ZeroOrMore(expr_separator + expr)

more.parseString("""
    ENABLE A.1 B.1.1;
    DISABLE ALL
    """, parseAll=True)

1 Ответ

4 голосов
/ 09 июня 2011

Это проблема?

#match any of the above
item = area ^ category ^ criteria
#keyword to perform action on ALL items
all_ = pp.CaselessLiteral("all")

Должно быть:

#keyword to perform action on ALL items
all_ = pp.CaselessLiteral("all")
#match any of the above
item = area ^ category ^ criteria ^ all_

РЕДАКТИРОВАТЬ - если вам интересно ...

Ваши регулярные выражения настолько похожи, что я подумал, что увижу, как их объединить в одно. Вот фрагмент кода для анализа ваших трехточечных обозначений с помощью одного регулярного выражения, а затем с помощью действия синтаксического анализа, чтобы выяснить, какой тип вы получили:

import pyparsing as pp

dotted_notation = pp.Regex(r'[a-zA-Z](\.\d{1,2}(\.\d{1,2})?)?') 
def name_notation_type(tokens):
    name = {
        0 : "area",
        1 : "category",
        2 : "criteria"}[tokens[0].count('.')]
    # assign results name to results - 
    tokens[name] = tokens[0] 
dotted_notation.setParseAction(name_notation_type)

# test each individually
tests = "A A.1 A.2.2".split()
for t in tests:
    print t
    val = dotted_notation.parseString(t)
    print val.dump()
    print val[0], 'is a', val.getName()
    print

# test all at once
tests = "A A.1 A.2.2"
val = pp.OneOrMore(dotted_notation).parseString(tests)
print val.dump()

Печать:

A
['A']
- area: A
A is a area

A.1
['A.1']
- category: A.1
A.1 is a category

A.2.2
['A.2.2']
- criteria: A.2.2
A.2.2 is a criteria

['A', 'A.1', 'A.2.2']
- area: A
- category: A.1
- criteria: A.2.2

EDIT2 - я вижу исходную проблему ...

Что вас запутало, так это пропуски незаполненных пробелов в pyparsing. Pyparsing пропустит пробел между определенными токенами, но обратное неверно - для разбора не требуется пробел между отдельными выражениями парсера. Таким образом, в вашей версии all_less «ALL» выглядит как 3 области: «A», «L» и «L». Это верно не только для Regex, но и для любого класса pyparsing. Посмотрите, может ли класс WordEnd pyparsing быть полезным для обеспечения этого.

РЕДАКТИРОВАТЬ3 - Тогда, может быть, что-то вроде этого ...

toggle_item = (toggle + pp.OneOrMore(item)).setParseAction(toggle_item_action)
toggle_all = (toggle + all_).setParseAction(toggle_all_action)

toggle_directive = toggle_all | toggle_item

При форматировании ваших команд вы должны сначала сделать так, чтобы синтаксический анализатор проверял, переключается ли ALL, прежде чем искать отдельные области и т. Д. Если вам нужно поддерживать что-то, что может читать «ENABLE A.1 ALL», тогда используйте негативный прогноз на item: item = ~all_ + (area ^ etc...). (Обратите внимание, что я заменил item + pp.ZeroOrMore(item) просто pp.OneOrMore(item).)

...