Expression Parsing

Introduction

Parsing is the process of understanding a sequence of symbols written in computer or natural language and applying formal syntax to them. The phrase mathematical and logical expression evaluation is called parsing into data structures.

A symbol that can serve as an operator or parenthesis can be written alongside variables and constants in an expression. Only a few guidelines must be adhered to by this phrase. The expression is parsed using a grammar by this rule.

Notation is a way of expressing an expression in mathematics. An expression in arithmetic may now be written in three different ways:

  • Infix Notation
  • Prefix Notation
  • Postfix Notation

It is important to note that the intended expression yields the same result when written.

Associativity

The Associativity property gives you the guidelines to reorder parenthesis in an expression to provide legitimate evidence; therefore, you should understand it before you begin. Consequently, the bracket must be rearranged to provide the same value as the parent equation. As an alternative to the operators, it offers a feasible rule.

The action is irrelevant in an expression with several operators unless the operand sequence is not switched. A positional change will not affect the value of an expression expressed in infix and with brackets.  

While operators are assessed in the same precedence, most infix operators are left-associative, as expressions in Indo-European languages are read from left to right.

When evaluating the infix operators, the rising in power rule is applied. Postfix operators are left-associative, while prefix operators are often right-also right-associative

gauges that assign equal weight to operators and operands; associativity is not considered, explicitly stating this language sequence. Programmers must utilize sophisticated expressions to employ brackets, even if some languages provide non-associative operators. This adds another layer of complexity to their work.

Precedence in the Data Structure

In a statement of expressions, the order of precedence specifies which operators to use. When using Infix Notation, this is a frequently used method.

Selecting the preferred operator to assign is difficult when an operand exists between the two operators. Thus, for computation, the operator precedence rules are used. For instance, the addition operation is carried out later in this scenario, with multiplication taking precedence. 

According to a widely used but not as apparent rule, multiplication and division operations must be completed before adding or subtracting. Every operator receives equal importance since they are usually gathered similarly. "And" and "or" exhibit variance when this operation is examined logically. The "or" operator is given more precedence in several languages, which assign equal weight to it. While most languages provide arithmetic operations with the greatest Priority, some languages provide equal precedence to multiplication or "&," addition or "&," and so on.   

When priorities are not assigned correctly, overloading occurs. Negation (true/false) has a greater precedence in many languages than in vector algebra expressions, although it can also have an equal precedence.  

Types of Notation

Let's see how the operator's location determines the kind of Notation.

1. Infix Notation

Between each operand in Infix Notation, there are operators used. Infix Notation is very simple for people to read when interpreting a phrase. However, processing an infix argument with a computer algorithm takes much time and space. For instance, p + q

To do the analysis, Infix Notation requires additional data. Operators Associativity, Precedence, and brackets () are incorporated into the expression language to modify the rules.

Example: p * ( q + r ) / s

According to associativity laws, the expression must be completed from left to right, dividing by q before multiplying by p.

Similarly, principles about precedence indicate that multiplication and division operations should be completed before those involving addition and subtraction.

2. Prefix Notation

In this case, the operand is written after the operator. Another name for it is Polish Notation. For instance, +pq

<operators> <operands> <operands>

For example: p * (q + r) / s

Left-to-right evaluation is required, and the equation pattern is not altered or changed by brackets. The location "+" is to the left of "*" in this case; therefore, addition must be finished before multiplication.

In this instance, each operator operates on values directly to their left. The above "+" utilizes the "q" and "r," for instance. For more clarity, we can add up the brackets:

((p (q r+) *) s /)

As a result, the previous "p" value and the outcome of the + are taken into account and utilized by the "( )". The result of the multiplication expression and the "s" are also used by the "/".

3. Postfix Notation

The operand is specified first in postfix notation; then, an operator comes after. Reverse Polish Notation is another name for it; for example, pq+

<operands> <operands> \operators>

Concerning Postfix, it functions in the same from left to right manner as Prefix and does not require the use of "()". Operators operate on the two values closest to the right in this case. To indicate no effect on the assessment, brackets are superfluously included in the example below.

(/ (*p + q r) ) s)

The sequence of assessment changes if values include computations, as in this case when "operator evaluation is from left-to-right" and operation values are to their right. Concerning the previously mentioned example, the main operator on the left is "/."

Infix to Postfix Algorithm

