SLR, CLR and LALR Parsers
Last Updated :
23 Jul, 2025
Parsing is a fundamental process in compiler design that helps analyze and validate the syntax of programming languages. It converts a sequence of tokens into a structured format, often represented as a parse tree. Among various parsing techniques, LR parsers are widely used due to their efficiency and ability to handle a broad class of grammars.
LR parsers are a type of bottom-up parsers that construct the parse tree from the leaves (tokens) to the root (start symbol). They are deterministic and capable of handling context-free grammars (CFGs) efficiently. The three main types of LR parsers include:
- SLR (Simple LR) Parser – The most basic LR parser, using LR(0) items and FOLLOW sets for table construction.
- CLR (Canonical LR) Parser – A more powerful parser that utilizes LR(1) items to resolve conflicts and recognize a broader range of grammars.
- LALR (Look-Ahead LR) Parser – A memory-optimized version of CLR that merges states to reduce table size while maintaining most of its parsing power.
Each of these parsers differs in terms of complexity, power, and efficiency.
Block Diagram of LR ParserBottom-Up Parsing
Bottom-up parsing is a method used in compilers to analyze and understand code. It starts with the smallest parts of a program (tokens) and gradually builds up to form the complete structure (syntax tree).
In bottom-up parsing:
- It reads the input from left to right.
- It groups tokens into larger structures based on grammar rules.
- It keeps combining these structures until it forms the final result (start symbol).
The most powerful bottom-up parsers are LR Parsers (used in programming languages like C and Java). Examples include:
- SLR (Simple LR) – Basic and easy to implement.
- CLR (Canonical LR) – More powerful but complex.
- LALR (Look-Ahead LR) – A mix of both, balancing power and efficiency.
Read more about Bottom-Up Parsing.

SLR(1) Parsing
SLR(1) stands for Simple LR Parsing. It is similar to LR(0) Parsing because LR(0) parsing requires just the canonical items for the construction of a parsing table, The only thing that differentiates the LR(0) parser from the SLR parser is the possibility of a shift reduced conflict because we are entering reduce corresponding to all terminal states in the LR(0) parsing table. In LR(0) Parsing, the parser sometimes mistakenly reduces when it shouldn't, causing shift-reduce conflicts. SLR(1) fixes this issue by only allowing reduce operations in places where they match the FOLLOW set of the left-hand side (LHS) of the production rule. This means that SLR(1) uses FOLLOW sets to make better decisions, reducing conflicts.
Constructing the SLR(1) parsing Table
1. Construct the collection of sets of LR(0) items for G′.
2. For each state i of the CFSM, constructed from Ii:
1. If [A → α • aβ] ∈ Ii and GOTO(Ii, a) = Ij,
⇒ ACTION[i, a] ← “shift j”, ∀a ≠ $.
2. If [A → α •] ∈ Ii, A ≠ S′,
⇒ ACTION[i, a] ← “reduce A → α”, ∀a ∈ FOLLOW(A).
3. If [S′ → S • $] ∈ Ii,
⇒ ACTION[i, $] ← “accept”.
3. For each state i:
- If GOTO(Ii, A) = Ij,
⇒ GOTO[i, A] ← j.
4. Set undefined entries in ACTION and GOTO to “error”.
5. The initial state of the parser s₀ is CLOSURE([S′ → •S$]).
- If in the parsing table we have multiple entries then it is said to be a conflict.
Example:
Consider the grammar E -> T+E | T
T ->id
Augmented grammar - E’ -> E
E -> T+E | T
T -> id

Read more about SLR (1) Parsing.
CLR(1) Parsing
CLR(1) Parsing, also called Canonical LR(1) Parsing, is a type of bottom-up parsing used in compilers to analyze the structure of programming languages. It is an advanced version of SLR(1) Parsing that eliminates conflicts by using look-ahead symbols. These symbols help the parser decide whether to shift or reduce based on what comes next in the input.
Unlike SLR(1), which relies on FOLLOW sets to make reduction decisions, CLR(1) associates a specific look-ahead symbol with each grammar rule. This allows the parser to differentiate between situations where a rule should be applied or postponed, making it more powerful and capable of handling a wider range of grammars.
CLR(1) Parsing works by constructing LR(1) items, which are similar to LR(0) items but include a look-ahead symbol. These items help in building a more accurate parsing table that reduces errors caused by shift-reduce or reduce-reduce conflicts. Because of this, CLR(1) parsing is much more precise than SLR(1), but it also has a drawback—it generates large parsing tables that require more memory and processing time.
Constructing the LR(1) Parsing Table
1. Build lookahead into the DFA to begin with
2. Construct the collection of sets of LR(1) items for G′.
3. For each state i of the LR(1) machine, constructed from Ii:
1. If [A → α • aβ, b] ∈ Ii and GOTO₁(Ii, a) = Ij,
⇒ ACTION[i, a] ← “shift j”.
2. If [A → α •, a] ∈ Ii, A ≠ S′,
⇒ ACTION[i, a] ← “reduce A → α”.
3. If [S′ → S •, $] ∈ Ii,
⇒ ACTION[i, $] ← “accept”.
4. For each state i:
- If GOTO₁(Ii, A) = Ij,
⇒ GOTO[i, A] ← j.
5. Set undefined entries in ACTION and GOTO to “error”.
6. The initial state of the parser s₀ is CLOSURE₁([S′ → •S, $]).
CLOSURE | GOTO |
---|
 |
 |
