0% found this document useful (0 votes)
10 views83 pages

Lec04 Sematic Analysis

Uploaded by

Cherish Mahajan
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
10 views83 pages

Lec04 Sematic Analysis

Uploaded by

Cherish Mahajan
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 83

SYMANTEC ANALYSIS

3RD PHASE OF COMPILER CONSTRUCTION

1
BEYOND SYNTAX

 There is a level of correctness that is deeper than grammar

fie(a,b,c,d) What is wrong with this program?


int a, b, c, d; (let me count the ways …)
{…}
fee() {
int f[3],g[0],
h, i, j, k;
char *p;
fie(h,i,“ab”,j, k);
k = f * i + j;
h = g[17];
printf(“<%s,%s>.\n”,
p,q);
p = 10;
}
2
SEMANTICS

 There is a level of correctness that is deeper than grammar

fie(a,b,c,d) What is wrong with this program?


int a, b, c, d; (let me count the ways …)
{…}
• declared g[0], used g[17]
fee() {
int f[3],g[0], • wrong number of args to fie()
h, i, j, k; • “ab” is not an int
char *p; • wrong dimension on use of f
fie(h,i,“ab”,j, k);
k = f * i + j; • undeclared variable q
h = g[17]; • 10 is not a character string
printf(“<%s,%s>.\n”,
p,q); All of these are “deeper than
p = 10; syntax”
}
3
SEMANTIC ANALYSIS

 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
’ put information into the symbol table
’ perform type checking
’ issue error messages
’ perform almost any activities.
ROLE OF SEMANTIC ANALYZER
 A semantic analyzer checks the source program for semantic errors
and collects the type information for the code generation.
 Type-checking is an important part of semantic analyzer.

 Normally semantic information cannot be represented by a context-


free language used in syntax analyzers, so they are integrated with
attributes (semantic rules) , resulting in
’ Syntax-directed translation,
’ Attribute grammars
 Ex:
newval := oldval + 12

The type of the identifier newval must match with type of the expression
(oldval+12) 5
SYNTAX-DIRECTED TRANSLATION

 In syntax-directed translation, we attach ATTRIBUTES to grammar symbols.


 The values of the attributes are computed by SEMANTIC RULES associated
with grammar productions.
 Conceptually, we have the following flow:

 In practice, however, we do everything in a single pass.


6
ATTRIBUTES
 Attribute values may represent
’ Numbers (literal constants)
’ Strings (literal constants)
’ Memory locations, such as a frame index of a local variable or function argument
’ A data type for type checking of expressions
’ Scoping information for local declarations
’ Intermediate program representations
Attributes are two tuple value,
<attribute name,="" attribute="" value="">

 There are two type of Attributes:


’ Synthesized Attributes
’ Inherited Attributes 7
SYNTAX-DIRECTED TRANSLATION

 There are two ways to represent the semantic rules we associate


with grammar symbols.

’ SYNTAX-DIRECTED DEFINITIONS (SDDs) do not specify the order


in which semantic actions should be executed

’ TRANSLATION SCHEMES explicitly specify the ordering of the


semantic actions.

 SDDs are higher level; translation schemes are closer to an


implementation
8
3.1 SYNTAX-DIRECTED DEFINITIONS

9
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→α ).
in either case, we say b DEPENDS on c1, c2, …, ck.
10
SYNTHESIZED ATTRIBUTES

 Synthesized attributes depend only on the attributes of children. They are the most
common attribute type.

 Synthesized attributes never take values from their parent nodes or any sibling
nodes.
 The non terminal concerned must be in the head of production.
 Terminals have synthesized attributes which are the lexical values (denoted by
lexval) generated by the lexical analyzer. 11
EXAMPLE SDD: DESK CALCULATOR (EX. 1)

Production Semantic Rules


L → E return print(E.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

 Symbols E, T, and F are associated with a synthesized attribute val.


 Terminals do not have inherited attibutes, it is assumed that their
value is evaluated by the lexical analyzer. 12

 Here, token digit has a synthesized attribute lexval


SYNTAX-DIRECTED DEFINITION (EX. 2)
Production Semantic Rules
E → E1 E.loc=newtemp(), E.code=E1.code
E → E1 + T E.loc=newtemp(), E.code = E1.code || T.code || add E1.loc,T.loc,E.loc
E→T E.loc = T.loc, E.code=T.code
T → T1 * F T.loc=newtemp(), T.code = T1.code || F.code || mult T1.loc,F.loc,T.loc
T→F T.loc = F.loc, T.code=F.code
F→(E) F.loc = E.loc, F.code=E.code
F → id 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
CALCULATING SYNTHESIZED ATTRIBUTES
Input string: 3*5+4 return
Annotated tree:

14
INHERITED ATTRIBUTES

 An inherited attribute is defined in terms of the attributes of the node’s


parents and/or siblings. They cannot be defined in terms of attribute
values at children nodes.

 Inherited attributes are often used in compilers for passing contextual


information forward, for example, the type keyword in a variable
declaration statement.
 Inherited attributes are useful when the structure of a parse tree does not match the 15
abstract syntax of the source code.
EXAMPLE OF SDD WITH AN INHERITED ATTRIBUTE

 Suppose we want to describe declarations like “real x, y, z”

Production Semantic Rules


addtype() is just
1) D→TL L.in := T.type a procedure that
2) T → int T.type := integer sets the type
field in the
3) T → real T.type := real symbol table.
4) L → L1, id L1.in := L.in
addtype( id.entry, L.in )
5) L → id addtype( id.entry, L.in )
 Productions 4 and 5 have a rule in which a function addType is called with
two arguments:
1. id.entry, a lexical value that points to a symbol-table object, and
2. L.inh, the type being assigned to every identifier on the list.
 L.in is inherited since it depends on a sibling or parent. L is taking value16
from T (D→TL)
ATTRIBUTE GRAMMAR

 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).
17
SIDE EFFECTS

 Side Effects: Evaluation of semantic rules may generate intermediate codes,


may put information into the symbol table, may perform type checking and may
issue error messages. These are known as side effects.
 Semantic Rules with Controlled Side Effects:
In practice translation involves side effects. Attribute grammars have no side
effects and allow any evaluation order consistent with dependency graph
whereas translation schemes impose left-to-right evaluation and allow
schematic actions to contain any program fragment.
 Ways to Control Side Effects
1. Permit incidental side effects that do not disturb attribute evaluation.
2. Impose restrictions on allowable evaluation orders, so that the same
translation is produced for any allowable order
18
NEED FOR DEPENDENCY GRAPH

 An SDD with both inherited and synthesized attributes does not ensure any
guaranteed order; even it may not have an order at all.
 For example, consider nonterminals A and B, with synthesized and inherited
attributes A.s and B.i, respectively, along with the production and rules as:
Production Semantic Rule
A →B A.s = B.i
B.i= A.s+1

 These rules are circular; it is impossible to evaluate either A.s at a node N or B.i at
the child of N without first evaluating the other. The circular dependency of A.s
and B.i at some pair of nodes in a parse tree.

19

The circular dependency of A.s and b.s on one another


3.2 DEPENDENCY GRAPHS

20
DEPENDENCY GRAPHS
 Dependency Graph predicts the flow of information among the
attribute instances in a particular parse tree.
 The interdependencies among the inherited and synthesized attributes
at the node in the parse tree is depicted by dependency graph.
 If an attribute b depends on attribute c, then attribute b has to be
evaluated AFTER c.
 DEPENDENCY GRAPHS visualize:

’ Each attribute is a node


’ We add edges from the node for attribute c to the node for
attribute b, if b depends on c.
’ For procedure calls, we introduce a dummy synthesized attribute
that depends on the parameters of the procedure calls. 21
DEPENDENCY GRAPH EXAMPLE

 Production Semantic Rule


 E -> E1 + E2 E.val := E1.val + E2.val

 Wherever this rule appears in the parse tree we draw:

