我正在尝试实施&&和||用于赋值的bash shell的运算符 - 在C.中输入如:
a && b && c
我的解析器使用“符号”和“连接符”创建链接列表,即:
a - &&
b - &&
c - END
所以我可以评估符号以确定是否检查下一个条件。我知道基本的想法,但是有一个如何实现条件的通用算法?主要是,如果某人传递了类似a && (b | c) || d && e || f
的内容,那么我只是不知道什么是攻击它的好方法。我尝试评估第一个符号并存储状态以检查下一步要读什么,但这不是一个好方法。
有什么建议吗?
答案 0 :(得分:3)
实现此操作的一般方法导致语法树,而不是列表。您需要将令牌列表(这是您创建的)解析为描述表达式的AST(抽象语法树)。
现在无法按照您现在的方式获得优先权。这对&&
和||
运算符的短路评估属性至关重要。
以下是Python中的一些示例代码,用于构建树结构。它有点难看,如果我真的这样做,我会写一个更漂亮的解析器。这只是为了给你一般的想法:
def parsellst(lst):
stack = []
for tok in lst:
if tok == '(':
if (len(stack) > 0) and (stack[-1] not in ('&&', '||')):
raise RuntimeError("Malformed expression")
stack.append(tok)
elif tok == ')':
while (len(stack) >= 4) and (stack[-2] != '('):
if stack[-2] not in ('&&', '||'):
raise RuntimeError("Malformed expression")
node = (stack[-2], stack[-3], stack[-1])
stack[-3:] = [node]
if (len(stack) <= 1) or (stack[-2] != '(') or \
(stack[-1] in ('&&', '||')):
raise RuntimeError("Malformed expression")
stack.pop(-2)
elif tok == '||':
if len(stack) <= 0:
raise RuntimeError("Malformed expression")
elif stack[-1] in ('&&', '||', '('):
raise RuntimeError("Malformed expression")
while (len(stack) >= 3) and (stack[-2] != '('):
node = (stack[-2], stack[-3], stack[-1])
stack[-3:] = [node]
stack.append(tok)
elif tok == '&&':
if len(stack) <= 0:
raise RuntimeError("Malformed expression")
elif stack[-1] in ('&&', '||', '('):
raise RuntimeError("Malformed expression")
if (len(stack) > 1) and (len(stack) < 3):
raise RuntimeError("Malformed expression")
while (len(stack) >= 3) and (stack[-2] not in ('(', '||')):
node = (stack[-2], stack[-3], stack[-1])
stack[-3:] = [node]
stack.append(tok)
else:
stack.append(tok)
while len(stack) > 1:
if len(stack) < 3:
raise RuntimeError("Malformed expression")
if stack[-2] == '(':
raise RuntimeError("Malformed expression: missing )")
else:
node = (stack[-2], stack[-3], stack[-1])
stack[-3:] = [node]
if len(stack) > 0:
return stack[-1]
else:
return ()
此代码构建的树结构非常低迷。树的每个节点都包含一个Python中的'元组'(有点像lisp列表),看起来像(operator, arg1, arg2)
。由你来决定如何使用它。作为提示,您必须编写一个以特定顺序处理树的求值程序。
此外,此解析器会删除括号,因为它将括号视为仅仅是一个分组构造。但对于bash
而言,这并不明智,因为括号的含义超出了单纯的分组。因此,应该修改(这是相对微不足道的)将“括号运算符”放回到结果树中。
以下是如何使用解析器及其输出结果的示例:
>>> parsellst(['a', '&&', '(', 'b', '||', 'c', ')', '||', 'd', '&&', 'e', '||', 'f'])
('||', ('||', ('&&', 'a', ('||', 'b', 'c')), ('&&', 'd', 'e')), 'f')
答案 1 :(得分:1)
使用解析树:http://en.wikipedia.org/wiki/Parse_tree
我举了一个你可以在这里使用的树的例子:http://csclub.uwaterloo.ca/~mtahmed/parse.txt
在这个txt文件中,\
字符是链接,-
个字符构成分支,字母组成变量,其他所有字符都是&&
和{{}之类的运算符1}}和|
等。
另外,请注意,由于存在短路,您可以在解析功能中添加检查,以查看是否存在短路并停止。所以例如如果您看到||
为false且下一个运算符为a
,则停止解析并返回false。