Equation (expression) parser with precedence?

前端 未结 23 1503
遇见更好的自我
遇见更好的自我 2020-11-22 11:44

I\'ve developed an equation parser using a simple stack algorithm that will handle binary (+, -, |, &, *, /, etc) operators, unary (!) operators, and parenthesis.

<
相关标签:
23条回答
  • 2020-11-22 12:19

    It would help if you could describe the grammar you are currently using to parse. Sounds like the problem might lie there!

    Edit:

    The fact that you don't understand the grammar question and that 'you've written this by hand' very likely explains why you're having problems with expressions of the form '1+11*5' (i.e., with operator precedence). Googling for 'grammar for arithmetic expressions', for example, should yield some good pointers. Such a grammar need not be complicated:

    <Exp> ::= <Exp> + <Term> |
              <Exp> - <Term> |
              <Term>
    
    <Term> ::= <Term> * <Factor> |
               <Term> / <Factor> |
               <Factor>
    
    <Factor> ::= x | y | ... |
                 ( <Exp> ) |
                 - <Factor> |
                 <Number>
    

    would do the trick for example, and can be trivially augmented to take care of some more complicated expressions (including functions for example, or powers,...).

    I suggest you have a look at this thread, for example.

    Almost all introductions to grammars/parsing treat arithmetic expressions as an example.

    Note that using a grammar does not at all imply using a specific tool (a la Yacc, Bison,...). Indeed, you most certainly are already using the following grammar:

    <Exp>  :: <Leaf> | <Exp> <Op> <Leaf>
    
    <Op>   :: + | - | * | /
    
    <Leaf> :: <Number> | (<Exp>)
    

    (or something of the kind) without knowing it!

    0 讨论(0)
  • 2020-11-22 12:19

    i released an expression parser based on Dijkstra's Shunting Yard algorithm, under the terms of the Apache License 2.0:

    http://projects.congrace.de/exp4j/index.html

    0 讨论(0)
  • 2020-11-22 12:19

    A Python solution using pyparsing can be found here. Parsing infix notation with various operators with precedence is fairly common, and so pyparsing also includes the infixNotation (formerly operatorPrecedence) expression builder. With it you can easily define boolean expressions using "AND", "OR", "NOT", for example. Or you can expand your four-function arithmetic to use other operators, such as ! for factorial, or '%' for modulus, or add P and C operators to compute permutations and combinations. You could write an infix parser for matrix notation, that includes handling of '-1' or 'T' operators (for inversion and transpose). The operatorPrecedence example of a 4-function parser (with '!' thrown in for fun) is here and a more fully featured parser and evaluator is here.

    0 讨论(0)
  • 2020-11-22 12:22

    The shunting yard algorithm is the right tool for this. Wikipedia is really confusing about this, but basically the algorithm works like this:

    Say, you want to evaluate 1 + 2 * 3 + 4. Intuitively, you "know" you have to do the 2 * 3 first, but how do you get this result? The key is to realize that when you're scanning the string from left to right, you will evaluate an operator when the operator that follows it has a lower (or equal to) precedence. In the context of the example, here's what you want to do:

    1. Look at: 1 + 2, don't do anything.
    2. Now look at 1 + 2 * 3, still don't do anything.
    3. Now look at 1 + 2 * 3 + 4, now you know that 2 * 3 has to to be evaluated because the next operator has lower precedence.

    How do you implement this?

    You want to have two stacks, one for numbers, and another for operators. You push numbers onto the stack all the time. You compare each new operator with the one at the top of the stack, if the one on top of the stack has higher priority, you pop it off the operator stack, pop the operands off the number stack, apply the operator and push the result onto the number stack. Now you repeat the comparison with the top of stack operator.

    Coming back to the example, it works like this:

    N = [ ] Ops = [ ]

    • Read 1. N = [1], Ops = [ ]
    • Read +. N = [1], Ops = [+]
    • Read 2. N = [1 2], Ops = [+]
    • Read *. N = [1 2], Ops = [+ *]
    • Read 3. N = [1 2 3], Ops = [+ *]
    • Read +. N = [1 2 3], Ops = [+ *]
      • Pop 3, 2 and execute 2*3, and push result onto N. N = [1 6], Ops = [+]
      • + is left associative, so you want to pop 1, 6 off as well and execute the +. N = [7], Ops = [].
      • Finally push the [+] onto the operator stack. N = [7], Ops = [+].
    • Read 4. N = [7 4]. Ops = [+].
    • You're run out off input, so you want to empty the stacks now. Upon which you will get the result 11.

    There, that's not so difficult, is it? And it makes no invocations to any grammars or parser generators.

    0 讨论(0)
  • 2020-11-22 12:22

    As you put your question there is no need for recursion whatsoever. The answer is three things: Postfix notation plus Shunting Yard algorithm plus Postfix expression evaluation:

    1). Postfix notation = invented to eliminate the need for explicit precedence specification. Read more on the net but here is the gist of it: infix expression ( 1 + 2 ) * 3 while easy for humans to read and process not very efficient for computing via machine. What is? Simple rule that says "rewrite expression by caching in precedence,then always process it left-to-right". So infix ( 1 + 2 ) * 3 becomes a postfix 12+3*. POST because operator is placed always AFTER the operands.

    2). Evaluating postfix expression. Easy. Read numbers off postfix string. Push them on a stack until an operator is seen. Check operator type - unary? binary? tertiary? Pop as many operands off stack as needed to evaluate this operator. Evaluate. Push result back on stack! And u r almost done. Keep doing so until stack has only one entry = value u r looking for.

    Let's do ( 1 + 2 ) * 3 which is in postfix is "12+3*". Read first number = 1. Push it on stack. Read next. Number = 2. Push it on stack. Read next. Operator. Which one? +. What kind? Binary = needs two operands. Pop stack twice = argright is 2 and argleft is 1. 1 + 2 is 3. Push 3 back on stack. Read next from postfix string. Its a number. 3.Push. Read next. Operator. Which one? *. What kind? Binary = needs two numbers -> pop stack twice. First pop into argright, second time into argleft. Evaluate operation - 3 times 3 is 9.Push 9 on stack. Read next postfix char. It's null. End of input. Pop stack onec = that's your answer.

    3). Shunting Yard is used to transform human (easily) readable infix expression into postfix expression (also human easily readable after some practice). Easy to code manually. See comments above and net.

    0 讨论(0)
  • 2020-11-22 12:23

    There's a nice article here about combining a simple recursive-descent parser with operator-precedence parsing. If you've been recently writing parsers, it should be very interesting and instructive to read.

    0 讨论(0)
提交回复
热议问题