Chapter 8 - Syntax Analysis

Download as pdf or txt
Download as pdf or txt
You are on page 1of 92

Chapter 4

Syntax Analysis
Outline
 Role of parser
 Context free grammars
 Top down parsing
 Bottom up parsing
 Parser generators
The role of parser
token
Source Lexical Parse tree Rest of Intermediate
program Analyzer Parser
Front End representation
getNext
Token

Symbol
table
Uses of grammars
E -> E + T | T
T -> T * F | F
F -> (E) | id

E -> TE’
E’ -> +TE’ | Ɛ
T -> FT’
T’ -> *FT’ | Ɛ
F -> (E) | id
Error handling
 Common programming errors
 Lexical – misspelling identifier,keyword,or operator
 Syntactic – an arithmetic expression with unbalanced
parenthesis
 Semantic – an operator applied to an incompatible operand
 Lexical – an infinitely recursive call
 Error handler goals
 Report the presence of errors clearly and accurately
 Recover from each error quickly enough to detect subsequent
errors
 Add minimal overhead to the processing of correct programs
Error-recover strategies
 Panic mode recovery
 Discard input symbol one at a time until one of
designated set of synchronization tokens is found
 Phrase level recovery
 Replacing a prefix of remaining input by some string
that allows the parser to continue
 Error productions
 Augment the grammar with productions that generate
the erroneous constructs
 Global correction
 Choosing minimal sequence of changes to obtain a
globally least-cost correction
Context free grammars
 Terminals
 Nonterminals
 Start symbol
 productions

Stmt -> if expr then stmt else stmt


Derivations
 Productions are treated as rewriting rules to generate a
string
 Rightmost and leftmost derivations
 E -> E + E | E * E | -E | (E) | id
 Derivations for –(id+id)
 E => -E => -(E) => -(E+E) => -(id+E)=>-(id+id)
Parse trees
 -(id+id)
 E => -E => -(E) => -(E+E) => -(id+E)=>-(id+id)
Ambiguity
 For some strings there exist more than one parse tree
 Or more than one leftmost derivation
 Or more than one rightmost derivation
 Example: id+id*id
Elimination of ambiguity
Elimination of ambiguity (cont.)
 Idea:
 A statement appearing between a then and an else
must be matched
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 recursion elimination (cont.)
 There are cases like following
 S -> Aa | b
 A -> Ac | Sd | ɛ
 Left recursion elimination algorithm:
 Arrange the nonterminals in some order A1,A2,…,An.
 For (each i from 1 to n) {
 For (each j from 1 to i-1) {
 Replace each production of the form Ai-> Aj γ by the production
Ai -> δ1 γ | δ2 γ | … |δk γ where Aj-> δ1 | δ2 | … |δk are
all current Aj productions
 }
 Eliminate left recursion among the Ai-productions
 }
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
 E -> b
Introduction
 A Top-down parser tries to create a parse tree from the
root towards the leafs scanning input from left to right
 It can be also viewed as finding a leftmost derivation
for an input string
 Example: id+id*id

E -> TE’ E
lm
E
lm
E
lm
E
lm
E
lm
E
E’ -> +TE’ | Ɛ E’ E’
T E’ T E’ T E’ T T
T -> FT’
T’ -> *FT’ | Ɛ F T’ F T’ F T’ F T’ + T E’
F -> (E) | id id id id
Ɛ Ɛ
Recursive descent parsing
 Consists of a set of procedures, one for each
nonterminal
 Execution begins with the procedure for start symbol
 A typical procedure for a non-terminal