---|
Example:
Consider the following grammar
S -> AaAb | BbBa
A -> ?
B -> ?
Augmented grammar - S’ -> S
S -> AaAb | BbBa
A -> ?
B -> ?
GOTO graph for this grammar will be -

Read more about CLR(1) Parsing.
LALR Parsing
LALR(1) (Look-Ahead LR) Parsing is an optimized version of CLR(1) (Canonical LR(1)) Parsing that reduces the size of the parsing table while maintaining most of its power. In CLR(1), the number of states can become very large because each state keeps look-ahead symbols separately. LALR(1) solves this problem by merging states that have the same LR(0) core items but different look-ahead symbols.
By merging similar states, LALR(1) reduces memory usage while keeping the parsing process efficient. However, in some cases, this merging can lead to shift-reduce or reduce-reduce conflicts, making LALR(1) slightly weaker than CLR(1). Despite this, it is widely used in practical compilers and parser generators like YACC and Bison because it offers a good balance between power and efficiency.
Constructing the LALR(1) Parsing Table
- Construct the collection of sets of LR(1) items for G′.
- For each core present among the set of LR(1) items:
- Find all sets having the same core.
- Replace these sets with their union.
- Update the GOTO₁ function incrementally.
- For each state i of the LALR(1) machine, constructed from Ii:
- If [A → α • aβ, b] ∈ Ii and GOTO₁(Ii, a) = Ij, ⇒ ACTION[i, a] ← “shift j”.
- If [A → α •, a] ∈ Ii and A ≠ S′, ⇒ ACTION[i, a] ← “reduce A → α”.
- If [S′ → S •, $] ∈ Ii, ⇒ ACTION[i, $] ← “accept”.
- If GOTO₁(Ii, A) = Ij, ⇒ GOTO[i, A] ← j.
- Set undefined entries in ACTION and GOTO to “error”.
- The initial state of the parser s₀ is CLOSURE₁([S′ → •S, $]).
Example:
consider the grammar S ->AA
A -> aA | b
Augmented grammar - S’ -> S
S ->AA
A -> aA | b

