如何打开 pyparsing 辅助函数的结果?

How can I unwrap results from pyparsing helper functions?

我目前正在 python 中实现 prolog 方言。为此,我正在使用很棒的 pyparsing 模块,我发现它对涉及上下文无关语法的其他项目非常有效。

随着我转向上下文相关语法,我逐渐习惯了 pyparsing 的风格。 pyparsing.nestedExprpyparsing.delimitedList 是我仍然熟悉的两件事。现在我遇到了 pyparsing.delimitedList 的问题;它实现了我正在寻找的东西,但是下面示例代码中的每个人 term 都在列表中返回,我没有在任何条件下使用 pyparsing.Group

重构以使用 pyparsing.nestedExprpyparsing.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)

最初的问题是 Groupcompound_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']

一些补充说明:

  1. atom 定义为

    atom = pp.Word(pp.alphanums)
    

    这也将匹配“123”作为 atom。为确保您只获得变量 names ,请使用 pp.Word(pp.alphas, pp.alphanums)。这表示首字母必须是字母,任何后续字母都可以是字母或数字(与 variable 相同)。

  2. 我不会在术语上添加结果名称“terms*”,因为它最终会在“:-”运算符的左右两侧使用。我建议人们 通常 保留结果名称的附件,直到表达式被用于更高级别的表达式中。例如,我将规则定义为:

    rule = pp.Group(term("rule_lhs") 
                    + ":-" 
                    + pp.delimitedList(term)("goals") 
                    )
    
  3. 我不会真正称“:-”为“中缀”运算符,我认为像“+”、“-”、“AND”、“OR”这样的运算符是中缀运算符。例如,我认为 x :- y :- z 无效。您可能会像这样添加“AND”和“OR”运算符:

    logical_term_expression = pp.infixNotation(term,
                [
                ("&&", 2, pp.opAssoc.LEFT,),
                ("||", 2, pp.opAssoc.LEFT,),
                ])
    

    term 中有一个结果名称真的会把它弄得一团糟,更有可能在你的运算符元组上使用 类,正如你在像 [=60= 这样的 pyparsing 示例中看到的那样].

  4. 您提到使用 nestedExpr - 请不要。该助手最适用于为 C 代码之类的东西编写扫描器,在这种情况下您可能只想跳过一些嵌套的大括号而不实际解析内容。在您的 DSL 中,您将希望正确解析所有内容 - 但我认为 infixNotation 可能就是您所需要的。