Вы находитесь на странице: 1из 122

UNIT -II

2.1 Syntax Analysis


Syllabus:
• Syntax Analysis CFG, top-down and bottom-up
parsers, RDP, Predictive parser, SLR, LR(1),
LALR parsers, using ambiguous grammar, Error
detection and recovery, automatic
construction of parsers using YACC,
Introduction to Semantic analysis Need of
semantic analysis, type checking and type
conversion
Outline
• Role of parser
• Context free grammars
• Top down parsing
• Bottom up parsing
• Parser generators
Parsing
From text to abstract syntax
program text 5 + (7 * x)

Lexical
Analyzer

token stream num + ( num * id )

Grammar:
E  id parse tree
E  num Parser
E
EE+E
EE*E syntax valid E + E
E(E) error
+ num ( E )

Abstract syntax tree num * E * E

num id
7 x 4
Issues in Parsing
• Specification of syntax
• Representation of input after parsing
• Parsing algorithm
The role of parser

token
Source Lexical Parse tree Rest of Front Intermediate
Parser
program Analyzer End representation
getNext
Token

Symbol
table
Why Lexical analyzer and Syntax analyzer
are separate out?
• Two advantages
1. Accelerates process of compilation
2. Errors in source i/p can be identified
precisely.
Context free grammar
• G is a collection of following things.
1. V  set of non terminals
2. T  terminals
3. S  start symbol
4. P production rules
Thus G can be represented as G=(V,T,S,P)
Example:
Let language L=a^nb^n where n>=1
G=(V,T,S,P)
Where V={S}
T={a,b}
And S is a start symbol, then give production
rules.
P=?
P={
S aSb
Sab
}
Example 1:
Define CFG for declarative statement.
State Type List Terminator
Type int | float
List List,id
List id
Terminator ;
Parse tree

State

Type List Terminator

int
List , id ;

List , id

id
Parsing Technique
Two parsing technique:
1. Top-down
2. Bottom-up
Top Down Parsing
Example:

S → aB | bA
A → a | aS | bAA
B → b | bS | aBB

Input: aabb

Derivation: S → aB → aaBB → aabB → aabb

13
Input: aabb

14
Issues in Top-Down Parsing
• Backtracking
• Left recursion
• Left factoring
• Ambiguity
Elimination of left recursion
• A grammar is left recursive if it has a non-terminal A
such that there is a derivation A=> Aα
• Top down parsing methods + cant handle left-
recursive grammars
• A simple rule for direct left recursion elimination:
– For a rule like:
• A -> A α|β
– We may replace it with
• A -> β A’
• A’ -> α A’ | ɛ
Left factoring
• Left factoring is a grammar transformation that is useful
for producing a grammar suitable for predictive or top-
down parsing.
• Consider following grammar:
– Stmt -> if expr then stmt else stmt
– | if expr then stmt
• On seeing input if it is not clear for the parser which
production to use
• We can easily perform left factoring:
– If we have A->αβ1 | αβ2 then we replace it
with
• A -> αA’
• A’ -> β1 | β2
Left factoring (cont.)
• Algorithm
– For each non-terminal A, find the longest prefix α
common to two or more of its alternatives. If α<>
ɛ, then replace all of A-productions A->αβ1
|αβ2 | … | αβn | γ by
• A -> αA’ | γ
• A’ -> β1 |β2 | … | βn
• Example:
– S -> I E t S | I E t S e S | a S  iEtSS’| a
S’  es|
– E -> b Eb
Ambiguity
• For some strings there exist more than one
parse tree
• Or more than one leftmost derivation
• Or more than one rightmost derivation
• Example: E E+E|E*E|id i/p:-id+id*id
The unambiguous grammar-
EE+T
ET
TT*F
TF
F->id
Types of Top-down parser
Top-down parser
1. Backtracking
2. Predictive parser
2.1 Recursive descent
2.2 LL(1) parser
Backtracking
• A Backtracking parser will try different
production rules to find match for the input
string by backtracking each time.
• Backtracking is powerful than predictive
parsing.
• But it is slower & requires exponential time.
Predictive Parser
• It tries to predict the next construction using
one or more lookahead symbols from input
string.
• There are two types of predictive parsers:
1. Recursive descent
2. LL(1) parser
Recursive descent parsing
• A parser that uses collection of recursive procedures
for parsing the given input string is called Recursive
Descent (RD) Parser.
• CFG is used to build the recursive routines.
• The RHS of production rule is directly converted to a
program.
• For each nonterminal a separate procedure is
written.
Basic steps for construction of RD parser
• The RHS of the rule is directly converted into
program code symbol by symbol.
1. If the i/p symbol is non-terminal then a call to the
procedure is made.
2. If the i/p symbol is terminal then it is matched with
the lookahead from i/p.
3. If the prod rule has many alternates then all these
alternates has to be combined into a single body of
proc.
4. The parser should be activated by a procedure
corresponding to the start symbol.
Example-
Consider the grammar having start symbol E.
E num T
T * num T | ε
if lookahead=$
Procedure E
{
{
if lookahead=num then declare success;
}
{
else
match(num);
error;
T;
}
}
else
error;
procedure T procedure match(token t)
{ {
if lookahead=‘*’ if lookahead=t
{ lookahead=next_token;
match(‘*’); else
if lookahead=num then error;
{ }
match(num); procedure error
T; {
} printf(“Error!!!”);
else }
error;
}
else NULL
}
3*4$ E num T

3*4$ T * num T
3*4$ T * num T
3*4$ Declare succes!
Recursive descent parsing (cont)
• General recursive descent may require backtracking
• The previous code needs to be modified to allow
backtracking
• In general form it cant choose an A-production easily.
• So we need to try all alternatives
• If one failed the input pointer needs to be reset and
another alternative should be tried
• Recursive descent parsers cant be used for left-
recursive grammars
Example

S->cAd
A->ab | a Input: cad

S S S

c A d c A d c A d

a b a
LL(1) Grammars
• Predictive parsers are those recursive descent
parsers needing no backtracking
• Grammars for which we can create predictive
parsers are called LL(1)
– The first L means scanning input from left to right
– The second L means leftmost derivation
– And 1 stands for using one input symbol for
lookahead
*
Non-recursive predicting parsing
a + b $

Predictive output
stack parsing
a program
+
b
$
a + b $
A

B
Construction of Predictive LL(1) Parser

• Steps:-
1. Computation of FIRST and FOLLOW function
2. Construct the predictive parsing table
3. Parse the i/p string
Computing First
• To compute First(X) for all grammar symbols X, apply
following
*
rules until no more terminals or ɛ can be
added to any First set:
1. If x is a terminal then First(X) = {x}.
2. If X-> ɛ is a production then First(X)= ɛ
3. For the rule AX1X2X3…Xk
FIRST(A)=(FIRST(X1)UFIRST(X2)U….FIRST(Xk)
*
Computing follow
• To compute Follow(A) for all non terminals A,
apply following rules until nothing can be
added to any follow set:
1. Place $ in Follow(S) where S is the start symbol
2. If there is a production A-> αBβ then
Follow(B)= First(β) except ɛ.
3. If there is a production A-> αB or a production
A->αBβ where First(β) contains ɛ, then
Follow(A) =Follow(B)
Example:-
Consider grammar
E -> TE’
E’ -> +TE’ | Ɛ
T -> FT’
T’ -> *FT’ | Ɛ
F -> (E) | id
Find first & follow functions.
E -> TE’
1) E -> TE’ E’ -> +TE’ | Ɛ
FIRST(E)=FIRST(T)=FIRST(F)={(,id} T -> FT’
T’ -> *FT’ | Ɛ
F -> (E) | id
2) FIRST(E’)= {+, Ɛ}

3) FIRST(T’)={*, Ɛ}
E -> TE’
E’ -> +TE’ | Ɛ
T -> FT’
1) FOLLOW(E)- T’ -> *FT’ | Ɛ
Rule: F -> (E) | id F -> (E) | id
i. Symbol appears immediately to the right of E-{)}
ii. Computation rule is,
A αBβ map it with rule
F( E )
then,
FOLLOW(B)= FIRST(Β) - Ɛ =FIRST( ) )={)}
So, FOLLOW(B)={), $} (Since E is start symbol)
FOLLOW(E)={), $}
E -> TE’
E’ -> +TE’ | Ɛ
2) FOLLOW(E’)-
T -> FT’
Rule: T’ -> *FT’ | Ɛ
i. E -> TE’ F -> (E) | id
Computation rule is,
A αBβ map it with rule
E -> T E’ Ɛ
then, FOLLOW(B) =FOLLOW(A)
FOLLOW(E’)={), $}
ii. E’ -> +TE’ | Ɛ
A=E’, α=+T, B=E’β= Ɛ
FOLLOW(B) =FOLLOW(A
=FOLLOW(E’)={), $}
E -> TE’
3) FOLLOW(T) E’ -> +TE’ | Ɛ
T -> FT’
Rule: T’ -> *FT’ | Ɛ
i. E -> TE’ F -> (E) | id
A=E, α= Ɛ, B=T, β= E’
FOLLOW(B)= FIRST(β) - Ɛ =FIRST( E’ ) - Ɛ ={+}
FOLLOW(B) =FOLLOW(A) =FOLLOW(E)= {), $}

ii. E’ -> +TE’ | Ɛ


A=E’, α= +, B=T, β= E’
FOLLOW(B)= FIRST(β)=FIRST( E’ ) - Ɛ ={+}
FOLLOW(T)- {+,), $}
E -> TE’
4) FOLLOW(T’) E’ -> +TE’ | Ɛ
Rule: T -> FT’
i. T -> FT’ T’ -> *FT’ | Ɛ
F -> (E) | id
A=T, α= F, B=T’, β= Ɛ
FOLLOW(B) =FOLLOW(A)
=FOLLOW(T)= {+,), $}

ii. T’ -> *FT’ | Ɛ


A=T’, α= *F, B=T’, β= Ɛ

FOLLOW(B) =FOLLOW(A)
=FOLLOW(T’)= {+,), $}
FOLLOW(T’) = {+,), $}
E -> TE’
FOLLOW(F)- E’ -> +TE’ | Ɛ
Rule: T -> FT’
i. T -> FT’ T’ -> *FT’ | Ɛ
F -> (E) | id
A=T, α= Ɛ, B=F, β=T’
FOLLOW(B)= FIRST(β) - Ɛ =FIRST( T’ ) - Ɛ ={*}
FOLLOW(B) =FOLLOW(A)
=FOLLOW(T)= {+,), $}
i. T’ -> *FT’ | Ɛ
A=T’, α= *, B=F, β=T’
FOLLOW(B)= FIRST(β) - Ɛ =FIRST( T’ ) - Ɛ ={*}
FOLLOW(B) =FOLLOW(A)
=FOLLOW(T’)= {+,), $}
FOLLOW(F)-{+,*,), $}
Construction of predictive parsing
table
• For each production A->α in grammar do the
following:
1. For each terminal a in First(α) add A-> in M[A,a]
2. If ɛ is in First(α), then for each terminal b in
Follow(A) add A-> ɛ to M[A,b]. If ɛ is in First(α)
and $ is in Follow(A), add A-> ɛ to M[A,$] as well
• If after performing the above, there is no
production in M[A,a] then set M[A,a] to error
Example First Follow
E -> TE’ F
E’ -> +TE’ | Ɛ {(,id} {+, *, ), $}
T -> FT’ T {(,id} {+, ), $}
T’ -> *FT’ | Ɛ E {(,id} {), $}
F -> (E) | id
E’ {+,ɛ} {), $}
T’ {*,ɛ} {+, ), $}
Input Symbol
Non -
id + * ( ) $
terminal
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)
Stack Input Action

$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$
$E’T’F* *id$ T’ -> *FT’
$E’T’F id$
$E’T’id id$ F -> id
$E’T’ $
$E’ $ T’ -> Ɛ
$ $ E’ -> Ɛ
Another example
Construct LL(1) parser table for the following grammar

S -> iCtS | iCtSeS | a


C -> b

Is the grammar LL(1)?


Another example
Rewrite the grammar as:
S -> iCtSS’ | a
S’ -> eS | Ɛ
C -> b

Non - Input Symbol


terminal a b e i t $
S S -> a S -> iCtSS’

S’ S’ -> Ɛ S’ -> Ɛ
S’ -> eS
C C -> b

For M[S’, e] there are two entries in parsing table, so this


grammar is not LL(1) grammar
Bottom-up Parsing
• Constructs parse tree for an input string beginning at
the leaves (the bottom) and working towards the
root (the top)
• Types of Bottom-Up parser-
1. Shift reduce parser
2. LR parser
1. SLR parser
2. LALR parser
3. LR parser
Bottom-up Parsing
• Example:
Consider grammar
S TL;
Tint|float
LL,id|id
i/p string is: float id,id;
Parse tree:
S TL;
1. Start from leaf node T int | float
Float L L,id | id

2. Reducing float to T. T float i/p string : float id,id;


T

Float

3. Read next string


T

Float id
4. Reduce id to L. Lid S TL;
T int | float
T L
L L,id | id

Float id i/p string : float id,id;

5. Read next string


T L

Float id ,
6. Read next string
T L

Float id , id
S TL;
T int | float
7. Id, id gets reduced L L,id | id
T L
i/p string : float id,id;

Float id , id ;

8. TL; reduced to S S

T L ;

Float id , id
9. Sentential form produced while constructing this
tree is-
float id,id;
T id,id;
T L,id;
T L;
S
Thus basic steps in bottom-up parsing are:
1. Reduction of i/p string to start symbol.
2. The sentential forms that are produced in the
reduction process should trace out rightmost
derivation in reverse.
Shift-reduce parser
• The general idea is to shift some symbols of input to
the stack until a reduction can be applied
• At each reduction step, a specific substring matching
the body of a production is replaced by the
nonterminal at the head of the production
• A reduction is a reverse of a step in a derivation
• The goal of a bottom-up parser is to construct a
derivation in reverse:
Shift reduce parsing
• A stack is used to hold grammar symbols
• Handle always appear on top of the stack
• Initial configuration:
Stack Input
$ w$
• Acceptance configuration
Stack Input
$S $
Handles
• Handle of a string: Substring that matches the
RHS of some production AND whose reduction
to the non-terminal on the LHS is a step along
the reverse of some rightmost derivation.
Shift-reduce Parsing (cont.)
A shift-reduce parser has just four actions
• Shift — moving the symbols from buffer onto
the stack is called shift.
• Reduce — If the handle is on top of the stack
then reduction of it by appropriate rule.
• Accept — If stack contains start symbol only
and input buffer is empty at the same time.
• Error — If parser can’t either shift or reduce
and even can’t perform accept action
Shift reduce parsing (cont.)

E -> E + T | T Stack Input Action


T -> T * F | F
$ id*id$ shift
F -> (E) | id
$id *id$ reduce by F->id
$F *id$ reduce by T->F
• Basic operations: $T *id$ shift
$T* id$ shift
– Shift $T*id $ reduce by F->id
$ reduce by T->T*F
– Reduce $T*F
$T $ reduce by E->T
– Accept $E $ accept

– Error
• Example: id*id
Consider grammar
S TL;
Tint|float
LL,id|id
Parse i/p string int id,id; using shift-reduce
parser
Operator-Precedence Parser
• Operator grammar
– small, but an important class of grammars
– we may have an efficient operator precedence parser
(a shift-reduce parser) for an operator grammar.
• In an operator grammar, no production rule can have:
–  at the right side
– two adjacent non-terminals at the right side.
• Ex:
EAB EEOE EE+E |
Aa Eid E*E |
Bb O+|*|/ E/E | id
not operator grammar not operator grammar operator grammar
Precedence Relations
• In operator-precedence parsing, we define three
disjoint precedence relations between certain pairs
of terminals.
a <. b b has higher precedence than a
a =· b b has same precedence as a
a .> b b has lower precedence than a

• The determination of correct precedence relations


between terminals are based on the traditional
notions of associativity and precedence of operators.
(Unary minus causes a problem).
Using Operator-Precedence Relations
• The intention of the precedence relations is to find
the handle of a right-sentential form,
<. with marking the left end,
=· appearing in the interior of the handle, and
.> marking the right hand.

• In our input string $a1a2...an$, we insert the


precedence relation between the pairs of terminals
(the precedence relation holds between the
terminals in that pair).
Using Operator -Precedence Relations
E  E+E | E-E | E*E | E/E | E^E | (E) | -E | id
The partial operator-precedence table for this grammar
id + * $
id .> .> .>

+ <. .> <. .>

* <. .> .> .>

$ <. <. <.

• Then the input string id+id*id with the precedence relations


inserted will be:
$ <. id .> + <. id .> * <. id .> $
To Find The Handles
1. Scan the string from left end until the first .> is
encountered.
2. Then scan backwards (to the left) over any =· until a
<. is encountered.
3. The handle contains everything to left of the first .>
and to the right of the <. is encountered.

$ <. id .> + <. id .> * <. id .> $ E  id $ id + id * id $


$ <. + <. id .> * <. id .> $ E  id $ E + id * id $
$ <. + <. * <. id .> $ E  id $ E + E * id $
$ <. + <. * .> $ E  E*E $ E + E * .E $
$ <. + .> $ E  E+E $E+E$
$$ $E$
Operator-Precedence Parsing Algorithm
The input string is w$, the initial stack is $ and a table holds
precedence relations between certain terminals
Algorithm:
set p to point to the first symbol of w$ ;
repeat forever
if ( $ is on top of the stack and p points to $ ) then return
else {
let a be the topmost terminal symbol on the stack and let b be the
symbol pointed to by p;

if ( a <. b or a b ) then { /* SHIFT */
push b onto the stack;
advance p to the next input symbol;
}
else if ( a .> b ) then /* REDUCE */
repeat pop stack
until ( the top of stack terminal is related by <. to the terminal most
recently popped );
else error();
Operator-Precedence Parsing Algorithm -- Example
stack input action
$ id+id*id$ $ <. id shift
$id +id*id$ id .> + reduce E  id
$ +id*id$ shift
$+ id*id$ shift
$+id *id$ id .> * reduce E  id
$+ *id$ shift
$+* id$ shift
$+*id $ id .> $ reduce E  id
$+* $ id + * $ * .> $ reduce E  E*E
id .> .> .>
$+ $ + .> $ reduce E  E+E
$ $ + <. .> <. .> accept
* <. .> .> .>
$ <. <. <.
LR Parsers
• The most powerful shift-reduce parsing (yet efficient)
is:

LR(k) parsing.

left to right right-most k lookhead


scanning derivation (k is omitted  it is 1)
LR Parsing: Advantages
• LR Parsers can recognize any language for which a
context free grammar can be written.
• LR Parsing is the most general non-backtracking shift-
reduce method known, yet it is as efficient as ither
shift-reduce approaches
• The class of grammars that can be parsed by an LR
parser is a proper superset of that that can be parsed
by a predictive parser.
• An LR-parser can detect a syntactic error as soon as it
is possible to do so on a left-to-right scan of the
input.

68
LR-Parsing: Drawback/Solution
• The main drawback of LR parsing is that it is too
much work to construct an LR parser by hand for a
typical programming language grammar.
• Fortunately, specialized tools to construct LR parsers
automatically have been designed.
• With such tools, a user can write a context-free
grammar and have a parser generator automatically
produce a parser for that grammar.
• An example of such a tool is Yacc “Yet Another
Compiler-Compiler”

69
LR Parsing Algorithm
input a1 ... ai ... an $
stack

Sm
Xm output
LR Parsing Algorithm
Sm-1
Xm-1
.
.
Action Table Goto Table
S1 terminals and $ non-terminal
X1 s s
t four different t each item is
S0 a actions a a state number
t t
e e
s s
LR Parsing Algorithms: Details I
• An LR parser consists of an input, output, a stack, a
driver program and a parsing table that has two
parts: action and goto.
• The driver program is the same for all LR Parsers.
Only the parsing table changes from one parser to
the other.
• The program uses the stack to store a string of the
form s0X1s1X2…Xmsm, where sm is the top of the stack.
The Sk‘s are state symbols while the Xi‘s are grammar
symbols. Together state and grammar symbols
determine a shift-reduce parsing decision.

71
LR Parsing Algorithms: Details II
• The parsing table consists of two parts: a parsing
action function and a goto function.
• The LR parsing program determines sm, the state on
top of the stack and ai, the current input. It then
consults action[sm, ai] which can take one of four
values:
–Shift
–Reduce
–Accept
–Error

72
LR Parsing Algorithms: Details III
• If action[sm, ai] = Shift s, where s is a state, then the
parser pushes ai and s on the stack.
• If action[sm, ai] = Reduce A  β, then ai and sm are
replaced by A, and, if s was the state appearing
below ai in the stack, then goto[s, A] is consulted and
the state it stores is pushed onto the stack.
• If action[sm, ai] = Accept, parsing is completed
• If action[sm, ai] = Error, then the parser discovered an
error.

73
Types of LR Parsers
• LR-Parsers
1. SLR – simple LR parser
2. LR – most general LR parser
3. LALR – intermediate LR parser (look-head LR
parser)
– SLR, LR and LALR work same (they used the same
algorithm), only their parsing tables are different.
Working of SLR
Context free grammar

Construction of canonical set of items

Construction of SLR parsing table

Parsing of input string Input


string
Output
SLR Parsing
• Definition of related terms
1. LR(0) items: An LR(0) item of a grammar G is a
production of G with a dot at some position of the right
side.
• Example: A  XYZ yields the four following items:
A  .XYZ
A  X.YZ
A  XY.Z
A  XYZ.
• The production A  є generates only one item, A  .

76
2. Augmented Grammar G’ :
This equals G  {S’  S} where S is the start
state of G. The start state of G’ = S’. This is
done to signal to the parser when the parsing
should stop to announce acceptance of input.
3. The Closure Operation:

• If I is a set of items for a grammar G, then


closure(I) is the set of items constructed
from I by the two rules:
1. Initially, every item in I is added to closure(I)
2. If A  α . B β is in closure(I) and B  γ is a
production, then add the item B  . γ to I, if it is
not already there. We apply this rule until no
more new items can be added to closure(I).

78
The Closure Operation – Example
Original grammar Augmented grammar
0. E’  E
• EE+T 1. E  E + T
• ET 2. E  T
• TT*F 3. E  T * F
• TF 4. T  F
• F  (E) 5. F  (E)
• F  id 6. F  id
Let I = {[E’  E]} then
Closure(I)= { [E’  .E], [E  .E + T],
[E  .T], [E  .T*F],
[T  .F], [F  .(E)]
[F  .id] } 79
4. The Goto Operation
• Goto(I,X), where I is a set of items and X is a
grammar symbol, is defined as the closure of the set
of all items [A  αX.β] such that [A  α.Xβ] is in I.
• Example: If I is the set of two items {E’  E.], [E 
E.+T]}, then goto(I, +) consists of

