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

时间:2021-01-01 23:44:21

标签: python pyparsing

我目前正在 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)

1 个答案:

答案 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']

一些补充说明:

  1. atom 定义为

    atom = pp.Word(pp.alphanums)
    

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

  2. 我不会在 term 上添加结果名称“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 中使用结果名称真的会弄得一团糟,更有可能在您的运算符元组上使用类,正如您在 simple_bool.py 之类的 pyparsing 示例中所见。

  4. 您提到使用 nestedExpr - 请不要。在为 C 代码之类的内容编写扫描程序时,最好使用该帮助程序,您可能只想跳过一些嵌套的大括号而不实际解析内容。在您的 DSL 中,您需要正确解析所有内容 - 但我认为 infixNotation 可能就是您所需要的。

相关问题