在shell中实现条件(&&和||)

时间:2011-11-27 05:17:36

标签: bash conditional

我正在尝试实施&&和||用于赋值的bash shell的运算符 - 在C.中输入如:

a && b && c

我的解析器使用“符号”和“连接符”创建链接列表,即:

a - &&

b - &&

c - END

所以我可以评估符号以确定是否检查下一个条件。我知道基本的想法,但是有一个如何实现条件的通用算法?主要是,如果某人传递了类似a && (b | c) || d && e || f的内容,那么我只是不知道什么是攻击它的好方法。我尝试评估第一个符号并存储状态以检查下一步要读什么,但这不是一个好方法。

有什么建议吗?

2 个答案:

答案 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。