You are on page 1of 73

Compiler Design (KCS-502)

3rd year (Semester – V)


Session – 2023 - 24
Unit – II
Part – A
Ratish Srivastava
Asst. Prof.
CSE Dept.
UCER, Prayagraj
Introduction
• The context free grammar is used for the syntax
analyzer or parser.
• Syntax analysis (Parsing) is the second phase of the
compiler design process that comes after lexical
analysis.
• It analyses the syntactical structure of the given
input.
• It checks if the given input is in the correct syntax of
the programming language in which the input has
been written (confirming to the grammar of the
programming language).
Compiler Design, KCS-502 2
Role of Parser
• It obtains a string of tokens from the lexical analyzer.

• It groups the tokens appearing in the input in order to


identify larger structures in the program.

• It should report any syntax error in the program.

• It should also recover from the errors so that it can


continue to process the rest of the input.

• The output of the parser is the parse tree.

Compiler Design, KCS-502 3


Role of Parser
• The parser uses different parsing techniques such
as Top-down parsing, Bottom-up parsing.

– When the parse tree can be constructed from root


and expanded to leaves then such type of parser is
called Top-down parser i.e., it can be built from top to
bottom.

– When the parse tree can be constructed from leaves


to root, then such type of parser is called as Bottom-
up parser. Thus the parse tree is built in bottom-up
manner.

Compiler Design, KCS-502 4


Types of Parsers

Compiler Design, KCS-502 5


Types of Parser
Top down paring
• The top down parsing is known as recursive
parsing or predictive parsing.
• In the top down parsing, the parsing starts
from the start symbol and transform it into
the input symbol.
• It construct parse tree by tracing out the
leftmost derivations of string.

Compiler Design, KCS-502 6


Types of Parser
Bottom up parsing
• Bottom up parsing is also known as shift-
reduce parsing.
• In the bottom up parsing, the parsing starts
with the input symbol and construct the
parse tree up to the start symbol.
• It construct parse tree by tracing out the
rightmost derivations of string in reverse.

Compiler Design, KCS-502 7


Top-Down Parsing
• A top-down parser may be considered as an
attempt to construct a parse tree starting from
the root and creates the nodes of the parse tree
in preorder.
– This means it starts the derivation from the start
symbol of the grammar. So, a top down parser starts
constructing the left-most derivation from the start
symbol.

• Left-most derivation has the property that there


are only terminal symbols preceding the left most
non-terminal in any sentential form.
Compiler Design, KCS-502 8
Top-Down Parsing
• The next step is to find a suitable production
rule in such a way that by using that rule, it
can move from a left most sentential form to
its succeeding one.

• In top-down parsing, selection of proper rule


is very important task and this is based on trial
and error technique.

Compiler Design, KCS-502 9


Left Recursion
• The production is left-recursive if the leftmost
symbol on the right side is same as the non-
terminal on the left side.

• For example,
A→Aα
Here A can be any non-terminal and α denotes
some input string.

• Because of left recursion the top-down parser


can enter in infinite loop.
Compiler Design, KCS-502 10
Left Recursion
• To eliminate left recursion we need to modify the
grammar.

• Let G be a CFG having a production rule with left


recursion
A→Aα|β
Then we eliminate left recursion by rewriting the
production rule as
A→βA’
A’→αA’
A’→Є

Compiler Design, KCS-502 11


Left Recursion
• General algorithm for removing left recursion
A→Aα1|Aα2|……|Aαm|β1|β2|……|βm
where no βi begins with an A.

We can write above production as


A→β1A’|β2A’|…….|βmA’
A’→α1A’|α2A’|……|αmA’|Є

Compiler Design, KCS-502 12


Left Recursion
Example:

Consider the following CFG


S→Bb|a
B→Bc|Sd|e
Find out the left recursion and remove it.

Compiler Design, KCS-502 13


Left Recursion
Solution:
We can write the given productions as
S→Bb|a
B→Bc|Bbd|ad|e
We will remove left recursion as follows:
B→adB’|eB’
B’→cB’|bdB’|Є

