【问题标题】:Concatenated ternary operators with pyparsing使用 pyparsing 连接三元运算符
【发布时间】:2020-04-03 18:08:48
【问题描述】:

使用 pyparsing,我希望能够解析以下语法:

1?1:0?1:0

应该理解为标准的三元运算符condition ? true_part : false_part,将其中的两个简单连接起来,使得第一个的结果成为第二个的条件。

到目前为止,我有以下代码(简化):

import pyparsing as pp

TERNARY_INFIX = pp.infixNotation(
    pp.pyparsing_common.integer, [
        (("?", ":"), 3, pp.opAssoc.LEFT),
])

TERNARY_INFIX.parseString("1?1:0?1:0", parseAll=True)

产量:

ParseException:预期的文本结尾(在字符 5 处),(行:1,列:6)

除非我在两个三元表达式之一周围添加括号,例如 "(1?1:0)?1:0""1?1:(0?1:0)" 有效。

但是我怎样才能让它在没有括号的情况下工作,基本上只是从左到右阅读,以严格的左关联方式?

编辑:

很好地阅读了三元运算符的关联性如何工作:Ternary operator left associativity - 结果左关联没有多大意义。然而,我试图模仿的语言实际上确实从左到右处理此类表达。

【问题讨论】:

    标签: python pyparsing


    【解决方案1】:

    我认为这个运算符实际上是右结合的,而不是左结合的。如果我将您的代码更改为:

    import pyparsing as pp
    
    TERNARY_INFIX = pp.infixNotation(
        pp.pyparsing_common.integer, [
            (("?", ":"), 3, pp.opAssoc.RIGHT),
    ])
    
    TERNARY_INFIX.runTests("""\
    1?1:(0?1:0)
    (1?1:0)?1:0
    1?1:0?1:0
    """, fullDump=False)
    

    然后我得到合理的输出,没有括号的输入没有错误:

    1?1:(0?1:0)
    [[1, '?', 1, ':', [0, '?', 1, ':', 0]]]
    
    (1?1:0)?1:0
    [[[1, '?', 1, ':', 0], '?', 1, ':', 0]]
    
    1?1:0?1:0
    [[1, '?', 1, ':', [0, '?', 1, ':', 0]]]
    

    这是一个更大的表达式,用于评估 3 个变量中的最大值(来自本 C 教程:http://cprogramming.language-tutorial.com/2012/01/biggest-of-3-numbers-using-ternary.html):

    TERNARY = pp.infixNotation(
        pp.Char("abc"), [
            (pp.oneOf("> <"), 2, pp.opAssoc.LEFT), 
            (("?", ":"), 3, pp.opAssoc.RIGHT),
        ])
    TERNARY.runTests("""\
    (a > b) ? ((a > c) ? a : c) : ((b > c) ? b : c) 
    a > b ? a > c ? a : c : b > c ? b : c
    """, fullDump=False)
    

    给予:

    (a > b) ? ((a > c) ? a : c) : ((b > c) ? b : c)
    [[['a', '>', 'b'], '?', [['a', '>', 'c'], '?', 'a', ':', 'c'], ':', [['b', '>', 'c'], '?', 'b', ':', 'c']]]
    
    a > b ? a > c ? a : c : b > c ? b : c
    [[['a', '>', 'b'], '?', [['a', '>', 'c'], '?', 'a', ':', 'c'], ':', [['b', '>', 'c'], '?', 'b', ':', 'c']]]
    

    编辑:我现在看到这种情况类似于重复的二元运算符,例如“1 + 2 + 3”。左关联,pyparsing 不会将它们解析为[['1' '+' '2'] '+' '3'],而只是['1' '+' '2' '+' '3'],并且由评估器进行重复的从左到右的评估。

    当我添加三元运算符时,我没有想到像您正在解析的那样的链式形式。对infixNotation 的单行更改使用左关联性成功解析您的表达式,但就像链接的二元运算符一样,会给出未分组的结果:

    [1, '?', 1, ':', 0, '?', 1, ':', 0]
    

    就像重复加法的例子一样,由评估者进行从左到右的连续评估,例如:

    def eval_ternary(tokens):
        operands = tokens[0]
        ret = bool(operands[0])
        i = 1
        while i < len(operands):
            ret = bool(operands[i+1]) if ret else bool(operands[i+3])
            i += 4
        return ret
    

    如果您想手动修补您的 pyparsing 代码,请更改:

           elif arity == 3:
                matchExpr = _FB(
                    lastExpr + opExpr1 + lastExpr + opExpr2 + lastExpr
                ) + Group(lastExpr + opExpr1 + lastExpr + opExpr2 + lastExpr)
    

    到:

           elif arity == 3:
                matchExpr = _FB(
                    lastExpr + opExpr1 + lastExpr + opExpr2 + lastExpr
                ) + Group(lastExpr + OneOrMore(opExpr1 + lastExpr + opExpr2 + lastExpr))
                                     ^^^^^^^^^^
    

    在 pyparsing.py 中进行此更改,或将infxNotation 的定义复制到您自己的代码中并在那里进行更改。

    我将在 pyparsing 的下一个版本中进行此更改。

    EDIT - 在 pyparsing 2.4.6 中修复,刚刚发布。

    【讨论】:

    • 这是正确的答案,除非您正在解析 PHP,它莫名其妙地具有左关联三元运算符。 stackoverflow.com/questions/20559150/…
    • update 我用我的解析器应该模仿的另一个解析器进行了测试,它确实是左关联的。不管这是否有意义,但我需要它以完全相同的方式表现。 :/ 所以问题仍然存在,为什么这个小改动会完全破坏解析器?
    • 这里更详细地讨论了三元运算符的左结合与右结合:stackoverflow.com/questions/20559150/…
    • 抱歉,我之前没有注意到您的编辑。使用相应的评估器测试了 2.4.6 版,现在它与我的左关联三元运算符完美配合。感谢更新! :)
    【解决方案2】:

    您可以尝试分别编写每个运算符。

    import pyparsing as pp
    
    TERNARY_INFIX = pp.infixNotation(
        pp.pyparsing_common.integer, [
            (("?"), 2, pp.opAssoc.LEFT),
            ((":"), 2, pp.opAssoc.LEFT)
    ])
    
    TERNARY_INFIX.parseString("1?1:0?1:0", parseAll=True)
    

    输出:

    [[[1, '?', 1], ':', [0, '?', 1], ':', 0]]
    

    【讨论】:

    • 是的,但我需要将每个三元运算符的 3 个操作数放在一起,以便我的 parseActioneval 函数工作。我将在问题中添加...
    猜你喜欢
    • 2014-09-07
    • 1970-01-01
    • 2010-11-21
    • 2015-07-31
    • 2019-01-09
    • 2016-01-15
    • 2015-06-09
    相关资源
    最近更新 更多