void A() {
choose an A-production, A->X1X2..Xk
for (i=1 to k) {
if (Xi is a nonterminal
call procedure Xi();
else if (Xi equals the current input symbol a)
advance the input to the next symbol;
else /* an error has occurred */
}
}
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 can’t 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
First and Follow
 First() is set of terminals that begins strings derived from
 If α=>ɛ
* then is also in First(ɛ)
 In predictive parsing when we have A-> α|β, if First(α)
and First(β) are disjoint sets then we can select
appropriate A-production by looking at the next input
 Follow(A), for any nonterminal A, is set of terminals a that
can appear immediately after A in some sentential form
* αAaβ for some αand βthen a is in
 If we have S =>
Follow(A)
 If A can be the rightmost symbol in some sentential form,
then $ is in Follow(A)
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 nonterminal and X->Y1Y2…Yk is a production
for some k>=1, then place a in First(X) if for some i a is
in First(Yi) and ɛ is in all of First(Y1),…,First(Yi-1) that
*
is Y1…Yi-1 => ɛ. if ɛ is in First(Yj) for j=1,…,k then add ɛ
to First(X).
3. If X-> ɛ is a production then add ɛ to First(X)
 Example!
Computing follow
 To compute First(A) for all nonterminals 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 everything in
First(β) except ɛ is in Follow(B).
3. If there is a production A->B or a production
A->αBβ where First(β) contains ɛ, then everything
in Follow(A) is in Follow(B)
 Example!
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
 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 αandβ both derive strings beginning with a
 At most one of α or βcan derive empty string
*
 If α=> ɛ then βdoes not derive any string beginning with a
terminal in Follow(A).
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
F {(,id} {+, *, ), $}
E -> TE’ {(,id} {+, ), $}
E’ -> +TE’ | Ɛ T
E {(,id} {), $}
T -> FT’
T’ -> *FT’ | Ɛ E’ {+,ɛ} {), $}
T’ {*,ɛ} {+, ), $}
F -> (E) | id
Input Symbol
Non -
terminal 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)


Another example
S -> iEtSS’ | a
S’ -> eS | Ɛ
E -> b

Input Symbol
Non -
terminal a b e i t $
S S -> a S -> iEtSS’

S’ S’ -> Ɛ S’ -> Ɛ
S’ -> eS
E E -> b
Table driven predictive parsing
• A predictive parser can be built by maintaining a stack
explicitly.
• The table driven parser has an input buffer, stack
containing sequence of grammar symbols, parsing
table and an output stream.
• The input buffer contains the string to be parsed
followed by $.
• Initially the stack contains start symbol of the
grammar on the top followed by $.
• The parsing table deterministically guesses the correct
production to be used.
Model of a table driven predicting
parsing
Input a + b $

Predictive
parsing output
stack X
Y program
Z
$
Parsing
Table
M
Predictive parsing algorithm
Set ip point to the first symbol of w;
Set X to the top stack symbol;
While (X<>$) { /* stack is not empty */
if (X is a) pop the stack and advance ip;
else if (X is a terminal) error();
else if (M[X,a] is an error entry) error();
else if (M[X,a] = X->Y1Y2..Yk) {
output the production X->Y1Y2..Yk;
pop the stack;
push Yk,…,Y2,Y1 on to the stack with Y1 on top;
}
set X to the top stack symbol;
}
Procedure of predictive parser
• The current symbol of the input string is
maintained by a pointer say ‘ip’.
• In every step consider the set {α,a} where ‘α’ is the
top of the stack and ‘a’ is the symbol pointed by
the ‘ip’.
• If ‘α’ is a Non Terminal ,then see the table cell
M{α,a} for the production.
1. If M{α,a} is a valid production then pop the
stack , push the production into the stack.
2. If M{α,a} is error or blank then report an error
• If ‘α’ is a terminal then pop it from the stack and
also increment the input pointer ‘ip’ to point the
next symbol in the input string.
• The output will be the set of productions
• The following example illustrates the top-down
predictive parser using parser table .
String: id + id * id
Grammar: Mentioned LL(1) Grammar in
Previous slides
MATCHED STACK INPUT ACTION
E$ id+id * id$
TE’$ id+id * id$ E->TE’
FT’E’$ id+id * id$ T->FT’
id T’E’$ id+id * id$ F->id
id T’E’$ +id * id$ Match id
id E’$ +id * id$ T’->Є
id +TE’$ +id * id$ E’-> +TE’
id+ TE’$ id * id$ Match +
id+ FT’E’$ id * id$ T-> FT’
id+ idT’E’$ id * id$ F-> id
id+id T’E’$ * id$ Match id
id+id * FT’E’$ * id$ T’-> *FT’
id+id * FT’E’$ id$ Match *
id+id * idT’E’$ id$ F-> id
id+id * id T’E’$ $ Match id
id+id * id E’$ $ T’-> Є
id+id * id $ $ E’-> Є
Error recovery in predictive parsing
 Panic mode
 Place all symbols in Follow(A) into synchronization set for