Compiler Design, KCS-502 14


Left Recursion
Example:
Consider the following grammar for arithmetic
expressions
E→E+T|T
T →T*F|F
F →(E)|id
id →a|b|c
Remove the left recursion.
Compiler Design, KCS-502 15
Left Recursion
Solution:
Eliminating the immediate left recursion to the
production for E and then for T, we obtain
E →TE’
E’ →+TE’|Є
T →FT’
T’ →*FT’|Є
F →(E)|id
id →a|b|c

Compiler Design, KCS-502 16


Left Factoring
• Left factoring is a process which isolates the common
parts of two productions into a single production.

• Any production of the form


A→αβ1| αβ2|……..| αβn
can be replaced by
A→αA’
A’→β1| β2|……| βn

• Left factoring is useful for producing a grammar


suitable for a predictive parser.

Compiler Design, KCS-502 17


Left Factoring
Example:

Consider the following grammar


A→aAB|aA|a
B→bB|b
Remove the left factoring from it.

Compiler Design, KCS-502 18


Left Factoring
Solution:
A→aAB|aA|a
It can be replaced by
A→aA’
A’→AB|A|Є
Similarly, B→bB|b can be replaced by
B→bB’
B’→B|Є
By left factoring the grammar will be
A→aA’
A’→AB|A|Є
B→bB’
B’→B|Є
Compiler Design, KCS-502 19
Types of Top-Down Parsers
• There are mainly two types by which the top-down
parsing can be performed
– Backtracking parsing
– Predictive parsing

• Backtracking parser will try different production rules


to find the match for the input string by backtracking
each time.

• Predictive parser tries to predict the next construction


using one or more lookahead symbols from input
string.

Compiler Design, KCS-502 20


Types of Top-Down Parsers
• Note:
– Backtracking is powerful than predictive parsing
but this technique is slower and it requires
exponential time. So, backtracking is not preferred
for practical compilers.

• There are two types of predictive parsers:


– Recursive Descent Parser
– LL(1) Parser
Compiler Design, KCS-502 21
Recursive-Descent Parsing
• A top-down parser that executes a set of recursive
procedures to process the input without backtracking
is called recursive-descent parser and parsing is called
recursive-descent parsing.

• There is a procedure for each non-terminal in the


grammar. We assume a global variable, lookahead,
holding the current input token and a procedure
match.

• match() is the action of recognizing the next token in


the parsing process.

Compiler Design, KCS-502 22


Recursive-Descent Parsing
• Basic steps for construction of Recursive-
Descent Parser:
– The R.H.S of the production rule is directly
converted to a program code symbol by symbol.
• If the input-symbol is non-terminal then a call to the
procedure corresponding to the non-terminal is made.
• If the input symbol is terminal then it is matched with
the lookahead from input symbol. The lookahead
pointer has to be advanced on matching of the input
symbol.

Compiler Design, KCS-502 23


Recursive-Descent Parsing
• If the production rule has many alternates then all these alternates have
to be combined into a single body of procedure. The parser should be
activated by a procedure corresponding to the start symbol.

• Construction of recursive descent parser is easy.


However, the programming language that we choose
for recursive descent parser should support recursion.

• One of the major drawbacks of recursive-descent


parsing is that it can be implemented only for those
languages which support recursive procedure calls and
it suffers with the problem of left recursion.

Compiler Design, KCS-502 24


Recursive-Descent Parsing
Example:

Write a code for the Recursive-descent parsing


of the following grammar:
expr→digit rest
rest→+ digit rest|- digit rest|Є
digit→0|1|2|3|……|9

Compiler Design, KCS-502 25


