如何以编程方式将表达式括起来?

时间:2009-02-14 06:35:06

标签: algorithm parsing operator-precedence parentheses

我想要一个简单的程序来帮助我在C语言中使用运算符优先级。最困难的部分是括号表达式。例如,我想要这个:

*a.x++ = *b.x++

转换为:

((*(((a).(x))++)) = (*(((b).(x))++)))

我在这些步骤中手动完成了这项工作:

           *a.x++ = *b.x++
       *(a).(x)++ = *(b).(x)++
     *((a).(x))++ = *((b).(x))++
   *(((a).(x))++) = *(((b).(x))++)
 (*(((a).(x))++)) = (*(((b).(x))++))
((*(((a).(x))++)) = (*(((b).(x))++)))

以编程方式完成此操作的最佳方法是什么?我可以使用的解决方案吗?我更喜欢在PHP,C,C ++,Python或Ruby中执行此操作。

(这不是我的计划的全部想法,这只是第一步。)

10 个答案:

答案 0 :(得分:6)

您将需要某种能够理解运算符优先级的解析器。 C的常用版本是Lexx / Yacc或flex / bison,最简单的方法是构造一个解析树。完成后,只需按照“预购”顺序浏览解析树,并在进入和离开节点时发出parens。

答案 1 :(得分:4)

最可靠的方法是parse the expression(当然要考虑precedence rules),然后以自上而下的方式处理生成的AST(抽象语法树),为您添加括号继续前进

答案 2 :(得分:3)

如何转换为后缀和评估。 如果以下方法有效,您能试试吗? 让我们拿* a.x ++

Operator    Precedence  Arguments Needed
.           3               2
++          2               1
*           1               1

所以现在将表达式转换为后缀表示法。这应该给你

a x . ++ *

现在对postfix的评估就像将事物推入堆栈一样简单,当你点击运算符时,弹出顶部n(运算符需要)元素并将它们作为参数传递,将结果存储回堆栈。 在您的情况下,您将返回操作的文本表示

,而不是评估
        Stack
Read a      a
Read x      x
Read .      (a.x)
Read ++     ((a.x)++)
Read *      (*((a.x)++))

如果有帮助,您可能需要查看:
http://www.spsu.edu/cs/faculty/bbrown/web_lectures/postfix/
Bart de smet's DynCalc series of posts
My attempt at TDDing a similar solution

答案 3 :(得分:2)

您可以从运算符创建二进制表达式树。

我相信有几种算法可以在线创建这样的树。

我能想到的一个简单方法是通过优先级对运算符进行排序,然后由首先执行优先级最低的运算符将字符串拆分为2部分,然后继续递归地将其他2个部分反复拆分,然后最终,你将以二叉树形式表达。

然后当你有二进制树形式的表达式时,你可以从树的树叶“括起来”直到根。

当然,你可以通过yacc / bison编译一个完整的解析器。

答案 4 :(得分:2)

只需选择所选语言的解析器,例如C parser,解析表达式/源代码并以您希望的方式打印AST。

test.c的:

void main(void){
    int c = 2;
}

终端:

$ python
>>> import pycparser
>>> test = pycparser.parse_file('test.c')
>>> test.show()
FileAST: 
  FuncDef: 
    Decl: main, [], []
      FuncDecl: 
        ParamList: 
          Typename: []
            TypeDecl: None, []
              IdentifierType: ['void']
        TypeDecl: main, []
          IdentifierType: ['void']
    Compound: 
      Decl: c, [], []
        TypeDecl: c, []
          IdentifierType: ['int']
        Constant: int, 2
>>> for node in test.ext:
...     print node
...
<pycparser.c_ast.FuncDef object at 0x7fe1436db750>
>>>

答案 5 :(得分:1)

举个简单的例子:

Exp = Term | Exp, AddOp, Term 
Term = Factor | Term, MulOp, Factor
Factor = Number | Ident | PreOp, Factor | (, Exp, ) | Factor, PostOp

您可以使用语法编写翻译:

Exp    = Term                    -> Term
       | Exp, AddOp, Term        -> (, Exp, AddOp, Term, )
