pyparsing, как использовать infixNotation для представления iif (cond, если true, если false) - PullRequest
1 голос
/ 01 июня 2019

Мне нужно разобрать это, используя 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) )")

но я не смог изменить этот код в соответствии со своими потребностями. Как мне этого добиться?

UPDATE

Благодаря мистеру Полу, я придумал это решение:

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

Я использую некоторые из моих предыдущих правил. Сейчас я голосую, чтобы закрыть этот пост.

1 Ответ

1 голос
/ 02 июня 2019

Как @ sepp2k упоминает в своем комментарии, строка, которую вы пытаетесь проанализировать , не является инфиксной нотацией, хотя вы можете в конечном итоге использовать ее как операнд в инфиксной нотации. И аргументы, которые вы передаете iif, сами могут быть выражениями инфиксной нотации. Таким образом, инфиксная запись определенно будет частью этого синтаксического анализатора, но она не будет той частью, которая анализирует ваш iif вызов функции.

Вот как будет выглядеть вызов функции в pyparsing:

fn_call = pp.Group(var_name + LPAREN - pp.Group(pp.Optional(pp.delimitedList(arith_expr))) + RPAREN)

Операнды, которые вы используете для определения арифметического выражения, могут сами включать вызов функции, поэтому рекурсия парсера потребует от вас использования класса Forward в pyparsing.

arith_expr = pp.Forward()

Это позволит вам использовать arith_expr в других подвыражениях (как мы только что сделали в fn_call), прежде чем мы полностью определим, как выглядит arith_expr.

Сокращение до погони, вот минимальный парсер для разбора вашей 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
...