22
DEPENDENCY GRAPH EXAMPLE

Input: 5+3*4 L

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 23
DEPENDENCY GRAPH – SYNTHETIZED ATTRIBUTES
S.code=‘t1:=c*d t3=b+t1 a=t3’
Input a:=b+c*d S
E.loc=‘t3’
E.code=‘t1:=c*d t3=b+t1’
id.loc=‘a’ id := E
E.loc=‘t1’
E.loc=‘b’ E.code=‘t1:=c*d ‘
E.code=‘ ‘ E + E
E.loc=‘d’

id.loc=‘b’ id E.loc=‘c’
E * E E.code=‘ ‘

E.code=‘ ‘

id.loc=‘c’ id id id.loc=‘d’

E → E1 + T E.loc=newtemp(), E.code = E1.code || T.code || add E1.loc,T.loc,E.loc


E→T E.loc = T.loc, E.code=T.code
T → T1 * F T.loc=newtemp(), T.code = T1.code || F.code || mult T1.loc,F.loc,T.loc 24
T→F T.loc = F.loc, T.code=F.code
F→(E) F.loc = E.loc, F.code=E.code
F → id F.loc = id.name, F.code=“”
DEPENDENCY GRAPH – INHERITED ATTRIBUTES

25
DEPENDENCY GRAPH – INHERITED ATTRIBUTES

26
DEPENDENCY GRAPH – INHERITED ATTRIBUTES

27
DEPENDENCY GRAPH
Input: real p q

D T.type=real L.in=real

T L L1.in=real addtype(q,real)

real L id addtype(p,real)

Id.entry=q
id id.entry=p

28
parse tree dependency graph
FINDING A VALID EVALUATION ORDER
TOPOLOGICAL SORT OF THE DEPENDENCY GRAPH

 An ordering embeds a directed graph into a linear order, and is


called a topological sort of the graph
 A topological sort of a directed acyclic graph orders the nodes so
that for any nodes a and b if there is a flow a → b, a appears
BEFORE b in the ordering.
 If there is any cycle in the graph, then there are no topological
sorts; that is, there is no way to evaluate the SDD on this parse
tree. If there are no cycles, then there is always at least one
topological sort.
 There are many possible topological orderings for a DAG.

 Each of the possible orderings gives a valid order for evaluation


29
of the semantic rules.
3.2 SYNTAX TREES AND PARSE TREES

30
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.

 The order of these computations depends on the dependency


graph induced by the semantic rules.

31
ANNOTATED PARSE TREE -- EXAMPLE

Input: 5+3*4 L

E.val=17 return

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
32
SYNTAX TREE CONSTRUCTION

 One thing SDDs are useful for is construction of SYNTAX TREES.


 A syntax tree is a condensed form of parse tree.
 Syntax trees are useful for representing programming language
constructs like expressions and statements.
 They help compiler design by decoupling parsing from translation.

33
SYNTAX TREES

 Leaf nodes for operators and keywords are removed.


 Internal nodes corresponding to uninformative non-terminals are replaced
34
by the more meaningful operators.
SDD FOR SYNTAX TREE CONSTRUCTION

 We need some functions to help us build the syntax tree:


’ mknode(op,left,right) constructs an operator node with label op, and two
children, left and right
’ mkleaf(id,entry) constructs a leaf node with label id and a pointer to a
symbol table entry
’ mkleaf(num,val) constructs a leaf node with label num and the token’s
numeric value val

 Use these functions to build a syntax tree for a-4+c:


’ P1 := mkleaf( id, st_entry_for_a )
’ P2 := …
35
SDD FOR SYNTAX TREE CONSTRUCTION

This is an example of S-attributed definition.

Production Semantic Rules


E -> E1 + T E.nptr := mknode( ‘+’, E1.nptr,T.nptr)
E -> E1 - T E.nptr := mknode( ‘-’, E1.nptr,T.nptr)
E -> T E.nptr := T.nptr
T -> ( E ) T.nptr := E.nptr
T -> id T.nptr := mkleaf( id, id.entry )
T -> num T.nptr := mkleaf( num, num.val )

 Try to derive the annotated parse tree for a-4+c. 36


3.3 EVALUATING SDDS BOTTOM-UP

37
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.

 Two sub-classes of the syntax-directed definitions are:


’ 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.
 Implementations of S-attributed Definitions are a little bit easier than
implementations of L-Attributed Definitions 38
SYNTHESIZED ATTRIBUTES

 If a SDD has synthesized attributes ONLY, it is called a S-


ATTRIBUTED DEFINITION.

 S-attributed definitions are convenient since the attributes can be


calculated in a bottom-up traversal of the parse tree.

39
BOTTOM-UP EVALUATION OF S-ATTRIBUTED DEFINITIONS

 Put the values of the synthesized attributes of the grammar symbols


into a parallel stack.
 Parser’s stack stores grammar symbols AND attribute values.

 For every production A → XYZ with semantic rule A.a := f( X.x,


Y.y, Z.z ), before XYZ is reduced to A, we should already have X.x
Y.y and Z.z on the stack. (Here all attributes are synthesized.)

40
DESK CALCULATOR EXAMPLE
Production Semantic Rule Code
L -> E return print( E.val ) print val[top-1]
E -> E1 + T E.val := E1.val + T.val val[newtop] = val[top-2]+val[top]
E -> T E.val := T.val /*newtop==top, so nothing to do*/
T -> T1 * F T.val := T1.val x F.val val[newtop] = val[top-2]+val[top]
T -> F T.val := F.val /*newtop==top, so nothing to do*/
F -> ( E ) F.val := E.val val[newtop] = val[top-1]
F -> digit F.val := digit.lexval /*newtop==top, so nothing to do*/

 Symbol E, T and F are associated with an attribute num.


 Token digit the implementation of the semantic rule for a bottom-up parser.
 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).
 The above model is suited for a desk calculator where the purpose is to evaluate ad
to generate code. 41
CANONICAL LR(0) COLLECTION FOR THE GRAMMAR
.. L I1: L’→L . . .
I0: L’→
.
L I7: L →Er I11: E →E+T * 9

.. .. .
L→ Er r T
E T →T *F
E→
E→
..
E+T
T
I2: L →E r
E →E +T
+
..
I8: E →E+ T
T → T*F (
F 4
5
T→
T→
F→..
T*F
F
(E)
T I3: E →T ..
T →T *F
..
T→ F
F → (E)
F→ d
d
6

F→ d
F I4: T →F . *

.
. ..
I9: T →T* F F
I12: T →T*F .
..
( I5: F → ( E) F → (E)
(
E→ E+T E
F→ d 5

..
E→ T d
6
T→
T→
F→..
T*F
F
(E)
T
3
..
I10: F →(E )
E →E +T
)
+
I13: F →(E) .
F→ d F
4 8
d I6: F →d . (
d
5
6
42
BOTTOM-UP EVALUATION -- EXAMPLE
 At each shift of digit, we also push digit.lexval into val-stack.
stack val-stack input action semantic rule
0 5+3*4r s6 d.lexval(5) into val-stack
0id6 5 +3*4r F→id F.val=id.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+8id6 5-3 *4r F→id 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
0E2+8T11*9 5-3- 4r s6 d.lexval(4) into val-stack
0E2+8T11*9id6 5-3-4 r F→id 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
43
0L1 17 $ acc
TOP-DOWN EVALUATION OF S-ATTRIBUTED
DEFINITIONS
Productions Semantic Rules
A→B print(B.n0), print(B.n1)
B → 0 B1 B.n0=B1.n0+1, B.n1=B1.n1
B → 1 B1 B.n0=B1.n0, B.n1=B1.n1+1
B→ B.n0=0, B.n1=0

where B has two synthesized attributes (n0 and n1).

44
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(); A→B
}
procedure B() {
if (currtoken=0) { consume 0; call B(); } B→0B
else if (currtoken=1) { consume 1; call B(); } B→1B
else if (currtoken=$) {} // $ is end-marker B→
else error(“unexpected token”);
45
}
TOP-DOWN EVALUATION (OF S-ATTRIBUTED
procedure A() {
DEFINITIONS)
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”);
} 46
L-ATTRIBUTED DEFINITIONS

 S-attributed definitions only allow synthesized attributes.

 We saw earlier that inherited attributes are useful.

 But we prefer definitions that can be evaluated in one pass.

 L-ATTRIBUTED definitions are the set of SDDs whose attributes can


be evaluated in a DEPTH-FIRST traversal of the parse tree.

47
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).