Recursive-Descent Parsing
Solution:
In a recursive-descent parsing, we write code for each non-terminal
of a grammar.
Since there is only one production for expr, the procedure expr() is
as follows:
expr()
{
digit();
rest();
return();
}
Since there are three productions for ‘rest’, so procedure rest() uses
a global variable lookahead to select the correct production or
simply selects “no action”, i.e., Є-production.
Compiler Design, KCS-502 26
Recursive-Descent Parsing
rest()
{
if(lookahead==‘+’)
{
match();
digit();
rest();
return();
}
else if(lookahead==‘-’)
{
match();
digit();
rest();
return();
}
else
{
return();
}
} Compiler Design, KCS-502 27
Recursive-Descent Parsing
The procedure digit() checks whether global variable is a digit.
digit()
{
if(isdigit(lookahead))
{
match();
return();
}
else
{
error();
}
}

Compiler Design, KCS-502 28


Recursive-Descent Parsing
The procedure match() .
match(char c)
{
if(lookahead==‘c’)
{
lookahead=getchar();
}
else
{
printf(“error");
}
}

Compiler Design, KCS-502 29


Recursive-Descent Parsing
The main() function.
main()
{
expr();
if(lookahead==“$”)
{
printf(“Parsing Successful”);
}
else
{
error();
}
}

Compiler Design, KCS-502 30


Recursive-Descent Parsing
• After loading first input token into variable
‘lookahead’ predictive parser is started by
calling start symbol ‘expr’.

• If the input string is error free, the parser


conducts a depth-first traversal of the parse
tree and returns to caller routine through
‘expr’.

Compiler Design, KCS-502 31


Predictive LL(1) Parser
• A predictive parser is an efficient way of
implementing recursive-descent parsing since a
stack is maintained in predictive parsing for
handling the activation records.

• In top-down predictive parsers, the grammar will


be able to predict the right alternative for the
expansion of non-terminal during the parsing
process, and hence, it needs no backtrack.
Compiler Design, KCS-502 32
Predictive LL(1) Parser
• For LL(1) –
– The first L means the input is scanned from left to
right.
– The second L means it uses leftmost derivation for
input string and
– The number 1 in the input symbol means it uses
only one input symbol (lookahead) to predict the
parsing process.

Compiler Design, KCS-502 33


Predictive LL(1) Parser
• The data structures used by LL(1) are:
– Input buffer
– Stack
– Parsing table

• The input buffer contains the string to be


parsed, followed by $, a symbol used as a right
end marker to indicate the end of the input
string.

Compiler Design, KCS-502 34


Predictive LL(1) Parser
• The stack contains a sequence of grammar
symbol with $ on the bottom, indicating the
bottom of the stack. Initially, the stack
contains the start symbol of the grammar on
the top.

• The parsing table is a two-dimensional array


M[A,a], where A is a non-terminal and ‘a’ is a
terminal or the symbol $.

Compiler Design, KCS-502 35


FIRST and FOLLOW
• The construction of a predictive parser is
aided by two functions associated with a
grammar G.

• These functions, FIRST and FOLLOW, allow us


to fill in the entries of a predictive parsing
table for G, whenever possible.

Compiler Design, KCS-502 36


FIRST function
• We define a function FIRST(α), where α is in (V U Σ)*, as follows:
– FIRST(α) is the set of terminal symbols that are first symbols
appearing at R.H.S. in derivation of α.
– If α = XYZ, then FIRST(α) is computed as
• FIRST(α) = FIRST(XYZ)
= {X}, if X is terminal
otherwise
• FIRST(α) = FIRST(X), if X does not derive to an empty string i.e., if FIRST(X) does not
contain Є
otherwise
• If FIRST(X) contains Є, then
– FIRST(α) = FIRST(XYZ) = FIRST(X) – {Є} U FIRST(YZ)
and FIRST(YZ) is computed as follows:
– FIRST(YZ) = {Y}, if Y is terminal
otherwise
= FIRST(Y), if Y does not derive to an empty string
otherwise
= FIRST(Y) – {Є} U FIRST(Z)
Compiler Design, KCS-502 37
FIRST function
Example:

Consider a grammar
S → ACB|CbB|Ba
A → da|BC
B → g|Є
C → f|Є

Compiler Design, KCS-502 38


FIRST function
Solution:
FIRST(S) = FIRST(ACB) U FIRST(CbB) U FIRST(Ba)
FIRST(A) = FIRST(da) U FIRST(BC)
FIRST(B) = FIRST(g) U FIRST(Є) = {g,Є}
FIRST(C) = FIRST(f) U FIRST(Є) = {f,Є}

Now, FIRST(BC) = FIRST(B) – {Є} U FIRST(C)


= {g,Є} – {Є} U {f,Є}
= {g} U {f,Є}
= {g,f,Є}
and FIRST(da) = {d}
Therefore, FIRST(A) = FIRST(da) U FIRST(BC)
= {d} U {g,f,Є}
= {d,g,f,Є}

Compiler Design, KCS-502 39


FIRST function

Now, FIRST(ACB) = FIRST(A) – {Є} U FIRST(CB)


= {d,g,f,Є} – {Є} U FIRST(CB)

and FIRST(CB) = FIRST(C) – {Є} U FIRST(B)


= {f,Є} – {Є} U {g,Є}
= {f,g,Є}

Therefore, FIRST(ACB) = {d,g,f} U {f,g,Є}


= {d,g,f,Є}

Compiler Design, KCS-502 40


FIRST function
Similarly, FIRST(CbB) = FIRST(C) – {Є} U FIRST(bB)
= {f, Є} – {Є} U {b}
= {f,b}

and FIRST(Ba) = FIRST(B) – {Є} U FIRST(a)


= {g, Є} – {Є} U {a}
= {a,g}

Therefore, FIRST(S) = {d,f,g, Є} U {f,b} U {a,g}


= {a,b,d,f,g, Є}

Compiler Design, KCS-502 41


FOLLOW function
• We define a function FOLLOW(A), where A is a non-
terminal as follows:
– FOLLOW(A) = set of terminals that immediately follow A in any
string occurring on the right side of productions of the grammar.
– The rules for computing FOLLOW function are as follows:
1) FOLLOW(S) = { $ } // where S is the starting Non-Terminal
2) If A -> pBq is a production, where p, B and q are any grammar
symbols, then everything in FIRST(q) except Є is in FOLLOW(B).
3) If A->pB is a production, then everything in FOLLOW(A) is in
FOLLOW(B).
4) If A->pBq is a production and FIRST(q) contains Є, then FOLLOW(B)
contains { FIRST(q) – Є } U FOLLOW(A)
– Note:
• If FOLLOW(A) can be computed from different productions with
different values then union of all FOLLOW(A) from all productions will
give FOLLOW(A). Compiler Design, KCS-502 42
FOLLOW function
Example 1:

Consider the following grammar


S → aABb
A → c|Є
B → d|Є

Compiler Design, KCS-502 43


FOLLOW function
Solution:
FIRST(S) = FIRST(aABb) = {a}
FIRST(A) = FIRST(c) U FIRST(Є) = {c, Є}
FIRST(B) = FIRST(d) U FIRST(Є) = {d, Є}
Since S is the start symbol, so place $ in FOLLOW(S). Also S
is not present in R.H.S.,
Therefore, FOLLOW(S) = {$}
FOLLOW(A) = FIRST(Bb)
= FIRST(B) – {Є} U FIRST(b)
= {d, b}
and FOLLOW(B) = FIRST(b) = {b}
Compiler Design, KCS-502 44
FOLLOW function
Example 2:

Production Rules:
S → ACB|Cbb|Ba
A → da|BC
B → g|Є
C → h| Є

Compiler Design, KCS-502 45


FOLLOW function
Solution:

FIRST set:
FIRST(S) = FIRST(A) U FIRST(B) U FIRST(C) = { d, g, h, b, a, Є}
FIRST(A) = { d } U FIRST(B) = { d, g, h, Є }
FIRST(B) = { g, Є }
FIRST(C) = { h, Є }

FOLLOW Set:
FOLLOW(S) = { $ }
FOLLOW(A) = { h, g, $ }
FOLLOW(B) = { a, $, h, g }
FOLLOW(C) = { b, g, $, h }

