Thanks to visit codestin.com
Credit goes to www.tutorialspoint.com

Left Recursion (LR) and Left Factoring (LF)



Context-free grammars play a vital role in in compilers and programming languages. CFGs are used to define how valid programs should be organized. We use recursion in parsing, whereby examining a sequence of tokens to its grammatical structure, is greatly dependent on these grammars.

In this chapter, we will cover two important characteristics of CFG which are left recursion and left factoring. Let us understand them through examples.

Left Recursion

A context-free grammar is said to be left recursive if it contains a production rule where the non-terminal on the left-hand side of the rule also appears as the first symbol on the right-hand side. In other words, the grammar is trying to define a non-terminal in terms of itself, creating a recursive loop.

This can be represented formally as −

$$\mathrm{A \:\rightarrow\: A\: \alpha \: |\: \beta}$$

Where −

  • A is a non-terminal symbol.
  • α represents a sequence of terminals and/or non-terminals.
  • β represents another sequence of terminals and/or non-terminals.

The most important part here is the presence of A on both sides of the production rule, with it appearing first on the right-hand side.

To visualize this, consider the following parse tree

Understanding Left Recursion

It is generated by a left-recursive grammar. As the grammar recursively expands the non-terminal 'A' on the left, the tree grows indefinitely downwards on the left side. This continuous expansion makes it unsuitable for top-down parsing, as the parser could get trapped in an infinite loop, trying to expand 'A' repeatedly.

Problem of Left Recursion for Top-Down Parsing

As we have seen, the top-down parsing works by starting with the start symbol of the grammar and attempting to derive the input string by applying production rules.

When encountering a left-recursive rule, the parser keeps expanding the same non-terminal, leading to an infinite loop. This inability to handle left recursion directly is a significant drawback of top-down parsing methods.

Eliminating Left Recursion

To solve this we can eliminate immediate left recursion from a grammar without altering the language it generates. The general approach involves introducing a new non-terminal and rewriting the recursive rules.

Let's illustrate this with our previous example −

$$\mathrm{A \:\rightarrow\: A\: \alpha \: |\: \beta}$$

We can eliminate the left recursion by introducing a new non-terminal 'A'` and rewriting the rule as follows −

$$\mathrm{A \:\rightarrow\: \beta\:A'}$$

$$\mathrm{A' \:\rightarrow\: \alpha \:A'\: |\: \varepsilon}$$

In this transformed grammar

  • A now derives the non-recursive part 'β' followed by the new non-terminal A'.
  • A' handles the recursion, deriving either α A' (continuing the recursion) or ϵ (empty string), which terminates the recursion.

Let us see this through an example for a better view

Consider a simplified arithmetic expression grammar −

$$\mathrm{E \:\rightarrow\: E\: +\: T\: |\: T}$$

$$\mathrm{T \:\rightarrow\: T\: * \: F\: |\: F}$$

$$\mathrm{F \:\rightarrow\: (E)\: | \: id}$$

This grammar contains left recursion in the rules for both `E` and `T`. Let's eliminate it −

  • Eliminating Left Recursion in `E`:

$$\mathrm{E \: \rightarrow\: TE'}$$

$$\mathrm{E' \: \rightarrow\: +TE' \: |\:\varepsilon}$$

  • Eliminating Left Recursion in `T`:

$$\mathrm{T \: \rightarrow\: FT'}$$

$$\mathrm{T' \: \rightarrow\: * FT' \: |\:\varepsilon}$$

The final transformed grammar, free from left recursion, becomes −

$$\mathrm{E \: \rightarrow \: TE'}$$

$$\mathrm{E' \: \rightarrow \: +TE'\:|\:\varepsilon}$$

$$\mathrm{T \: \rightarrow \: FT'}$$

$$\mathrm{T' \: \rightarrow\: * FT' \: |\:\varepsilon}$$

$$\mathrm{F \: \rightarrow\: (E) \:|\:id}$$

Left Factoring

After the left recursion, let us see the idea of left factoring. While left recursion presents a parsing challenge, left factoring is a desirable property for top-down parsing. It involves restructuring the grammar to eliminate common prefixes in production rules.

Importance of left factoring

When a grammar has multiple production rules for a non-terminal with a common prefix, the parser faces ambiguity during the parsing process. It has to choose between these rules without knowing which one will ultimately lead to a successful parse.

Left factoring helps in resolving this ambiguity by postponing the decision point, making the parsing process more efficient.

Performing Left Factoring

The process of left factoring involves identifying the longest common prefix (α) in the alternatives for a non-terminal and rewriting the rules to factor out this common prefix.

Consider a grammar with the following rules −

$$\mathrm{A\:\rightarrow\: \alpha\beta_{1} \:|\:\alpha\beta_{2}\:|\:\dotso\:|\:\alpha\beta_{n}\:|\:\gamma}$$

Here, 'α' is the longest common prefix in the first 'n' alternatives for non-terminal 'A'.

We can left factor this as follows −

$$\mathrm{A \: \rightarrow \: \alpha A' \:|\: \gamma}$$

$$\mathrm{A\:\rightarrow\: \beta_{1} \:|\: \beta_{2}\:|\:\dotso\:|\: \beta_{n}}$$

In this factored grammar

  • A now derives either the common prefix 'α' followed by the new non-terminal 'A'` or the alternative 'γ'.
  • A'` encapsulates the different options that were originally prefixed by 'α'. This defers the decision of which alternative to choose until more of the input has been processed.

Let us understand this through an example. Let's consider a simple grammar

$$\mathrm{S\:\rightarrow\:\: \text{if E then S, else S}}$$

$$\mathrm{S\:\rightarrow\:\: \text{if E then S}}$$

$$\mathrm{S\:\rightarrow\: A}$$

Here, the first two rules for 'S' have a common prefix, "if E then". We can apply left factoring to obtain −

$$\mathrm{S\:\rightarrow\:\: \text{if E then S S' | A}}$$

$$\mathrm{S'\:\rightarrow\:\: \text{else S | $\varepsilon$}}$$

This factored grammar is more suitable for top-down parsing as it avoids the initial choice between the first two rules.

Conclusion

Left recursion and left factoring are two important concepts in compiler design and more specifically for context-free grammars and parsing.

Left recursion can be problematic for top-down parsing and needs to be eliminated. Left factoring, on the other hand, improves the efficiency of top-down parsing by reducing ambiguity.

Advertisements