Pertemuan 15, 16, 17 Syntax Directed Translation Matakuliah : T0174 / Teknik Kompilasi

advertisement
Matakuliah
Tahun
Versi
: T0174 / Teknik Kompilasi
: 2005
: 1/6
Pertemuan 15, 16, 17
Syntax Directed Translation
1
Learning Outcomes
Pada akhir pertemuan ini, diharapkan mahasiswa
akan mampu :
• Mahasiswa dapat menjelaskan konsep dan
peranan syntax directed translation dan definisi
L-attributed dan inherited attribute (C2)
• Mahasiswa dapat mendemonstrasikan
pembuatan syntax tree dan evaluasi bootom up
dari S-atributed definition (C3)
• Mahasiswa dapat mendemonstrasikan Topdown translation dan evaluasi bottom-up dari
inherited attribute (C3)
2
Outline Materi
• Definisi dan konsep syntax directed
translation
• Konstruksi syntax tree
• L-attributes definition
• Bottom-up evaluation
• S-attributes definition
• Top-down translation
• Recursive evaluator
• Alokasi memori
3
Syntax-Directed Translation
• Grammar symbols are associated with attributes to
associate information with the programming language
constructs that they represent.
• Values of these attributes are evaluated by the semantic
rules associated with the production rules.
• Evaluation of these semantic rules:
–
–
–
–
–
–
may generate intermediate codes
may put information into the symbol table
may perform type checking
may issue error messages
may perform some other activities
in fact, they may perform almost any activities.
• An attribute may hold almost any thing.
– a string, a number, a memory location, a complex record.
4
Syntax-Directed Definitions and Translation
Schemes
• When we associate semantic rules with productions, we
use two notations:
– Syntax-Directed Definitions
– Translation Schemes
• Syntax-Directed Definitions:
– give high-level specifications for translations
– hide many implementation details such as order of evaluation of
semantic actions.
– We associate a production rule with a set of semantic actions,
and we do not say when they will be evaluated.
• Translation Schemes:
– indicate the order of evaluation of semantic actions associated
with a production rule.
– In other words, translation schemes give a little bit information
about implementation details.
5
Syntax-Directed Definitions
• A syntax-directed definition is a generalization of a
context-free grammar in which:
– Each grammar symbol is associated with a set of attributes.
– This set of attributes for a grammar symbol is partitioned into
two subsets called synthesized and inherited attributes of that
grammar symbol.
– Each production rule is associated with a set of semantic rules.
• Semantic rules set up dependencies between attributes
which can be represented by a dependency graph.
• This dependency graph determines the evaluation order
of these semantic rules.
• Evaluation of a semantic rule defines the value of an
attribute. But a semantic rule may also have some side
effects such as printing a value.
6
Annotated Parse Tree
• A parse tree showing the values of
attributes at each node is called
an
annotated parse tree.
• The process of computing the attributes
values at the nodes is called annotating
(or decorating) of the parse tree.
• Of course, the order of these computations
depends on the dependency graph
induced by the semantic rules.
7
Syntax-Directed Definition
• In a syntax-directed definition, each production
A→α is associated with a set of semantic rules
of the form:
b=f(c1,c2,…,cn) where f is a function, and b
can be one of the followings:
 b is a synthesized attribute of A and c1,c2,…,cn are attributes
