Fact-checked by Grok 2 weeks ago

Left recursion

Left recursion is a property of context-free grammars in which a nonterminal symbol can derive a sentential form that begins with the same nonterminal, potentially causing nontermination in algorithms. This occurs when the leftmost symbol in a production rule for a nonterminal is the nonterminal itself, either directly or indirectly through a chain of derivations. There are two primary forms of left recursion: direct left recursion, where a production is of the form AA α (with α being a of symbols), and indirect left recursion, where A derives some B that in turn leads back to A as the leftmost symbol through subsequent . For example, in an arithmetic expression , a rule like EE + T exemplifies direct left recursion, commonly used to specify left-associative operators. Left recursion poses significant challenges in predictive parsing methods, such as LL(1) parsers, because it results in infinite recursion without consuming input , preventing the parser from progressing. To address this, standard algorithms exist to eliminate left recursion by transforming the into an equivalent right-recursive form, preserving the generated language while enabling efficient ; for instance, converting EE + T | T to ET + E | T. Such transformations are crucial in design and applications.

Definition

Direct Left Recursion

Direct left recursion in a occurs when a non-terminal A derives itself immediately as the leftmost symbol in one of its production rules, formally expressed as A \to A \alpha, where \alpha is any (possibly of terminals and non-terminals. This form of recursion contrasts with right recursion, where the non-terminal appears at the end of the production, and it arises naturally when specifying left-associative constructs, such as arithmetic expressions in programming languages. Consider the simple for strings consisting of a 'b' followed by zero or more 'a's:
S → S a | b
Here, the production S \to S a exemplifies direct left recursion, as the leftmost symbol on the right-hand side is again S. This generates the \{ b, b a, b a a, b a a a, \dots \}, but the recursive structure leads to issues in certain strategies. In terms of derivation trees, direct left recursion produces an infinite descending branch without consuming input, preventing termination. For instance, starting from S and applying S \to S a repeatedly yields a tree where each S node has a child S followed by a, forming an unending chain to the left: the root S branches to another S and a, which itself branches to yet another S and a, ad infinitum, with no terminal b reachable in finite steps without choosing the base production. This structure illustrates the lack of progress in left-to-right expansion, as the parser would keep substituting S indefinitely. The concept of direct left recursion was first formally discussed in parsing literature around the early , particularly in the context of recursive descent parsers, as part of efforts to handle context-free grammars for programming languages. In a naive simulation, such as recursive descent, direct left recursion causes an . To parse the string "baa", the parser begins at S: it selects S \to S a, recursively calls the procedure for S (consuming nothing yet), which again selects S \to S a, and repeats, never advancing past the initial position or matching the first symbol. Only if the base case S \to b is chosen would it terminate, but the recursive path exhausts the call stack without input progress, leading to non-termination for any derivation requiring multiple a's.

Indirect Left Recursion

Indirect left recursion arises in context-free grammars when a nonterminal A can derive a string that begins with another nonterminal, which through one or more production steps eventually leads back to A as the leftmost symbol, forming a cycle denoted as A \Rightarrow^+ A \beta where \Rightarrow^+ indicates one or more derivation steps and \beta is some string. This contrasts with direct left recursion, which is a simpler special case involving immediate in a single production. Consider the following grammar exhibiting indirect left recursion:
A → B a
B → A b | c
Here, the cycle emerges as A \Rightarrow B a \Rightarrow A b a, where the derivation loops indefinitely by returning to A on the left. Such cycles can involve multiple nonterminals, as in mutually recursive productions like N_1 \rightarrow N_2 \alpha_1 and N_2 \rightarrow N_1 \alpha_2. To identify these left-recursive cycles, one constructs a dependency graph among nonterminals, with directed edges from each nonterminal to the leftmost symbols of its production right-hand sides; the presence of a cycle in this graph signals indirect left recursion. This graph-based approach reveals dependencies that may not be immediately apparent in the production rules. Unlike right recursion, where the nonterminal appears at the end of the right-hand side (e.g., A \rightarrow \alpha A), indirect left recursion positions the recursive reference at the beginning after a chain of derivations, altering the order of expansion in parsing. In top-down parsers, indirect left recursion leads to non-termination, as the parser repeatedly expands the leftmost nonterminal without consuming input. For the example grammar above, attempting to parse a string starting with A might select A \rightarrow B a, then expand B \rightarrow A b, reverting to expanding A again, resulting in an infinite loop without advancing the input pointer.

Role in Parsing

Uses in Grammar Specification

Left recursion plays a key role in specifying left-associative constructs within context-free , allowing natural modeling of operations where precedence binds from left to right, such as in arithmetic expressions. For instance, the Exp → Exp - Term | Term directly encodes the left-associativity of , ensuring that an expression like a - b - c is interpreted as (a - b) - c through successive left derivations. This approach appears in grammars for programming languages, including ALGOL 60's arithmetic expressions, where rules like <simple arithmetic expression> ::= <simple arithmetic expression> <adding operator> <term> | <term> implement left-associativity for addition and subtraction. Similarly, the C language grammar employs left recursion in additive expressions via productions such as <additive-expression> ::= <additive-expression> + <multiplicative-expression> | <additive-expression> - <multiplicative-expression> | <multiplicative-expression>, reflecting standard left-to-right evaluation of operators. In natural language grammars, left recursion models left-branching phrase structures, such as stacked adjectives in phrases like "big red ball," using rules like NP → Adj NP | N to capture sequential modification from left to right. Compared to right recursion, left recursion offers advantages in handling operator precedence by aligning derivation trees more intuitively with left-to-right evaluation semantics, reducing the need for post-processing to enforce associativity and simplifying the representation of chained operations. This results in grammars that more closely mirror the intended parse structure without additional transformations during specification. Historically, left recursion gained adoption in compiler grammars following the , particularly with the rise of techniques that tolerate it. Despite challenges in , left recursion remains preferred in grammar specifications for its direct expressiveness in capturing linguistic and computational associativity, with implementation often involving parser adaptations or transformations to resolve runtime issues.

Problems in Top-Down Parsing

In algorithms, such as recursive descent parsers, left recursion triggers an infinite mechanism where the parser repeatedly expands a nonterminal using a that begins with the same nonterminal, without advancing the input . This occurs because top-down parsers predict and expand productions from the start symbol downward, and a left-recursive rule like A → A α | β causes the parser to call the procedure for A indefinitely, as the first expansion returns control to A without consuming any tokens. A concrete example illustrates this failure. Consider the left-recursive grammar:
S → S a | a
When parsing the input "a a" starting with the nonterminal S, the parser invokes the S procedure, selects the production S → S a (as it matches the lookahead 'a'), and recursively calls S again. This repeats endlessly—S calls S calls S—without consuming the first 'a' or progressing to the second token, trapping the parser in a loop. This unbounded recursion poses stack overflow risks in practical implementations, where the call stack has limited capacity. For instance, in many programming environments with default stack sizes of 1-8 MB, recursion depths exceeding 1000 levels (assuming modest frame sizes) trigger a exception, halting execution before any meaningful occurs. The performance impact of left recursion is severe, manifesting as non-termination rather than the confusion from ambiguity in other grammar issues; while ambiguous rules might explore multiple finite paths and eventually fail or succeed, left recursion establishes a zero-progress loop that uniquely prevents input advancement and guarantees divergence. Early recognition of these problems dates to the 1970s, when foundational compiler design literature and emerging parser generator tools for top-down methods began explicitly flagging left recursion as an error during grammar analysis to avert runtime infinite loops.

Elimination Techniques

Direct Left Recursion Removal

Direct left recursion, where a nonterminal A immediately derives a beginning with A through productions of the form A \to A \alpha_1 \mid \cdots \mid A \alpha_m \mid \beta_1 \mid \cdots \mid \beta_n (with none of the \beta_i beginning with A), can be eliminated by introducing a new nonterminal A' to refactor the into an equivalent right-recursive form. This transformation preserves the generated by the original while enabling top-down parsers to terminate. The standard algorithm proceeds as follows: Replace the original productions with A \to \beta_1 A' \mid \cdots \mid \beta_n A' and A' \to \alpha_1 A' \mid \cdots \mid \alpha_m A' \mid \varepsilon. This shifts the to the right via A', avoiding infinite descent in predictive parsing. If any \alpha_i or \beta_j can derive the (i.e., are nullable), the \varepsilon-production for A' ensures that derivations terminating without further remain possible, maintaining equivalence. Consider the grammar with productions S \to S a \mid b. Applying the algorithm yields S \to b S' and S' \to a S' \mid \varepsilon. To verify equivalence, note that the original generates strings b a^k for k \geq 0 via left-recursive s, such as S \Rightarrow S a \Rightarrow b a for k=1. In the transformed , the same strings are generated right-recursively: S \Rightarrow b S' \Rightarrow b a S' \Rightarrow \cdots \Rightarrow b a^k S' \Rightarrow b a^k by choosing \varepsilon at the end. Every original corresponds to a unique transformed one by postponing the non-recursive \beta (here, b) and tail-recursing via A', and vice versa, ensuring the languages are identical. The transformation guarantees that the generated language remains unchanged because it systematically enumerates all possible concatenations of \beta's followed by zero or more \alpha's, mirroring the left-recursive expansions without altering associativity or introducing extraneous strings. For nullable cases, such as if a \beta_i includes nullable symbols, the \varepsilon-rule in A' allows optional suffixes, preserving derivations that would have used nullable parts in the original. In preprocessors, the removal can be implemented via the following , applied to each nonterminal exhibiting direct left :
function removeDirectLeftRecursion([G](/page/G), A):
    productions = [G](/page/G).productions[A]
    alpha = []  // list of right-hand sides starting with A
    beta = []   // list of right-hand sides not starting with A
    for each rhs in productions:
        if rhs[0] == A:
            alpha.append(rhs[1:])  // remove the leading A
        else:
            beta.append(rhs)
    if alpha is empty:
        return [G](/page/G)  // no recursion to remove
    A_prime = newNonterminal()  // e.g., A_follow or similar
    new_prods_A = [beta_i + [A_prime] for beta_i in beta]
    new_prods_Aprime = [alpha_i + [A_prime] for alpha_i in alpha] + [[epsilon]]
    [G](/page/G).productions[A] = new_prods_A
    [G](/page/G).productions[A_prime] = new_prods_Aprime
    return [G](/page/G)
This procedure scans productions once per nonterminal, introducing at most one new nonterminal per application, and can be iterated over all nonterminals in if combined with indirect handling, though for direct cases it suffices standalone. This method addresses only immediate (direct) self- within a single nonterminal's productions; nested or indirect recursion involving multiple nonterminals requires separate substitution techniques.

Indirect Left Recursion Removal

Indirect left recursion occurs when a nonterminal derives a left-recursive through a chain of other nonterminals, complicating . The standard algorithm for its elimination, as described in seminal design literature, proceeds by ordering the nonterminals arbitrarily as A_1, A_2, \dots, A_n and iteratively substituting productions from earlier nonterminals to expose and resolve recursive dependencies. Specifically, for each i from 2 to n, all productions of the form A_i \to A_j \gamma (where j < i) are replaced by substituting the right-hand sides of A_j's productions into them. This substitution may introduce left recursion in A_i, which is then eliminated using the left recursion removal , introducing a new nonterminal A_{i}' for the tail-recursive part. This process converts indirect recursion into a right-recursive equivalent, preserving the generated by the . To illustrate, consider the grammar with productions A \to B a and B \to A b \mid c, ordered as A_1 = B, A_2 = A. B first yields no substitutions since there are no prior nonterminals. For A, substitute B's productions into A \to B a, resulting in A \to A b a \mid c a. This exposes direct left recursion in A, which is removed as follows: \begin{align*} A &\to c a \mid c a A' \\ A' &\to b a A' \mid \epsilon \end{align*} The resulting grammar is free of left recursion and generates the same language. A dependency graph, where nodes represent nonterminals and directed edges indicate productions starting with another nonterminal (e.g., an edge from A to B if A \to B \gamma), visualizes potential left-recursive cycles. Such graphs help identify cyclic dependencies, ensuring the substitution process resolves all paths by propagating recursions forward in the ordering until direct forms are isolated and eliminated. The algorithm's completeness follows from the fact that substitutions exhaustively inline prior derivations, breaking all left-recursive cycles by transforming them into right-recursive tails without introducing new left recursions or altering the , as each step is a conservative rewrite equivalent to the original s. In the worst case, the is O(n^2) substitutions for n nonterminals, arising from repeated expansions during iterative inlining.

Parser Adaptations

Top-Down Parser Modifications

Top-down parsers, such as recursive descent and predictive parsers, traditionally cannot handle left-recursive grammars due to infinite recursion risks, but several modifications enable them to process such grammars directly without grammar rewriting. These adaptations typically involve lookahead mechanisms, memoization, or iterative structures to detect and avoid loops while maintaining predictive behavior. Packrat parsing, a memoized top-down approach based on parsing expression grammars (PEGs), supports left recursion through enhanced memoization that detects and prunes recursive loops. The modification involves a memo table indexed by rule and input position, storing partial results (success with AST and new position, or failure); for direct left recursion, an initial "seed" parse of non-recursive alternatives is grown iteratively by re-evaluating and extending successful subparses, while position-based caching ensures each (rule, position) pair is computed at most once, achieving linear time. For indirect left recursion, a stack tracks involved rules in the recursion cycle, and a heads table maps positions to cycle participants, allowing targeted re-parsing of affected rules without re-memoizing the entire input. For example, in a grammar like expr ::= expr "-" num / num, the parser seeds with num at position 0 for input "1-2-3", then grows the parse by applying "-" num extensions iteratively until no further matches. Left recursion detection in these modified parsers often employs fixed-point computation during grammar analysis to identify recursive cycles. This involves iteratively computing the set of nonterminals reachable from each via left derivations until a fixed point is reached, flagging cycles where a nonterminal derives itself through a chain of left expansions; for indirect cases, the computation propagates reachability across interdependent rules. In practice, this preprocessing builds a , solving for least fixed points to classify direct (immediate ) versus indirect , enabling the parser to apply appropriate or curtailment strategies, such as limiting depth to the remaining input length. A practical adaptation appears in recursive descent parsers for left-recursive expression grammars, where lookahead checks replace direct recursion with . Consider the left-recursive rule Exp → Exp + [Term](/page/Term) | [Term](/page/Term); the modified parser first calls the procedure to parse the initial factor, then enters a checking if the lookahead is '+': if so, it consumes the '+' and parses another , attaching it left-associatively to the prior ; the loop exits if the lookahead is in FOLLOW() or does not match '+', avoiding infinite recursion while preserving associativity. These modifications introduce trade-offs compared to grammar rewriting: while they preserve the original grammar's structure for easier specification and maintenance, they increase preprocessing time for fixed-point analysis and memo table setup, potentially raising overall complexity to O(n^3) or higher in ambiguous cases, versus the constant-time dispatch of rewritten LL(1) parsers.

Bottom-Up Parsing Tolerance

Bottom-up parsers, particularly shift-reduce varieties like LR parsers, inherently tolerate left recursion because they construct the incrementally from the input tokens upward, following the reverse of a rightmost . In this process, the parser maintains a of symbols and alternates between two actions: shifting the next input symbol onto the stack or reducing a suffix of the stack (known as a ) that matches the right-hand side of a to its left-hand side non-terminal. Left recursion does not induce infinite loops here, as reductions only occur after the recursive non-terminal's components—starting from terminals—have been recognized and shifted onto the stack, preventing premature expansion of the leftmost symbol. This mechanism relies on LR(0) items, which represent partial productions with a dot indicating the parser's progress, allowing states to model possible reductions without descending infinitely into recursive calls. To illustrate, consider a simple left-recursive grammar for basic arithmetic expressions that enforces left-associativity:
E → E + T | T
T → id
For the input string id + id, an LR(0) parser processes it without conflicts arising from the recursion. The parser begins by shifting the first id onto the stack and reducing it via T → id to T, then further reducing T to E using E → T. It then shifts + and the second id, reducing the latter to T and finally applying E → E + T to complete the parse. The following table summarizes the key actions in the parse table for this grammar (simplified for LR(0), with states abbreviated; full construction follows Knuth's algorithm):
Stateid+$ET
0s4accept5
1 (E→E+)s6r1
2 (E→T)r2
3 (after E)r0
4 (T→id)r2
5s712
6s48
7 (after +)9
8r1r1
9 (after T)r0
Here, sN denotes shift to state N, rN denotes reduce by N, and blank cells indicate errors. The recursion enables sequential reductions for the left-associative + without shift-reduce conflicts specific to the left-recursive form. A key advantage of this tolerance is the ability to specify left-associative operators directly in the grammar without transformations, promoting natural and concise grammar descriptions that align with . Tools like and its successor exploit this by generating efficient LR(1) or LALR(1) parsers from such grammars, widely used in compilers for languages like and , where left recursion facilitates stack-efficient of expression chains. In contrast to top-down parsers, which risk infinite descent by repeatedly expanding the left-recursive non-terminal before consuming input, bottom-up approaches commence from input symbols, ensuring progress as terminals drive the reductions. However, left recursion does not eliminate all challenges; bottom-up parsers may encounter shift-reduce or reduce-reduce conflicts in ambiguous grammars, such as those with precedence issues, though these stem from rather than recursion direction itself.

References

  1. [1]
    [PDF] Removing Left Recursion from Context-Free Grammars
    In this paper we develop a number of improvements to Panll's algorithm, which help somewhat but do not completely solve the prob- lem. We then go on to develop ...
  2. [2]
    Recursive Patterns and Context Free Grammars
    * Left recursion: any grammar containing productions with left recursion, that is, productions of the form A --> A X1...Xm, cannot be LL(1). The problem is ...<|control11|><|separator|>
  3. [3]
    [1207.0443] Left Recursion in Parsing Expression Grammars - arXiv
    Jul 2, 2012 · A frequently missed feature of PEGs is left recursion, which is commonly used in Context-Free Grammars (CFGs) to encode left-associative operations.
  4. [4]
    Compilers Lecture #5 - NYU Computer Science
    A → A α1 | A α2 | ... A αn | β1 | β2 | ... βm where the α's and β's are ... This removes direct left recursion where a production with A on the left ...
  5. [5]
    [PDF] Removing Left Recursion from Context-Free Grammars* - Microsoft
    We present a new method for removing left recur- sion from CFGs that is both theoretically superior to the standard algorithm, and produces very compact non- ...
  6. [6]
    Syntactic Analysis and Operator Precedence | Journal of the ACM
    FLOYD, R.W. An algorithm for coding efficient arithmetic operations. Comm ... Syntactic Analysis and Operator Precedence. Software and its engineering.
  7. [7]
    [PDF] Basics of Compiler Design
    Rewriting a grammar to turn indirect left-recursion into direct left-recursion ... The input to the compiler (i.e., the source program) is shown at the left of ...
  8. [8]
    4.0 Introduction
    ... indirect left-recursion. For example,. A --> B x y | x. B --> C D. C --> A | c. D --> d. is indirectly recursive because. A ==> B x y ==> C D x y ==> A D x ...
  9. [9]
    Detect (indirect) left-recursion in a context-free grammar?
    Sep 6, 2024 · It's straightforward to determine whether the grammar is directly left-recursive - it is, if any of the αi begin with A , or any of the βi begin ...Conversion of left-recursive context-free grammars to strongly ...Eliminate left recursion from grammarMore results from cs.stackexchange.com
  10. [10]
    Recursion (Bison 3.8.1) - GNU.org
    Any kind of sequence can be defined using either left recursion or right recursion, but you should always use left recursion, because it can parse a ...
  11. [11]
    Left recursion in Parsing Expression Grammars - ScienceDirect.com
    Dec 15, 2014 · The use of left recursion is common in context free grammar definitions, and several attempts have been made to give meaning to left ...
  12. [12]
    [PDF] Algol 60 grammar in BNF
    Algol 60 grammar in BNF. <program> ::= <block> | <compound statement> ... <left part list> ::= <left part> | <left part list> <left part>. <left part> ...
  13. [13]
    The syntax of C in Backus-Naur form
    ### Summary of Grammar Rules for Additive-Expression and Expression
  14. [14]
    Parsing: a timeline -- V3.1 - GitHub Pages
    Jul 6, 2023 · Left association is implemented by post-processing. ... left recursion, and use parentheses to implement precedence and associativity.
  15. [15]
    Compilers: Principles, Techniques, and Tools (2nd Edition)
    Compilers: Principles, Techniques, and Tools (2nd Edition)August 2006 ... Top-down parsing in Coco-2, ACM SIGPLAN Notices, 26:3, (79-87), Online ...
  16. [16]
    Why can't a recursive-descent parser handle left recursion
    May 11, 2009 · Could someone please explain to me why recursive-descent parsers can't work with a grammar containing left recursion?avoid stackoverflow in recursive algorithm in recursive-descent parserWhy do right recursive parsers not loop infinitely? - Stack OverflowMore results from stackoverflow.comMissing: depth | Show results with:depth
  17. [17]
    Principles of Compiler Design (Addison-Wesley series in computer ...
    Principles of Compiler Design (Addison-Wesley series in computer science and information processing)August 1977 · Downloads (12 months) · Downloads (cumulative).
  18. [18]
    [PDF] Top-down parsing, left-recursion removal
    Eliminating Left Recursion. The transformation eliminates immediate left recursion. What about more general, indirect left recursion ?
  19. [19]
    [PDF] Packrat Parsers Can Support Left Recursion
    With these modifications, our parser supports both direct and indirect left recursion ... top-down parsers that support left recursion and polynomial parse times.
  20. [20]
    [PDF] Parsing Parsing involves: Two approaches to constructing parsers
    Algorithm for removing left recursion. ▷ Two kinds of left recursion ... ▷ Removal of immediate left recursion: for each nonterminal. 1. Separate ...
  21. [21]
    [PDF] Top down Parsing - GitHub Pages
    Add symbol in first(A) in synch set. Then it may be possible to resume parsing according to A if a symbol in first(A) appears in input.
  22. [22]
    [PDF] Modular and Efficient Top-Down Parsing for Ambiguous Left ...
    The method accommodates left recursion and maintains modularity and clarity of the code. However, it has exponential complex- ity, even for recognition. 6) ...
  23. [23]
    Parsing Expressions - Crafting Interpreters
    Some parsing techniques, including the one we're going to use, have trouble with left recursion. ... In a top-down parser, you reach the lowest-precedence ...
  24. [24]
    [PDF] Introduction to Bottom-Up Parsing Lecture Notes by Profs. Alex ...
    Bottom-up parsing is more general than top- down parsing. – Don't need left-factored grammars. – Left recursion fine. ... Bottom-up parsing uses only two ...
  25. [25]
    [PDF] CSE 431S Bottom-Up Parsing
    Bottom-Up Parsing. Washington University. Spring 2013. Page 2. Bottom-Up Parsing. • Instead of starting from a ... – Left recursion alternates shift and reduce.
  26. [26]
    [PDF] Yacc: Yet Another Compiler-Compiler - ePaperPress
    Yacc: Yet Another Compiler-Compiler. Stephen C. Johnson. ABSTRACT. Computer program input generally has some structure; in fact, every computer pro- gram that ...