48
L-ATTRIBUTED GRAMMAR
 Informally – dependency-graph edges may go from left to right, not
other way around.
 Given production A → X1X2 ···Xn, inherited attributes of Xj depend only
on:
’ inherited attributes of A
’ arbitrary attributes of X1,X2,···Xj−1
 Synthesized attributes of A depend only on its inherited attributes and
arbitrary RHS attributes
 Synthesized attributes of an action depends only on its inherited
attributes
’ i.e., evaluation order: Inh(A), Inh(X1), Syn(X1), . . . , Inh(Xn),
Syn(Xn), Syn(A)
’ This is precisely the order of evaluation for an LL parser 49
L-ATTRIBUTED DEFINITIONS

Is the following SDD L-attributed?

Production Semantic Rules


A -> L M L.i := l(A.i)
M.i := m(L.s)
A.s := f(M.s)
A -> Q R R.i := r(A.i)
Q.i := q(R.s)
A.s := f(Q.s)
50
A DEFINITION WHICH IS NOT L-ATTRIBUTED

Productions Semantic Rules


A→LM L.in=l(A.i), M.in=m(L.s), A.s=f(M.s)
A→QR 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 51
before we enter to Q.
COMPARISON BETWEEN L-ATTRIBUTED AND S-
ATTRIBUTED SDT

Attributes in L-attributed SDTs are evaluated by depth-first


and left-to-right parsing manner.

52
3.4 TRANSLATION SCHEMES

53
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
Translation schemes are closer to a real implementation because they54
specify when, during the parse, attributes should be computed.
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.
55
TURNING A SDD INTO A TRANSLATION SCHEME

 For a translation scheme to work, it must be the case that an attribute is computed
BEFORE it is used.
 If the SDD is S-attributed, it is easy to create the translation scheme implementing it:

Production Semantic Rule


T -> T1 * F T.val := T1.val x F.val

 Translation scheme:
T -> T1 * F { T.val = T1.val * F.val }

 Each associated semantic rule in a S-attributed syntax-directed definition will be


inserted as a semantic action into the end of the right side of the associated
production.
 That is, we just turn the semantic rule into an action and add at the far right hand
side. This DOES NOT WORK for inherited attributes! 56
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 ➔ ab+c+

infix expression postfix expression


57
A TRANSLATION SCHEME EXAMPLE (CONT.)
E→TR
E R → + T { print(“+”) } R1
R→
T → id { print(id.name) }
T R

id{print(“a”)} + 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
58
order) will produce the postfix representation of the infix expression.
COMPARISON BETWEEN SDD AND SDT

 SDD: Specifies the values of attributes by associating semantic rules


with the productions.
 SDD is easier to read; easy for specification.

 SDT scheme: embeds program fragments (also called semantic


actions) within production bodies. The position of the action defines
the order in which the action is executed (in the middle of production
or end).
 SDT scheme:can be more efficient; easy for implementation.

59
EXAMPLE OF SDD AND SDT

SDD and SDT for Infix to Postfix Notation


INHERITED ATTRIBUTES IN TRANSLATION SCHEMES

With inherited attributes, the translation scheme designer needs to


follow three rules:
1. An inherited attribute for a symbol on the RHS MUST be
computed in an action BEFORE the occurrence of the symbol.
2. An action MUST NOT refer to the synthesized attribute of a
symbol to the right of the action.
3. A synthesized attribute for the LHS nonterminal can ONLY be
computed in an action FOLLOWING the symbols for all the
attributes it references.
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 syntax-directed translation). 61
EXAMPLE

