but the syntax diagram view of the NFA is easier to read as shown in figure 2. In the early ANTLR
LL(k) lookahead analysis algorithms [7], these NFA were called grammar lookahead automata.
ANTLR’s next task is to construct the set of DFA needed by the generated parser, one DFA
for each fork in the NFA. A fork represents a parsing decision where each branch corresponds to
a pro duction in the grammar. ANTLR must create a DFA that uniquely predicts productions by
differentiating accept states with the predicted production number (e.g., states s4 and s5 Figure 1).
In addition, the DFA must represent the exact lookahead language rather than an approximation in
order to maximize parser strength. The lookahead language is generally a subset of the language
generated by the grammar rule and is the minimal set of input sequences that dis tinguishes between
alternative productions.
ANTLR expresses the DFA construction problem as a variant of the classical NFA to DFA subset
construction algorithm that uses reach and closure operations [6]. The classical algorithm uses a
simple set of NFA states to represent a DFA state. This set of states encodes the set of possible
NFA configurations that the NFA could be in after accepting a particular input sequence. An NFA
configuration is just a state number in the classical algorithm, but ANTLR’s NFA configurations are
tuples: (NFA state, production, context). ANTLR uses the predicted production to split DFA accept
states and uses grammatical context to differentiate NFA configurations within a DFA state. The
grammatical context is purely a rule invocation stack in its most basic form. NFA closure operations
push NFA states as they transition to rule start states. Closure operations that reach a rule’s stop
NFA state “return” to the state that invoked that rule rather than following all emanating transitions.
With this context information, ANTLR pursues only those NFA paths corresponding to valid rule
invocation sequences in a grammar; i.e., ANTLR examines the exact lookahead language rather than
an approximation. ANTLR needs a stack per NFA state not because the NFA has no stack, but
because the DFA conversion algorithm pursues all possible lookahead sequences in parallel.
The closure operation and the entire algorithm terminate due to a finite amount of work like the
original subset construction algorithm. Because recursion could force closure operations to build in-
finitely large context stacks, the closure operation caps context stack size. Decisions where recursion
would force infinitely large stacks during closure are not LL(∗) anyway, so ANTLR can immediately
report an LL(∗) nondeterminism in such situations (recursion in more than one alternative produc-
tion). With finite stacks, there are a finite number of NFA configurations per DFA state and, hence,
a finite number of unique DFA states (sets of NFA configurations). A finite number of DFA states
leads to a finite amount of work.
ANTLR’s LL(∗) algorithm is the dual of Bermudez’s LAR(m) [8], which uses cyclic lookahead
DFA to resolve nondeterministic LR(0) states. The only substantive difference is that Bermudez
bounds the LAR(m) algorithm’s context stack depth at analysis time, m, whereas ANTLR limits
only the recursion depth rather than absolute stack size.
In the natural language realm, Nederhof [9] first converts grammars to recursive transition net-
works [10] (RTNs) and then to NFA identical to ANTLR’s. Nederhof seeks to reduce the complexity
of matching natural languages by collapsing a complete context-free grammar to a single DFA.
ANTLR, on the other hand, generates a recursive descent parser to match the entire context-free
language but needs a set of DFA predictors, one for each grammar decision point.
There is no strict ordering between LL(∗) and LR(k) because there is at least one grammar that
is LL(∗) but not LR(k). For example, the following LL(∗) grammar segregates the set of declaration
modifiers into two different rules in an effort to b e more strict syntactically.
6