Первая проблема заключается в использовании вами оператора '&'.В pyparsing '&' создает Each
выражений, которые похожи на And
s, но принимают подвыражения в любом порядке:
Word('a') & Word('b') & Word('c')
будет соответствовать 'aaa bbb ccc', но также 'bbb aaa ccc',' ccc bbb aaa 'и т. д.
В вашем парсере вы захотите использовать оператор' + ', который выдает And
выражений.And
s соответствуют нескольким подвыражениям, но только в указанном порядке.
Во-вторых, одна из причин использования pyparsing заключается в том, чтобы принимать переменные пробелы.Пробелы являются проблемой для синтаксических анализаторов, особенно при использовании str.find
или регулярных выражений - в регулярных выражениях это обычно проявляется в виде множества фрагментов \s+
во всех выражениях соответствия.В вашем синтаксическом анализаторе, если входная строка содержит 'first item'
(два пробела между 'first' и 'item'), попытка сопоставить буквенную строку 'first item' не удастся.Вместо этого вы должны сопоставлять несколько слов по отдельности, возможно, используя класс Keyword
pyparsing, и позволить pyparsing пропустить любой пробел между ними.Чтобы упростить это, я написал короткий метод wordphrase
:
def wordphrase(s):
return And(map(Keyword, s.split())).addParseAction(' '.join)
keywords = wordphrase('first item') | wordphrase('second item')
print(keywords)
prints:
{{"first" "item"} | {"second" "item"}}
, указывающий, что каждое слово будет анализироваться индивидуально с любым количеством пробелов между словами.
Наконец, вы должны написать синтаксический анализатор, зная, что синтаксический анализ не выполняет никаких действий.В вашем синтаксическом анализаторе префиксное выражение ZeroOrMore(Word(alphas))
будет соответствовать всем словам в слове «aa bb first item ee ff» - тогда не останется ничего, чтобы соответствовать выражению ключевых слов, поэтому синтаксический анализатор завершится ошибкой.Чтобы закодировать это в pyparsing, вы должны написать в вашем ZeroOrMore
выражение для префиксных слов, которое переводится как «соответствует каждому слову альфа, но сначала убедитесь, что мы не собираемся анализировать выражение ключевого слова».В pyparsing этот вид негативного просмотра реализован с помощью NotAny
, который можно создать с помощью унарного оператора ~
.Для удобства чтения мы будем использовать keywords
выражение сверху:
non_keyword = ~keywords + Word(alphas)
a = ZeroOrMore(non_keyword)('prefix') + keywords('word') + ZeroOrMore(Word(alphas))('suffix')
Вот полный анализатор и результаты, полученные с помощью runTests для различных примеров строк:
def wordphrase(s):
return And(map(Keyword, s.split())).addParseAction(' '.join)
keywords = wordphrase('first item') | wordphrase('second item')
non_keyword = ~keywords + Word(alphas)
a = ZeroOrMore(non_keyword)('prefix') + keywords('word') + ZeroOrMore(Word(alphas))('suffix')
text = """
# prefix and suffix
aa bb first item ee ff
# suffix only
first item ee ff
# prefix only
aa bb first item
# no prefix or suffix
first item
# multiple spaces in item, replaced with single spaces by parse action
first item
"""
a.runTests(text)
Дает:
# prefix and suffix
aa bb first item ee ff
['aa', 'bb', 'first item', 'ee', 'ff']
- prefix: ['aa', 'bb']
- suffix: ['ee', 'ff']
- word: 'first item'
# suffix only
first item ee ff
['first item', 'ee', 'ff']
- suffix: ['ee', 'ff']
- word: 'first item'
# prefix only
aa bb first item
['aa', 'bb', 'first item']
- prefix: ['aa', 'bb']
- word: 'first item'
# no prefix or suffix
first item
['first item']
- word: 'first item'
# multiple spaces in item, replaced with single spaces by parse action
first item
['first item']
- word: 'first item'