Compiler Design, KCS-502 46


FOLLOW function
Example 3:
Consider the following grammar
E → TE’
E’ → +TE’|Є
T → FT’
T’ → *FT’|Є
F → (E)|id
Find the FIRST and FOLLOW functions for the
above grammar.

Compiler Design, KCS-502 47


FOLLOW function
Solution:
FIRST set:
FIRST(E) = {(, id}
FIRST(E’) = {+, Є}
FIRST(T) = {(, id}
FIRST(T’) = {*, Є}
FIRST(F) = {(, id}

FOLLOW set:
FOLLOW(E) = {), $}
FOLLOW(E’) = {), $}
FOLLOW(T) = {+, ), $}
FOLLOW(T’) = {+, ), $}
FOLLOW(F) = {*, +, ), $}
Compiler Design, KCS-502 48
Construction of Predictive Parsing
Table
• Once we know the FOLLOW set for each non-
terminal, we can construct a parse table.

• The number of rows of table are equal to the


number of non-terminals.

• The number of columns are equal to the


number of terminals, including the end
marker.
Compiler Design, KCS-502 49
Algorithm for Predictive Parsing
Table
• This algorithm requires FIRST and FOLLOW sets.
Input: The Context Free Grammar G.
Output: Predictive Parsing Table M.
Method: For the rule A→α of grammar G.
(i) For each ‘a’ in FIRST(α) create entry M[A,a] = A→α,
where ‘a’ is terminal symbol.
(ii) For Є in FIRST(α) create entry M[A,b] = A→α,
where b is the symbol from FOLLOW(A).
(iii) If Є is in FIRST(α) and $ is in FOLLOW(A) then create
entry M[A,$] = A→α.
(iv) All the remaining entries in table M are marked as
SYNTAX ERROR.
Compiler Design, KCS-502 50
Construction of Predictive Parsing
Table
Example:
Consider the following grammar
E → TE’
E’ → +TE’|Є
T → FT’
T’ → *FT’|Є
F → (E)|id
Construct the predictive parsing table.

Compiler Design, KCS-502 51


Construction of Predictive Parsing
Table
Solution:
Number of rows = number of non-terminals = 5
Number of columns = number of terminals + 1 = 5 + 1 = 6
Now, we will fill up the entries in the table.
Consider the production rule E→TE’
Compare it with A→α, we get A=E and α=TE’
FIRST(TE’) = FIRST(T) = {(, id}
So, M[E,(] = E→TE’ and
M[E,id] = E→TE’
Now E’→+TE’
FIRST(+TE’) = {+}
So, M[E’,+] = E’→+TE’
Now E’→Є
Compare it with A→α, we get A=E’ and α=Є
So, FOLLOW(A) = FOLLOW(E’) = {), $}
So, M[E’,)] = E’→Є and
M[E’,$] = E’→Є Compiler Design, KCS-502 52
Construction of Predictive Parsing
Table
• Continuing in this manner we create the parsing
table as follows.
id + * ( ) $
E E → TE’ E → TE’
E’ E’ → +TE’ E’ → Є E’ → Є
T T → FT’ T → FT’
T’ T’ → Є T’ → *FT’ T’ → Є T’ → Є
F F → id F → (E)

• In the above parsing table blanks are error


entries.
Compiler Design, KCS-502 53
Implementation of a table-driven
Predictive parser
• A table-driven parser can be implemented using an input buffer, a
stack and a parsing table.

• The input buffer is used to hold the string to be parsed.

• The string is followed by a “$” symbol that is used as a right-end


marker to indicate the end of the input string.

• The stack is used to hold the sequence of grammar symbols.

• A “$” symbol indicates the bottom of the stack.

• Initially, the stack has the start symbol of a grammar above the $.

Compiler Design, KCS-502 54


