COP CD Unit3
COP CD Unit3
COP CD Unit3
Chapter 5:
Mr. Prakash C O
Asst. Professor,
Dept. of CSE, PESU,
coprakasha@pes.edu
Introduction
Semantics
The plain parse tree constructed in the syntax analysis phase is generally of
no use for a compiler for further processing, as it does not carry any
information of how to evaluate the tree.
Parse tree
The productions of CFG, which makes the rules of the language, do not
accommodate how to interpret them.
For example:
• int a = “value”;
• These rules are set by the grammar of the language and evaluated
in semantic analysis.
E → E+T|T id * id
T → T * F| F
F → (E) | id
How will this tree and production rules help in translation/semantic analysis?
The SDT techniques are also useful for implementing little languages for
specialized tasks.
Semantic rules: They are used to specify how attribute values are
computed. Attribute computation semantic rules are associated with
grammar productions.
Examples:
Production Semantic rule Production Semantic rule
Semantic Errors: Mentioned some of the semantics errors that the semantic
analyzer is expected to recognize:
➢ Type mismatch
➢ Undeclared variable
Synthesized attribute (The attribute which takes data values from its child nodes)
➢ A synthesized attribute for a nonterminal A at a parse-tree node N is
defined by a semantic rule associated with the production at N.
➢ The lexical analyzer usually supplies the attributes of terminals and the
synthesized ones are built up for the nonterminals and passed up the
tree.
Introduction
Synthesized attribute
Synthesized Attribute of the node-N is defined in terms of:
N
Introduction
Inherited attribute (The attribute which takes values from parents or sibling nodes or both)
➢ An inherited attribute for a nonterminal B at a parse-tree node N is defined
by a semantic rule associated with the production at the parent of N. Note
that the production must have B as a symbol in its body.
➢ Inherited attributes are those that are passed down a parse tree, i.e., the
right-side attributes are derived from the left-side attributes (or other
right-side attributes).
➢ Inherited attributes are used for passing information about the context to
nodes further down the tree.
Introduction
Inherited attribute
➢ Unlike synthesized attributes, the order in which the inherited
by non-terminals.
Inherited attribute
Inherited attribute of the node-N is defined in terms of:
An inherited attribute at node N is defined only in terms of attribute values at N's parent, N
itself, and N's siblings.
It can be evaluated during a single top-down and sideways traversal of parse tree.
Introduction
Syntax-directed translation(SDT)
Introduction
Syntax-directed translation(SDT)
SDT scheme embeds program fragments called semantic actions within
production bodies of CFG, as in
By convention, semantic actions are enclosed within curly braces. (If curly
braces occur as grammar symbols, we enclose them within single quotes).
In the above production (5.2), the action occurs at the end; in general,
Example
o Action2: takes place after consuming the input for B, but before
consuming the input for C.
2. S-attributed SDT
Introduction
Syntax-directed translation(SDT)
L-attributed SDT
S-attributed SDT
Introduction
Syntax-directed translation(SDT)
Syntax-Directed Definition(SDD)
Syntax Directed Definitions(SDD)
A SDD is a Context Free Grammar(CFG) with attributes and semantic rules.
1. synthesized attributes or
2. Inherited attributes.
➢ Production 2), E → E1 + T, also has one rule, which computes the val
attribute for the head E as the sum of the values at E1 and T.
➢ Production 7 gives F.val the value of a digit, that is, the numerical value of
the token digit that the lexical analyzer returned.
Syntax Directed Definitions
An SDD that involves only synthesized attributes is called S-attributed; the
SDD in Fig. 5.1 has this property.
In fact, the SDD in Fig. 5.1 mirrors the Yacc program of Fig. 4.58, which
illustrates translation during LR parsing. The difference is that, in the rule for
production 1, the Yacc program prints the value E.val as a side effect,
instead of defining the attribute L.va1.
Fig. 4.58
Evaluating SDDs - Parse Tree method
❑ Exercise 5.1.1: For the SDD of Fig. 5.1, give annotated parse trees for
the following expressions:
When Are Inherited Attributes Useful?
➢ The CFG in Fig. 5.8 takes a simple declaration D consisting of a basic type T
followed by a list L of identifiers. T can be int or float.
➢ Write an SDD, that enters, for each identifier on the list, the type info into the
symbol-table entry for the identifier.
D
T L
float L , id2
id1
Fig: Parse tree for float x,y
Symbol table
Name Type Size Scope
Leftmost Derivation: 1 x
2 y
D ⇒ TL ⇒ float L ⇒ float L,id2 ⇒ float id1,id2 …..
……
When Are Inherited Attributes Useful?
L-Attributed SDT:
D → T { L.inh = T.type } L
T → int { T.type=integer }
T → float { T.type=float }
L → { L1.inh=L.inh } L1 , id { addType(id.entry, L.inh) }
L → id { addType(id.entry, L.inh) }
➢ The SDD in Fig. 5.8 takes a simple declaration D consisting of a basic type
T followed by a list L of identifiers. T can be int or float.
➢ For each identifier on the list, the type is entered into the symbol-table
entry for the identifier.
➢ The purpose of L.inh is to pass the declared type down the list of identifiers,
so that it can be added to the appropriate symbol-table entries.
When Are Inherited Attributes Useful?
L-Attributed SDT:
D → T { L.inh = T.type } L
T → int { T.type=integer }
T → float { T.type=float }
L → { L1.inh=L.inh } L1 , id { addType(id.entry, L.inh) }
L → id { addType(id.entry, L.inh) }
Inherited attributes are useful when the structure of a parse tree does not
match the abstract syntax of the source code.
≠
Evaluating an SDD at the Nodes of a Parse Tree
Note:
In L-attributed, if there is a production A → X1X2…Xn and there is an inherited
attribute inh of symbol Xi computed by a rule associated with this production,
then the rule may only use:
Inherited attributes never flows from child node to parent node. (i.e., from body
symbols to head symbol in production)
Evaluating an SDD at the Nodes of a Parse Tree
Inherited attributes are useful when the structure of a parse tree does not
match the abstract syntax of the source code.
≠
Evaluating an SDD at the Nodes of a Parse Tree
Example 5.3 : The SDD in Fig. 5.4 computes terms like 3 * 5 and 3 * 5 * 7.
➢ The top-down parse of input 3 * 5 begins with the production T → F T'
Here, F generates the digit 3, but the operator * is generated by T'.
the left operand 3 appears in a different subtree of the parse tree from *.
An inherited attribute will therefore be used to pass the operand to the operator.
T => FT’ => digitT’ => digit*FT’ => digit*digitT’ => digit*digit
➢ Each of the nonterminals T and F has a synthesized attribute val; the terminal digit has a synthesized attribute lexval.
➢ The nonterminal T' has two attributes: an inherited attribute inh and a synthesized attribute syn.
Evaluating an SDD at the Nodes of a Parse Tree
The semantic rules are based on the idea that the left operand of the operator * is
inherited. More precisely, the head T' of the production T’ → * F T’1 inherits the left
operand of * in the production body.
Given a term x * y * z, the root of the subtree for * y * z inherits x. Then, the root of the
subtree for * z inherits the value of x * y, and so on, if there are more factors in the
term.
Once all the factors have been accumulated, the result is passed back up the tree
using synthesized attributes.
Evaluating an SDD at the Nodes of a Parse Tree
Evaluating an SDD at the Nodes of a Parse Tree
Evaluation orders for SDD’s
Dependency graph:
▪ An edge from one attribute instance to another means that the value of
the first is needed to compute the second.
▪ If a semantic rule defines the value of synthesized attribute A.b in terms of the value
of X.c then the dependency graph has an edge from X.c to A.b
▪ If a semantic rule defines the value of inherited attribute B.c in terms of the value of
X.a then the dependency graph has an edge from X.a to B.c
2. Thus the only allowable orders of evaluation are those sequence of nodes
1,2,3…..k such that if there is an edge from i to j then i<j.
▪ Example:
The dependency graph of Fig. 5.7 has no cycles,
then it can be evaluated.
Topological sort 1:
1,2,3,4,5,6,7,8,9 (Evaluation order(1) for attributes)
Topological sort 2:
1,3,5,2,4,6,7,8,9 (Evaluation order(2) for attributes)
Symbol table
Name Type Size Scope
1 X
2 y
3 z
……
L-Attributed SDT:
Figure 5.8: SDD for simple type declarations
D → T { L.inh = T.type } L
T → int { T.type=integer }
T → float { T.type=float }
L → { L1.inh=L.inh } L1 , id { addType(id.entry, L.inh) }
L → id {addType(id.entry, L.inh)}
Ordering the evaluation of attributes
❑Exercise 1: Write dependency graph for the declaration statement float x,y,z
L-Attributed SDT:
D → T { L.inh = T.type } L
T → int { T.type=integer } addType(id3.entry, L.inh)
T → float { T.type=float }
L → { L1.inh=L.inh } L1 , id { addType(id.entry, L.inh) }
L → id {addType(id.entry, L.inh)}
float
addType(id2.entry, L.inh)
❑ Nodes 1, 2, and 3 represent the attribute entry
associated with each of the leaves labeled id.
❑ Nodes 6, 8, and 10 are the dummy attributes that
represent the application of the function addType
addType(id1.entry, L.inh)
to a type and one of these entry values.
❑ id.entry, a lexical value that points to a symbol-
table object. a lexical value that points to a
symbol-table object
Ordering the evaluation of attributes
Symbol table
Name Type Size Scope
Leftmost Derivation:
D ⇒ TL addType(id1.entry, L.inh)
⇒ float L
⇒ float L,id3
⇒ float L,id2,id3
⇒ float id1,id2,id3
Ordering the evaluation of attributes
❑ Exercise 2: Write dependency graph and annotated parse tree for the
expression 3*5*7. Token stream: <digit,3> <*> <digit,5> <*> <digit,7>
Leftmost Derivation:
T ⇒ FT’
⇒ digit T’
⇒ digit *FT’
⇒ digit * digit T’
⇒ digit * digit *FT’
⇒ digit * digit * digit T’
⇒ digit * digit * digit
Ordering the evaluation of attributes
❑ Exercise 2: Write dependency graph and annotated parse tree for the
expression 3*5*7. Token stream: <digit,3> <*> <digit,5> <*> <digit,7>
Leftmost Derivation:
T ⇒ FT’
⇒ digit T’
⇒ digit *FT’
⇒ digit * digit T’
⇒ digit * digit *FT’
⇒ digit * digit * digit T’
⇒ digit * digit * digit
S-Attributed definitions
Productions
L → L1 B
L → B
B → 0
B → 1
L => LB => L1 => LB1 => L11 => LB11 => L011 => B011 => 1011
Examples of S-Attributed SDDs
SDD:
L => LB => L1 => LB1 => L11 => LB11 => L011 => B011 => 1011
SDD:
SDD:
CFG:
Production
L → L1 B
L → B
B → 0
B → 1
Examples of S-Attributed SDDs
SDD:
L => LB => L0 => LB0 => L00 => LB00 => L100 => B100 => 1100
S => L.L => L.LB => L.L1 => L.B1 => L.11 => LB.11 => L1.11 => B1.11 => 11.11
B.val=1 L.val=B.val B.val=1 B.val=1 L.val=B.val B.val=1
B.count=1 L.count=B.count B.count=1
B.count=1 L.count=B.count B.count=1
CFG:
S → (S1)
S → x
Examples of S-Attributed SDDs
SDD:
S → x S.count = 0
Input: (((x)))
Rightmost derivation:
E => E+T => E+T*F => E+T*num => E+F*num => E+num*num => T+num*num
=> F+num*num => num+num*num
print(num.lexval)
Postfix expression: 456*+
Examples of S-Attributed SDDs
E → T
T → num.num
T → num
Examples of S-Attributed SDDs
Exercise 1: Below is a grammar for expressions involving operator + and integer or
floating-point operands. Floating-point numbers are distinguished by having a
decimal point.
E → E + T | T
T → num . num | num
Exercise 2:
Production Semantic Rules
S→E if(E.sign == POS)
print(“Result is Positive”);
else
print(“Result is Negative”);
E → num E.sign = POS;
E → + E1 E.sign = E1.sign
E → - E1 if(E1.sign == POS)
E.sign = NEG;
else
E.sign = POS;
E → E1 * E2 if(E1.sign == E2.sign)
E.sign = POS;
else
E.sign = NEG;
Examples of S-Attributed SDDs
Exercise 2:
Production Semantic Rules
S → E If(E.sign == POS)
print(“Result is Positive”);
else
print(“Result is Negative”);
E → num E.sign = POS;
E → + E1 E.sign = E1.sign
E → - E1 If(E1.sign == POS)
E.sign = NEG;
else
E.sign = POS;
E → E1 * E2 If(E1.sign == E2.sign)
E.sign = POS;
else
E.sign = NEG;
2) Show the parse tree for input 2 * - 3 (where 2 and 3 are “unsigned_ints”).
Indicate at each node what the values of associated attributes are.
Examples of S-Attributed SDDs
Production Semantic Rules
Exercise 2: S → E If(E.sign == POS)
print(“Result is Positive”);
else
1) Is E.sign an inherited attribute or a print(“Result is Negative”);
E → num E.sign = POS;
synthesized attribute?
E → + E1 E.sign = E1.sign
Answer: Synthesized attribute
E → - E1 If(E1.sign == POS) E.sign = NEG;
else E.sign = POS;
2) Show the parse tree for input 2 * - 3 (where 2 and 3 are “unsigned_ints”).
Indicate at each node what the values of associated attributes are.
L-Attributed definitions
L-Attributed SDT:
D → T { L.inh = T.type } L
T → int { T.type=integer }
T → float { T.type=float }
L → { L1.inh=L.inh } L1 , id { addType(id.entry, L.inh) }
L → id {addType(id.entry, L.inh)}
L-Attributed definitions
Example 2: L-attributed SDD to implement Desk Calculator (arithmetic
expression evaluation)
L-Attributed definitions
d) D.i = C.i
C.i = B.i
Exercise 2: Write an SDD to find out whether the given type is a Basic Type or
an array type and calculate the width required . (Note: int-4 bytes, float-8 bytes)
L-Attributed definitions
Exercise 2: Write an SDD to find out whether the given type is a Basic Type or
an array type and calculate the width required . (Note: int-4 bytes, float-8 bytes)
L-Attributed definitions
17)T.Type=C.Type i.e., T.Type = array(2, array(3,integer))
18)T.Width=C.Width i.e., T.Width = 24
int
7)C1.inhType = C.inhType i.e., C.inhType =integer
[ 1)num.lexval=2 ] 8)C1.inhWidth = C.inhWidth i.e., C.inhWidth = 4
13)C.Type= array(num.lexval,C1.Type)
i.e., C.Type=array(3,integer)
14)C.Width = num.lexval* C1.width = 3*4= 12
10)C1.inhWidth=C.inhWidth i.e., C =4
.inhWidth
❑ Update the type and storage required for the identifier x and y in the
symbol table.
2. Intermediate-code generation.
o It is more compact.
Application of Syntax Directed Definition
2 c
Application of Syntax Directed Definition
Construction of syntax trees
The nodes of a syntax tree are implemented by objects with a suitable number
of fields.
Pointer to child-1
op field Pointer to child-2
1 a
Each object will have an op field that is the label of the node.
2 c
Application of Syntax Directed Definition
Construction of syntax trees
The nodes of a syntax tree are implemented by objects with a suitable number
of fields.
Pointer to child-1
op field Pointer to child-2
If the node is an interior node, there are as many additional fields as the node
has children in the syntax tree.
A constructor function Node takes two or more arguments:
Node(op,c1,c2,... ,ck) creates an object with first field op and k additional fields for
the k children c1,c2,... ,ck.
Construction of syntax trees
Example 1: (Constructing syntax trees during bottom-up parsing)
Figure 5.11 shows the construction of a syntax tree for the input a - 4 + c.
The nodes of the syntax tree are shown as records, with the op field first.
▪ The dashed lines, represents the values of E.node and T.node; each line points to
the appropriate syntax-tree node.
Construction of syntax trees
SDT: E → E1+T {E.node=new Node(‘+’,E1.node,T.node)}
Example 1: Input: a-4+c E → E1-T {E.node=new Node(‘-’,E1.node,T.node)}
Token stream: E → T {E.node=T.node}
<id,entry for a> <-> <num,4> <+> <id,entry for c> T → (E) {T.node=E.node }
Rightmost Derivation: T → id {T.node= new Leaf(id,id.entry)}
E => E+T => E+id => E-T+id => E-num+id => T-num+id => id-num+id T → num {T.
node=new Leaf(num,num.val)}
6) 5) 4) 3) 2) 1)
Fig. 5.12: Steps in the construction of the syntax tree for a-4+c
1) T.node = new Leaf (id, entry-a)
2) E.node = T.node
3) T.node = new Leaf (num, 4);
4) E.node = new Node(‘-’, E.node, T.node )
5) T.node = new Leaf (id, entry-c)
6) E.node = new Node(‘+’, E.node, T.node )
If the rules are evaluated during a postorder traversal of the parse tree, or with
reductions during a bottom-up parse, then the sequence of steps shown in Fig. 5.12
ends with E.node(Step 6) pointing to the root of the constructed syntax tree.
Construction of syntax trees
Example 2: (Constructing syntax trees during Top-down parsing)
❑ Nonterminal E' has an inherited attribute inh and a synthesized attribute syn.
Attribute E'.inh represents the partial syntax tree constructed so far. Specifically,
it represents the root of the tree for the prefix of the input string that is to the left
of the subtree for E'.
Construction of syntax trees
Example 2: Cont...
❑ At node 5 in the dependency graph for expression a-4+c in Fig. 5.14, E'.inh denotes
the root of the partial syntax tree for the identifier a; that is, the leaf for a.
At node 6, E'.inh denotes the root for the partial syntax tree for the input a - 4.
At node 9, E'.inh denotes the syntax tree for a - 4 + c.
if ( x > 10)
{
x = 10;
}
Construction of syntax trees
❑ Exercise: Construct Syntax tree for:
Construction of syntax trees
❑ Exercise: Construct Syntax tree for:
Construction of syntax trees
❑ Exercise: Construct Syntax tree for:
Application of Syntax Directed Translation
The Structure of a Type
❑ Example 5.13:
❑ In C, the type int [2][3] can be read as, "array of 2 arrays of 3 integers."
b-inherited attribute
t-synthesized attribute
❑ The inherited b attribute pass a basic type down the tree, and
the synthesized t attribute accumulate the result.
Syntax Directed Translation(SDT) schemes
Note:
SDD is easier to read; easy for specification.
Those program fragments are called semantic actions and can appear at
any position within a production body.
Examples: E → { print(‘+’); } E1 + T
F → digit { print(digit.lexval); }
Any SDT can be implemented by first building a parse tree and then
performing the actions in a left-to-right depth-first order; that is, during a
preorder traversal.
In both these cases, the semantic rules in an SDD can be converted into an
SDT with actions that are executed at the right time.
Using the above SDTs, the output printed by a bottom-up parser, for
the input aab is
Syntax Directed Translation(SDT) schemes
Using the above SDTs, the output printed by a bottom-up parser, for
the input aab is 231
1 3 2
Syntax Directed Translation(SDT) schemes
evaluate below SDT, what is the output produced for the input
id+id*id
E → T * F {print(‘+’)}
T → id {}
F → G * id {print(‘*’)}
G → id {}
Using the above SDTs, the output printed by a bottom-up parser, for
the input id+id*id is
Syntax Directed Translation(SDT) schemes
evaluate below SDT, what is the output produced for the input
id+id*id
E → T * F {print(‘+’)}
T → id {}
F → G * id {print(‘*’)}
G → id {}
Using the above SDTs, the output printed by a bottom-up parser, for
the input id+id*id is *+
Syntax Directed Translation(SDT) schemes
evaluate below SDT, what is the output produced for the input
babcc
S → S1S2c { S.val = S1.val + S2.val }
S → a { S.val = 4 }
S → b { S.val = 7 }
Using the above SDTs, the output printed by a bottom-up parser, for
the input babcc is
S => SSc => SSScc => SSbcc => Sabcc => babcc
Syntax Directed Translation(SDT) schemes
evaluate below SDT, what is the output produced for the input
babcc
S → S1S2c { S.val = S1.val + S2.val }
S → a { S.val = 4 }
S → b { S.val = 7 }
Using the above SDTs, the output printed by a bottom-up parser, for
the input babcc is 18
S => SSc => SSScc => SSbcc => Sabcc => babcc
Syntax Directed Translation(SDT) schemes
❑ infix to postfix
❑ prefix to postfix
❑ postfix to prefix
❑ infix to prefix
❑ postfix to infix
❑ prefix to infix
Syntax Directed Translation(SDT) schemes
❑ infix to postfix
❑ prefix to postfix
❑ postfix to prefix
❑ infix to prefix
❑ postfix to infix
❑ prefix to infix
Syntax Directed Translation(SDT) schemes
abc*+$
Postfix translation schemes
Simplest SDDs are those that we can parse the grammar bottom-up and
For such cases we can construct SDT where each action is placed at the
end of the production and is executed along with the reduction of the
SDT’s with all semantic actions at the right ends of the production bodies
Since the underlying grammar is LR, and the SDD is S-attributed, these
actions can be correctly performed along with the reduction steps of the
parser.
Example of postfix SDT
Evaluate an expression 2+3*4n using the following SDT
Postfix translation schemes
o Then in a reduction step we can execute the semantic action at the end of a
production and put the value on the stack in replace of the RHS of production
T → T1 * F
When reduction
T -> T1*F {stack[top-2].val = stack[top-2].val * stack[top].val;
top = top – 2; }
Parser-Stack implementation of postfix SDT’s
Example
Parser-Stack implementation of postfix SDT’s
top
top
top
top
Parser-Stack implementation of postfix SDT’s
top
top
top
top
Parser-Stack implementation of postfix SDT’s
top
top
top
SDT’s with actions inside productions
SDT’s with actions inside productions
Consider the simple case, in which the only thing we care about is the
order in which the actions in an SDT are performed.
▪ Example:
▪ If each action simply prints a string, we care only about the order in which
the strings are printed.
▪ When transforming the grammar, treat the actions as if they were terminal
symbols.
A → A | A → A'
A' → A' |
E → TE'
E → E1 + T {print(‘+’);}
E' → + T {print(‘+’);} E'
E → T
E'→
Eliminating Left Recursion From SDT’s
When the actions of an SDD compute attributes rather than merely printing
output, we must be more careful about how we eliminate left recursion from a
grammar !
Consider
A→XR
R→YR|
Eliminating Left Recursion From SDT’s
As long as the underlying grammar is LR, postfix SDT's can be parsed and translated
bottom-up.
Example:
2. Place the actions that compute a synthesized attribute for the head of a
production at the end of the body of that production.
Consider,
S → while ( C ) S1
o In the above CFG, S is the nonterminal that generates all kinds of
statements
Consider,
S → while ( C ) S1
o The meaning of our while-statement is that the conditional C is
evaluated. If it is true, control goes to the beginning of the code for S1.
If false, then control goes to the code that follows the while-statement's
code.
o The code for S1 must be designed to jump to the beginning of the code
for the while-statement when finished.
SDT’s for L-Attributed definitions: Example
S → while (C) S1
3. The inherited attribute C.true labels the beginning of the code that must be
executed if C is true.
4. The inherited attribute C.false labels the beginning of the code that must be
executed if C is false.
▪ L2 is the beginning of the code for S1, and it becomes the value of C.true, because we
branch there when C is true.
SDT’s for L-Attributed definitions: Example
Production Semantic Rules
S -> while (C) S1 L1=new();
L2=new();
S1.next=L1;
C.false=S.next;
C.true=L2;
S.code=label||L1||C.code||label||L2|| S1.code
L2=new();
S1.next=L1;
C.false=S.next;
C.true=L2;
S.code=label||L1||C.code||label||L2|| S1.code
While generating intermediate code, we generate explicit instructions of the form label Li
where Li is an identifier, to indicate that Li is the label of the instruction that follows.
SDT’s for L-Attributed definitions: Example
Production Semantic Rules
Since L1 and L2 do not depend on any other attributes, they can be assigned to the first
action in the production.
SDT’s for L-Attributed definitions: Exercise 1
Production Semantic Rules
S -> do S1 while(C) L 1:
L2: S
S.next:
L1:
L2: S
L3:
S.next:
C.true=L2; L2: S
L3:
C.false=S.next;
S.next:
S4.next=L3;
S3.next=L1;
S.code=S1.code||label||L1||C.code||label||L2||S4.code
||label||L3||S3.code
C.true=L2; L2: S
L3:
C.false=S.next;
S.next:
S4.next=L3;
S3.next=L1;
S.code=S1.code||label||L1||C.code||label||L2||S4.code
||label||L3||S3.code
L-Attributed SDT:
D → T { L.inh = T.type } L
T → int { T.type=integer }
T → float { T.type=float }
L → { L1.inh=L.inh } L1 , id { addType(id.entry, L.inh) }
L → id {addType(id.entry, L.inh)}
SDT’s for L-Attributed definitions
Implementing L-Attributed SDD's
b) Build the parse tree, add actions, and execute the actions in preorder. This
approach works for any L-attributed definition.
Ex., Arithmetic expression
evaluation/ Infix to Postfix
conversion
1. Start with the SDT constructed, which places embedded actions before
each nonterminal to compute its inherited attributes and an action at
the end of the production to compute synthesized attributes.
(a) Copies, as inherited attributes of M, any attributes of A or symbols of that action a needs.
(b) Computes attributes in the same way as a, but makes those attributes be synthesized
attributes of M.
o This change appears illegal, since typically the action a' associated with production
M → ϵ will have to access attributes belonging to grammar symbols that do not
appear in this production.
o However, we shall implement the actions on the LR parsing stack, so the necessary
attributes will always be available a known number of positions down the stack.
For example:
A → M B C
A→ {B.i = f(A.i);} B C M → ϵ { M.i = A.i; M.s = f(M.i); }
Bottom-Up Parsing of L-Attributed SDD’s
Example: Consider a production A → B C in an LL grammar, and the inherited
attribute B.i is computed from inherited attribute A.i by some formula B.i = f(A.i).
That is, the fragment of an SDT is
A → {B.i = f(A.i);} B C
Note: The rule for M does not have A.i available to it, but in fact we shall arrange that every
inherited attribute for a nonterminal such as A appears on the stack immediately below where
the reduction to A will later take place.
When we reduce ϵ to M, we shall find A.i immediately below it, from where it may be read.
Also, the value of M.s, which is left on the stack along with M, is really B.i and properly is found
right below where the reduction to B will later occur.
Bottom-Up Parsing of L-Attributed SDD's
Example 1: Consider, SDT for while-statement
Let us turn this SDT(L-attributed) into an SDT that can operate with an LR parse.
o We introduce a marker nonterminal M before C and a marker nonterminal N before
S1 in place of each embedded action, so the underlying grammar becomes
Bottom-Up Parsing of L-Attributed SDD's
Example 1: Consider, SDT for while-statement
▪ Let us turn this SDT(L-attributed) into an SDT that can operate with an LR
parse.
o We introduce a marker nonterminal M before C and a marker nonterminal N before
S1 in place of each embedded action, so the underlying grammar becomes
▪ Let us turn this SDT(L-attributed) into an SDT that can operate with an LR
parse.
Bottom-Up Parsing of L-Attributed SDD's
Example 2: Consider, SDT for do-while statement
▪ Let us turn this SDT(L-attributed) into an SDT that can operate with an LR
parse.
▪ Let us turn this SDT(L-attributed) into an SDT that can operate with an LR
parse.
Bottom-Up Parsing of L-Attributed SDD's
Example 3: Consider, SDT for if-else statement
▪ Let us turn this SDT(L-attributed) into an SDT that can operate with an LR
parse.
2. Inherited attributes C.true and C.false will be just below the stack record for C.
The appearance of while on the input assures us that the while-production is the
only one that can be recognized, so we can be sure that M will appear
immediately below C on the stack, and M's record will hold the inherited attributes
of C.
3. Similarly, the inherited attribute S1.next must appear immediately below S1 on the
stack, so we may place that attribute in the record for N.
Bottom-Up Parsing of L-Attributed SDD's
4. The synthesized attribute C.code will appear in the record for C. As always
when we have a long string as an attribute value, we expect that in
practice a pointer to (an object representing) the string will appear in the
record, while the string itself is outside the stack.
5. Similarly, the synthesized attribute S1.code will appear in the record for S1.
Bottom-Up Parsing of L-Attributed SDD's
C -> B1 && B2
C -> !B1
Bottom-Up Parsing of L-Attributed SDD's
SDD to generate three-address intermediate code for Conditional Boolean expressions
C -> B1 && B2 L1 = new() Note: The function new generates new labels.
B1.true = L1 Where L1 is the label for the beginning of B .code
2
B1.false = C.false
B2.true = C.true
B2.false = C.false
C.code = B1.code||label||L1||B2.code
▪ We shift terminal while onto the stack, so the LR parser can shift "(" and determine
that its next step must be to reduce ϵ to M. The stack at this time is shown in Fig. 5.36.
Continuing with the recognition of the while-statement, the parser should next find ")"
on the input, which it pushes onto the stack in a record of its own.
At that point, the parser, will reduce ϵ to N. The single piece of data associated with
N is the inherited attribute S1.next.
o S1.next attribute needs to be in the record for N because that will be just below the
record for S1. The code that is executed to compute the value of S1.next is
Next, the parser reduces some prefix of the remaining input to S, which we have
consistently referred to as S1 to distinguish it from the S at the head of the production.
The value of S1.code is computed and appears in the stack record for S1. This step
takes us to the condition that is illustrated in Fig. 5.37.
Let us turn this SDT(L-attributed) into an SDT that can operate with an LR parse.
L1=new();
C.true=L1
C.false=Stack[top-3].next;
S1.next=stack[top-6].next
Top=top-6
S.code = tempcode
Bottom-Up Parsing of L-Attributed SDD's
Exercise 2:
Bottom-Up Parsing of L-Attributed SDD's
Exercise 2:
Bottom-Up Parsing of L-Attributed SDD's
Symbol table
Name Type
1 x
Exercise 2: Input: int x,y,z Token stream: <int> <id,1> <,> <id,2> <,> <id,3>
2 y
3 z
……
top
Shift id
Shift ,
Shift id
Bottom-Up Parsing of L-Attributed SDD's
Symbol table
Name Type
1 x int
Exercise 2: Input: int x,y,z Token stream: <int> <id,1> <,> <id,2> <,> <id,3>
2 y
3 z
……
Shift id
Shift ,
Shift id
Bottom-Up Parsing of L-Attributed SDD's
Symbol table
Name Type
1 x int
Exercise 2: Input: int x,y,z Token stream: <int> <id,1> <,> <id,2> <,> <id,3>
2 y int
3 z int
……
Reduce TML to D
Bottom-Up Parsing of L-Attributed SDD's
Exercise 3:
References
2. Check that each terminal appears on the input when it is required. We shall
assume that no backtracking is needed, but the extension to recursive
descent parsing with backtracking can be done by restoring the input
position upon failure, as discussed in Section 4.4.1.
L2=new();
S1.next=L1;
C.false=S.next;
C.true=L2;
S.code=label||L1||C.code||label||L2|| S1.code
Figure 5.27: SDD for while-statements
Example:
L-Attributed SDD's and LL Parsing
Example 5.23:
This example implements the SDT of Fig. 5.32, which generates code on the
fly for the while-production. This SDT does not have synthesized attributes,
except for dummy attributes that represent labels.
L-Attributed SDD's and LL Parsing
Example 5.23: (cont…)
Figure 5.33(a) shows the situation as we are about to use the while-production
to expand S, presumably because the lookahead symbol on the input is while.
The record at the top of stack is for S, and it contains only the inherited attribute
S.next, which we suppose has the value x.
Since we are now parsing top-down, we show the stack top at the left,
according to our usual convention.
The values of inherited attributes in the record for C have been filled in properly, as
have the temporaries al1 and al2 in the second action record.
At this point, C is expanded, and we presume that the code to implement its test
containing jumps to labels x and z, as appropriate, is generated.
L-Attributed SDD's and LL Parsing
Example 5.24:
Now, let us consider the same while-statement, but with a translation that
produces the output S.code as a synthesized attribute.
Every nonterminal that has code associated with it leaves that code, as
a string, in the synthesize-record just below it on the stack.
Figure 5.35(a) shows the situation just before S is expanded using the
production for while-statements.
At the top of the stack we see the record for S; it has a field for its inherited
attribute S.next. Immediately below that record is the synthesize-record for this
occurrence of S. The latter has a field for S.code, as all synthesize-records for S
must have.
We also show it with some other fields for local storage and actions, since the
SDT for the while production in Fig. 5.28 is surely part of a larger SDT.
L-Attributed SDD's and LL Parsing
Example 5.24: (cont…)
Let us examine what each record does when it becomes the top of stack.
By our inductive hypothesis, S1 nonterminal is expanded, and the net effect is that its code is
correctly constructed and placed in the field for code in the synthesize-record for S1.
Now, all the data fields of the synthesize-record for S1 have been filled in, so when it becomes the
top of stack, the action in that record can be executed.
The action causes the labels and code from C.code and S1.code to be concatenated in the
proper order. The resulting string is placed in the record below; that is, in the synthesize-record for S.
We have now correctly computed S.code, and when the synthesize-record for S becomes the top,
that code is available for placement in another record further down the stack, where it will
eventually be assembled into a larger string of code implementing a program element of which
this S is a part.
L-Attributed SDD's and LL Parsing
Exercise: Convert the following SDD to SDT scheme and provide its
Implementation during LL Parsing for input string num1*num2 (num1.lexval=4,
num2.lexval=5)