繁体   English   中英

使用 pyparsing 连接的三元运算符

[英]Concatenated ternary operators with pyparsing

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

1?1:0?1:0

应该理解为标准的三元运算符condition ? true_part : false_part 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)"有效。

但是如何在没有括号的情况下使其工作,基本上只是以严格的左关联方式从左到右阅读?

编辑:

很好地阅读了三元运算符的结合性如何工作: 三元运算符左结合性- 结果是 left-assoc 没有多大意义。 然而,我试图模仿的语言实际上是从左到右处理这些表达的。

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

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 的下一个版本中进行此更改。

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

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

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]]

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM