【问题标题】:PyParsing and nested parens: unexpected EOF errorPyParsing 和嵌套括号:意外的 EOF 错误
【发布时间】:2018-02-01 18:12:43
【问题描述】:

我有参与者调查数据,其中包含每个变量的变量名称、它在该观察中的值以及提出该问题所需的条件(如果先前的答案确定某个问题不适用,则参与者不会提示)。我的任务之一是将表示 N/A 的空白与表示已询问但未回答的提示的空白区分开来。遗憾的是,我们的数据采集工具中的导出功能不提供此功能。

为了解决这个问题,我将每个变量分支的条件与记录的观察结果进行比较,并查看是否应该显示提示。这可能会让人感到困惑,例如,想象一下主题 A 的记录:

Variable Name | Observation Value | Branching Logic
foo           |         5         | 
bar           |         2         | foo != 2
baz           |         7         | foo < 10 or bar == 5

无论如何都会出现foo 的提示;将显示bar 的提示,因为foo = 5 满足其条件foo != 2,同样会观察到baz。我将其视为 pandas 数据框,因此在构建模块的玩具版本时,我使用 dict 来表示测试数据。我几乎让它工作了,但缺少一件:嵌套括号。

有很多类似的问题(例如pyparsing and line breaks),我在处理逻辑符号的 PyParsing 文档中找到了一个非常相似的示例,但是我不擅长 python,并且在使用多个类时遇到了麻烦,子类等。我可以将其用作以下内容的起点:

import pyparsing as pp
test_data = {
    'a' : 3,
    'b' : 6,
    'c' : 2,
    'd' : 4 
    }

# Functions applied by parser
def toInt(x):
    return [int(k) for k in x]
def useKey(x):
    try: return [test_data[k] for k in x]
    except KeyError: print("Value not a key:", x)
def checkCond(parsed):
    allinone = parsed[0]
    print("Condition:", allinone)
    humpty = " ".join([str(x) for x in allinone])
    return eval(humpty)

# Building the parser
key = pp.Word(pp.alphanums + '_')('key')
op = pp.oneOf('> >= == != <= <')('op')
val = pp.Word(pp.nums + '-')('value')
joint = pp.oneOf("and or")
key.setParseAction(useKey)
val.setParseAction(toInt)
cond = pp.Group(key + op + val)('condition')
cond.addParseAction(checkCond)
logic = cond + pp.Optional(joint) + pp.Optional(cond)

# Tests
if __name__ == "__main__":
    tests = [
        ("a == 5", False),
        ("b < 3", False),
        ("c > 1", True),
        ("d != 2", True),
        ("a >= 1", True),
        ("b <= 5", False),
        ("a <= 6 and b == 2", False),
        ("a <= 6 or b == 2", True)]
        #("b > 2 and (a == 3 or d > 2 or c < 1)", True)]
    for expr, res in tests:
        print(expr)
        out = logic.parseString(expr)
        out = " ".join([str(x) for x in out])
        out = bool(eval(out))
        if bool(out) == bool(res):
            print("PASS\n")
        else: print("FAIL\n", 
            "Got:", bool(out), 
            "\nExpected:",bool(res), "\n")

经过大量的反复试验,我得到了预期的结果。但是请注意,最后一个测试已被注释掉;如果你取消注释并运行它,你会得到:

b > 2 and (a == 3 or d > 2 or c < 1)
Condition: [6, '>', 2]
Traceback (most recent call last):
  File "testdat/pptutorial.py", line 191, in <module>
    out = bool(eval(out))
  File "<string>", line 1
    True and
           ^
SyntaxError: unexpected EOF while parsing

我确定我错过了一些非常愚蠢的东西,但对于我的生活,我无法弄清楚这件作品。括号似乎使解析器认为这是新语句的开始。还有其他答案建议寻找空值,打印出单个令牌等,但我没有这样的运气。我的猜测是这与我在解析器中设置组的方式有关。我以前从未建造过,所以这对我来说绝对是未知领域!非常感谢您的帮助,如果我可以提供更多信息,请告诉我。

【问题讨论】:

  • 如果这是一个迟钝的问题,请原谅。您的语法是否在某处提供了带括号的表达式?
  • @BillBell 没有明确说明,但我的理解是 .setParseAction() 将括号读取为分组语句,除非另有说明。我发现但不能很好遵循的示例 (pyparsing.wikispaces.com/file/view/simpleBool.py/451074414/…) 正确处理了括号,但似乎只在其类中定义字符串表示 str 时才提及它们。我误会了吗?
  • 显然你有一个非常直接的答案。

标签: python python-3.x parsing pyparsing


【解决方案1】:

语法的任何部分都不允许在输入中使用括号,这就是 pyparsing 在遇到括号时停止解析的原因。

您可以通过对logic 的定义稍作调整来允许在条件周围使用括号:

cond_chain_with_parentheses = pp.Forward()
cond_chain = cond + pp.Optional(joint + cond_chain_with_parentheses)
cond_chain_with_parentheses <<= cond_chain | '(' + cond_chain + ')'

logic = cond_chain_with_parentheses + pp.StringEnd()

在这里,我使用了cond_chain_with_parentheses 中的forward declaration,即使它尚未定义,我也可以在语法定义中使用它。我还添加了StringEnd,以便在无法解析整个输入时引发异常。


此语法可以正确解析您的所有输入:

>>> logic.parseString("b > 2 and (a == 3 or d > 2 or c < 1)")
Condition: [6, '>', 2]
Condition: [3, '==', 3]
Condition: [4, '>', 2]
Condition: [2, '<', 1]
([True, 'and', '(', True, 'or', True, 'or', False, ')'], {'condition': [True, True, True, False]})

【讨论】:

  • 非常感谢,我知道我错过了一些愚蠢的事情,但没想到会这么简单!我混淆了 infixNotation 和 setParseAction;前者在文档中提到默认处理parens,但我认为是后者。我没有足够的代表给你+1,但非常感谢帮助:)
猜你喜欢
  • 1970-01-01
  • 2019-10-13
  • 1970-01-01
  • 1970-01-01
  • 2015-12-08
  • 1970-01-01
  • 2013-01-29
  • 1970-01-01
相关资源
最近更新 更多