Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/357.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python pyparsing nestedExpr和嵌套括号_Python_Nested_Pyparsing - Fatal编程技术网

Python pyparsing nestedExpr和嵌套括号

Python pyparsing nestedExpr和嵌套括号,python,nested,pyparsing,Python,Nested,Pyparsing,我正在研究一种非常简单的“查询语法”,可以被具有合理技术技能的人使用(即,不是编码人员本身,而是能够触及主题) 他们在表格上输入内容的典型示例如下: address like street AND vote = True AND ( ( age>=25 AND gender = M ) OR ( age between [20,30] AND gender = F ) OR ( age >= 70

我正在研究一种非常简单的“查询语法”,可以被具有合理技术技能的人使用(即,不是编码人员本身,而是能够触及主题)

他们在表格上输入内容的典型示例如下:

address like street
AND
vote =  True
AND
(
  (
    age>=25
    AND
    gender = M
  )
  OR
  (
    age between [20,30]
    AND
    gender = F
  )
  OR
  (
    age >= 70
    AND
    eyes != blue
  )
)

  • 不需要报价
  • 括号的潜在无限嵌套
  • 简单和|或链接
  • 我正在使用pyparsing(嗯,无论如何都在尝试)并实现以下目标:

    from pyparsing import *
    
    OPERATORS = [
        '<',
        '<=',
        '>',
        '>=',
        '=',
        '!=',
        'like'
        'regexp',
        'between'
    ]
    
    unicode_printables = u''.join(unichr(c) for c in xrange(65536)
                                  if not unichr(c).isspace())
    
    # user_input is the text sent by the client form
    user_input = ' '.join(user_input.split())
    user_input = '(' + user_input + ')'
    
    AND = Keyword("AND").setName('AND')
    OR = Keyword("OR").setName('OR')
    
    FIELD = Word(alphanums).setName('FIELD')
    OPERATOR = oneOf(OPERATORS).setName('OPERATOR')
    VALUE = Word(unicode_printables).setName('VALUE')
    CRITERION = FIELD + OPERATOR + VALUE
    
    QUERY = Forward()
    NESTED_PARENTHESES = nestedExpr('(', ')')
    QUERY << ( CRITERION | AND | OR | NESTED_PARENTHESES )
    
    RESULT = QUERY.parseString(user_input)
    RESULT.pprint()
    
    我对此只感到部分满意-主要原因是期望的最终输出如下所示:

    [
      {
        "field" : "address",
        "operator" : "like",
        "value" : "street",
      },
      'AND',
      {
        "field" : "vote",
        "operator" : "=",
        "value" : True,
      },
      'AND',
      [
        [
          {
            "field" : "age",
            "operator" : ">=",
            "value" : 25,
          },
          'AND'
          {
            "field" : "gender",
            "operator" : "=",
            "value" : "M",
          }
        ],
        'OR',
        [
          {
            "field" : "age",
            "operator" : "between",
            "value" : [20,30],
          },
          'AND'
          {
            "field" : "gender",
            "operator" : "=",
            "value" : "F",
          }
        ],
        'OR',
        [
          {
            "field" : "age",
            "operator" : ">=",
            "value" : 70,
          },
          'AND'
          {
            "field" : "eyes",
            "operator" : "!=",
            "value" : "blue",
          }
        ],
      ]
    ]
    
    非常感谢

    编辑

    在Paul的回答之后,代码就是这样的。显然,它工作得更好:-)

    输出为:

    [['address',
      'like',
      'street',
      'AND',
      'vote',
      '=',
      'True',
      'AND',
      [['age>=25', 'AND', 'gender', '=', 'M'],
       'OR',
       ['age', 'between', '[20,30]', 'AND', 'gender', '=', 'F'],
       'OR',
       ['age', '>=', '70', 'AND', 'eyes', '!=', 'blue']]]]
    
    [
      [
        <[snip]ComparisonExpr instance at 0x043D0918>,
        'AND',
        <[snip]ComparisonExpr instance at 0x043D0F08>,
        'AND',
        [
          [
            <[snip]ComparisonExpr instance at 0x043D3878>,
            'AND',
            <[snip]ComparisonExpr instance at 0x043D3170>
          ],
          'OR',
          [
            [
              <[snip]ComparisonExpr instance at 0x043D3030>,
              'AND',
              <[snip]ComparisonExpr instance at 0x043D3620>
            ],
            'AND',
            [
              <[snip]ComparisonExpr instance at 0x043D3210>,
              'AND',
              <[snip]ComparisonExpr instance at 0x043D34E0>
            ]
          ]
        ]
      ]
    ]
    
    哪些产出:

    [{'field': 'address', 'operator': 'LIKE', 'value': 'street'},
       'AND',
       {'field': 'vote', 'operator': '=', 'value': 'true'},
       'AND',
       [[{'field': 'age', 'operator': '>=', 'value': '25'},
         'AND',
         {'field': 'gender', 'operator': '=', 'value': 'M'}],
        'OR',
        [[{'field': 'age', 'operator': 'BETWEEN', 'value': '[20,30]'},
          'AND',
          {'field': 'gender', 'operator': '=', 'value': 'F'}],
         'AND',
         [{'field': 'age', 'operator': '>=', 'value': '70'},
          'AND',
          {'field': 'eyes', 'operator': '!=', 'value': 'blue'}]]]]
    
    再次感谢保罗为我指明了正确的方向


    唯一未知的是,我将
    'true'
    转换为
    true
    并将
    '[20,30]
    转换为
    [20,30]
    nestedExpr
    是pyparsing中的一个方便表达式,可以轻松定义具有匹配的开头和结尾字符的文本。当您想要解析嵌套内容时,
    nestedExpr
    的结构通常不够好

    使用pyparsing的
    infixNotation
    方法可以更好地解析您试图解析的查询语法。您可以在pyparsing wiki的示例页面上看到几个示例—SimpleBool与您正在解析的非常相似

    “中缀表示法”是一个通用的解析术语,用于运算符位于其相关操作数之间的表达式(与运算符紧跟操作数的“后缀表示法”不同,如“23+”而不是“2+3”;或“前缀表示法”类似于“+23”)。运算符的求值顺序可以覆盖从左到右的顺序-例如,在“2+3*4”中,运算的优先级指示乘法在加法之前求值。中缀表示法还支持使用括号或其他分组字符来覆盖该优先级,如“(2+3)*4”中强制首先执行加法操作

    pyparsing的
    infixNotation
    方法采用一个基本操作数表达式,然后按照优先级顺序采用一个运算符定义元组列表。例如,4函数整数算法如下所示:

    parser = infixNotation(integer,
                 [
                 (oneOf('* /'), 2, opAssoc.LEFT),
                 (oneOf('+ -'), 2, opAssoc.LEFT),
                 ])
    
    这意味着我们将按顺序解析整数操作数,其中包含“*”和“/”二进制左关联运算以及“+”和“-”二进制运算。
    infixNotation
    内置了对括号覆盖顺序的支持

    查询字符串通常是布尔运算的一些组合,而不是、和、和或,并且通常按该优先顺序进行计算。在您的例子中,这些运算符的操作数是比较表达式,如“address=street”或“age-between[20,30]”。因此,如果您为比较表达式定义了一个表达式,其形式为
    fieldname运算符值
    ,则可以使用
    infixNotation
    对AND和OR进行正确分组:

    import pyparsing as pp
    query_expr = pp.infixNotation(comparison_expr,
                    [
                        (NOT, 1, pp.opAssoc.RIGHT,),
                        (AND, 2, pp.opAssoc.LEFT,),
                        (OR, 2, pp.opAssoc.LEFT,),
                    ])
    
    最后,我建议您定义一个类,将比较标记作为类init args,然后可以将行为附加到该类以评估比较并输出调试字符串,如:

    class ComparisonExpr:
        def __init__(self, tokens):
            self.tokens = tokens
    
        def __str__(self):
            return "Comparison:('field': {!r}, 'operator': {!r}, 'value': {!r})".format(
                                *self.tokens.asList())
    
    # attach the class to the comparison expression
    comparison_expr.addParseAction(ComparisonExpr)
    
    然后您可以得到如下输出:

    query_expr.parseString(sample).pprint()
    
    [[Comparison:({'field': 'address', 'operator': 'like', 'value': 'street'}),
      'AND',
      Comparison:({'field': 'vote', 'operator': '=', 'value': True}),
      'AND',
      [[Comparison:({'field': 'age', 'operator': '>=', 'value': 25}),
        'AND',
        Comparison:({'field': 'gender', 'operator': '=', 'value': 'M'})],
       'OR',
       [Comparison:({'field': 'age', 'operator': 'between', 'value': [20, 30]}),
        'AND',
        Comparison:({'field': 'gender', 'operator': '=', 'value': 'F'})],
       'OR',
       [Comparison:({'field': 'age', 'operator': '>=', 'value': 70}),
        'AND',
        Comparison:({'field': 'eyes', 'operator': '!=', 'value': 'blue'})]]]]
    
    py示例提供了更多详细信息,说明如何创建此类以及NOT、and、and或运算符的相关类

    编辑:

    “是否有一种方法可以使用字典返回结果,而不使用ComparisonExpr实例?” 正在调用
    comparisonextpr
    类上的
    \uuuuu repr\uuuu
    方法,而不是
    \uuu str\uuuu
    。最简单的解决方案是向类中添加:

    __repr__ = __str__
    
    或者将
    \uuuu str\uuuu
    重命名为
    \uuuu repr\uuuu

    “对我来说,唯一未知的事情就是把‘真’变成真,‘[20,30]”变成[20,30]”

    尝试:

    然后将这些添加到值表达式中:

    VALUE = bool_literal | num_list | Word(unicode_printables)
    
    最后:

    from pprint import pprint
    pprint(RESULT)
    
    我厌倦了一直导入
    pprint
    来完成这项工作,我只是将它添加到API中,用于
    ParseResults
    。尝试:

    RESULT.pprint()  # no import required on your part
    

    EDIT2

    最后,结果名称很好学习。如果您对比较进行了此更改,则一切仍按您所拥有的方式运行:

    COMPARISON = FIELD('field') + OPERATOR('operator') + VALUE('value')
    
    但现在你可以写:

    def asDict(self):
        return self.tokens.asDict()
    

    您可以通过名称而不是索引位置来访问解析的值(使用
    result['field']
    notation或
    result.field
    notation)。

    ,我花时间在某地和其他地方,检查我选择的东西是否受到社区的欢迎。pyparsing不仅如此,它的作者还提供了惊人的答案和支持。这真是个道具,保罗!然后回到主题:谢谢,我将修改我的代码,并相应地修改问题!
    RESULT.pprint()  # no import required on your part
    
    print(RESULT.dump()) # will also show indented list of named fields
    
    COMPARISON = FIELD('field') + OPERATOR('operator') + VALUE('value')
    
    def asDict(self):
        return self.tokens.asDict()