3 ms·
The real solution to this problem is to properly remove the left recursion. Student's often make the mistake the author did. Let's examine our favorite grammar:
by timtadh 12y ago
The real solution to this problem is to properly remove the left recursion. Student's often make the mistake the author did. Let's examine our favorite grammar:
E -> E + T
| E - T
| T
T -> T * F
| T / F
| F
F -> id
| number
| ( E )
The obvious way to remove left recursion in the grammar is to "flip" the non-terminals. Like so:
E -> T + E
| T - E
| T
T -> F * T
| F / T
| F
F -> id
| number
| ( E )
This is wrong. Consider the string "28 / 7 * 2". The orginal grammar produces the following parse:
E
|
T
/|\
T * F
/|\ |
T / F 2
| |
F 7
|
28
The new grammar produces:
E
|
T
/|\
F / T
| /|\
28 F * T
| |
7 F
|
2
When computing the expression. These tree produce different answers. The first gives "8", the correct answer, the second gives "2". Basically, it is parenthesized incorrectly.
To fix this problem the solution is to insert intermediate non-terminals into the grammar:
E -> T E'
E' -> + T E'
| - T E'
| e <--- standing in for epsilon, the empty string
T -> F T'
T' -> * F T'
| / F T'
| e
F -> id
| number
| ( E )
Consider the new parse tree
E
/ \
/ \
T E'
/ \ |
F T' e
| /|\
28 / | \
/ | \
/ F T'
| /|\
7 * F T'
| |
2 e
If processed correctly this tree will yield the correct expression tree.
*
/ \
/ 2
/ \
28 7
The "trick" is to swing up subtrees from the prime nodes. The operator coming up from the prime node is going to be the root of the subtree (for that precedence level) and the new operator is going to be inserted as the left most descendent.
To see how this works with code. Checkout this example recursive descent parser for this grammar.
https://github.com/timtadh/tcel/blob/master/frontend/parser.go#L361 https://github.com/timtadh/tcel/blob/master/frontend/parser....
The swing and collapse functions are defined here:
https://github.com/timtadh/tcel/blob/master/frontend/parser.go#L361 https://github.com/timtadh/tcel/blob/master/frontend/parser....
You can read all about how to do this correctly in the "Dragon Book" Section 4.3.3 (page 212) in the second edition. "Compilers: Principles, Techniques, & Tools" by Aho et. al.