[英]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.