#python #pyparsing
#питон #pyparsing
Вопрос:
В настоящее время я нахожусь в процессе реализации диалекта prolog в python. Я использую замечательный pyparsing
модуль для этой цели, и я обнаружил, что он очень хорошо работает для других проектов, связанных с контекстно-свободными грамматиками.
По мере того, как я перехожу к контекстно-зависимым грамматикам, я постепенно привыкаю к pyparsing
стилю. pyparsing.nestedExpr
и pyparsing.delimitedList
это две вещи, с которыми я все еще знакомлюсь. Прямо сейчас у меня возникли проблемы с pyparsing.delimitedList
; он достигает того, что я ищу, но каждый человек term
в приведенном ниже примере кода возвращается в списке, и я не использовал pyparsing.Group
ни на каких условиях.
Рефакторинг для использования pyparsing.nestedExpr
и pyparsing.infixNotation
являются следующими в моих задачах после решения этой проблемы, поэтому, пожалуйста, не паникуйте, что я их еще не использую. Я также подозреваю, но пока не знаю, что мне придется предотвращать совпадения для term_list
в левой части выражения правила. Это означает, что код находится в стадии разработки и со временем будет претерпевать значительные изменения по мере дальнейших экспериментов с библиотекой.
Я думаю pyparsing.ungroup
, что может быть использован для решения проблемы, но pyparsing.ungroup(pyparsing.delimitedList...
, похоже, в данном случае это не имеет никакого эффекта.
Логика вывода
result = root.parseString('''
A :- True
Z :- 5
''')
print(result.dump())
print(result.rules[0].goals)
Результаты
[['A', 'True'], ['Z', '5']]
- rules: [['A', 'True'], ['Z', '5']]
[0]:
['A', 'True']
- goals: [['True']]
[0]:
['True']
[1]:
['Z', '5']
- goals: [['5']]
[0]:
['5']
[['True']]
Ожидаемые результаты
[['A', 'True'], ['Z', '5']]
- rules: [['A', 'True'], ['Z', '5']]
[0]:
['A', 'True']
- goals: ['True']
[1]:
['Z', '5']
- goals: ['5']
['True']
Полный код
import pyparsing as pp
# These types are the language primitives
atom = pp.Word(pp.alphanums)
number = pp.Word(pp.nums)
variable = pp.Word(pp.alphanums)
string = pp.quotedString
# Terms are the basic unit of expression here
compound_term = pp.Forward()
term = (atom ^ number ^ variable ^ pp.Group(compound_term))('terms*')
# A compound term includes a few rules for term composition, such as lists or an atom containing arguments
term_list = pp.Forward()
compound_term <<=
string ^
term_list ^
atom('functor') pp.Suppress('(') pp.delimitedList(term('arguments*')) pp.Suppress(')')
term_list <<= pp.Suppress('[') pp.delimitedList(term('items*')) pp.Suppress(']')
# The rule operator is an infix operator represented by :-
# On the right side, multiple goals can be composed using AND or OR operators
rule = pp.Group(
term pp.Suppress(':-')
pp.delimitedList(term('goals*'))
)('rules*')
root = pp.ZeroOrMore(rule)
result = root.parseString(
'''
A :- True
Z :- 5
''')
print(result.dump())
print(result.rules[0].goals)
Ответ №1:
Первоначальная проблема заключается в наличии Group
в compound_term
:
term = (atom ^ number ^ variable ^ pp.Group(compound_term))('terms*')
должно быть
term = (atom ^ number ^ variable ^ (compound_term))('terms*')
После внесения этого изменения и добавления названия результатов «lhs» в ваше правило (см. Ниже), я получаю следующее:
[['A', 'True'], ['Z', '5']]
- rules: [['A', 'True'], ['Z', '5']]
[0]:
['A', 'True']
- goals: ['True']
- lhs: 'A'
[1]:
['Z', '5']
- goals: ['5']
- lhs: 'Z'
['True']
Некоторые добавленные примечания:
atom
определяется какatom = pp.Word(pp.alphanums)
Это будет соответствовать «123»
atom
также. Чтобы убедиться , что вы просто получаете имена переменных , используйтеpp.Word(pp.alphas, pp.alphanums)
. Это указывает на то, что начальная буква должна быть буквой, а любые последующие буквы могут быть буквенными или цифровыми (то же самое дляvariable
).- Я бы не стал добавлять название результатов «термины *» в термин, так как в конечном итоге оно будет использоваться как с левой, так и с правой стороны вашего оператора «:-«. Я рекомендую, чтобы люди обычно оставляли вложение имен результатов до тех пор, пока это выражение не будет использоваться в выражениях более высокого уровня. Например, я бы определил правило как:
rule = pp.Group(term("rule_lhs") ":-" pp.delimitedList(term)("goals") )
- Я бы на самом деле не назвал «:-» «инфиксным» оператором, я рассматриваю такие операторы, как » «, «-«, «И», «ИЛИ», как инфиксные операторы. Например, я не думаю
x :- y :- z
, что это действительно так. Вероятно, вы сделаете что-то подобное, чтобы добавить свои операторы «И» и «ИЛИ»:logical_term_expression = pp.infixNotation(term, [ ("amp;amp;", 2, pp.opAssoc.LEFT,), ("||", 2, pp.opAssoc.LEFT,), ])
Наличие имени результата
term
действительно приведет к путанице, с большей вероятностью будут использоваться классы в ваших кортежах операторов, как вы можете видеть в примерах pyparsing, таких как simple_bool.py . - Вы упомянули об использовании
nestedExpr
— пожалуйста, не делайте этого. Этот помощник лучше всего использовать при написании сканера для чего-то вроде кода на C, где вы можете просто перепрыгнуть через некоторые вложенные фигурные скобки, фактически не анализируя содержимое. В вашем DSL вы захотите разобрать все правильно — но я думаюinfixNotation
, это может быть все, что вам нужно.
Комментарии:
1. Спасибо за подробный ответ! Я отметил это как правильный ответ из-за множества рекомендаций по наилучшей практике, которые вы включили, а также из-за наличия решения, которое я реализовал самостоятельно. Хотя мне несколько неудобно использовать «lhs» вместо чего-то более специфичного для выражения, это в основном вопрос стиля. Приношу свои извинения за задержку!