nonterminal A: skip tokens until an element of Follow(A) is seen
and pop A from stack.
 Add to the synchronization set of lower level construct the symbols
that begin higher level constructs
 Add symbols in First(A) to the synchronization set of nonterminal
A
 If a nonterminal can generate the empty string then the production
deriving can be used as a default
 If a terminal on top of the stack cannot be matched, pop the
terminal, issue a message saying that the terminal was insterted
Fig. Synchronising tockens added to parsing table
Introduction
 Constructs parse tree for an input string beginning at
the leaves (the bottom) and working towards the root
(the top)
 Example: id*id

E -> E + T | T id*id F * id T * id T*F F E


T -> T * F | F
T*F F
F -> (E) | id id F F id

id id F id T*F

id F id

id
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
 The key decisions during bottom-up parsing are about
when to reduce and about what production to apply
 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:
 E=>T=>T*F=>T*id=>F*id=>id*id
Handle pruning
 A Handle is a substring that matches the body of a
production and whose reduction represents one step
along the reverse of a rightmost derivation

Right sentential form Handle Reducing production


id*id id F->id
F*id F T->F
T*id id F->id
T*F T*F E->T*F
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 $
Shift reduce parsing (cont.)
 Basic operations:
 Shift Stack Input Action
 Reduce
$ id*id$ shift
 Accept $id *id$ reduce by F->id
 Error $F *id$ reduce by T->F
$T *id$ shift
 Example: id*id $T* id$ shift
$T*id $ reduce by F->id
$T*F $ reduce by T->T*F
$T $ reduce by E->T
$E $ accept
Handle will appear on top of
the stack
S S
A
B
B A
α β γ z α γ z
y x y

Stack Input Stack Input


$αβγ yz$ $αγ xyz$
$αβB yz$ $αBxy z$
$αβB z$
y
Conflicts during shit reduce
parsing
 Two kind of conflicts
 Shift/reduce conflict
 Reduce/reduce conflict
 Ambiguous grammars always cause conflicts
 Consider grammar given below
Operator-Precedence Parsing
 problems encountered so far in shift/reduce parsing:
 IDENTIFY a handle.
 resolve conflicts (if they occur).
 operator grammars: a class of grammars where handle
identification and conflict resolution is easy.

 Operator Grammars: no production right side is 


or has two adjacent non-terminals.
E → E - E | E + E | E * E | E / E | E ^ E | - E | ( E ) | id

 note: this is typically ambiguous grammar.


Basic Technique
 For the terminals of the grammar,
define the relations <. .> and .=.
 a <. b means that a yields precedence to b
 a .=. b means that a has the same precedence as b.
 a .> b means hat a takes precedence over b
 E.g. * .> + or + <. *
 Many handles are possible. We will use <. .=. And .> to
find the correct handle (i.e., the one that respects the
precedence).
Using Operator-Precedence
Relations
 GOAL: delimit the handle of a right
sentential form
 <. will mark the beginning, .> will mark the
end and .=. will be in between.
 Since no two adjacent non-terminals appear in the
RHS of any production, the general form sentential
forms is as:
0 a1 1 a2 2 … an n, where each i is either a
nonterminal or the empty string.
 At each step of the parse, the parser considers the top
most terminal of the parse stack (i.e., either top or top-
1), say a, and the current token, say b, and looks up
their precedence relation, and decides what to do next:
Operator-Precedence Parsing
1. If a .=. b, then shift b into the parse stack
2. If a <. b, then shift <. And then shift b into the parse
stack
3. If a .> b, then find the top most <. relation of the
parse stack; the string between this relation (with
the non-terminal underneath, if there exists) and
the top of the parse stack is the handle (the handle
should match (weakly) with the RHS of at least one
grammar rule); replace the handle with a typical
non-terminal
Example
STACK INPUT Remark
$ id + id * id$ $ <. id + * ( ) id $
$ <. id + id * id$ id >. +
$E + id * id$ + .> <. <. .> <. .>
$ <. +
$ E <. + id * id$ + <. id * .> .> <. .> <. .>
$ E <. + <. id * id$ id .> *
* id$ ( <. <. <. .=. <.
$ E <. + E + <. *
$ E <. + E <. * id$ * <. id ) .> .> .> .>
$ id .> $
$ E <. + E <. * <. id id
$ * .> $ .> .> .> .>
$ E <. + E <. * E
$ + .> $ $
$ E <. + E <. <. <. <. .=.
$
$E $ accept
Parse Table

1-2 E → E + T | T
3-4 T → T * F | F
5-6 T → ( E ) | id
Producing the parse table
 FirstTerm(A) = {a | A + a or A + Ba}
 LastTerm(A) = {a | A + a or A + aB}

 a .=. b iff  U → ab or  U → aBb

 a <. b iff  U → aB and b  FirsTerm(B)

 a .> b iff  U → Bb and a  LastTerm(B)

57
Example:
 FirstTerm (E) = {+, *, id, (}
 FirstTerm (T) = {*, id, (}
 FirstTerm (F) = {id, (}

 LastTerm (E) = {+, *, id, )}


 LastTerm (T) = {*, id, )}
1-2 E → E + T | T
 LastTerm (F) = {id, )} 3-4 T → T * F | F
5-6 T → ( E ) | id

58
Precedence Functions vs
Relations + - * /  ( ) id $
f 2 2 4 4 4 0 6 6 0
g 1 1 3 3 5 5 0 5 0

 f(a) < g(b) whenever a <. b


 f(a) = g(b) whenever a .=. b
 f(a) > g(b) whenever a .> b

59
Constructing precedence
functionsg id f id

f * g *

g + f +

f $ g $

+ * id $
f 2 4 4 0
g 1 3 5 0

60
Advantages
The advantages of operator precedence parsing are-
 The implementation is very easy and simple.
 The parser is quite powerful for expressions in
programming language
Disadvantages
The disadvantages of operator precedence parsing are-
 The handling of tokens known to have two different
precedence becomes difficult.
 Only small class of grammars can be parsed using this
par
Extracting Precedence relations from parse tables
E

E + T  + <. *

T * F

id  * <. id 1-2 E → E + T | T
3-4 T → T * F | F
5-6 T → ( E ) | id

63
Extracting Precedence relations from parse tables
E

T * F
 * .> *
T * F 1-2 E → E + T | T
3-4 T → T * F | F
5-6 T → ( E ) | id
F
id  id .> *
64
LR Parser
 LR parser
 It is an efficient bottom-up syntax analysis technique
that can be used to parse large classes of context free
grammar is called LR(0) parsing.
1. L stands for the left to right scanning
2. R stands for rightmost derivation in reverse
0 stands for no. of input symbols of lookaheads :
Advantages of LR parsing :
 It recognizes virtually all programming language
constructs for which CFG can be written
 It is able to detect syntactic errors
 It is an efficient non-backtracking shift reducing
parsing method.
Type of LR parser
 SLR
 CLR
 LALR
LR Parsing
 The most prevalent type of bottom-up parsers
 LR(k), mostly interested on parsers with k<=1
 Why LR parsers?
 Table driven
 Can be constructed to recognize all programming language
constructs
 Most general non-backtracking shift-reduce parsing method
 Can detect a syntactic error as soon as it is possible to do so
 Class of grammars for which we can construct LR parsers are
superset of those which we can construct LL parsers
States of an LR parser
 States represent set of items
 An LR(0) item of G is a production of G with the dot at
some position of the body:
 For A->XYZ we have following items
 A->.XYZ
 A->X.YZ
 A->XY.Z
 A->XYZ.
 In a state having A->.XYZ we hope to see a string
derivable from XYZ next on the input.
 What about A->X.YZ?
Constructing canonical LR(0)
item sets
 Augmented grammar:
 G with addition of a production: S’->S
 Closure of item sets:
 If I is a set of items, closure(I) is a set of items constructed from I by
the following rules:
 Add every item in I to closure(I)

 If A->α.Bβ is in closure(I) and B->γ is a production then add the


item B->.γ to clsoure(I).
 Example: I0=closure({[E’->.E]}
E’->E E’->.E
E -> E + T | T E->.E+T
T -> T * F | F E->.T
T->.T*F
F -> (E) | id T->.F
F->.(E)
F->.id
Constructing canonical LR(0)
item sets (cont.)
 Goto (I,X) where I is an item set and X is a grammar
symbol is closure of set of all items [A-> αX. β] where
[A-> α.X β] is in I
I1
 Example E’->E.
E E->E.+T
I0=closure({[E’->.E]}
E’->.E I2
E->.E+T T
E’->T.
E->.T T->T.*F
T->.T*F I4
T->.F ( F->(.E)
F->.(E) E->.E+T
E->.T
F->.id T->.T*F
T->.F
F->.(E)
F->.id
Closure algorithm
SetOfItems CLOSURE(I) {
J=I;
repeat
for (each item A-> α.Bβ in J)
for (each prodcution B->γ of G)
if (B->.γ is not in J)
add B->.γ to J;
until no more items are added to J on one round;
return J;
GOTO algorithm
SetOfItems GOTO(I,X) {
J=empty;
if (A-> α.X β is in I)
add CLOSURE(A-> αX. β ) to J;
return J;
}
Canonical LR(0) items
Void items(G’) {
C= CLOSURE({[S’->.S]});
repeat
for (each set of items I in C)
for (each grammar symbol X)
if (GOTO(I,X) is not empty and not in C)
add GOTO(I,X) to C;
until no new set of items are added to C on a round;
}
E’->E
E -> E + T | T

Example acc
$
T -> T * F | F
F -> (E) | id
I6 I9
E->E+.T
I1 T->.T*F T
E’->E. + T->.F
E->E+T.
T->T.*F
E E->E.+T
F->.(E)
F->.id
I0=closure({[E’->.E]}
*
I2 I10
E’->.E T I7
F
E->.E+T E’->T. * T->T*.F
F->.(E)
T->T.*F T->T*F.
E->.T id F->.id
T->.T*F id
T->.F I5
F->.(E) F->id.
F->.id ( +
I4
F->(.E)
E->.E+T I8 I11
E->.T
E E->E.+T )
T->.T*F F->(E.) F->(E).
T->.F
F->.(E)
F->.id

I3
T>F.
Use of LR(0) automaton
 Example: id*id
Line Stack Symbols Input Action
(1) 0 $ id*id$ Shift to 5
(2) 05 $id *id$ Reduce by F->id
(3) 03 $F *id$ Reduce by T->F
(4) 02 $T *id$ Shift to 7
(5) 027 $T* id$ Shift to 5
(6) 0275 $T*id $ Reduce by F->id
(7) 02710 $T*F $ Reduce by T->T*F
(8) 02 $T $ Reduce by E->T
(9) 01 $E $ accept
LR-Parsing model
INPUT a1 … ai … an $

LR Parsing Output
Sm
Program
Sm-1

$
ACTION GOTO
LR parsing algorithm
let a be the first symbol of w$;
while(1) { /*repeat forever */
let s be the state on top of the stack;
if (ACTION[s,a] = shift t) {
push t onto the stack;
let a be the next input symbol;
} else if (ACTION[s,a] = reduce A->β) {
pop |β| symbols of the stack;
let state t now be on top of the stack;
push GOTO[t,A] onto the stack;
output the production A->β;
} else if (ACTION[s,a]=accept) break; /* parsing is done */
else call error-recovery routine;
}
Example (0) E’->E
(1) E -> E + T
(2) E-> T
STATE ACTON GOTO
(3) T -> T * F
id + * ( ) $ E T F (4) T-> F
0 S5 S4 1 2 3 (5) F -> (E) id*id+id?
(6) F->id
1 S6 Acc
Line Stac Symbol Input Action
2 R2 S7 R2 R2
k s
3 R R7 R4 R4 (1) 0 id*id+id$ Shift to 5
4
(2) 05 id *id+id$ Reduce by F->id
4 S5 S4 8 2 3
(3) 03 F *id+id$ Reduce by T->F
5 R R R6 R6
(4) 02 T *id+id$ Shift to 7
6 6
(5) 027 T* id+id$ Shift to 5
6 S5 S4 9 3
(6) 0275 T*id +id$ Reduce by F->id
7 S5 S4 10
(7) 02710 T*F +id$ Reduce by T-
8 S6 S11 >T*F
(8) 02 T +id$ Reduce by E->T
9 R1 S7 R1 R1
(9) 01 E +id$ Shift
10 R3 R3 R3 R3
(10) 016 E+ id$ Shift
11 R5 R5 R5 R5
(11) 0165 E+id $ Reduce by F->id
(12) 0163 E+F $ Reduce by T->F
(13) 0169 E+T` $ Reduce by E-
>E+T
(14) 01 E $ accept
Constructing SLR parsing table
 Method
 Construct C={I0,I1, … , In}, the collection of LR(0) items for G’
 State i is constructed from state Ii:
 If [A->α.aβ] is in Ii and Goto(Ii,a)=Ij, then set ACTION[i,a] to “shift j”
 If [A->α.] is in Ii, then set ACTION[i,a] to “reduce A->α” for all a in
follow(A)
 If {S’->.S] is in Ii, then set ACTION[I,$] to “Accept”
 If any conflicts appears then we say that the grammar is not
SLR(1).
 If GOTO(Ii,A) = Ij then GOTO[i,A]=j
 All entries not defined by above rules are made “error”
 The initial state of the parser is the one constructed from the
set of items containing [S’->.S]
Example grammar which is not
SLR(1) S -> L=R | R
L -> *R | id
R -> L
I0 I1 I3 I5 I7
S’->.S S’->S. S ->R. L -> id. L -> *R.
S -> .L=R
S->.R I2 I4 I6
I8
L -> .*R | S ->L.=R L->*.R S->L=.R
R -> L.
L->.id R ->L. R->.L R->.L
R ->. L L->.*R L->.*R I9
L->.id L->.id S -> L=R.

Action
=
Shift 6
2 Reduce R->L
More powerful LR parsers
 Canonical-LR or just LR method
 Use lookahead symbols for items: LR(1) items
 Results in a large collection of items
 LALR: lookaheads are introduced in LR(0) items
Canonical LR(1) items
 In LR(1) items each item is in the form: [A->α.β,a]
 An LR(1) item [A->α.β,a] is valid for a viable prefix γ if
*
there is a derivation S=>δAw=>δαβw, where
rm
 Γ= δα
 Either a is the first symbol of w, or w is ε and a is $
 Example:
*
S=>aaBab=>aaaBab
 S->BB rm

 B->aB|b Item [B->a.B,a] is valid for γ=aaa


and w=ab
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;
}

SetOfItems Goto(I,X) {
initialize J to be the empty set;
for (each item [A->α.Xβ,a] in I)
add item [A->αX.β,a] to set J;
return closure(J);
}

void items(G’){
initialize C to Closure({[S’->.S,$]});
repeat
for (each set of items I in C)
for (each grammar symbol X)
if (Goto(I,X) is not empty and not in C)
add Goto(I,X) to C;
until no new sets of items are added to C;
}
Example
S’->S
S->CC
C->cC
C->d
Canonical LR(1) parsing table
 Method
 Construct C={I0,I1, … , In}, the collection of LR(1) items for G’
 State i is constructed from state Ii:
 If [A->α.aβ, b] is in Ii and Goto(Ii,a)=Ij, then set ACTION[i,a] to
“shift j”
 If [A->α., a] is in Ii, then set ACTION[i,a] to “reduce A->α”
 If {S’->.S,$] is in Ii, then set ACTION[I,$] to “Accept”
 If any conflicts appears then we say that the grammar is not
LR(1).
 If GOTO(Ii,A) = Ij then GOTO[i,A]=j
 All entries not defined by above rules are made “error”
 The initial state of the parser is the one constructed from the
set of items containing [S’->.S,$]
Example
S’->S
S->CC
C->cC
C->d
LALR Parsing Table
 For the previous example we had:

I4
C->d. , c/d
I47
C->d. , c/d/$
I7
C->d. , $

 State merges cant produce Shift-Reduce conflicts.


Why?
 But it may produce reduce-reduce conflict
Example of RR conflict in state
merging
S’->S
S -> aAd | bBd | aBe | bAe
A -> c
B -> c
An easy but space-consuming
LALR table construction
 Method:
1. Construct C={I0,I1,…,In} the collection of LR(1) items.
2. For each core among the set of LR(1) items, find all sets
having that core, and replace these sets by their union.
3. Let C’={J0,J1,…,Jm} be the resulting sets. The parsing actions
for state i, is constructed from Ji as before. If there is a
conflict grammar is not LALR(1).
4. If J is the union of one or more sets of LR(1) items, that is J =
I1 UI2…IIk then the cores of Goto(I1,X), …, Goto(Ik,X) are
the same and is a state like K, then we set Goto(J,X) =k.
 This method is not efficient, a more efficient one is
discussed in the book
Compaction of LR parsing table
 Many rows of action tables are identical
 Store those rows separately and have pointers to them
from different states
 Make lists of (terminal-symbol, action) for each state
 Implement Goto table by having a link list for each
nonterinal in the form (current state, next state)
Using ambiguous grammars STATE ACTON GO
TO
E->E+E id + * ( ) $ E

E->E*E 0 S3 S2 1

1 S4 S5 Acc
E->(E) 2 S3 S2 6

E->id 3 R4 R4 R4 R4

4 S3 S2 7

5 S3 S2 8

I0: E’->.E I1: E’->E. I2: E->(.E) 6 S4 S5


E->.E+E E->E.+E E->.E+E 7 R1 S5 R1 R1
E->.E*E E->E.*E E->.E*E
8 R2 R2 R2 R2
E->.(E) E->.(E)
E->.id E->.id 9 R3 R3 R3 R3

I4: E->E+.E I5: E->E*.E I6: E->(E.) I7: E->E+E.


I3: E->.id E->.E+E E->(.E) E->E.+E E->E.+E
E->.E*E E->.E+E E->E.*E E->E.*E
E->.(E) E->.E*E
E->.id E->.(E) I8: E->E*E. I9: E->(E).
E->.id E->E.+E
E->E.*E

You might also like