Term   = Factor                  -> Factor
       | Term, MulOp, Factor     -> (, Term, MulOp, Factor, )
Factor = Number                  -> Number
       | Ident                   -> Ident
       | PreOp, Factor           -> (, PreOp, Factor, )
       | (, Exp, )               -> (, Exp, )
       | Factor, PostOp          -> (, Factor, PostOp, )

在哪种情况下:

a-- + b * (a+b) 

转换为:

((a--) + (b * ((a+b))))

答案 6 :(得分:1)

解析是一个很大的话题。由于您只是想用它来解决特定问题,因此尽量不要沉浸在人们所建议的所有这些特定解析算法中。相反,有许多解析器生成器,例如鹿角或野牛,它们在给定适当的语法的情况下,将解析文本并允许您对组件执行编程操作 - 例如在它们周围放置括号。其中一些系统带有C语法,或者有这样的语法。

antlr可以使用您提到的任何语言生成解析器;见http://www.antlr.org/

答案 7 :(得分:1)

您可以在旧的net.sources新闻组的档案中找到“cparen”。

如果你搜索(谷歌)'cparen',你会得到太多的噪音,但如果你搜索 对于net.sources和'cparen.c',它使搜索范围变得足够有用。

这是一个网站:

http://www.megalextoria.com/usenet-archive/news005f3/b14/net/sources/00000360.html

正如我所料,它不是一个shell存档。 它看起来像一个纯ASCII文本tar文件。 你可以只有足够的文件 用手打开包装。

答案 8 :(得分:1)

我在Python中编写了一个程序来括起表达式字符串。

def pref(op):
    print "called with op", op
    ret = -1
    if op == '+':
        print "matched +"
        ret = 1
    if op == '-':
        print "matched -"
        ret = 2
    if op == '*':
        print "matched *"
        ret = 3
    if op == '/':
        print "matched /"
        ret = 4

    return ret

def evaluate(expr, operand_stack, operator_stack):
    print "**In evaluate**"
    print operator_stack
    print operand_stack

    expr1 = operand_stack.pop()
    expr2 = operand_stack.pop()
    op    = operator_stack.pop()

    # Parenthesize the expression
    expr = "(" + expr2 + op + expr1 + ")"
    print "expr1", expr1
    print "expr2", expr2
    print "expr", expr

    # Push the result back on the stack
    operand_stack.append(expr)

    print operator_stack
    print operand_stack
    print "**Out evaluate**"
    return expr

def looper(str, expr, operator_stack, operand_stack):
    l = 0
    cnt = len(str)

    # Loop over the input string
    while  l < cnt:
        if str[l] in ('+', '-', '*', '/'):
            print "operator found: op, index", str[l], l
            print operator_stack, len(operator_stack)

            x = len(operator_stack) - 1
            if x > 0:
                print "Comparing:", operator_stack[x], str[l]

                # If op on stack has higher preference than the op in question
                if (pref(operator_stack[x]) > pref(str[l])):
                    expr = evaluate(expr, operand_stack, operator_stack)
            operator_stack.append(str[l])
        else:
            # Add the operand to operand stack
            operand_stack.append(str[l])
        l += 1

    print operator_stack
    print operand_stack

    print "Take care of last elements"
    op_cnt = len(operator_stack)
    while op_cnt:
        expr = evaluate(expr, operand_stack, operator_stack)
        op_cnt -= 1

    print operator_stack
    print operand_stack

if __name__ == '__main__':
    str = "a+c*d-e/w*x+a-s"
    cnt = len(str)

    operand_stack  = []
    operator_stack  = []
    expr = ""
    looper(str, expr, operator_stack, operand_stack)

    print "Output=>", operand_stack[0]

答案 9 :(得分:0)

有一个非常古老的(1980年代)开源程序正是这样做的。 它被称为“cparen”,但如果我能在网上找到它,我该死的。只有热情提及它,例如 https://groups.google.com/group/comp.lang.c/tree/browse_frm/month/1990-03/1583b4728a6d94db http://www.language-c.info/re-should-i-capitalize-const-identifiers

如果你找到它比我更幸运,请写下