Parser
Parser
The parse tree visually represents how the tokens fit together according to the rules of the
language's syntax. This tree structure is crucial for understanding the program's structure
and helps in the next stages of processing, such as code generation or execution.
Additionally, parsing ensures that the sequence of tokens follows the syntactic rules of
the programming language, making the program valid and ready for further analysis or
execution.
A parser performs syntactic and semantic analysis of source code, converting it into an
intermediate representation while detecting and handling errors.
1. Context-free syntax analysis: The parser checks if the structure of the code
follows the basic rules of the programming language (like grammar rules). It looks
at how words and symbols are arranged.
5. Attempts error correction: Sometimes, the parser tries to fix small mistakes in
your code so it can keep working without breaking completely.
Types of Parsing
• Top-down Parsing
• Bottom-up Parsing
Top-Down Parsing
Top-down parsing is a method of building a parse tree from the start symbol (root) down
to the leaves (end symbols). The parser begins with the highest-level rule and works its
way down, trying to match the input string step by step.
• Process: The parser starts with the start symbol and looks for rules that can help
it rewrite this symbol. It keeps breaking down the symbols (non-terminals) into
smaller parts until it matches the input string.
Top-down parsing is useful for simple languages and is often easier to implement.
However, it can have trouble with more complex or ambiguous grammars.
Top-down parsers can be classified into two types based on whether they use
backtracking or not:
In this approach, the parser tries different possibilities when it encounters a choice, if one
possibility doesn’t work (i.e., it doesn’t match the input string), the parser backtracks to
the previous decision point and tries another possibility.
Example: If the parser chooses a rule to expand a non-terminal, and it doesn't work, it
will go back, undo the choice, and try a different rule.
Advantage: It can handle grammars where there are multiple possible ways to expand a
non-terminal.
Disadvantage: Backtracking can be slow and inefficient because the parser might have
to try many possibilities before finding the correct one.
In this approach, the parser does not backtrack. It tries to find a match with the input
using only the first choice it makes, if it doesn’t match the input, it fails immediately
instead of going back to try another option.
Example: The parser will always stick with its first decision and will not reconsider other
rules once it starts parsing.
Advantage: It is faster because it doesn’t waste time going back to previous steps.
Disadvantage: It can only handle simpler grammars that don’t require trying multiple
choices.
Top-down parsing is a way of analyzing a sentence or program by starting with the start
symbol (the root of the parse tree) and working down to the leaves (the actual input
symbols). It tries to match the input string by expanding the start symbol using grammar
rules. The process of constructing the parse tree which starts from the root and goes
down to the leaf is Top-Down Parsing.
In this approach, the parser works from the top of the tree and progressively breaks down
non-terminals into smaller parts, following the leftmost derivation. It's called recursive
because the process often uses recursion to handle different parts of the string.
• Top-down parsers work with grammars that are free from ambiguity and left
recursion.
• They construct the parse tree by following a leftmost derivation, which means they
always expand the leftmost non-terminal first.
Recursive Descent Parsing is a top-down parsing technique used to analyze the syntax of
a given input string based on a set of grammar rules. It uses a set of recursive procedures
(or functions) to process each non-terminal in the grammar and try to match the input
string.
2. Check the first possible option (alternative) for the non-terminal and try to match
it with the input string.
5. If at least one option matches the input string, the parsing is successful.
In simple terms, it's like trying different combinations to find the right one that works for
the input string.
A recursive descent parser is a top-down parser that processes input based on a set of
recursive functions, where each function corresponds to a grammar rule. It parses the
input from left to right, constructing a parse tree by matching the grammar's production
rules. This parser is simple to implement and is suitable for LL(1) grammars, where
decisions can be made based on a single lookahead token. While straightforward,
recursive descent parsers struggle with left-recursive grammars and may require
grammar transformations to handle such cases effectively.
Example:
E –> T E’
E –> E + T | T E’ –> + T E’ | e
T –> T * F | F T –> F T’
F –> (E) | id T’ –> * F T’ | e
F –> (E) | id
• The second L means creating the Left-most Derivation (building the structure step
by step from the left side).
• The 1 means looking one step ahead in the input to decide what to do next.
Rules for LL(1) Parsing
• No common prefixes (if two rules start the same way, they should be rewritten to
avoid confusion).
Because LL(1) parsing is predictable and fast, it is used in compilers and interpreters to
check if code is written correctly.
Here the 1st L represents that the scanning of the Input will be done from the Left to Right
manner and the second L shows that in this parsing technique, we are going to use the
Left most Derivation Tree. And finally, the 1 represents the number of look-ahead, which
means how many symbols you will see when you want to decide.
To construct a working LL(1) parsing table, a grammar must satisfy these conditions:
• Unambiguous Grammar: Ensure each string can be derived in only one way.
• Left Factoring: Make the grammar deterministic, so the parser can proceed
without guessing.
Step 1: First check all the essential conditions mentioned above and go to step 2.
1. First (): If there is a variable, and from that variable, if we try to drive all the
strings then the beginning Terminal Symbol is called the First.
2. Follow (): What is the Terminal Symbol which follows a variable in the process of
derivation.
1. Find First(α) and for each terminal in First(α), make entry A --> α in the table.
2. If First(α) contains ε (epsilon) as terminal, then find the Follow(A) and for each
terminal in Follow(A), make entry A --> ε in the table.
3. If the First(α) contains ε and Follow(A) contains $ as terminal, then make entry A -
-> ε in the table for the $.
In the table, rows will contain the non-Terminals, and the column will contain the Terminal
Symbols. All the Null Productions of the Grammars will go under the Follow elements,
and the remaining productions will lie under the elements of the First set.
E --> TE'
E' --> +TE' | ε
T --> FT'
T' --> *FT' | ε
F --> id | (E)
*ε denotes epsilon
Id + * ( ) $
T' T' --> ε T' --> *FT' T' --> ε T' --> ε
As you can see that all the null productions are put under the Follow set of that symbol
and all the remaining productions lie under the First of that symbol.
Note: Every grammar is not feasible for LL (1) Parsing table. It may be possible that one
cell may contain more than one production.
S --> A | a
A --> a
Step 1: The grammar does not satisfy all properties in step 1, as the grammar is
ambiguous. Still, let's try to make the parser table and see what happens
First Follow
Parsing Table:
a $
S S --> A, S --> a
A A --> a
Here, we can see that there are two productions in the same cell. Hence, this grammar is
not feasible for LL (1) Parser.
Trick - Above grammar is ambiguous grammar. So, the grammar does not satisfy the
essential conditions. So, we can say that this grammar is not feasible for LL (1) Parser
even without making the parse table.
S -> (L) | a
L -> SL'
L' -> )SL' | ε
First Follow
S (,a $, )
L (,a )
L' ), ε )
Parsing Table:
( ) a $
L'->(SL'
L'
L'->ε
Here, we can see that there are two productions in the same cell. Hence, this grammar is
not feasible for LL (1) Parser. Although the grammar satisfies all the essential conditions
in step 1, it is still not feasible for LL (1) Parser. We saw in example 2 that we must have
these essential conditions and in example 3 we saw that those conditions are insufficient
to be a LL (1) parser.
• Clear Decision-Making: With an LL(1) parsing table, the parser can decide what
to do by looking at just one symbol ahead. This makes it easy to choose the right
rule without confusion or guessing.
• Fast Parsing: Since there’s no need to go back and forth or guess the next step,
LL(1) parsing is quick and efficient. This is useful for applications like compilers
where speed is important.
• Easy to Spot Errors: The table helps identify errors right away. If the current
symbol doesn’t match any rule in the table, the parser knows there’s an error and
can handle it immediately.
• Simple to Implement: Once the table is set up, the parsing process is
straightforward. You just follow the instructions in the table, making it easier to
build and maintain.
• Good for Predictive Parsing: LL(1) parsing is often called “predictive parsing”
because the table lets you predict the next steps based on the input. This makes
it reliable for parsing programming languages and structured data.
Important Notes
Eg - S -> Sa | b
S -> Sa goes to FIRST(S) = b
S -> b goes to b; thus, b has 2 entries hence not LL (1)
Every regular grammar need not be LL (1) because regular grammar may contain left
factoring, left recursion or ambiguity.
Bottom-Up Parsing
Bottom-up parsing is a method of building a parse tree starting from the leaf nodes (the
input symbols) and working towards the root node (the start symbol). The goal is to
reduce the input string step by step until we reach the start symbol, which represents the
entire language.
• Process: The parser begins with the input symbols and looks for patterns that can
be reduced to non-terminals based on the grammar rules. It keeps reducing parts
of the string until it forms the start symbol.
• LR (0)
• SLR (1)
• LALR
• CLR
Shift-reduce parsing is a popular bottom-up technique used in syntax analysis, where the
goal is to create a parse tree for a given input based on grammar rules. The process works
by reading a stream of tokens (the input) and then working backwards through the
grammar rules to discover how the input can be generated.
1. Input Buffer: This stores the string or sequence of tokens that needs to be parsed.
2. Stack: The parser uses a stack to keep track of which symbols or parts of the parse
it has already processed. As it processes the input, symbols are pushed onto and
popped off the stack.
3. Parsing Table: Like a predictive parser, a parsing table helps the parser decide
what action to take next.
Shift-reduce parsing works by processing the input left to right and gradually building up
a parse tree by shifting tokens onto the stack and reducing them using grammar rules,
until it reaches the start symbol of the grammar.
Shift: Move the next input symbol onto the stack when no reduction is possible.
Reduce: Replace a sequence of symbols at the top of the stack with the left-hand side of
a grammar rule.
Accept: Successfully complete parsing when the entire input is processed and the stack
contains only the start symbol.
Error: Handle unexpected or invalid input when no shift or reduce action is possible.
Shift-reduce parsers use a Deterministic Finite Automaton (DFA) to help recognize these
handles. The DFA helps track what symbols are on the stack and decides when to shift or
reduce by following a set of rules. Instead of directly analyzing the structure, the DFA
helps the parser determine when reductions should occur based on the stack's contents.
The shift-reduce parser is a bottom-up parsing technique that breaks down a string into
two parts: the undigested part and the semi-digested part. Here’s how it works:
1. Undigested Part: This part contains the remaining tokens that still need to be
processed. It is the input that hasn’t been handled yet.
2. Semi-Digested Part: This part is on a stack. It's where tokens or parts of the string
that have been processed are stored.
Parsing Process
At the beginning, the input string is entirely undigested, and the stack is empty.
1. Shift
• When the parser can’t reduce the sequence on the stack (because the stack
doesn’t match any production rule), it shifts the next token from the input to the
stack.
• This means the parser takes one token from the undigested part (the input) and
places it on the stack.
• The stack keeps growing as tokens are added until a valid reduction is possible.
2. Reduce
• If the sequence of tokens on the stack matches the right side of a production rule,
the parser can reduce it. This means the sequence of tokens on the stack is
replaced with a non-terminal symbol (the left side of the production rule).
• For example, if the stack has id, and there’s a production rule T → id, the stack will
change from id to T.
• The reduction continues until the stack is reduced all the way to the start symbol
of the grammar, which indicates that the input has been successfully parsed.
o There’s one special case of reduction: If the entire stack contains the start
symbol and there’s no more input left, the parse is successful (the input is
valid).
o The sequence being reduced (e.g., id in the rule T → id) is called a handle.
Identifying the handle is one of the most important tasks of a shift-reduce
parser.
3. Error
• If neither shift nor reduce actions are possible, the parser encounters an error.
1. The sequence on the stack doesn't match any rule in the grammar (so no
reduction can be made).
1. For example, if the parser has E + on the stack and the next input token is ),
no reduction is possible and shifting ) would result in an invalid sequence.
Input Parsing
Stack Buffer Action
$ (a,(a,a))$ Shift
$( a,(a,a))$ Shift
$ ( L, ( L ))$ Shift
$ ( L, ( L ) )$ Reduce S → (L)
$ ( L, S )$ Reduce L → L, S
Input Parsing
Stack Buffer Action
$(L )$ Shift
$S $ Accept
LR Parser
LR parser is a bottom-up parser for context-free grammar that is very generally used by
computer programming language compiler and other associated tools. LR parser reads
their input from left to right and produces a right-most derivation. It is called a Bottom-up
parser because it attempts to reduce the top-level grammar productions by building up
from the leaves. LR parsers are the most powerful parser of all deterministic parsers in
practice.
Description of LR parser: The term parser LR(k) parser, here the L refers to the left-to-
right scanning, R refers to the rightmost derivation in reverse and k refers to the number
of unconsumed "look ahead" input symbols that are used in making parser decisions.
Typically, k is 1 and is often omitted. A context-free grammar is called LR (k) if the LR (k)
parser exists for it. This first reduces the sequence of tokens to the left. But when we read
from above, the derivation order first extends to non-terminal.
1. The stack is empty, and we are looking to reduce the rule by S'→S$.
2. Using a “.” in the rule represents how many of the rules are already on the stack.
3. A dotted item, or simply, the item is a production rule with a dot indicating how
much RHS has so far been recognized. Closing an item is used to see what
production rules can be used to expand the current structure. It is calculated as
follows:
1. The first item from the given grammar rules adds itself as the first closed set.
2. If an object is present in the closure of the form A→ α. β. γ, where the next symbol
after the symbol is non-terminal, add the symbol's production rules where the dot
precedes the first item.
3. Repeat steps (B) and (C) for new items added under (B).
LR parser algorithm: LR Parsing algorithm is the same for all the parser, but the parsing
table is different for each parser. It consists following components as follows.
1. Input Buffer - It contains the given string, and it ends with a $ symbol.
Stack - The combination of state symbol and current input symbol is used to refer
to the parsing table to take the parsing decisions.
Parsing Table: Parsing table is divided into two parts- Action table and Go-To table.
The action table gives a grammar rule to implement the given current state and current
terminal in the input stream. There are four cases used in action table as follows.
1. Shift Action- In shift action the present terminal is removed from the input stream
and the state n is pushed onto the stack, and it becomes the new present state.
3. The symbol m mentioned in the left-hand side of rule m says that state is removed
from the stack.
4. The symbol m mentioned in the left-hand side of rule m says that a new state is
looked up in the goto table and made the new current state by pushing it onto the
stack.
Note -
The go-to table indicates which state should proceed.
LR parser diagram:
Prerequisite: LR Parser.
The LR parser is an efficient bottom-up syntax analysis technique that can be used for a
large class of context-free grammar. This technique is also called LR (0) parsing.
L stands for the left to right scanning
R stands for rightmost derivation in reverse
0 stands for no. of input symbols of lookahead.
S-->AA
A-->aA|b
Solution:
STEP 1- Find augmented grammar -
RULE - if any nonterminal has ' . ' preceding it, we must write all its production and add '
. ' preceding each of its-production.
RULE - from each state to the next state, the ' . ' shifts to one place to the right.
• Io goes to I1 when ' . ' of 0th production is shifted towards the right of S(S'->S.).
This state is the accepted state. S is seen by the compiler
• Io goes to I2 when ' . ' of 1st production is shifted towards the right (S->A.A) . A is
seen by the compiler
• I0 goes to I3 when ' . ' of the 2nd production is shifted towards the right (A->a.A) .
a is seen by the compiler.
• I0 goes to I4 when ' . ' of the 3rd production is shifted towards the right (A->b.) . b
is seen by the compiler.
• I2 goes to I5 when ' . ' of 1st production is shifted towards the right (S->AA.) . A is
seen by the compiler
• I2 goes to I4 when ' . ' of 3rd production is shifted towards the right (A->b.) . b is
seen by the compiler.
• I2 goes to I3 when ' . ' of the 2nd production is shifted towards the right (A->a.A) .
a is seen by the compiler.
• I3 goes to I4 when ' . ' of the 3rd production is shifted towards the right (A->b.) . b
is seen by the compiler.
• I3 goes to I6 when ' . ' of 2nd production is shifted towards the right (A->aA.) . A is
seen by the compiler
• I3 goes to I3 when ' . ' of the 2nd production is shifted towards the right (A->a.A) .
a is seen by the compiler.
• similarly, 5 is written in A column and 2nd row, 6 is written in A column and 3 rows.
• I4 is reduced state as ' . ' is at the end. I4 is the 3rd production of grammar. So write
r3(reduce 3) in terminals.
• I5 is reduced state as ' . ' is at the end. I5 is the 1st production of grammar. So write
r1(reduce 1) in terminals.
• I6 is reduced state as ' . ' is at the end. I6 is the 2nd production of grammar. So
write r2(reduce 2) in terminals.
As each cell has only 1 value in it, hence, the given grammar is LR (0).
Advantages of LR parsing
• SLR parser
• LALR parser
• Canonical LR parser
SLR Parser
• a grammar for which SLR parser can be constructed is called SLR grammar
Steps for constructing the SLR parsing table
S-->AA
A-->aA|b
Solution:
STEP1: Find augmented grammar. The augmented grammar of the given grammar is:-
RULE - If any non-terminal has ' . ' preceding it, we have to write all its production and add
' . ' preceding each of its production.
RULE - from each state to the next state, the ' . ' shifts to one place to the right.
• Io goes to I1 when ' . ' of 0th production is shifted towards the right of S(S'->S.).
this state is the accepted state. S is seen by the compiler.
• Io goes to I2 when ' . ' of 1st production is shifted towards right (S->A.A) . A is seen
by the compiler
• I0 goes to I3 when ' . ' of the 2nd production is shifted towards right (A->a.A) . a is
seen by the compiler.
• I0 goes to I4 when ' . ' of the 3rd production is shifted towards right (A->b.) . b is
seen by the compiler.
• I2 goes to I5 when ' . ' of 1st production is shifted towards right (S->AA.) . A is seen
by the compiler
• I2 goes to I4 when ' . ' of 3rd production is shifted towards right (A->b.) . b is seen
by the compiler.
• I2 goes to I3 when ' . ' of the 2nd production is shifted towards right (A->a.A) . a is
seen by the compiler.
• I3 goes to I4 when ' . ' of the 3rd production is shifted towards right (A->b.) . b is
seen by the compiler.
• I3 goes to I6 when ' . ' of 2nd production is shifted towards the right (A->aA.) . A is
seen by the compiler
• I3 goes to I3 when ' . ' of the 2nd production is shifted towards right (A->a.A) . a is
seen by the compiler.
FOLLOW(S)=$
FOLLOW(A)=a,b,$
• I4 is reduced state as ' . ' is at the end. I4 is the 3rd production of grammar(A--
>.b).LHS of this production is A. FOLLOW(A)=a,b,$ . write r3(reduced 3) in the
columns of a,b,$ and 4th row
• I5 is reduced state as ' . ' is at the end. I5 is the 1st production of grammar(S-->.AA).
LHS of this production is S. FOLLOW(S)=$ . write r1(reduced 1) in the column of $
and 5th row
• I6 is a reduced state as ' . ' is at the end. I6 is the 2nd production of grammar( A--
>.aA). The LHS of this production is A. FOLLOW(A)=a,b,$ . write r2(reduced 2) in
the columns of a,b,$ and 6th row
LALR Parser: LALR Parser is lookahead LR parser. It is the most powerful parser which
can handle large classes of grammar. The size of CLR parsing table is quite large as
compared to another parsing table. LALR reduces the size of this table. LALR works
similar to CLR. The only difference is, it combines the similar states of CLR parsing table
into one single state.
The general syntax becomes [A->∝.B, a ] where A->∝.B is production and a is a terminal
or right end marker $ LR(1) items=LR(0) items + look ahead
CASE 1 - A->∝.BC, a
Suppose this is the 0th production. Now, since ' . ' precedes B, so we have to write B's
productions as well.
Suppose this is B's production. The look ahead of this production is given as- we look at
previous production i.e. - 0th production. Whatever is after B, we find FIRST (of that value)
, that is the lookahead of 1st production. So, here in 0th production, after B, C is there.
Assume FIRST(C)=d, then 1st production becomes.
B->.D, d
A->∝.B, a
Here, we can see there's nothing after B. So the lookahead of 0th production will be the
lookahead of 1st production. i.e.-
B->.D, a
Here, the 1st production is a part of the previous production, so the lookahead will be the
same as that of its previous production.
EXAMPLE Construct CLR parsing table for the given context free grammar
S-->AA
A-->aA|b
Solution:
STEP1- Find augmented grammar: The augmented grammar of the given grammar is: -
• Now, the 1st production came into existence because of ' . ' before 'S' in 0th
production. There is nothing after 'S', so the lookahead of 0th production will be
the lookahead of 1st production. i.e. : S-->.AA ,$
• Now, the 2nd production came into existence because of ' . ' before 'A' in the 1st
production.
After 'A', there's 'A'. So, FIRST(A) is a,b. Therefore, the lookahead of the 2nd
production becomes a|b.
• Now, the 3rd production is a part of the 2nd production. So, the look ahead will be
the same.
STEP2 - Find LR (0) collection of items. Below is the figure showing the LR(0) collection of
items. We will understand everything one by one.
1. If any non-terminal has ' . ' preceding it, we have to write all its production and add
' . ' preceding each of its production.
2. from each state to the next state, the ' . ' shifts to one place to the right.
• Io goes to I1 when ' . ' of 0th production is shifted towards the right of S(S'->S.).
This state is the accept state . S is seen by the compiler. Since I1 is a part of the
0th production, the lookahead is same i.e. $
• Io goes to I2 when ' . ' of 1st production is shifted towards right (S->A.A) . A is seen
by the compiler. Since I2 is a part of the 1st production, the lookahead is same i.e.
$.
• I0 goes to I3 when ' . ' of 2nd production is shifted towards the right (A->a.A) . a is
seen by the [Link] I3 is a part of 2nd production, the lookahead is same
i.e. a|b.
• I0 goes to I4 when ' . ' of 3rd production is shifted towards right (A->b.) . b is seen
by the compiler. Since I4 is a part of 3rd production, the lookahead is same i.e. a|b.
• I2 goes to I5 when ' . ' of 1st production is shifted towards right (S->AA.) . A is seen
by the compiler. Since I5 is a part of the 1st production, the lookahead is same i.e.
$.
• I2 goes to I6 when ' . ' of 2nd production is shifted towards the right (A->a.A) . A is
seen by the compiler. Since I6 is a part of the 2nd production, the lookahead is
same i.e. $.
• I2 goes to I7 when ' . ' of 3rd production is shifted towards right (A->b.) . A is seen
by the compiler. Since I6 is a part of the 3rd production, the lookahead is same i.e.
$.
• I3 goes to I3 when ' . ' of the 2nd production is shifted towards right (A->a.A) . a is
seen by the compiler. Since I3 is a part of the 2nd production, the lookahead is
same i.e. a|b.
• I3 goes to I8 when ' . ' of 2nd production is shifted towards the right (A->aA.) . A is
seen by the compiler. Since I8 is a part of the 2nd production, the lookahead is
same i.e. a|b.
• I6 goes to I9 when ' . ' of 2nd production is shifted towards the right (A->aA.) . A is
seen by the compiler. Since I9 is a part of the 2nd production, the lookahead is
same i.e. $.
• I6 goes to I6 when ' . ' of the 2nd production is shifted towards right (A->a.A) . a is
seen by the compiler. Since I6 is a part of the 2nd production, the lookahead is
same i.e. $.
• I6 goes to I7 when ' . ' of the 3rd production is shifted towards right (A->b.) . b is
seen by the compiler. Since I6 is a part of the 3rd production, the lookahead is
same i.e. $.
Once we make a CLR parsing table, we can easily make a LALR parsing table from it.
• We combine two 47 row into one by combining each value in the single 47 row.
• We combine two 89 row into one by combining each value in the single 89 row.
CLR Parser: The CLR parser stands for canonical LR parser. It is a more powerful LR
parser. It makes use of lookahead symbols. This method uses a large set of items called
LR (1) items. The main difference between LR (0) and LR (1) items is that, in LR (1) items,
it is possible to carry more information in a state, which will rule out useless reduction
states. This extra information is incorporated into the state by the lookahead symbol. The
general syntax becomes [A->∝.B, a ] where A->∝. B is the production and a is a terminal
or right end marker $ LR (1) items=LR (0) items + look ahead
CASE 1 - A->∝.BC, a
Suppose this is the 0th production. Now, since ' . ' precedes B, so we have to write B's
productions as well.
Suppose this is B's production. The look ahead of this production is given as we look at
previous productions i.e. 0th production. Whatever is after B, we find FIRST(of that value)
, that is the lookahead of 1st production. So, here in 0th production, after B, C is there.
assume FIRST(C)=d, then 1st production becomes
B->.D, d
A->∝.B, a
Here, we can see there's nothing after B. So the lookahead of 0th production will be the
lookahead of 1st production. ie-
B->.D, a
Here, the 1st production is a part of the previous production, so the lookahead will be the
same as that of its previous production.
EXAMPLE
Construct a CLR parsing table for the given context-free grammar
S-->AA
A-->aA|b
• Now, the 1st production came into existence because of ' . ' Before 'S' in 0th
production. There is nothing after 'S', so the lookahead of 0th production will be
the lookahead of 1st production. ie: S-->.AA ,$
• Now, the 2nd production came into existence because of ' . ' Before 'A' in the 1st
production. After 'A', there's 'A'. So, FIRST(A) is a,b. Therefore, the look ahead for
the 2nd production becomes a|b.
• Now, the 3rd production is a part of the 2nd production. So, the look ahead will be
the same.
STEP 2 - Find LR(1) collection of items Below is the figure showing the LR(1) collection of
items. We will understand everything one by one.
1. If any non-terminal has ' . ' preceding it, we have to write all its production and add
' . ' preceding each of its production.
2. from each state to the next state, the ' . ' shifts to one place to the right.
• Io goes to I1 when ' . ' of 0th production is shifted towards the right of S(S'->S.).
This state is the accept state . S is seen by the compiler. Since I1 is a part of the
0th production, the lookahead is the same ie $
• Io goes to I2 when ' . ' of 1st production is shifted towards right (S->A.A) . A is seen
by the compiler. Since I2 is a part of the 1st production, the lookahead is the same
i.e. $.
• I0 goes to I3 when ' . ' of the 2nd production is shifted towards right (A->a.A) . a is
seen by the compiler. Since I3 is a part of the 2nd production, the lookahead is the
same ie a|b.
• I0 goes to I4 when ' . ' of the 3rd production is shifted towards right (A->b.) . b is
seen by the compiler. Since I4 is a part of the 3rd production, the lookahead is the
same i.e. a | b.
• I2 goes to I5 when ' . ' of 1st production is shifted towards right (S->AA.) . A is seen
by the compiler. Since I5 is a part of the 1st production, the lookahead is the same
i.e. $.
• I2 goes to I6 when ' . ' of 2nd production is shifted towards the right (A->a.A) . A is
seen by the compiler. Since I6 is a part of the 2nd production, the lookahead is the
same i.e. $.
• I2 goes to I7 when ' . ' of 3rd production is shifted towards right (A->b.) . A is seen
by the compiler. Since I6 is a part of the 3rd production, the lookahead is the same
i.e. $.
• I3 goes to I3 when ' . ' of the 2nd production is shifted towards right (A->a.A) . a is
seen by the compiler. Since I3 is a part of the 2nd production, the lookahead is the
same i.e. a|b.
• I3 goes to I8 when ' . ' of 2nd production is shifted towards the right (A->aA.) . A is
seen by the compiler. Since I8 is a part of the 2nd production, the lookahead is the
same i.e. a|b.
• I6 goes to I9 when ' . ' of 2nd production is shifted towards the right (A->aA.) . A is
seen by the compiler. Since I9 is a part of the 2nd production, the lookahead is the
same i.e. $.
• I6 goes to I6 when ' . ' of the 2nd production is shifted towards right (A->a.A) . a is
seen by the compiler. Since I6 is a part of the 2nd production, the lookahead is the
same i.e. $.
• I6 goes to I7 when ' . ' of the 3rd production is shifted towards right (A->b.) . b is
seen by the compiler. Since I6 is a part of the 3rd production, the lookahead is the
same ie $.
• similarly 5 is written in A column and 2nd row, 8 is written in A column and 3rd
row, 9 is written in A column and 6th row.
• Similarly, S6(shift 6) is added on 'a' column and 2,6 row, S7(shift 7) is added on b
column and 2,6 row, S3(shift 3) is added on 'a' column and 3 row, S4(shift 4) is
added on b column and 3 row.
• I4 is reduced as ' . ' is at the end. I4 is the 3rd production of grammar. So write
r3(reduce 3) in lookahead columns. The lookahead of I4 are a and b, so write R3 in
a and b column.
• I5 is reduced as ' . ' is at the end. I5 is the 1st production of grammar. So write
r1(reduce 1) in lookahead columns. The lookahead of I5 is $ so write R1 in $
column.
• Similarly, write R2 in a,b column and 8th row, write R2 in $ column and 9th row.
1. If the front of input $ and top of stack both have $, it's done
else
if b! = '⋗'
then push b
3. if b == '⋗'
pop ⋖ also
then push ⋖ S
Components Operator
Example -
Let's take an example to understand the role of operator precedence as follows.
E-> E+T/T
T-> T*V/V
V->a/b/c/d
string= "a+b*c*d"
$ a+b*c*d$ $ a shift a
$E +b*c*d$ E + shift +
$E+T* d$ * d shift d
$T $ E $ accept
1. Generate a function Xa for each grammar terminal a and for the end of the string
symbol.
2. Partition the symbol in groups so that Xa and Yb are the same groups if a ≐ b.
3. Generate a directed graph whose nodes are in the groups, for each symbol a and
b, do place an edge from the group of Yb to the group of Xa if a ⋖ b, otherwise if a
⋗ b place an edge from the group of Xa to that of Yb.
4. If the constructed graph has a cycle then no procedure functions exist. When
there are no cycles collect the length of the longest paths from the groups of Xa
and yb respectively.
Difference Between Bottom-Up and Top-Down Parser
Direction Builds tree from root to leaves. Builds tree from leaves to root.
Example
Recursive descent, LL parser. Shift-reduce, LR parser.
Parsers