Read more about LALR(1) Parsing.
SLR(1) v/s CLR(1) v/s LALR(1)
Feature | SLR(1) Parser (Simple LR) | CLR(1) Parser (Canonical LR) | LALR(1) Parser (Look-Ahead LR) |
---|
Parsing Table Size | Smallest (fewer states) | Largest (most states) | Medium (states merged to reduce size) |
Grammar Handling | Limited (only simple grammars) | Most powerful (handles almost all grammars) | Nearly as powerful as CLR but compact |
Basis for Decisions | Uses FOLLOW sets for reductions | Uses look-ahead symbols to make precise decisions | Uses merged look-ahead symbols, similar to CLR but optimized |
Conflicts (Shift-Reduce, Reduce-Reduce) | More conflicts due to reliance on FOLLOW sets | Least conflicts because of look-ahead symbols | May introduce reduce-reduce conflicts when merging states |
Error Detection | Delayed (errors detected later) | Delayed (similar to SLR) | Similar to CLR, not always immediate |
Time and Space Complexity | Low (fast but limited) | High (slow due to large tables but powerful) | Medium (optimized for efficiency) |
Ease of Implementation | Easiest (simplest to build) | Most complex (large tables make it harder) | Easier than CLR but slightly more complex than SLR |
Used In | Simple parsers and educational tools | Strong theoretical compilers (not widely used in practice) | Most real-world compilers (YACC, Bison, etc.) |
Similar Reads
Introduction of Compiler Design A compiler is software that translates or converts a program written in a high-level language (Source Language) into a low-level language (Machine Language or Assembly Language). Compiler design is the process of developing a compiler.The development of compilers is closely tied to the evolution of
9 min read
Compiler Design Basics
Introduction of Compiler DesignA compiler is software that translates or converts a program written in a high-level language (Source Language) into a low-level language (Machine Language or Assembly Language). Compiler design is the process of developing a compiler.The development of compilers is closely tied to the evolution of
9 min read
Compiler construction toolsThe compiler writer can use some specialized tools that help in implementing various phases of a compiler. These tools assist in the creation of an entire compiler or its parts. Some commonly used compiler construction tools include: Parser Generator - It produces syntax analyzers (parsers) from the
4 min read
Phases of a CompilerA compiler is a software tool that converts high-level programming code into machine code that a computer can understand and execute. It acts as a bridge between human-readable code and machine-level instructions, enabling efficient program execution. The process of compilation is divided into six p
10 min read
Symbol Table in CompilerEvery compiler uses a symbol table to track all variables, functions, and identifiers in a program. It stores information such as the name, type, scope, and memory location of each identifier. Built during the early stages of compilation, the symbol table supports error checking, scope management, a
8 min read
Error Handling in Compiler DesignDuring the process of language translation, the compiler can encounter errors. While the compiler might not always know the exact cause of the error, it can detect and analyze the visible problems. The main purpose of error handling is to assist the programmer by pointing out issues in their code. E
5 min read
Language Processors: Assembler, Compiler and InterpreterComputer programs are generally written in high-level languages (like C++, Python, and Java). A language processor, or language translator, is a computer program that convert source code from one programming language to another language or to machine code (also known as object code). They also find
5 min read
Generation of Programming LanguagesProgramming languages have evolved significantly over time, moving from fundamental machine-specific code to complex languages that are simpler to write and understand. Each new generation of programming languages has improved, allowing developers to create more efficient, human-readable, and adapta
6 min read
Lexical Analysis
Introduction of Lexical AnalysisLexical analysis, also known as scanning is the first phase of a compiler which involves reading the source program character by character from left to right and organizing them into tokens. Tokens are meaningful sequences of characters. There are usually only a small number of tokens for a programm
6 min read
Flex (Fast Lexical Analyzer Generator)Flex (Fast Lexical Analyzer Generator), or simply Flex, is a tool for generating lexical analyzers scanners or lexers. Written by Vern Paxson in C, circa 1987, Flex is designed to produce lexical analyzers that is faster than the original Lex program. Today it is often used along with Berkeley Yacc
7 min read
Introduction of Finite AutomataFinite automata are abstract machines used to recognize patterns in input sequences, forming the basis for understanding regular languages in computer science. They consist of states, transitions, and input symbols, processing each symbol step-by-step. If the machine ends in an accepting state after
4 min read
Classification of Context Free GrammarsA Context-Free Grammar (CFG) is a formal rule system used to describe the syntax of programming languages in compiler design. It provides a set of production rules that specify how symbols (terminals and non-terminals) can be combined to form valid sentences in the language. CFGs are important in th
4 min read
Ambiguous GrammarContext-Free Grammars (CFGs) is a way to describe the structure of a language, such as the rules for building sentences in a language or programming code. These rules help define how different symbols can be combined to create valid strings (sequences of symbols).CFGs can be divided into two types b
7 min read
Syntax Analysis & Parsers
Syntax Directed Translation & Intermediate Code Generation
Syntax Directed Translation in Compiler DesignSyntax-Directed Translation (SDT) is a method used in compiler design to convert source code into another form while analyzing its structure. It integrates syntax analysis (parsing) with semantic rules to produce intermediate code, machine code, or optimized instructions.In SDT, each grammar rule is
8 min read
S - Attributed and L - Attributed SDTs in Syntax Directed TranslationIn Syntax-Directed Translation (SDT), the rules are those that are used to describe how the semantic information flows from one node to the other during the parsing phase. SDTs are derived from context-free grammars where referring semantic actions are connected to grammar productions. Such action c
4 min read
Parse Tree and Syntax TreeParse Tree and Syntax tree are tree structures that represent the structure of a given input according to a formal grammar. They play an important role in understanding and verifying whether an input string aligns with the language defined by a grammar. These terms are often used interchangeably but
4 min read
Intermediate Code Generation in Compiler DesignIn the analysis-synthesis model of a compiler, the front end of a compiler translates a source program into an independent intermediate code, then the back end of the compiler uses this intermediate code to generate the target code (which can be understood by the machine). The benefits of using mach
6 min read
Issues in the design of a code generatorA code generator is a crucial part of a compiler that converts the intermediate representation of source code into machine-readable instructions. Its main task is to produce the correct and efficient code that can be executed by a computer. The design of the code generator should ensure that it is e
7 min read
Three address code in CompilerTAC is an intermediate representation of three-address code utilized by compilers to ease the process of code generation. Complex expressions are, therefore, decomposed into simple steps comprising, at most, three addresses: two operands and one result using this code. The results from TAC are alway
6 min read
Data flow analysis in CompilerData flow is analysis that determines the information regarding the definition and use of data in program. With the help of this analysis, optimization can be done. In general, its process in which values are computed using data flow analysis. The data flow property represents information that can b
6 min read
Code Optimization & Runtime Environments
Practice Questions