簡體   English   中英

字符串表達式的遞歸括號解析器

[英]Recursive parentheses parser for expressions of strings

假設我有一個類似於下面的表達式(實際上這是一條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']]]

有什么想法如何實現這一目標?

您想標記您的輸入。 解析平衡表達式所需的最簡單的令牌生成器可以是此處的簡單正則表達式,分割() ,而忽略空格:

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()在輸入以()開始或結束或兩個()字符緊隨re.split()點處產生的空字符串。

演示:

>>> 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] ,請使用:

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

最后,我在這里不使用遞歸,而是使用顯式堆棧來代替構建遞歸的調用堆棧。 您自己的堆棧僅受內存限制,其中遞歸堆棧限制為固定大小(默認為1000):

def parse_conditions(expr):
    stack = []  # or a `collections.deque()` object, which is a little faster
    top = items = []
    for token in tokenize(expr):
        if token == '(':
            stack.append(items)
            items.append([])
            items = items[-1]
        elif token == ')':
            if not stack:
                raise ValueError("Unbalanced parentheses")
            items = stack.pop()  
        else:
            items.append(token)
    if stack:
        raise ValueError("Unbalanced parentheses")
    return top

您可能對tokenize模塊感興趣,該模塊為Python代碼實現了令牌化器。 源代碼使用一系列正則表達式將Python源代碼拆分為令牌(令牌不僅包含令牌文本,還包含令牌類型 ,起始和結束位置(列,行元組)以及整行)它來自)。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM