pyparsing 如何使用 infixNotation 表示 iif(cond,如果为真,如果为假)
pyparsing how to use infixNotation for representing iif(cond, if true, if false)
我需要用pyparsing解析这个:iif(condition,value if true,value if false)
,但是这种三元比较应该有另一个比较,我的意思是:
`iif(iif(condition1,value1,value2)>iif(condition2,value1,value2),value3,value4)`
我找到了这个:
integer = Word(nums)
variable = Word(alphas, alphanums)
boolLiteral = oneOf("true false")
operand = boolLiteral | variable | integer
comparison_op = oneOf("== <= >= != < >")
QM,COLON = map(Literal,"?:")
expr = infixNotation(operand,
[
(comparison_op, 2, opAssoc.LEFT),
((QM,COLON), 3, opAssoc.LEFT),
])
能够解析这个:
expr.parseString("(x==1? true: (y == 10? 100 : 200) )")
但我无法修改此代码以满足我的需要。我怎样才能做到这一点?
更新
感谢 Paul 先生,我想到了这个解决方案:
arith_expr = Forward()
iif = CaselessKeyword("iif")
open = Literal("(")
close = Literal(")")
var_name = pyparsing_common.identifier()
fn_call = Group(iif + open - Group(Optional(delimitedList(arith_expr))) + close)
arith_operand = fn_call | num
rel_comparison_operator = oneOf("< > <= >=")
eq_comparison_operator = oneOf("== !=")
plus_minus_operator = oneOf("+ -")
mult_div_operator = oneOf("* / %")
arith_expr <<= infixNotation(arith_operand,
[
# add other operators here - in descending order of precedence
# http://www.tutorialspoint.com/cprogramming/c_operators_precedence.htm
('-', 1, opAssoc.RIGHT),
(mult_div_operator, 2, opAssoc.LEFT,),
(plus_minus_operator, 2, opAssoc.LEFT,),
(rel_comparison_operator, 2, opAssoc.LEFT,),
(eq_comparison_operator, 2, opAssoc.LEFT,),
]
)
我正在使用我以前的一些规则。现在我投票关闭此 post.
正如@sepp2k 在他的评论中提到的,您尝试解析的字符串 不是 中缀表示法,尽管您最终可能会在中缀表示法中用作操作数。您传递给 iif
的参数本身可能是中缀符号表达式。所以中缀符号肯定会成为这个解析器的一部分,但它不会成为解析你的 iif
函数调用的部分。
下面是函数调用在 pyparsing 中的样子:
fn_call = pp.Group(var_name + LPAREN - pp.Group(pp.Optional(pp.delimitedList(arith_expr))) + RPAREN)
您用来定义算术表达式的操作数本身可能包含函数调用,因此解析器的递归将要求您使用 pyparsing 的 Forward class.
arith_expr = pp.Forward()
这将允许您在我们完全定义 arith_expr
的外观之前在其他 sub-expressions 中使用 arith_expr
(就像我们刚刚在 fn_call 中所做的那样)。
切入正题,这是一个用于解析您的 iif
函数的最小解析器:
import pyparsing as pp
# for recursive infix notations, or those with many precedence levels, it is best to enable packrat parsing
pp.ParserElement.enablePackrat()
LPAREN, RPAREN = map(pp.Suppress, "()")
arith_expr= pp.Forward()
var_name = pp.pyparsing_common.identifier()
integer = pp.pyparsing_common.integer()
fn_call = pp.Group(var_name + LPAREN - pp.Group(pp.Optional(pp.delimitedList(arith_expr))) + RPAREN)
arith_operand = fn_call | var_name | integer
rel_comparison_operator = pp.oneOf("< > <= >=")
eq_comparison_operator = pp.oneOf("== !=")
plus_minus_operator = pp.oneOf("+ -")
mult_div_operator = pp.oneOf("* / %")
arith_expr <<= pp.infixNotation(arith_operand,
[
# add other operators here - in descending order of precedence
# http://www.tutorialspoint.com/cprogramming/c_operators_precedence.htm
(mult_div_operator, 2, pp.opAssoc.LEFT,),
(plus_minus_operator, 2, pp.opAssoc.LEFT,),
(rel_comparison_operator, 2, pp.opAssoc.LEFT,),
(eq_comparison_operator, 2, pp.opAssoc.LEFT,),
]
)
使用 runTests,我们可以针对几个测试用例进行尝试:
tests = """\
cos(60)
sqrt(1 - sin(60) * sin(60))
divmod(a, 100)
iif(iif(condition1,value1,value2)>iif(condition2,value1,value2),value3,value4)
"""
arith_expr.runTests(tests)
打印:
cos(60)
[['cos', [60]]]
[0]:
['cos', [60]]
[0]:
cos
[1]:
[60]
sqrt(1 - sin(60) * sin(60))
[['sqrt', [[1, '-', [['sin', [60]], '*', ['sin', [60]]]]]]]
[0]:
['sqrt', [[1, '-', [['sin', [60]], '*', ['sin', [60]]]]]]
[0]:
sqrt
[1]:
[[1, '-', [['sin', [60]], '*', ['sin', [60]]]]]
[0]:
[1, '-', [['sin', [60]], '*', ['sin', [60]]]]
[0]:
1
[1]:
-
[2]:
[['sin', [60]], '*', ['sin', [60]]]
[0]:
['sin', [60]]
[0]:
sin
[1]:
[60]
[1]:
*
[2]:
['sin', [60]]
[0]:
sin
[1]:
[60]
divmod(a, 100)
[['divmod', ['a', 100]]]
[0]:
['divmod', ['a', 100]]
[0]:
divmod
[1]:
['a', 100]
iif(iif(condition1,value1,value2)>iif(condition2,value1,value2),value3,value4)
[['iif', [[['iif', ['condition1', 'value1', 'value2']], '>', ['iif', ['condition2', 'value1', 'value2']]], 'value3', 'value4']]]
[0]:
['iif', [[['iif', ['condition1', 'value1', 'value2']], '>', ['iif', ['condition2', 'value1', 'value2']]], 'value3', 'value4']]
[0]:
iif
[1]:
[[['iif', ['condition1', 'value1', 'value2']], '>', ['iif', ['condition2', 'value1', 'value2']]], 'value3', 'value4']
[0]:
[['iif', ['condition1', 'value1', 'value2']], '>', ['iif', ['condition2', 'value1', 'value2']]]
[0]:
['iif', ['condition1', 'value1', 'value2']]
[0]:
iif
[1]:
['condition1', 'value1', 'value2']
[1]:
>
[2]:
['iif', ['condition2', 'value1', 'value2']]
[0]:
iif
[1]:
['condition2', 'value1', 'value2']
[1]:
value3
[2]:
value4
我需要用pyparsing解析这个:iif(condition,value if true,value if false)
,但是这种三元比较应该有另一个比较,我的意思是:
`iif(iif(condition1,value1,value2)>iif(condition2,value1,value2),value3,value4)`
我找到了这个:
integer = Word(nums)
variable = Word(alphas, alphanums)
boolLiteral = oneOf("true false")
operand = boolLiteral | variable | integer
comparison_op = oneOf("== <= >= != < >")
QM,COLON = map(Literal,"?:")
expr = infixNotation(operand,
[
(comparison_op, 2, opAssoc.LEFT),
((QM,COLON), 3, opAssoc.LEFT),
])
能够解析这个:
expr.parseString("(x==1? true: (y == 10? 100 : 200) )")
但我无法修改此代码以满足我的需要。我怎样才能做到这一点?
更新
感谢 Paul 先生,我想到了这个解决方案:
arith_expr = Forward()
iif = CaselessKeyword("iif")
open = Literal("(")
close = Literal(")")
var_name = pyparsing_common.identifier()
fn_call = Group(iif + open - Group(Optional(delimitedList(arith_expr))) + close)
arith_operand = fn_call | num
rel_comparison_operator = oneOf("< > <= >=")
eq_comparison_operator = oneOf("== !=")
plus_minus_operator = oneOf("+ -")
mult_div_operator = oneOf("* / %")
arith_expr <<= infixNotation(arith_operand,
[
# add other operators here - in descending order of precedence
# http://www.tutorialspoint.com/cprogramming/c_operators_precedence.htm
('-', 1, opAssoc.RIGHT),
(mult_div_operator, 2, opAssoc.LEFT,),
(plus_minus_operator, 2, opAssoc.LEFT,),
(rel_comparison_operator, 2, opAssoc.LEFT,),
(eq_comparison_operator, 2, opAssoc.LEFT,),
]
)
我正在使用我以前的一些规则。现在我投票关闭此 post.
正如@sepp2k 在他的评论中提到的,您尝试解析的字符串 不是 中缀表示法,尽管您最终可能会在中缀表示法中用作操作数。您传递给 iif
的参数本身可能是中缀符号表达式。所以中缀符号肯定会成为这个解析器的一部分,但它不会成为解析你的 iif
函数调用的部分。
下面是函数调用在 pyparsing 中的样子:
fn_call = pp.Group(var_name + LPAREN - pp.Group(pp.Optional(pp.delimitedList(arith_expr))) + RPAREN)
您用来定义算术表达式的操作数本身可能包含函数调用,因此解析器的递归将要求您使用 pyparsing 的 Forward class.
arith_expr = pp.Forward()
这将允许您在我们完全定义 arith_expr
的外观之前在其他 sub-expressions 中使用 arith_expr
(就像我们刚刚在 fn_call 中所做的那样)。
切入正题,这是一个用于解析您的 iif
函数的最小解析器:
import pyparsing as pp
# for recursive infix notations, or those with many precedence levels, it is best to enable packrat parsing
pp.ParserElement.enablePackrat()
LPAREN, RPAREN = map(pp.Suppress, "()")
arith_expr= pp.Forward()
var_name = pp.pyparsing_common.identifier()
integer = pp.pyparsing_common.integer()
fn_call = pp.Group(var_name + LPAREN - pp.Group(pp.Optional(pp.delimitedList(arith_expr))) + RPAREN)
arith_operand = fn_call | var_name | integer
rel_comparison_operator = pp.oneOf("< > <= >=")
eq_comparison_operator = pp.oneOf("== !=")
plus_minus_operator = pp.oneOf("+ -")
mult_div_operator = pp.oneOf("* / %")
arith_expr <<= pp.infixNotation(arith_operand,
[
# add other operators here - in descending order of precedence
# http://www.tutorialspoint.com/cprogramming/c_operators_precedence.htm
(mult_div_operator, 2, pp.opAssoc.LEFT,),
(plus_minus_operator, 2, pp.opAssoc.LEFT,),
(rel_comparison_operator, 2, pp.opAssoc.LEFT,),
(eq_comparison_operator, 2, pp.opAssoc.LEFT,),
]
)
使用 runTests,我们可以针对几个测试用例进行尝试:
tests = """\
cos(60)
sqrt(1 - sin(60) * sin(60))
divmod(a, 100)
iif(iif(condition1,value1,value2)>iif(condition2,value1,value2),value3,value4)
"""
arith_expr.runTests(tests)
打印:
cos(60)
[['cos', [60]]]
[0]:
['cos', [60]]
[0]:
cos
[1]:
[60]
sqrt(1 - sin(60) * sin(60))
[['sqrt', [[1, '-', [['sin', [60]], '*', ['sin', [60]]]]]]]
[0]:
['sqrt', [[1, '-', [['sin', [60]], '*', ['sin', [60]]]]]]
[0]:
sqrt
[1]:
[[1, '-', [['sin', [60]], '*', ['sin', [60]]]]]
[0]:
[1, '-', [['sin', [60]], '*', ['sin', [60]]]]
[0]:
1
[1]:
-
[2]:
[['sin', [60]], '*', ['sin', [60]]]
[0]:
['sin', [60]]
[0]:
sin
[1]:
[60]
[1]:
*
[2]:
['sin', [60]]
[0]:
sin
[1]:
[60]
divmod(a, 100)
[['divmod', ['a', 100]]]
[0]:
['divmod', ['a', 100]]
[0]:
divmod
[1]:
['a', 100]
iif(iif(condition1,value1,value2)>iif(condition2,value1,value2),value3,value4)
[['iif', [[['iif', ['condition1', 'value1', 'value2']], '>', ['iif', ['condition2', 'value1', 'value2']]], 'value3', 'value4']]]
[0]:
['iif', [[['iif', ['condition1', 'value1', 'value2']], '>', ['iif', ['condition2', 'value1', 'value2']]], 'value3', 'value4']]
[0]:
iif
[1]:
[[['iif', ['condition1', 'value1', 'value2']], '>', ['iif', ['condition2', 'value1', 'value2']]], 'value3', 'value4']
[0]:
[['iif', ['condition1', 'value1', 'value2']], '>', ['iif', ['condition2', 'value1', 'value2']]]
[0]:
['iif', ['condition1', 'value1', 'value2']]
[0]:
iif
[1]:
['condition1', 'value1', 'value2']
[1]:
>
[2]:
['iif', ['condition2', 'value1', 'value2']]
[0]:
iif
[1]:
['condition2', 'value1', 'value2']
[1]:
value3
[2]:
value4