Implementation of a table-driven
Predictive parser
• The parser is controlled by a program that works
as follows:
– The program considers X, the symbol on top of the
stack, and the next input symbol ‘a’.
– If X=a=$, the parser announces the successful
completion of the parsing and halts.
– If X=aǂ$, then the parser pops X off the stack and
advances the input pointer to the next input symbol.
– If X is a non-terminal, then the program consults the
parsing table entry M[X,a].

Compiler Design, KCS-502 55


Implementation of a table-driven
Predictive parser
• If M[X,a] = X→ABC, then the parser replaces X
on the top of the stack by ABC in such a
manner that A will come on the top.

• If M[X,a] = ERROR, then the parser calls the


error-recovery routine.

Compiler Design, KCS-502 56


Construction of Predictive Parsing
Table
Example:
Suppose the parsing table is as shown below:
id + * ( ) $
E E → TE’ E → TE’
E’ E’ → +TE’ E’ → Є E’ → Є
T T → FT’ T → FT’
T’ T’ → Є T’ → *FT’ T’ → Є T’ → Є
F F → id F → (E)

Now the input string id+id*id$ can be parsed


using above table.
Compiler Design, KCS-502 57
Construction of Predictive Parsing
Table
At the initial configuration the stack will contain start
symbol E and in the input buffer input string is placed.
Stack Input Moves
$E id+id*id$

Now E is top of the stack and input pointer is at the first ‘id’.
Hence M[E,id] is referred i.e., E→TE’.
Push E’ first into the stack then T.
Stack Input Moves
$E’T id+id*id$ E→TE’
Now T is top of stack and input pointer is at the first ‘id’.
Therefore, M[T,id] = T→FT’.
Push T’ first into the stack then F. Similarly, we get
Compiler Design, KCS-502 58
Construction of Predictive Parsing
Table
Stack Input Moves
$E id+id*id$
$E’T id+id*id$ E→TE’
$E’T’F id+id*id$ T→FT’
$E’T’id id+id*id$ F→id
$E’T’ +id*id$
$E’ +id*id$ T’→Є
$E’T+ +id*id$ E’→+TE’
$E’T id*id$
$E’T’F id*id$ T→FT’
$E’T’id id*id$ F→id
$E’T’ *id$

Compiler Design, KCS-502 59


Construction of Predictive Parsing
Table
$E’T’F* *id$ T’→*FT’
$E’T’F id$
$E’T’id id$ F→id
$E’T’ $
$E’ $ T’→Є
$ $ E’→Є

• So, the parser announces the successful completion of parsing.


• It is observed that the input is scanned from left to right and we
always follow left most derivation while parsing the input string.
• Also at a time only one input symbol is referred to taking the
parsing action. Hence the name of this parser is LL(1).
• The LL(1) parser is a table driven predictive parser.
Compiler Design, KCS-502 60
LL(1) Grammar
• A parsing table for any grammar can be obtained by the
application of the algorithm, but some grammars, some of
the entries in the parsing table may end up being multiple
defined entries, whereas for certain grammars, all the
entries in the parsing table contain single entries.

• All such grammars (i.e., those grammars that after applying


the algorithm, contain no multiple entries in the parsing
table) constitute a subset of CFG called LL(1) grammars.

• If the table contains multiple entries, then the grammar is


not LL(1)
“A grammar is an LL(1) grammar if and only if its
parsing table has no entries that are multiple defined.”
Compiler Design, KCS-502 61
LL(1) Grammar
• LL(1) grammars have several distinctive
properties.
– No ambiguous or left-recursive grammar can be LL(1).
– It can also be shown that a grammar G is LL(1) if and
only if whenever A→α|β are two distinct productions
of G the following conditions hold:
• For no terminal ‘a’ do both α and β derive strings beginning
with ‘a’.
• At most one of α and β can derive the empty string.
• If β→Є then α does not derive any string beginning with a
terminal in FOLLOW(A).

Compiler Design, KCS-502 62


LL(1) Grammar
Example 1:
Consider the following grammar
S→iEtS|iEtSeS|a
E→b
Here i, t and e stand for if, then and else. E and
S for “expression” and “statement”. Check if it
is LL(1) grammar or not.

