Operator Precedence Parsing: Build the Table and Parse an Expression Step by Step
Build an operator-precedence table from a grammar, then use it to trace every shift and reduction for i+i*i.
KnowledgeGate Team
Exam prep & CS education

You may know that multiplication binds more tightly than addition, yet still find it difficult to explain how a parser turns that rule into shift and reduce decisions. The missing piece is a table of relations between terminals. The table derives from a grammar and gives explicit shift and reduce decisions for i+i*i.
Related reading: parsing MCQs and top-down and bottom-up parsing.
Operator precedence parsing: the decision behind 2+3*4
Take the token sequence i + i * i, and assign the three i tokens the values 2, 3, and 4. The intended structure is 2+(3*4)=2+12=14. The control expression (2+3)*4=5*4=20 gives a different answer, proving that precedence changes the parse tree before evaluation begins. This distinction sits inside the wider topic of top-down and bottom-up parsing.

An operator-precedence parser is a restricted bottom-up, shift-reduce parser that compares the topmost stack terminal with the next input terminal. Relations < and = mean shift across a handle boundary, while > means reduce the handle on the stack. These symbols are parsing relations, not arithmetic comparisons.
Operator-precedence grammar and the three terminal relations
The grammar is:
E -> E + T | T
T -> T * F | F
F -> ( E ) | iIts terminals are {i,+,*,(,)}, and E is the start symbol. An operator grammar has no epsilon production and no two adjacent nonterminals on the right-hand side of any production. It becomes an operator-precedence grammar only when every table cell receives at most one relation.
For terminals a and b, and a nonterminal B, the relations come from these patterns:
a = bif a right-hand side containsaboraBb.a < bif a right-hand side containsaBandbbelongs toFIRSTVT(B).a > bif a right-hand side containsBbandabelongs toLASTVT(B).
The boundary marker adds # < FIRSTVT(E), LASTVT(E) > #, and # = # for acceptance. For example, F -> ( E ) gives ( = ). Equality says that two terminals surround one nonterminal. It does not say that the terminals have equal arithmetic precedence.
FIRSTVT and LASTVT: compute the exact sets bottom up
FIRSTVT is not ordinary FIRST and FOLLOW. Here, FIRST(E) = {"(", "i"}, but FIRSTVT(E) also contains + and * because it records terminals that can become the first visible terminal beside a nonterminal in a sentential form.
Start at F and propagate upward:
From
F -> ( E ) | i,FIRSTVT(F) = {"(", "i"}andLASTVT(F) = {")", "i"}.From
T -> T * F | F, add*and inherit the sets ofF. Therefore,FIRSTVT(T) = {"*", "(", "i"}andLASTVT(T) = {"*", ")", "i"}.From
E -> E + T | T, add+and inherit the sets ofT. Therefore,FIRSTVT(E) = {"+", "*", "(", "i"}andLASTVT(E) = {"+", "*", ")", "i"}.
The propagation chain is: i belongs to FIRSTVT(F), so it enters FIRSTVT(T) through T -> F, and then FIRSTVT(E) through E -> T.
Operator-precedence table: fill every defined cell
Rows show the topmost stack terminal, and columns show the next input terminal. A blank cell is an error, not an implied relation.
Stack terminal / next input |
|
|
|
|
|
|
|---|---|---|---|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| blank |
|
|
| blank |
| blank |
|
|
|
| blank |
| blank |
|
|
|
|
| blank |
|
|
+ < * follows because +T occurs and * belongs to FIRSTVT(T). * > + follows because E+ occurs and * belongs to LASTVT(E). The entries + > + and * > * encode left associativity. The relation ( = ) comes directly from (E).
If one cell receives two different relations, the grammar is not usable as an operator-precedence grammar in that form.
Operator-precedence parsing worked example: trace i+i*i#
Each reduced nonterminal is written as N, which can stand for the appropriate F, T, or E. Precedence decisions depend only on terminals.
Step | Stack | Remaining input | Relation | Action |
|---|---|---|---|---|
0 |
|
|
| shift |
1 |
|
|
| reduce |
2 |
|
|
| shift |
3 |
|
|
| shift |
4 |
|
|
| reduce |
5 |
|
|
| shift |
6 |
|
|
| shift |
7 |
|
|
| reduce |
8 |
|
|
| reduce |
9 |
|
|
| reduce |
10 |
|
|
| accept |
The decisive shift happens at step 5. Since + < *, the parser shifts multiplication instead of reducing addition. At step 8, * > # reduces N*N first. With our values, that is 3*4=12, followed by 2+12=14.

Operator-precedence parsing traps and limits
Four traps cause most mistakes:
Confusing
FIRSTwithFIRSTVT: recompute the exact sets instead of copying ordinaryFIRST.Reducing on
<or=: shift on both, and reduce only on>.Treating a blank as precedence: reject malformed input such as adjacent
i i, since thei,icell is blank.Assuming every operator grammar is conflict-free: fill every cell and check for multiple relations.
Associativity is also visible in the table. The entry + > + makes i+i+i reduce the left pair first, and * > * does the same for multiplication. Unary minus cannot simply be inserted as another binary terminal. Its grammar and table must distinguish the unary role.
This method handles a useful class of expression grammars, not arbitrary context-free grammars or a whole programming language. When a grammar needs general LR states and conflict analysis, continue with SLR, CLR, and LALR parsers for GATE.
Operator-precedence exam patterns: what to practise
Practise five concrete question forms: test the two operator-grammar restrictions, compute FIRSTVT and LASTVT, derive a missing relation such as + < *, identify a conflict or blank error cell, and predict the next shift or reduction for a given stack and input.
Self-check: for stack #N+N with input *i#, the topmost terminal is +, so + < * means shift. For stack #N+N*N with input #, * > # means reduce N*N. These are parser actions, not arithmetic comparisons.
Use focused Operator Precedence questions to practise these decisions. For the wider preparation route, use the GATE CS Exam Preparation category to connect Compiler Design with the rest of the syllabus.
Operator precedence parsing: the short version and next step
The complete method has four steps: verify the grammar, compute FIRSTVT and LASTVT, construct a conflict-free table, then shift on < or = and reduce on > until #N with input # accepts. In the worked expression, i+i*i becomes 2+(3*4)=14.
Rebuild the table once without looking, then retrace all ten actions for i+i*i#. If you want Compiler Design sequenced with the wider GATE CS syllabus, GATE Guidance by Sanchit Sir is the relevant course path.
Keep learning

Intermediate Code Generation in Compiler Design: TAC, Backpatching and DAGs
Connect expressions, short-circuit control flow and local optimisation through a single worked translation, from source code to resolved TAC and a reusable DAG.

Ambiguous Grammars and Inherent Ambiguity: Worked CFG Examples for GATE
Learn what two derivations really prove, resolve expression ambiguity with precedence, and trace the classic inherently ambiguous language through aabbcc.

Simplification of CFG: Remove Epsilon, Unit and Useless Productions Step by Step
Simplify one context-free grammar from nine nonterminals to six. See each intermediate grammar, complete unit closures, symbol checks and final derivations.

Phases of Compiler Explained: Worked Example from Tokens to Target Code
Follow one four-line program through lexical, syntax and semantic analysis, then see its intermediate code optimized to a final stored value of 24.0.