of the grammar symbols in the production ( A→α ).
OR
b is an inherited attribute one of the grammar symbols in α (on
the right side of the production), and c1,c2,…,cn are attributes of
the grammar symbols in the production ( A→α ).
8
Attribute Grammar
• So, a semantic rule b=f(c1,c2,…,cn) indicates
that the attribute b depends on attributes
c1,c2,…,cn.
• In a syntax-directed definition, a semantic rule
may just evaluate
a value of an attribute or
it may have some side effects such as printing
values.
• An attribute grammar is a syntax-directed
definition in which the functions in the semantic
rules cannot have side effects (they can only
evaluate values of attributes).
9
Syntax-Directed Definition -Example
Production
Semantic Rules
L → E return
E → E1 + T
E→T
T → T1 * F
T→F
F→(E)
F → digit
print(E.val)
E.val = E1.val + T.val
E.val = T.val
T.val = T1.val * F.val
T.val = F.val
F.val = E.val
F.val = digit.lexval
• Symbols E, T, and F are associated with a synthesized
attribute val.
• The token digit has a synthesized attribute lexval (it is
assumed that it is evaluated by the lexical analyzer).
10
Annotated Parse Tree -- Example
L
Input: 5+3*4
E.val=17
E.val=5
+
return
T.val=12
T.val=5
T.val=3
*
F.val=5
F.val=3
digit.lexval=5
digit.lexval=3
F.val=4
digit.lexval=4
11
Dependency Graph
L
Input: 5+3*4
E.val=17
E.val=5
T.val=12
T.val=5
T.val=3
F.val=4
F.val=5
F.val=3
digit.lexval=4
digit.lexval=5
digit.lexval=3
12
Syntax-Directed Definition –
Example2
Production Semantic Rules
E → E1 + T
E→T
T → T1 * F
T→F
F→(E)
F → id
E.loc=newtemp(), E.code = E1.code || T.code || add
E1.loc,T.loc,E.loc
E.loc = T.loc, E.code=T.code
T.loc=newtemp(), T.code = T1.code || F.code || mult
T1.loc,F.loc,T.loc
T.loc = F.loc, T.code=F.code
F.loc = E.loc, F.code=E.code
F.loc = id.name, F.code=“”
• Symbols E, T, and F are associated with synthesized attributes loc
and code.
• The token id has a synthesized attribute name (it is assumed that it
is evaluated by the lexical analyzer).
• It is assumed that || is the string concatenation operator.
13
Syntax-Directed Definition –
Inherited Attributes
Production Semantic Rules
D→TL
T → int
T → real
L → L1 id
L → id
L.in = T.type
T.type = integer
T.type = real
L1.in = L.in, addtype(id.entry,L.in)
addtype(id.entry,L.in)
• Symbol T is associated with a synthesized
attribute type.
• Symbol L is associated with an inherited
attribute in.
14
A Dependency Graph – Inherited
Attributes
Input: real p q
D
L.in=real
T
real
L
L
id
parse tree
T.type=real
id
L1.in=real
addtype(q,real)
addtype(p,real)
id.entry=q
id.entry=p
dependency graph
15
S-Attributed Definitions
• Syntax-directed definitions are used to specify syntax-directed
translations.
• To create a translator for an arbitrary syntax-directed definition can
be difficult.
• We would like to evaluate the semantic rules during parsing (i.e. in a
single pass, we will parse and we will also evaluate semantic rules
during the parsing).
• We will look at two sub-classes of the syntax-directed definitions:
– S-Attributed Definitions: only synthesized attributes used in the
syntax-directed definitions.
– L-Attributed Definitions: in addition to synthesized attributes,
we may also use inherited attributes in a restricted fashion.
• To implement S-Attributed Definitions and L-Attributed Definitions
are easy (we can evaluate semantic rules in a single pass during the
parsing).
• Implementations of S-attributed Definitions are a little bit easier than
implementations of L-Attributed Definitions
16
Bottom-Up Evaluation of SAttributed Definitions
• We put the values of the synthesized attributes of the grammar
symbols into a parallel stack.
– When an entry of the parser stack holds a grammar symbol X
(terminal or non-terminal), the corresponding entry in the parallel
stack will hold the synthesized attribute(s) of the symbol X.
• We evaluate the values of the attributes during reductions.
A  XYZ
A.a=f(X.x,Y.y,Z.z) where all attributes are synthesized.
stack parallel-stack
top 
Z
Z.z
Y
Y.y
X
X.x
A
A.a
.
.
.
.