This translation scheme does NOT follow the rules:

S -> A1 A2 { A1.in = 1; A2.in = 2 }


A -> a { print( A.in ) }

If we traverse the parse tree depth first, A1.in has not been set
when referred to in the action print( A.in )

S -> { A1.in = 1 } A1 { A2.in = 2 } A2


A -> a { print( A.in ) }
62
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 left-recursive grammars.

63
TOP-DOWN TRANSLATION

 For each non-terminal A, construct a function that


’ Has a formal parameter for each inherited attribute of A
’ Returns the values of the synthesized attributes of A

 The code associated with each production does the


following:
’ Save the s-attribute of each token X into a variable X.x
’ Generate an assignment B.s=parse B(B.i1,B.i2,…,B.ik) for each
non-terminal B, where B.i1,…,B.ik are values for the L attributes
of B and B.s is a variable to store s-attributes of B.
’ Copy the code for each action, replacing references to attributes
64
by the corresponding variables
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.

65
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”); } 66
}
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 67
suitable grammar for the top-down parsing) we also have to change
semantic actions
AA| 

ELIMINATING LEFT RECURSION (IN GENERAL) A A'


A'   A ' | 

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 } 68
EVALUATING ATTRIBUTES
Parse tree of left recursive grammar
A

A 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)
69
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 } 70
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 } 71
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 }
}
72
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 }
73
else { char *idname; consume(id,&idname); *Floc=idname }
}
BOTTOM-UP EVALUATION OF INHERITED ATTRIBUTES

 The first step is to convert the SDD to a valid translation scheme.

 Then a few “tricks” have to be applied to the translation scheme.

 It is possible, with the right tricks, to do one-pass bottom-up


attribute evaluation for ALL LL(1) grammars and MOST LR(1)
grammars, if the SDD is L-attributed.

74
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 non-terminals).
’ Thus, we will be evaluate all semantic actions during reductions, and we find a
75
place to store an inherited attribute.
REMOVING EMBEDDING SEMANTIC ACTIONS

To transform our translation scheme into an equivalent translation scheme:


1. Remove an embedding semantic action Si, put a new 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.

76
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}
77
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) } 78

M →  { print(“+”) }
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 not be 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(...)} 79
M1 {M1.s=M1.i}
M2 {M2.s=M2.i}
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}
80
M2 {M2.s=M2.i}
M3 {M3.s=M3.i}
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 { s[ntop]=k(s[top-1],s[top]) }
M1  { s[ntop]=1 }
A  M2 B M3 C { s[ntop]=h(s[top-4],s[top-3],s[top-2],s[top-1],s[top]) }
M2  { s[ntop]=f(s[top]) }
M3  { s[ntop]=g(s[top-2],s[top-1],s[top])}
Bb { s[ntop]=m(s[top-1],s[top]) } 81
Cc { s[ntop]=n(s[top-1],s[top]) }
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) C.i=g(1,f(1),m(..))
B C
B.s=m(f(1),b.s) C.s=n(g(..),c.s)

b c

82
EVALUATION OF ATTRIBUTES
stack input s-attribute stack
bc$
M1 bc$ 1
M1 M 2 bc$ 1 f(1)
M1 M 2 b c$ 1 f(1) b.s
M1 M 2 B c$ 1 f(1) m(f(1),b.s)
M1 M2 B M3 c$ 1 f(1) m(f(1),b.s) g(1,f(1),m(f(1),b.s))
M1 M2 B M3 c $ 1 f(1) m(f(1),b.s) g(1,f(1),m(f(1),b.s)) c.s
M1 M2 B M3 C $ 1 f(1) m(f(1),b.s) g(1,f(1),m(f(1),b.s)) n(g(..),c.s)
M1 A $ 1 h(f(1),m(..),g(..),n(..))
S $ k(1,h(..))
83

You might also like