Non-Recursive Predictive Parser: Architecture and Algorithm (LL(1))
A predictive parser is typically implemented either recursively (calling parsing routines per nonterminal) or non-recursively using an explicit stack and a parsing table. The non-recursive predictive parser (most often the table-driven LL(1) parser) has a clear architecture: it maintains a stack of grammar symbols, reads the next input token (lookahead), and consults a parsing table to decide whether to expand a nonterminal or match a terminal, until it reaches an accepting configuration. This approach eliminates recursion by simulating what the call stack would do in a recursive descent parser.
Key components
- Parsing table
- Lookahead
- Parsing stack
- Start symbol
- [End marker]{def="A special token () appended to input to signal end"} $$$
- LL(1) grammar: determinism depends on FIRST/FOLLOW properties
Dataflow overview (architecture)
This architecture is commonly presented alongside the standard LL(1) table construction (using FIRST and FOLLOW). For a correct parsing table, the grammar must be LL(1), ensuring at most one production per table cell.
Important: If the parsing table cell is empty or conflicts, the parser must report an error (no valid derivation for the observed lookahead).
From Grammar to Non-Recursive Predictive Parsing
Compute FIRST sets
1Compute FIRST for terminals/nonterminals to know possible leading tokens."
Compute FOLLOW sets
2Compute FOLLOW to place productions into cells where can occur."
Build LL(1) parsing table M
3Fill M[A,a] with the unique production A→α for lookahead a (if LL(1))."
Run non-recursive parse loop
4Use explicit stack + table lookup with one-token lookahead."
Core algorithm idea
The non-recursive parser repeatedly inspects:
- the symbol on top of the stack, and
- the current input lookahead token.
Then:
- If the top symbol is a terminal:
- it must match the lookahead; otherwise error.
- If the top symbol is a nonterminal:
- consult parsing table :
- if a production exists: replace the nonterminal on the stack with the RHS (pushed in reverse order),
- else error.
- consult parsing table :
- When the stack top is \$$ and the input lookahead is also $$, the parser accepts.
This procedure is the iterative counterpart of the recursive approach that would “call” the appropriate production based on lookahead.
Key design invariants
- The parser stack always contains a suffix of the right-sentential form that should correspond to the remaining portion of the input.
- Table-driven expansion preserves derivation consistency: if the grammar is LL(1) and the table is correct, each step mimics choosing the same production that a deterministic recursive descent parser would choose.
Non-Recursive Predictive Parsing (Table-Driven LL(1))
- 1Step 1
Push start symbol onto stack . Append end marker \`$ to input. Set input pointer so the current lookahead is the first token.
- 2Step 2
Let be the top of stack. Let be the current lookahead token.
- 3Step 3
If is a terminal (including possibly \`X == aXS$ and advance input to the next token; otherwise report error (terminal mismatch).
- 4Step 4
If is a nonterminal: consult parsing table entry .
- 5Step 5
If for some production RHS , pop , then push the symbols of onto stack in reverse order. If , push nothing. If is empty, report error (no production for this lookahead).
- 6Step 6
If X == \
$ and $a == \$, accept the input. - 7Step 7
On any mismatch or empty table entry, halt and report that the input is not in the language (or cannot be parsed under the LL(1) grammar).
Table construction link (why FIRST/FOLLOW matter)
To make correct, a production must be placed into table cells that correspond to all possible lookahead tokens that can appear when is expanded. The standard LL(1) logic uses:
- For each terminal , add to .
- If , then must also be placed for all terminals , i.e., add to .
- If derives , ensure the FOLLOW propagation handles lookaheads appropriately so the parser knows when to effectively “skip” the production.
Minimal architectural consequence
Because the runtime algorithm only consults , all correctness depends on whether is constructed to satisfy LL(1) determinism (no conflicting productions in any cell).
Pro Tip: Push RHS in reverse order
When applying a production , you pop and push the RHS symbols so that the leftmost symbol of becomes the new top of stack. In practice: push RHS from right to left.
Warning: Empty or conflicting table cells are hard parse failures
If is empty, the parser cannot proceed deterministically. If multiple productions would apply (not LL(1)), you either need a different grammar (e.g., LL(2)) or a different parsing strategy.
Operational complexity
Let be the number of input tokens (excluding `$). Each loop iteration either:
- matches and advances the input (consuming at least one token), or
- expands a nonterminal by pushing RHS symbols (which increases stack content but is bounded by the derivation steps for valid inputs).
For an LL(1) predictive parser, the number of expansions and matches is linear in the length of a valid derivation, so runtime is typically described as for parsing (ignoring the cost of building ). Building FIRST/FOLLOW and is additional preprocessing.
Symbol handling guarantees
- Terminals: exactly matched or error.
- Nonterminals: expanded only when a table entry exists.
- Epsilon productions: represent “push nothing”, so the parser effectively performs a zero-length derivation step.
What Each Step Does in the Parse Loop
Runtime loop responsibilities (conceptual counts for a valid input).
Common Questions for Non-Recursive Predictive Parsing
LL(1) Parsing (Predictive Parsing) Tutorial
Knowledge Check
In a non-recursive predictive (LL(1)) parser, what is the typical role of the parsing table M?