top 
17
Bottom-Up Eval. of S-Attributed
Definitions (cont.)
Production
Semantic Rules
L → E return
E → E1 + T
E→T
T → T1 * F
T→F
F→(E)
F → digit
print(val[top-1])
val[ntop] = val[top-2] + val[top]
val[ntop] = val[top-2] * val[top]
val[ntop] = val[top-1]
• At each shift of digit, we also push digit.lexval into val-stack.
• At all other shifts, we do not put anything into val-stack because
other terminals do not have attributes (but we increment the stack
pointer for val-stack).
18
Canonical LR(0) Collection for The
Grammar
..
..
..
..
I0: L’→
L→
E→
E→
T→
T→
F→
F→
.
..
..
.
.
..
..
..
.
L I:
L
1 L’→L
Er
E I:
E+T
2 L →E r
T
E →E +T
T*F
T I:
F
3 E →T
(E)
T →T *F
d
F I:
4 T →F
(
d
I5: F →
E→
E→
T→
T→
F→
F→
I6: F →d
+
*
( E)
E+T
E
T
T*F
F
T
3
(E)
F
d
4
(
d
.
.
..
..
I7: L →Er
r
I8: E →E+ T
T → T*F
T→ F
F → (E)
F→ d
.
..
..
I9: T →T* F
F → (E)
F→ d
.
.
I11: E →E+T
T →T *F
T
F
4
(
*
9
5
d
6
.
F
I12: T →T*F
(
5
d
6
)
I10:F →(E )
E →E +T +
I13: F →(E)
.
8
5
6
19
Bottom-Up Evaluation -- Example
• At each shift of digit, we also push digit.lexval into val-stack.
stack
val-stack
0
input
action
semantic rule
5+3*4r
s6
d.lexval(5) into val-stack
0d6
5
+3*4r
F→d
F.val=d.lexval – do nothing
0F4
5
+3*4r
T→F
T.val=F.val – do nothing
0T3
5
+3*4r
E→T
E.val=T.val – do nothing
0E2
5
+3*4r
s8
push empty slot into val-stack
0E2+8
5-
3*4r
s6
d.lexval(3) into val-stack
0E2+8d6
5-3
*4r
F→d
F.val=d.lexval – do nothing
0E2+8F4
5-3
*4r
T→F
T.val=F.val – do nothing
0E2+8T11
5-3
*4r
s9
push empty slot into val-stack
s6
d.lexval(4) into val-stack
0E2+8T11*95-3-
4r
0E2+8T11*9d6
5-3-4
r
F→d
F.val=d.lexval – do nothing
0E2+8T11*9F12
5-3-4
r
T→T*F
T.val=T1.val*F.val
0E2+8T11
5-12
r
E→E+T
E.val=E1.val*T.val
0E2
17
r
s7
push empty slot into val-stack
0E2r7
17-
$
L→Er
print(17), pop empty slot from val-stack
0L1
17
$
acc
20
Top-Down Evaluation (of S-Attributed
Definitions)
Productions
A→B
B → 0 B1
B → 1 B1
B→
Semantic Rules
print(B.n0), print(B.n1)
B.n0=B1.n0+1, B.n1=B1.n1
B.n0=B1.n0, B.n1=B1.n1+1
B.n0=0, B.n1=0
where B has two synthesized attributes (n0 and n1).
21
Top-Down Evaluation (of S-Attributed
Definitions)
• Remember that: In a recursive predicate parser, each
non-terminal corresponds to a procedure.
procedure A() {
call B();
}
procedure B() {
if (currtoken=0) { consume 0; call B(); }
else if (currtoken=1) { consume 1; call B(); }
else if (currtoken=$) {} // $ is end-marker
else error(“unexpected token”);
}
A→B
B→0B
B→1B
B→
22
Top-Down Evaluation (of S-Attributed
Definitions)
procedure A() {
int n0,n1;
Synthesized attributes of non-terminal B
call B(&n0,&n1);
are the output parameters of procedure B.
print(n0); print(n1);
}
All the semantic rules can be evaluated
procedure B(int *n0, int *n1) { at the end of parsing of production rules
if (currtoken=0)
{int a,b; consume 0; call B(&a,&b); *n0=a+1; *n1=b;}
else if (currtoken=1)
{ int a,b; consume 1; call B(&a,&b); *n0=a; *n1=b+1; }
else if (currtoken=$) {*n0=0; *n1=0; } // $ is end-marker
else error(“unexpected token”);
}
23
L-Attributed Definitions
• S-Attributed Definitions can be efficiently
implemented.
• We are looking for a larger (larger than SAttributed Definitions) subset of syntax-directed
definitions which can be efficiently evaluated.
 L-Attributed Definitions
• L-Attributed Definitions can always be evaluated
by the depth first visit of the parse tree.
• This means that they can also be evaluated
during the parsing.
24
L-Attributed Definitions
• A syntax-directed definition is L-attributed if
each inherited attribute of Xj, where 1jn, on
the right side of A → X1X2...Xn depends only
on:
1.The attributes of the symbols X1,...,Xj-1
to the left of Xj in the production and
2.the inherited attribute of A
• Every S-attributed definition is L-attributed, the
restrictions only apply to the inherited attributes
(not to synthesized attributes).
25
A Definition which is NOT L-Attributed
Productions
A→LM
A→QR
Semantic Rules
L.in=l(A.i), M.in=m(L.s), A.s=f(M.s)
R.in=r(A.in), Q.in=q(R.s), A.s=f(Q.s)
• This syntax-directed definition is not L-attributed
because the semantic rule Q.in=q(R.s) violates the
restrictions of L-attributed definitions.
• When Q.in must be evaluated before we enter to Q
because it is an inherited attribute.
• But the value of Q.in depends on R.s which will be
available after we return from R. So, we are not be able
to evaluate the value of Q.in before we enter to Q.
26
Translation Schemes
• In a syntax-directed definition, we do not say anything
about the evaluation times of the semantic rules (when
the semantic rules associated with a production should
be evaluated?).
• A translation scheme is a context-free grammar in
which:
– attributes are associated with the grammar symbols
and
– semantic actions enclosed between braces {} are
inserted within the right sides of productions.
• Ex:
A → { ... } X { ... } Y { ... }
Semantic Actions
27
Translation Schemes
• When designing a translation scheme, some restrictions
should be observed to ensure that an attribute value is
available when a semantic action refers to that attribute.
• These restrictions (motivated by L-attributed definitions)
ensure that a semantic action does not refer to an
attribute that has not yet computed.
• In translation schemes, we use semantic action
terminology instead of semantic rule terminology used in
syntax-directed definitions.
• The position of the semantic action on the right side
indicates when that semantic action will be evaluated.
28
Translation Schemes for S-attributed
Definitions
• If our syntax-directed definition is S-attributed, the
construction of the corresponding translation scheme
will be simple.
• Each associated semantic rule in a S-attributed syntaxdirected definition will be inserted as a semantic action
into the end of the right side of the associated
production.
Production
Semantic Rule
E → E1 + T
E.val = E1.val + T.val

E → E1 + T { E.val = E1.val + T.val }
 a production of
a syntax directed definition
 the production of the
corresponding translation
scheme
29
A Translation Scheme Example
• A simple translation scheme that converts infix
expressions to the corresponding postfix expressions.
E→TR
R → + T { print(“+”) } R1
R→
T → id { print(id.name) }
a+b+c
infix expression
 ab+c+
postfix expression
30
A Translation Scheme Example (cont.)
E
T
id {print(“a”)}
R
+
T
{print(“+”)} R
id {print(“b”)}
+ T {print(“+”)} R
id {print(“c”)}

The depth first traversal of the parse tree (executing the semantic actions
in that order) will produce the postfix representation of the infix
expression.
31
Inherited Attributes in Translation
Schemes
•
If a translation scheme has to contain both synthesized and inherited
attributes, we have to observe the following rules:
1. An inherited attribute of a symbol on the right side of a production
must be computed in a semantic action before that symbol.
2. A semantic action must not refer to a synthesized attribute of a
symbol to the right of that semantic action.
3. A synthesized attribute for the non-terminal on the left can only be
computed after all attributes it references have been computed (we
normally put this semantic action at the end of the right side of the
production).
•
With a L-attributed syntax-directed definition, it is always possible to
construct a corresponding translation scheme which satisfies
these
three conditions (This may not be possible for a general syntaxdirected translation).
32
Top-Down Translation
• We will look at the implementation of L-attributed
definitions during predictive parsing.
• Instead of the syntax-directed translations, we
will work with translation schemes.
• We will see how to evaluate inherited attributes
(in L-attributed definitions) during recursive
predictive parsing.
• We will also look at what happens to attributes
during the left-recursion elimination in the leftrecursive grammars.
33
A Translation Scheme with
Inherited Attributes
D → T id { addtype(id.entry,T.type), L.in = T.type
}L
T → int { T.type = integer }
T → real { T.type = real }
L → id { addtype(id.entry,L.in), L1.in = L.in } L1
L→
• This is a translation scheme for an L-attributed
definitions.
34
Predictive Parsing (of Inherited
Attributes)
procedure D() {
int Ttype,Lin,identry;
call T(&Ttype); consume(id,&identry);
addtype(identry,Ttype); Lin=Ttype;
call L(Lin);
a synthesized attribute (an output parameter)
}
procedure T(int *Ttype) {
if (currtoken is int) { consume(int); *Ttype=TYPEINT; }
else if (currtoken is real) { consume(real); *Ttype=TYPEREAL; }
else { error(“unexpected type”); } an inherited attribute (an input parameter)
}
procedure L(int Lin) {
if (currtoken is id) { int L1in,identry; consume(id,&identry);
addtype(identry,Lin); L1in=Lin; call L(L1in); }
else if (currtoken is endmarker) { }
else { error(“unexpected token”); }
}
35
Eliminating Left Recursion from
Translation Scheme
• A translation scheme with a left recursive grammar.
E → E1 + T { E.val = E1.val + T.val }
E → E1 - T { E.val = E1.val - T.val }
E→T
{ E.val = T.val }
T → T1 * F { T.val = T1.val * F.val }
T→F
{ T.val = F.val }
F → ( E ) { F.val = E.val }
F → digit { F.val = digit.lexval }
• When we eliminate the left recursion from the grammar
(to get a suitable grammar for the top-down parsing) we
also have to change semantic actions
36
Eliminating Left Recursion (cont.)
inherited attribute
synthesized attribute
E → T { A.in=T.val } A { E.val=A.syn }
A → + T { A1.in=A.in+T.val } A1 { A.syn = A1.syn}
A → - T { A1.in=A.in-T.val } A1 { A.syn = A1.syn}
A →  { A.syn = A.in }
T → F { B.in=F.val } B { T.val=B.syn }
B → * F { B1.in=B.in*F.val } B1 { B.syn = B1.syn}
B →  { B.syn = B.in }
F → ( E ) { F.val = E.val }
F → digit { F.val = digit.lexval }
37
Eliminating Left Recursion (in general)
A → A1 Y { A.a = g(A1.a,Y.y) } a left recursive grammar with
A → X { A.a=f(X.x) }
synthesized attributes (a,y,x).
 eliminate left recursion
inherited attribute of the new non-terminal
synthesized attribute of the new non-terminal
A → X { R.in=f(X.x) } R { A.a=R.syn }
R → Y { R1.in=g(R.in,Y.y) } R1 { R.syn = R1.syn}
R →  { R.syn = R.in }
38
Evaluating attributes
A
A
parse tree of left recursive grammar
Y A.a=g(f(X.x),Y.y)
parse tree of non-left-recursive grammar
X X.x=f(X.x)
A
X R.in=f(X.x) R A.a=g(f(X.x,Y.y)
Y R1.in=g(f(X.x),Y.y) R1 R.syn=g(f(X.x),Y.y)
 R1.syn=g(f(X.x),Y.y)
39
Translation Scheme - Intermediate
Code Generation
E → T { A.in=T.loc } A { E.loc=A.loc }
A → + T { A1.in=newtemp(); emit(add,A.in,T.loc,A1.in) }
A1 { A.loc = A1.loc}
A →  { A.loc = A.in }
T → F { B.in=F.loc } B { T.loc=B.loc }
B → * F { B1.in=newtemp(); emit(mult,B.in,F.loc,B1.in) }
B1 { B.loc = B1.loc}
B →  { B.loc = B.in }
F → ( E ) { F.loc = E.loc }
F → id { F.loc = id.name }
40
Predictive Parsing – Intermediate
Code Generation
procedure E(char **Eloc) {
char *Ain, *Tloc, *Aloc;
call T(&Tloc); Ain=Tloc;
call A(Ain,&Aloc); *Eloc=Aloc;
}
procedure A(char *Ain, char **Aloc) {
if (currtok is +) {
char *A1in, *Tloc, *A1loc;
consume(+); call T(&Tloc); A1in=newtemp();
emit(“add”,Ain,Tloc,A1in);
call A(A1in,&A1loc); *Aloc=A1loc;
}
else { *Aloc = Ain }
}
41
Predictive Parsing (cont.)
procedure T(char **Tloc) {
char *Bin, *Floc, *Bloc;
call F(&Floc); Bin=Floc;
call B(Bin,&Bloc); *Tloc=Bloc;
}
procedure B(char *Bin, char **Bloc) {
if (currtok is *) {
char *B1in, *Floc, *B1loc;
consume(+); call F(&Floc); B1in=newtemp(); emit(“mult”,Bin,Floc,B1in);
call B(B1in,&B1loc); Bloc=B1loc;
}
else { *Bloc = Bin }
}
procedure F(char **Floc) {
if (currtok is “(“) { char *Eloc; consume(“(“); call E(&Eloc); consume(“)”);
*Floc=Eloc }
else { char *idname; consume(id,&idname); *Floc=idname }
}
42
Bottom-Up Evaluation of Inherited
Attributes
• Using a top-down translation scheme, we can
implement any L-attributed definition based on a
LL(1) grammar.
• Using a bottom-up translation scheme, we can
also implement any L-attributed definition based
on a LL(1) grammar (each LL(1) grammar is
also an LR(1) grammar).
• In addition to the L-attributed definitions based
on LL(1) grammars, we can implement some of
L-attributed definitions based on LR(1)
grammars (not all of them) using the bottom-up
translation scheme.
43
Removing Embedding Semantic
Actions
• In bottom-up evaluation scheme, the semantic actions are
evaluated during the reductions.
• During the bottom-up evaluation of S-attributed definitions, we
have a parallel stack to hold synthesized attributes.
• Problem: where are we going to hold inherited attributes?
• A Solution:
– We will convert our grammar to an equivalent grammar to
guarantee to the followings.
– All embedding semantic actions in our translation scheme will be
moved into the end of the production rules.
– All inherited attributes will be copied into the synthesized
attributes (most of the time synthesized attributes of new nonterminals).
– Thus we will be evaluate all semantic actions during reductions,
and we find a place to store an inherited attribute.
44
Removing Embedding Semantic
Actions
• To transform our translation scheme into an
equivalent translation scheme:
1. Remove an embedding semantic action Si, put new a
non-terminal Mi instead of that semantic action.
2. Put that semantic action Si into the end of a new
production rule Mi for that non-terminal Mi.
3. That semantic action Si will be evaluated when this
new production rule is reduced.
4. The evaluation order of the semantic rules are not
changed by this transformation.
45
Removing Embedding Semantic
Actions
A {S1} X1 {S2} X2 ... {Sn} Xn
 remove embedding semantic actions
A M1 X1 M2 X2 ... Mn Xn
M1 {S1}
M2 {S2}
.
.
Mn {Sn}
46
Removing Embedding Semantic
Actions
E→TR
R → + T { print(“+”) } R1
R→
T → id { print(id.name) }
 remove embedding semantic actions
E→TR
R → + T M R1
R→
T → id { print(id.name) }
M →  { print(“+”) }
47
Translation with Inherited
Attributes
• Let us assume that every non-terminal A has an inherited attribute A.i,
and every symbol X has a synthesized attribute X.s in our grammar.
• For every production rule A X1 X2 ... Xn ,
–
–
–
–
introduce new marker non-terminals M1,M2,...,Mn and
replace this production rule with A M1 X1 M2 X2 ... Mn Xn
the synthesized attribute of Xi will be not changed.
the inherited attribute of Xi will be copied into the synthesized attribute of Mi
by the new semantic action added at the end of the new production rule
Mi.
– Now, the inherited attribute of Xi can be found in the synthesized attribute of
Mi (which is immediately available in the stack.
A  {B.i=f1(...)} B {C.i=f2(...)} C {A.s= f3(...)}

A  {M1.i=f1(...)} M1 {B.i=M1.s} B {M2.i=f2(...)} M2 {C.i=M2.s} C {A.s= f3(...)}
M1 {M1.s=M1.i}
M2 {M2.s=M2.i}
48
Translation with Inherited Attributes
S  {A.i=1} A {S.s=k(A.i,A.s)}
A  {B.i=f(A.i)} B {C.i=g(A.i,B.i,B.s)} C {A.s= h(A.i,B.i,B.s,C.i,C.s)}
B  b {B.s=m(B.i,b.s)}
C  c {C.s=n(C.i,c.s)}
S  {M1.i=1} M1 {A.i=M1.s} A {S.s=k(M1.s,A.s)}
A  {M2.i=f(A.i)} M2 {B.i=M2.s} B
{M3.i=g(A.i,M2.s,B.s)} M3 {C.i=M3.s} C {A.s= h(A.i, M2.s,B.s,
M3.s,C.s)}
B  b {B.s=m(B.i,b.s)}
C  c {C.s=n(C.i,c.s)}
M1 {M1.s=M1.i}
M2 {M2.s=M2.i}
M3 {M3.s=M3.i}
49
Actual Translation Scheme
S  {M1.i=1} M1 {A.i=M1.s} A {S.s=k(M1.s,A.s)}
A  {M2.i=f(A.i)} M2 {B.i=M2.s} B {M3.i=g(A.i,M2.s,B.s)} M3 {C.i=M3.s} C {A.s=
h(A.i, M2.s,B.s, M3.s,C.s)}
B  b {B.s=m(B.i,b.s)}
C  c {C.s=n(C.i,c.s)}
M1 {M1.s= M1.i}
M2 {M2.s=M2.i}
M3 {M3.s=M3.i}
S  M1 A
M1 
A  M2 B M3 C
M2 
M3 
Bb
Cc
{ s[ntop]=k(s[top-1],s[top]) }
{ s[ntop]=1 }
{ s[ntop]=h(s[top-4],s[top-3],s[top-2],s[top-1],s[top]) }
{ s[ntop]=f(s[top]) }
{ s[ntop]=g(s[top-2],s[top-1],s[top])}
{ s[ntop]=m(s[top-1],s[top]) }
{ s[ntop]=n(s[top-1],s[top]) }
50
Evaluation of Attributes
S
S.s=k(1,h(..))
A.i=1
A
A.s=h(1,f(1),m(..),g(..),n(..))
B.i=f(1)
B
B.s=m(f(1),b.s)
b
C.i=g(1,f(1),m(..))
C
C.s=n(g(..),c.s)
c
51
Evaluation of Attributes
stack
M1
M1 M2
M1 M2 b
M1 M2 B
M1 M2 B M3
M1 M2 B M3 c
M1 M2 B M3 C
M1 A
S
input
bc$
bc$
bc$
c$
c$
c$
$
$
$
$
s-attribute stack
1
1 f(1)
1 f(1) b.s
1 f(1) m(f(1),b.s)
1 f(1) m(f(1),b.s) g(1,f(1),m(f(1),b.s))
1 f(1) m(f(1),b.s) g(1,f(1),m(f(1),b.s)) c.s
1 f(1) m(f(1),b.s) g(1,f(1),m(f(1),b.s)) n(g(..),c.s)
1 h(f(1),m(..),g(..),n(..))
k(1,h(..))
52
Problems
• All L-attributed definitions based on LR grammars cannot be
evaluated during bottom-up parsing.
S  { L.i=0 } L
L  { L1.i=L.i+1 } L1 1
L   { print(L.i) }
 this translations scheme cannot be implemented
during the bottom-up parsing
S  M1 L
L  M2 L1 1
 But since L   will be reduced first by the bottom-up
L   { print(s[top]) }
parser, the translator cannot know the number of 1s.
M1   { s[ntop]=0 }
M2   { s[ntop]=s[top]+1 }
53
Problems
• The modified grammar cannot be LR grammar
anymore.
LLb
La

LMLb
La
M
NOT LR-grammar
S’  .L, $
L  . M L b, $
L  . a, $
M  .,a
 shift/reduce conflict
54
Download