Compiler Design, KCS-502 63


LL(1) Grammar
Solution:
Left-factored, this grammar becomes
S→iEtSS’|a
S’→eS|Є
E→b
Now check it is LL(1) grammar or not.
Compute FIRST and FOLLOW for given non-terminals.
FIRST(S)={i,a} FOLLOW(S)={e,$}
FIRST(S’)={e,Є} FOLLOW(S’)={e,$}
FIRST(E)={b} FOLLOW(E)={t}

Compiler Design, KCS-502 64


LL(1) Grammar
The predictive parsing table will be
a b e i t $
S S→a S→iEtSS’
S’ S’→Є S’→Є
S’→eS
E E→b

The entry for M[S’,e] contains both S’→Є and


S’→eS i.e., multiple entries.
So given grammar is not LL(1) grammar.

Compiler Design, KCS-502 65


LL(1) Grammar
Example 2:

Design LL(1) parsing table for the following


grammar:
A→AcB|cC|C
B→bB|id
C→CaB|BbB|B

Compiler Design, KCS-502 66


LL(1) Grammar
Solution:
The given grammar rule A→AcB|cC|C is left recursive.
So we eliminate left recursion first.
So A→AcB|cC|C becomes
A→cCA’|CA’
A’→cBA’|Є
Now C→CaB|BbB|B is left recursive. So we eliminate
left recursion as
C→BbBC’|BC’
C’→aBC’|Є

Compiler Design, KCS-502 67


LL(1) Grammar
Now, consider the rule
C→BbBC’|BC’
This needs to be left factored. So, after left factoring, we
get
C→BC”
C”→bBC’|C'
Hence the final grammar is
A→cCA’|CA’
A’→cBA’|Є
B→bB|id
C→BC”
C”→bBC’|C’
C’→aBC’|Є
Compiler Design, KCS-502 68
LL(1) Grammar
Now, compute the FIRST and FOLLOW as follows:
FIRST(A) = {b,c,id} FOLLOW(A) = {$}
FIRST(A’) = {c,Є} FOLLOW(A’) = {$}
FIRST(B) = {b,id} FOLLOW(B) = {a,b,c,$}
FIRST(C) = {b,id} FOLLOW(C) = {c,$}
FIRST(C”) = {a,b,Є} FOLLOW(C”) = {c,$}
FIRST(C’) = {a,Є} FOLLOW(C’) = {c,$}

Compiler Design, KCS-502 69


LL(1) Grammar
• The predictive parsing table is as follows:
a b c id $
A A→CA’ A→cCA’ A→CA’
A’ A’→cBA’ A’→Є
B B→bB B→id
C C→BC” C→BC”
C” C”→C’ C”→bBC C”→C’ C”→C’
C’ C’→aBC’ C’→Є C’→Є

Compiler Design, KCS-502 70


LL(1) Grammar
Example 3:
Consider the following grammar:
S’→S# S→ABC A→a|bbD
B→a|Є C→b|Є D→c|Є
Construct the FIRST and FOLLOW sets for the
grammar. Also design LL(1) parsing table for
the grammar.

Compiler Design, KCS-502 71


LL(1) Grammar
Solution:
FIRST(S’) = {a,b} FOLLOW(S’) = {$}
FIRST(S) = {a,b} FOLLOW(S) = {#}
FIRST(A) = {a,b} FOLLOW(A) = {a,b,#}
FIRST(B) = {a,Є} FOLLOW(B) = {b,#}
FIRST(C) = {b,Є} FOLLOW(C) = {#}
FIRST(D) = {c,Є} FOLLOW(D) = {a,b,#}

Compiler Design, KCS-502 72


LL(1) Grammar
• The parsing table is as follows:
a b c # $
S’ S’→S# S’→S#
S S→ABC S→ABC
A A→a A→bbD
B B→a B→Є B→Є
C C→b C→Є
D D→Є D→Є D→c D→Є

Compiler Design, KCS-502 73

You might also like