E  E + .T
T  .T * F
T  .F
F  .(E)
F  .id
80
I. Construction of canonical collection of
set of item -
– By applying closure operation & goto function

Example: I0:
1) E  E+T E’  .E
2) ET E  .E+T
3) T  T*F E  .T
4) TF T  .T*F
5) F  (E) T  .F
6) F  id F  .(E)
F  .id 81
goto(I0, E) goto(I0,( ) goto(I1, +) goto(I4,E) goto(I4,( )
I1: I4: I6: I8: I9: (I4)
E’  E. F  (.E) E  E+.T F  (E.) F  (.E)
E  E.+T E  .E+T T  .T*F E  E.+T E  .E+T
E  .T T  .F goto(I4,T) E  .T
goto(I0, T) T  .T*F F  .(E) I9: T  .T*F
I2: T  .F F  .id E  T. (I2) T  .F
E  T. F  .(E) T  T.*F F  .(E)
goto(I2, *)
T  T.*F F  .id F  .id
I7:
goto(I4,F)
goto(I0, id) T  T*.F goto(I4,id)
goto(I0, F) I9:
F  .(E) I9:
I3: I5: T  F. (I3)
F  .id F  id. (I5)
T  F. F  id.
goto(I6, T) goto(I6,( ) goto(I7, F)
I9: I10: (I4) I10:
E  E+T. F  (.E) T  T*F.
T  T.*F E  .E+T
goto(I8,))
E  .T
goto(I6, F) I11:
T  .T*F
I10: F  (E).
T  .F
T  F. (I3)
F  .(E) goto(I8, +)
F  .id I12: (I6)
E  E+.T
T  .T*F
T  .F
F  .(E)
F  .id
The Canonical LR(0) collection for grammar G
I0: E’  .E I4: F  (.E) I7: T  T * .F I1:goto(I0,E)
E  .E + T E  .E + T F  .(E)
E  .T E  .T F  .id I2:goto(I0,T),
T  .T * F T  .T * F I8: F  (E.) goto(I4,T)
T  .F T  .F E  E.+T I3:goto(I0,F),
F  .(E) F  .(E) I9: E  E + T. goto(I4,F),
F  .id F  .id T  T.* F goto(I6,F)
I1: E’  E. I5: F  id. I10: T  T*F.
I4:goto(I0,()
E  E.+T I6: E  E+.T I11: F  (E).
I2: E  T. T  .T*F I5:goto(I0,id)
T  T. * F T  .F I6:goto(I1,+)
I3: T  F. F  .(E)
I7:goto(I2,*)
F  .id
I8:goto(I4,E)
I9:goto(I6,T)
I10:goto(I7,F)
I11:goto(I8,)) 84
II. Construction of SLR parsing table
• Create the parsing action table as follows:
1. If a is a terminal, A.a in Ii and goto(Ii, a)=Ij
then action[i, a] is shift j.
Parsing Tables of Expression Grammar
Action Table Goto Table
State id + * ( ) $ E T F
0 s5 s4 1 2 3
1 s6
2 s7
3
4 s5 s4 8 2 3
5
6 s5 s4 9 3
7 s5 s4 10
8 s6 s11
9 s7
10
11
Construction of SLR parsing table
Create the parsing action table as follows:
2. If A. is in Ii , then action[i,a] is reduce A
for all a in FOLLOW(A) where AS’.

3. If S’S. is in Ii , then action[i,$] is accept.

4. If any conflicting actions generated by these rules,


the grammar is not SLR(1).

5. All entries not defined by (2) and (3) are errors.

6. Initial state of the parser contains S’.S


The Canonical LR(0) collection for grammar G
I0: E’  .E I4: F  (.E) I7: T  T * .F
E  .E + T E  .E + T F  .(E)
E  .T E  .T F  .id
T  .T * F T  .T * F I8: F  (E.)
T  .F T  .F E  E.+T
F  .(E) F  .(E) I9: E  E + T.
F  .id F  .id T  T.* F
I1: E’  E. I5: F  id. I10: T  T*F.
E  E.+T I6: E  E+.T I11: F  (E).
I2: E  T. T  .T*F
T  T. * F T  .F
I3: T  F. F  .(E)
F  .id

88
II. Construction of SLR parsing table
1) E  E+T • Obtain FOLLOW of E, T & F
2) ET
FOLLOW(E) = {+,),$}
3) T  T*F
4) TF FOLLOW(T) = {+,),$,*}
5) F  (E) FOLLOW(F) = {+,*,),$}
6) F  id
Parsing Tables of Expression Grammar
Action Table Goto Table
State id + * ( ) $ E T F
0 s5 s4 1 2 3
1 s6 acc
2 r2 s7 r2 r2
3 r4 r4 r4 r4
4 s5 s4 8 2 3
5 r6 r6 r6 r6
6 s5 s4 9 3
7 s5 s4 10
8 s6 s11
9 r1 s7 r1 r1
10 r3 r3 r3 r3
11 r5 r5 r5 r5
The Canonical LR(0) collection for grammar G
I0: E’  .E I4: F  (.E) I7: T  T * .F I1:goto(I0,E)
E  .E + T E  .E + T F  .(E) I2:goto(I0,T),
E  .T E  .T F  .id goto(I4,T)
T  .T * F T  .T * F I8: F  (E.)
I3:goto(I0,F),
T  .F T  .F E  E.+T
F  .(E) F  .(E) I9: E  E + T. goto(I4,F),
F  .id F  .id T  T.* F goto(I6,F)
I1: E’  E. I5: F  id. I10: T  T*F. I4:goto(I0,()
E  E.+T I6: E  E+.T I11: F  (E). I5:goto(I0,id)
I2: E  T. T  .T*F
T  T. * F T  .F I6:goto(I1,+)
I3: T  F. F  .(E) I7:goto(I2,*)
F  .id I8:goto(I4,E)
I9:goto(I6,T)
I10:goto(I7,F)
I11:goto(I8,))
91
III. Parsing of input string
Example
stack input action output
0 id*id+id$ shift 5
0id5 *id+id$ reduce by Fid Fid
0F3 *id+id$ reduce by TF TF
0T2 *id+id$ shift 7
0T2*7 id+id$ shift 5
0T2*7id5 +id$ reduce by Fid Fid
0T2*7F10 +id$ reduce by TT*F TT*F
0T2 +id$ reduce by ET ET
0E1 +id$ shift 6
0E1+6 id$ shift 5
0E1+6id5 $ reduce by Fid Fid
0E1+6F3 $ reduce by TF TF
0E1+6T9 $ reduce by EE+T EE+T
0E1 $ accept
Transition Diagram (DFA) of Goto Function
I0 E I1 + I6 T I9 * to I7
F to I3
( to I4
T
id to I5
I2 I7
* I10
F
I3 F to I4
to I5
(
I4 I8
to I2 id I
( 11
I5 to I3 to I6
E
to I4 )
id id T
F +
(
shift/reduce and reduce/reduce conflicts

• If a state does not know whether it will make a shift


operation or reduction for a terminal, we say that
there is a shift/reduce conflict.
• If a state does not know whether it will make a
reduction operation using the production rule i or j
for a terminal, we say that there is a reduce/reduce
conflict.
• If the SLR parsing table of a grammar G has a conflict,
we say that that grammar is not SLR grammar.
Conflict Example
S  L=R I0: S’  .S I1:S’  S. I6:S  L=.R I9: S  L=R.
SR S  .L=R R  .L
L *R S  .R I2:S  L.=R L .*R
L  id L  .*R R  L. L  .id
RL L  .id
R  .L I3:S  R.
I4:L  *.R I7:L  *R.
Problem R  .L
FOLLOW(R)={=,$} L .*R I8:R  L.
= shift 6 L  .id
reduce by R  L Action[2,=] = shift 6
shift/reduce conflict I5:L  id. Action[2,=] = reduce by R  L
Conflict Example2
S  AaAb I0: S’  .S
S  BbBa S  .AaAb
A S  .BbBa
B A.
B.

Problem
FOLLOW(A)={a,b}
FOLLOW(B)={a,b}
a reduce by A   b reduce by A  
reduce by B   reduce by B  
reduce/reduce conflict reduce/reduce conflict
More Powerful LR Parsers
There are two different methods:
1. The "canonical-LR" or just "LR" method, which makes
full use of the lookahead symbol(s). This method
uses a large set of items, called the LR(1) items.
2. The "lookahead-LR" or "LALR" method, which is
based on the LR(0) sets of items, and has many fewer
states than typical parsers based on the LR(1) items.
LR(K) Parser
• Steps:
1. Construction of canonical set of items LR(1) along
with the lookahead.
- In LR(1) items each item is in the form: [A->α.Bβ,a]
2. Construction of LR parsing table.
3.Parsing of input string using Parsing table
Constructing LR(1) sets of items
SetOfItems Closure(I) {
repeat
for (each item [A->α.Bβ,a] in I)
for (each production B->γ in G’)
for (each terminal b in First(βa))
add [B->.γ, b] to set I;
until no more items are added to I;
return I;
}
Example
1. S’->S I1: goto(I0,S) I5: goto(I2,C) I9: goto(I3,d) [I4]
S’  S  , $ S  C C , $ C  d , c/d
2. S->CC
3. C->cC |d I2: goto(I0,C) I6: goto(I2,c) I9: goto(I6,C)
S  C  C, $ C  c  C, $ C  cC , $
C   c C, $ C   c C, $
• Initially add S’.S,$ C   d, $ C   d, $ I10: goto(I6,c) [I6]
as the first rule in I0 C  c  C, $
I3: goto(I0,c) I7: goto(I2,d) C   c C, $
I0: C  c  C, c/d C  d , $ C   d, $
C   c C, c/d
S’   S, $ C   d, c/d I8: goto(I3,C) I10: goto(I6,d)
C  c C , c/d
S   C C, $ I4: goto(I0,d)
C  d , $

C   c C, c/d C  d , c/d I9: goto(I3,c) [I3]


C  c  C, c/d
C   d, c/d C   c C, c/d
C   d, c/d
S’   S, $ S I1
S   C C, $ (S’  S  , $
C   c C, c/d
C   d, c/d C
I0 I5
S  C  C, $ C
C   c C, $ S  C C , $
I2 C   d, $
c c
I6
d C  c  C, $ C
C   c C, $
I9
C   d, $

d C  cC , $
I7

c C  d , $

c C  c  C, c/d C I8
C   c C, c/d
C   d, c/d C  c C , c/d
I3
d
I4
d
C  d , c/d
LALR Parsing Tables
1. LALR stands for Lookahead LR.
2. LALR parsers are often used in practice because
LALR parsing tables are smaller than LR(1) parsing
tables.
3. The number of states in SLR and LALR parsing tables
for a grammar G are equal.
4. But LALR parsers recognize more grammars than SLR
parsers.
5. yacc creates a LALR parser for the given grammar.
6. A state of LALR parser will be again a set of LR(1)
items.
Creating LALR Parsing Tables

Canonical LR(1) Parser  LALR


Parser shrink # of states

• This shrink process may introduce a reduce/reduce


conflict in the resulting LALR parser (so the grammar
is NOT LALR)
• But, this shrink process does not produce a
shift/reduce conflict.
S’   S, $ S I1
S   C C, $ (S’  S  , $
C   c C, c/d
C   d, c/d
I0 I5
S  C  C, $ C
C
C   c C, $ S  C C , $
I2 C   d, $
c c
I6
d C  c  C, $ C
C   c C, $
I9
C   d, $

d C  cC , $
I7

c C  d , $
c
C  c  C, c/d C I8
C   c C, c/d
C   d, c/d C  c C , c/d
I3
d
d I4
C  d , c/d
S’   S, $ S I1
S   C C, $ (S’  S  , $
C   c C, c/d
C   d, c/d
I0 I5
S  C  C, $ C
C
C   c C, $ S  C C , $
I2 C   d, $
c c
I6
d C  c  C, $ C
C   c C, $
C   d, $

d
I7

c C  d , $

c C  c  C, c/d C I89
C   c C, c/d
C   d, c/d C  c C , c/d/$
I3
d
I4
d
C  d , c/d
S’   S, $ S I1
S   C C, $ (S’  S  , $
C   c C, c/d
C   d, c/d
I0 I5
S  C  C, $ C
C
C   c C, $ S  C C , $
I2 C   d, $
c
I6
d c C  c  C, $ C
C   c C, $
C   d, $

d
I47
d C  d , c/d/$
c
c d
C  c  C, c/d C I89
C   c C, c/d
C   d, c/d C  c C , c/d/$
I3
S’   S, $ S I1
S   C C, $ (S’  S  , $
C   c C, c/d
C   d, c/d
I0 I5
S  C  C, $ C
C
C   c C, $ S  C C , $
I2 C   d, $
c
I36
c C  c  C, c/d/$ C
C   c C,c/d/$
C   d,c/d/$
c
d
I47
d C  d , c/d/$
d
I89
C  c C , c/d/$
Creation of LALR Parsing Tables

1. Create the canonical LR(1) collection of the sets of LR(1)


items for the given grammar.
2. For each core present; find all sets having that same core;
replace those sets having same cores with a single set which
is their union. C={I0,...,In}  C’={J1,...,Jm} where m  n
3. Create the parsing tables (action and goto tables) same as
the construction of the parsing tables of LR(1) parser.
1. Note that: If J=I1  ...  Ik since I1,...,Ik have same cores
 cores of goto(I1,X),...,goto(I2,X) must be same.
1. So, goto(J,X)=K where K is the union of all sets of items having same cores as goto(I1,X).

4. If no conflict is introduced, the grammar is LALR(1) grammar.


(We may only introduce reduce/reduce conflicts; we cannot
introduce a shift/reduce conflict)
LALR Parse Table

c d $ S C
0 s36 s47 1 2
1 acc
2 s36 s47 5
36 s36 s47 89
47 r3 r3 r3
5 r1
89 r2 r2 r2
The Core of A Set of LR(1) Items
• The core of a set of LR(1) items is the set of its first
component.
Ex: ..
S  L =R,$
R  L ,$

RL
..
S  L =R Core

• We will find the states (sets of LR(1) items) in a canonical LR(1)


parser with same cores. Then we will merge them as a single
state.

 id ,=
.
I1:L  id ,=
. A new state: I12: L

L
. .
id ,$
I2:L  id ,$

have same core, merge them

• We will do this for all states of a canonical LR(1) parser to get


Shift/Reduce Conflict
• We say that we cannot introduce a
shift/reduce conflict during the shrink process
for the creation of the states of a LALR parser.
• Assume that we can introduce a shift/reduce
conflict. In this case, a state of LALR parser
must have:
.
A   ,a and B   a,b.
• This means that a state of the canonical LR(1)
parser must have:
.
A   ,a and B   a,c .
But, this state has also a shift/reduce conflict.
Reduce/Reduce Conflict
• But, we may introduce a reduce/reduce
conflict during the shrink process for the
creation of the states of a LALR parser.

.
I1 : A   ,a .
I2: A   ,b
.
B   ,b .
B   ,c

.
I12: A   ,a/b 
reduce/reduce conflict
.
B   ,b/c
Canonical LALR(1) Collection –
S’  S I :S’  .S,$
Example2
I :S’  S.,$ I :L 
1) S  L=R
0
S S
1
*
R to I
.
411
* R,$/=
713

2) S  R .
L=R,$ ..
L I2:S  L =R,$ to I6 R 
.
L
to I810
3) L *R S . R,$ R  L ,$ L,$/=
*
id
to I411
L L
4) L  id
5) R  L .
*R,$/=
R
I3:S 
R ,$ .
i
d
..:L 
*R,$/=
I512
to I512

L 
id L,$/=
.
id,$/= .id,$/=
.
I6:S  L= R,$R 
R . L,$
to I9 .
I9:S  L=R ,$
..
R  L,$
L  *R,$
L
*
to I810
Same Cores
I4 and I11

.
L  id,$
id
to I411
to I512
I5 and I12

I7 and I13
I713:L 
.
*R ,$/=
I810: R 
I8 and I10
.
L ,$/=
LALR(1) Parsing Tables – (for
0
id
s5
*
s4
= $
Example2)
S
1
L
2
R
3
1 acc
2 s6 r5
3 r2
4 s5 s4 8 7
5 r4 r4 no shift/reduce or
no reduce/reduce conflict
6 s12 s11 10 9
7
8
r3
r5
r3
r5

so, it is a LALR(1) grammar
9 r1
Using Ambiguous Grammars
• All grammars used in the construction of LR-
parsing tables must be un-ambiguous.
• Can we create LR-parsing tables for
ambiguous grammars ?
– Yes, but they will have conflicts.
– We can resolve these conflicts in favor of one of them to disambiguate the
grammar.
– At the end, we will have again an unambiguous grammar.

• Why we want to use an ambiguous grammar?


– Some of the ambiguous grammars are much natural, and a corresponding
unambiguous grammar can be very complex.
– Usage of an ambiguous grammar may eliminate unnecessary reductions.

• Ex.
E  E+T | T
Sets of LR(0) Items for Ambiguous
I : E’  .E E I : E’  E.Grammar
+ I :EE E I : E  E+E.+ I
+.E E  E.+E* I
0 1 4 7 4
E EE (
.E+E .+E E  .E+E I E  E.*E 5

E  .E*E
2
id
E EE *
.E*E
E  .(E) (
. *E E  .(E)
E  .id
3I

E  .id I : E  E *.E
(
I : E  E*E. + I
E
E  .E+E
5

I : E  (.E) E  E.+E* I
( 8 4

E  .E*E I
E  E.*E
2
E id 2

.E+E E E  .id E  .(E) 3I


5

E
id . E*E id
E  .(E) I : E  (E.) ) I :E
E  .id E  E.+E + (E).
6 9

I :E E  E.*E * I
id.
3 4

I5
SLR-Parsing Tables for Ambiguous
FOLLOW(E) = { $,+,*,) } Grammar

State I7 has shift/reduce conflicts for symbols + and *.

I0 E I1 + I4 E I7

when current token is +


shift  + is right-associative
reduce  + is left-associative

when current token is *


shift  * has higher precedence than +
reduce  + has higher precedence than *
SLR-Parsing Tables for Ambiguous
FOLLOW(E) = { $,+,*,) } Grammar

State I8 has shift/reduce conflicts for symbols + and *.

I0 E I1 * I5 E I8

when current token is *


shift  * is right-associative
reduce  * is left-associative

when current token is +


shift  + has higher precedence than *
reduce  * has higher precedence than +
SLR-Parsing Tables for Ambiguous
Grammar
Action Goto

id + * ( ) $ E
0 s3 s2 1
1 s4 s5 acc
2 s3 s2 6
3 r4 r4 r4 r4
4 s3 s2 7
5 s3 s2 8
6 s4 s5 s9
7 r1 s5 r1 r1
8 r2 r2 r2 r2
9 r3 r3 r3 r3
Error Recovery in LR Parsing
• An LR parser will detect an error when it
consults the parsing action table and finds an
error entry. All empty entries in the action
table are error entries.
• Errors are never detected by consulting the
goto table.
• An LR parser will announce error as soon as
there is no valid continuation for the scanned
portion of the input.
• A canonical LR parser (LR(1) parser) will never
Panic Mode Error Recovery in LR
Parsing
• Scan down the stack until a state s with a goto
on a particular nonterminal A is found. (Get
rid of everything from the stack before this
state s).
• Discard zero or more input symbols until a
symbol a is found that can legitimately follow
A.
– The symbol a is simply in FOLLOW(A), but this may not work for all situations.

• The parser stacks the nonterminal A and the


state goto[s,A], and it resumes the normal
Phrase-Level Error Recovery in LR
Parsing
• Each empty entry in the action table is marked
with a specific error routine.
• An error routine reflects the error that the
user most likely will make in that case.
• An error routine inserts the symbols into the
stack or the input (or it deletes the symbols
from the stack and the input, or it can do both
insertion and deletion).
– missing operand
– unbalanced right parenthesis