我目前正在 python 中实现 prolog 的方言。为此,我使用了很棒的 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)
答案 0 :(得分: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
。为确保您只获得变量 names ,请使用 pp.Word(pp.alphas, pp.alphanums)
。这表示首字母必须是字母,后面的任何字母都可以是字母或数字(variable
也是如此)。
我不会在 term 上添加结果名称“terms*”,因为它最终会在“:-”运算符的左右两侧使用。我建议人们通常保留结果名称的附件,直到表达式用于更高级别的表达式。例如,我将规则定义为:
rule = pp.Group(term("rule_lhs")
+ ":-"
+ pp.delimitedList(term)("goals")
)
我不会真正将“:-”称为“中缀”运算符,我将“+”、“-”、“AND”、“OR”等运算符视为中缀运算符。例如,我认为 x :- y :- z
无效。您可能会执行以下操作来添加“AND”和“OR”运算符:
logical_term_expression = pp.infixNotation(term,
[
("&&", 2, pp.opAssoc.LEFT,),
("||", 2, pp.opAssoc.LEFT,),
])
在 term
中使用结果名称真的会弄得一团糟,更有可能在您的运算符元组上使用类,正如您在 simple_bool.py 之类的 pyparsing 示例中所见。
您提到使用 nestedExpr
- 请不要。在为 C 代码之类的内容编写扫描程序时,最好使用该帮助程序,您可能只想跳过一些嵌套的大括号而不实际解析内容。在您的 DSL 中,您需要正确解析所有内容 - 但我认为 infixNotation
可能就是您所需要的。