字符串表达式的递归括号解析器

时间:2019-03-02 15:04:34

标签: python list parsing recursion

假设我有一个类似于下面的表达式(实际上这是一条SQL语句):

"v1 and (v2 and (v3 or v4))"

我想解析它以便处理字符串并保持括号的优先级。为此,我使用了以下递归函数

def parse_conditions(expr):
    def _helper(iter):
        items = []
        for item in iter:
            if item == '(':
                result, closeparen = _helper(iter)
                if not closeparen:
                    raise ValueError("Unbalanced parentheses")
                items.append(result)
            elif item == ')':
                return items, True
            else:
                items.append(item)
        return items, False
    return _helper(iter(expr))[0] 

提供以下输出:

print(parse_conditions("v1 and (v2 and (v3 or v4))"))

['v', '1', ' ', 'a', 'n', 'd', ' ', ['v', '2', ' ', 'a', 'n', 'd', ' ', ['v', '3', ' ', 'o', 'r', ' ', 'v', '4']]]

但是,预期输出将是

['v1 and', ['v2 and', ['v3 or v4']]]

['v1', and', ['v2', and', ['v3', 'or', 'v4']]]

有什么想法要实现这一目标吗?

1 个答案:

答案 0 :(得分:3)

您要tokenize输入。解析平衡表达式所需的最简单的分词器可以是此处的简单正则表达式,分为(),忽略空格:

import re

_tokenizer = re.compile(r'\s*([()])\s*').split
def tokenize(s):
    return filter(None, _tokenizer(s))

并使用tokenize())代替iter()

def parse_conditions(expr):
    def _helper(tokens):
        items = []
        for item in tokens:
            if item == '(':
                result, closeparen = _helper(tokens)
                if not closeparen:
                    raise ValueError("Unbalanced parentheses")
                items.append(result)
            elif item == ')':
                return items, True
            else:
                items.append(item)
        return items, False
    return _helper(tokenize(expr))[0] 

filter(None, ...)调用过滤掉re.split()在输入以()开头或结尾,或者两个{{1 }}或(字符直接彼此紧随。

演示:

)

也要拆分运算符,可以将有效的运算符添加到拆分表达式中,也可以仅添加空格作为分隔符。

分割空白,在标记中不包含空格:

>>> s = 'v1 and (v2 and (v3 or v4))'
>>> parse_conditions(s)
['v1 and', ['v2 and', ['v3 or v4']]]

产生:

_tokenizer = re.compile(r'(?:([()])|\s+)').split

同时关注有效的运算符将是:

>>> parse_conditions(s)
['v1', 'and', ['v2', 'and', ['v3', 'or', 'v4']]]

,并且对于产生相同结果的示例输入。

请注意,您的代码存在错误;它不会检测到不均衡的结束括号:

_tokenizer = re.compile(r'\s*([()]|\b(?:or|and)\b)\s*').split

您需要验证第一个>>> parse_conditions('foo) and bar') ['foo'] 调用是否为返回的元组中的第二个元素返回_helper()。代替False,使用:

return _helper(tokenize(expr))[0]

最后,我在这里不使用递归,而是使用显式堆栈来代替构建递归的调用堆栈。您自己的堆栈仅受内存限制,其中递归堆栈限制为固定大小(默认为1000):

items, closing = _helper(tokenize(expr))
if closing:  # indicating there was a closing ) without opening (
    raise ValueError("Unbalanced parentheses")
return items

您可能对查看tokenize module(对Python代码实现标记器)感兴趣。 source code使用一系列正则表达式将Python源代码拆分为令牌(令牌不仅包含令牌文本,而且还包含token type,起始和结束位置(列,行元组)和它的完整行)。