A stack data structure will translate infix notation to postfix notation, and the postfix output is stored in a variable output. Here is the algorithm:

  • Proceed left to right while scanning the infix expression.
  • Add the character read to the output variable if it is an operand.
  • Check the next criteria to see if it's an operator; otherwise,
  • Push the scanning (incoming) operator to the topmost position of the stack if it is empty, has open brackets (...) at the top, or has a higher precedence order than the operator at the top of the stack.
  • Other Push the operator scanned to the stack and pop all operators from the stack that are more than or equal to the scanned operator or until an open parenthesis is found or the stack is empty.
  • Place the scanned character into the stack if it is an open parenthesis "(").
  • If the scanned character is a close parenthesis (")", remove the open parenthesis and the scanned character (close parenthesis) and pop all operators to the result until the open parenthesis "(" is found.
  • Until every character in the infix has been scanned, repeat steps 2 through 5.
  • Remove every last operator from the stack and transfer the contents to the output variable.
  • The output variable should be displayed.

Infix to Prefix Algorithm

The procedures for converting an infix to a prefix expression are the same as those for converting an expression to a Postfix, with a few extra stages. Steps 1, 2, and 10 are included in the subsequent algorithm.

  • Flip the phrase using the infix.
  • Transform every open parenthesis "(" to a closed parenthesis ")" and the opposite. (This step is necessary since, upon performing step 1, the expression's parenthesis will likewise be inverted, which we do not want.)
  • Proceed left to right while scanning the infix expression.
  • Add the scanned character to the output variable if it is an operand.
  • Check the next condition to see if it's an operator; otherwise,
  • 5.1 The (scanned) incoming operator is pushed to the stack if the stack contains nothing, the top of the stack has open braces "(")," or the precedence order of the scanned operator is higher than the operator.
  • 5.2 If necessary, remove any operators from the stack larger than or equal to the scanned operator or until an open bracket is found or the stack is cleared, and then add the scanned operator to the stack.
  • The scanned character should be pushed into the stack if it is an open parenthesis "(").
  • Should a close parenthesis (")" be found in the scanned character, remove both the open parenthesis and the scanned character (close parenthesis), then pop all operators to the output until an open parenthesis ("(")" is found.
  • To scan every character in the infix, repeat steps 4 through 7.
  • Pop all the remaining operators to empty the stack to the output variable.
  • Change the output variable's position.
  • Print the variable that was generated.

Code for converting infix to Postfix

# Priority of operators.

def Prior(char):

    if char is "^":

        return 3

    elif char is "/" or char is "*":

        return 2

    elif char is "+" or char is "-":

        return 1

    return float("inf")

def InfixToPostfix(express):

    result = ""

    stack = []

    for character in express:

        # if character is operand.

        if character.isalnum():

            result = result+ character

        # if character is an operator or '('.

        elif character in ["^", "*", "/", "+", "-", "("]:

            # Pop and append operators greater than the scanned operators.

            while (

                len(stack) > 0

                and stack[-1] != "("

                and Prior(stack[-1]) >= Prior(character)

            ):

                result += stack.pop()

            stack.append(character)

elif character is ")":

            # if character is ')'

            #   then pop until '('.

            while stack[-1] != "(":

                result += stack.pop()

            stack.pop()

    # Pop all remaining elements.

    while len(stack) > 0:

        result += stack.pop()

    return result

if __name__ == "__main__":

    infix = "s/k+(s*k)"

    print(f"Infix: {infix}")

    print(f"Postfix: {InfixToPostfix(infix)}")

Output:

Expression Parsing

Code for converting infix to Prefix

def Prior(char):

    if char is "^":

        return 3

    elif char is "/" or char is "*":

        return 2

    elif char is "+" or char is "-":

        return 1

    # When the char is '(', the loop should not be executed.

    return float("inf")

def InfixToPrefix(express):

    # Reverse the expression.

    express = express[::-1]

    result = ""

    stack = []

    for character in express:

        # Replace '(' to ')' and vice versa.

        if character is "(":

            character = ")"

        elif character is ")":

            character = "("

        # if the character is an operand.

        if character.isalnum():

            result += character

        # if character is an operator or '('.

        elif character in ["^", "*", "/", "+", "-", "("]:

            # Pop and append operators greater than the scanned operators.

            while (

                len(stack) > 0

                and stack[-1] != "("

                and Prior(stack[-1]) >= Prior(character)

            ):

                result += stack.pop()

            stack.append(character)

        elif character is ")":

            # if character is ')'

            #   then pop until '('.

            while stack[-1] != "(":

                result += stack.pop()

            stack.pop()

    # Pop all remaining elements.

    while len(stack) > 0:

        result += stack.pop()

    return result[::-1]

if __name__ == "__main__":

    infix = "a*(b+c)/d"

    print(f"Infix of this expression: {infix}")

    print(f"Prefix of this expression: {InfixToPrefix(infix)}")

Output:

Expression Parsing

Conclusion

An algorithmic technique for solving arithmetic statements is called expression parsing. As we explained, since a computer system can't understand infix statements, it takes a completely different approach than a person would.

Additionally, notations, precedence, associativity, and many aspects of expression parsing were covered. Many more symbols are used in the precedence order, comparable to BODMAS's mathematical symphony.