0% found this document useful (0 votes)
8 views

05+Syntax-Directed+Translation

The document discusses semantic analysis in compiler construction, focusing on syntax-directed translation and the evaluation of attributes associated with grammar symbols. It explains the concepts of synthesized and inherited attributes, dependency graphs, and various definitions such as S-attributed and L-attributed definitions. Additionally, it covers translation schemes, their requirements, and methods for eliminating left recursion in translation schemes.

Uploaded by

hidmid6
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)
8 views

05+Syntax-Directed+Translation

The document discusses semantic analysis in compiler construction, focusing on syntax-directed translation and the evaluation of attributes associated with grammar symbols. It explains the concepts of synthesized and inherited attributes, dependency graphs, and various definitions such as S-attributed and L-attributed definitions. Additionally, it covers translation schemes, their requirements, and methods for eliminating left recursion in translation schemes.

Uploaded by

hidmid6
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/ 32

Compiler Construction Semantic Analysis

Syntax Directed Translation


Reading: Section 5.1 – 5.6 of chapter 5

To translate a programming language construct, compiler


needs to keep track of many quantities to the grammar
symbol.

By Bishnu Gautam 1
Compiler Construction Semantic Analysis

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, data types,
intermediate representation etc.
By Bishnu Gautam 2
Compiler Construction Semantic Analysis

Syntax Directed Translation


When we associate semantic rules with productions, we use two
notations:
– Syntax-Directed Definitions
– Translation Schemes
Syntax-Directed Definitions are high level specifications for
translations. They hide many implementation details and free the user
from having to explicitly specify the order in which translation takes
place.
Translation Schemes indicates the order in which semantic rules are
to be evaluated (using a dependency graph), so they allow some
implementation details to be shown.
By Bishnu Gautam 3
Compiler Construction Semantic Analysis

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 synthesized and inherited attributes of that grammar
Semantic rules set up dependencies between attributes which can be
represented by a dependency graph.
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.

By Bishnu Gautam 4
Compiler Construction Semantic Analysis

Syntax-Directed Definitions
Given a production
A→α
then each semantic rule is of the form
b = f(c1,c2,…,ck)
where f is a function and ci are attributes of A and
α, and either
– b is a synthesized attribute of A
– b is an inherited attribute of one of the grammar
symbols in α

By Bishnu Gautam 5
Compiler Construction Semantic Analysis

Syntax-Directed Definition
Example
Production Semantic Rules
L → E return print(E.val)
E → E1 + T E.val = E1.val + T.val
E→T E.val = T.val Note: all attributes in
this example are of
T → T1 * F T.val = T1.val * F.val the synthesized type
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.


The token digit has a synthesized attribute lexval (it is assumed that it is
evaluated by the lexical analyzer).
By Bishnu Gautam 6
Compiler Construction Semantic Analysis

Annotated Parse Tree


Example
Output: The value, printed at the root of
L tree, is the value of E.val at the first child
Input: 5+3*4 of the root

Computation start from


E.val=17 return leaf node with
associated production
and corresponding
E.val=5 + T.val=12 semantic rule

T.val=5 T.val=3 * F.val=4

F.val=5 F.val=3 digit.lexval=4

digit.lexval=5 digit.lexval=3
By Bishnu Gautam 7
Compiler Construction Semantic Analysis

Inherited Attributes
An inherited attribute at any node is defined based on the attributes at
the parent and/or siblings of the node.

Useful for describing context sensitive behavior of grammar symbols.

For example, an inherited attribute can be used to keep track of


whether an identifier appears at the left or right side of an assignment
operator. This can be used to decide whether the address or the value
of the identifier needed.

By Bishnu Gautam 8
Compiler Construction Semantic Analysis

Inherited Attributes
Example

Production Semantic Rules inherited


D→TL L.in = T.type
T → int T.type = integer
T → real T.type = real synthesized
L → L1 id L1.in = L.in, addtype(id.entry,L.in)
L → id addtype(id.entry,L.in)

By Bishnu Gautam 9
Compiler Construction Semantic Analysis

Inherited Attributes
Parse Tree
The value of L.in at the three L-nodes
gives the type of identifiers id1, id2 &
Input : real id1 , id2 , id3
id3. These values are determine by
computing the value of attribute T.type
at the left child of the root and then
D evaluating L.in in top-down at the three
L-nodes in the right sub tree of the root
T.type = ‘real’ L.in = ‘real’

real L.in = ‘real’ , id3.entry

L.in = ‘real’ , id2.entry

id1.entry

By Bishnu Gautam 10
Compiler Construction Semantic Analysis

Dependency Graph
In order to correctly evaluate attributes of syntax tree nodes, a
dependency graph is useful. A dependency graph is a directed graph
that contains attributes as nodes and dependencies across attributes as
edges.
Algorithm for dependency graph construction
for each node n in the parse tree do
for each attribute a of the grammar symbol at n do
Construct a node in the dependency graph for a
for each node n in the parse tree do
for each semantic rule b = f(c1, c2, …, ck) associated with the
production used at n do
for i = 1 to k do
construct an edge from ci to the node for b

By Bishnu Gautam 11
Compiler Construction Semantic Analysis

Dependency Graph
Grammar in page 9 For Parse Tree of Page 10

Input : real id1 , id2 , id3 D

T.type = ‘real’ L.in = ‘real’

real L.in = ‘real’ , id3.entry

L.in = ‘real’ , id2.entry

id1.entry

By Bishnu Gautam 12
Compiler Construction Semantic Analysis

S-Attributed Definitions

A syntax-directed definition that uses synthesized attributes


exclusively is called an S-attributed definition (or S-
attributed grammar)
A parse tree of an S-attributed definition is annotated by
evaluating the semantic rules for the attribute at each node
bottom-up
Yacc/Bison only support S-attributed definitions

By Bishnu Gautam 13
Compiler Construction Semantic Analysis

Bison Example
%token DIGIT
%%
L : E ‘\n’ { printf(“%d\n”, $1); }
;
E : E ‘+’ T { $$ = $1 + $3; }
| T { $$ = $1; }
;
T : T ‘*’ F { $$ = $1 * $3; }
| F { $$ = $1; }
;
F : ‘(’ E ‘)’ { $$ = $2; }
| DIGIT { $$ = $1; } Synthesized attribute
; of parent node F
%%
By Bishnu Gautam 14
Compiler Construction Semantic Analysis

Bottom-up Evaluation of S-Attributed


Definitions
An S-attributed grammar can be translated bottom-up as and when the
grammar is being parsed using an LR parser.
The LR parser’s stack can be extended to hold not only a grammar
symbol, but also its semantic attribute as well.
Hence action[s,a] = shift s’ becomes action[s,a] = shift a.$$, s’

A → XYZ A.a=f(X.x,Y.y,Z.z) where all attributes are synthesized.


State Val
When an entry of the parser stack holds a grammar
top → Z Z.z symbol X (terminal or non-terminal), the
Y Y.y corresponding entry will hold the synthesized
Î
attribute(s) of the symbol X.
X X.x top → A A.a
. . . . the values of the attributes are evaluated during
reductions.
By Bishnu Gautam 15
Bottom-up Evaluation of S-Attributed
Compiler Construction Semantic Analysis

Definitions in Yacc
Stack val Input Action Semantic Rule
$ _ 3*5+4n$ shift
$3 3 *5+4n$ reduce F → digit $$ = $1
Input: 3*5+4n $ F 3 *5+4n$ reduce T → F $$ = $1
$T 3 *5+4n$ shift
$T* 3_ 5+4n$ shift
Grammar $T*5 3_5 +4n$ reduce F → digit $$ = $1
in Page 14 $T*F 3_5 +4n$ reduce T → T * F $$ = $1 * $3
$T 15 +4n$ reduce E → T $$ = $1
$E 15 +4n$ shift
$E+ 15 _ 4n$ shift
$E+4 15 _ 4 n$ reduce F → digit $$ = $1
$E+F 15 _ 4 n$ reduce T → F $$ = $1
$E+T 15 _ 4 n$ reduce E → E + T $$ = $1 + $3
$E 19 n$ shift
$En 19 _ $ reduce L → E n print $1
$L 19 $ accept
By Bishnu Gautam 16
Compiler Construction Semantic Analysis

L-Attributed Definitions
An L-attribute is an inherited attribute which can be evaluated in a left-to-
right fashion. L-attributed definitions can be evaluated using a depth-first
evaluation order. ( L –> attributes flow from left to right)
More precisely, a syntax-directed definition is L-attributed if each inherited
attribute of Xj on the right side of A → X1 X2 … Xn depends only on
1. the attributes of the symbols X1, X2, …, Xj-1 to the left of Xj in the
production and
2. the inherited attributes of A

Every S-attributed definition is L-attributed, the restrictions only apply to


the inherited attributes (not to synthesized attributes).

By Bishnu Gautam 17
Compiler Construction Semantic Analysis

Evaluation of L-Attributed Definitions


L-attributed definitions allow for a natural order of evaluating
attributes: depth-first and left to right

Procedure dfvisit(n: node); //depth-first evaluation


for each child m of n, from left to right do
evaluate inherited attributes of m;
dfvisit(m);
evaluate synthesized attributes of n

A→XY X.i:=A.i A A.s:=Y.s X.i := A.i


Y.i := X.s
X Y A.s := Y.s
Y.i:=X.s

By Bishnu Gautam 18
Compiler Construction Semantic Analysis

Translation Schemes
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
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.
By Bishnu Gautam 19
Compiler Construction Semantic Analysis

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

By Bishnu Gautam 20
Compiler Construction Semantic Analysis

A Translation Scheme
Example
E

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 order) will produce the postfix representation of the infix
expression.
By Bishnu Gautam 21
Compiler Construction Semantic Analysis

Translation Schemes Requirements


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 syntax-directed
translation).
By Bishnu Gautam 22
Compiler Construction Semantic Analysis

Top-Down Translation

L-attributed definitions can be evaluated in a top-down fashion


(predictive parsing) with translation schemes. The algorithm for
elimination of left recursion is extended to evaluate action and
attribute.

Attributes in L-attributed definitions implemented in translation


schemes are passed as arguments to procedures (synthesized) or
returned (inherited)

By Bishnu Gautam 23
Compiler Construction Semantic Analysis

Eliminating Left Recursion from a


Translation Scheme
A → A1 Y { A.a = g(A1.a,Y.y) } a left recursive grammar with
synthesized attributes (a,y,x).
A → X { A.a=f(X.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 } When we eliminate the


left recursion from the
R → Y { R1.in=g(R.in,Y.y) } R1 { R.syn = R1.syn} grammar (to get a
suitable grammar for
R → ε { R.syn = R.in } the top-down parsing)
we also have to change
semantic actions
By Bishnu Gautam 24
Eliminating Left Recursion from a
Compiler Construction Semantic Analysis

Translation Scheme
Evaluating attributes
Evaluation of string XYY
A.a = g(g(f(X.x), Y1.y), Y2.y)

A.a = g(f(X.x), Y1.y) Y2

A.a = f(X.x) Y1

The values are computed


X according to a left
recursive grammar
By Bishnu Gautam 25
Eliminating Left Recursion from a
Compiler Construction Semantic Analysis

Translation Scheme
Evaluating attributes
A
Evaluation of string XYY

X R1.i = f(X.x)

Y1 R2.i = g(f(X.x), Y1.y)

Y2 R3.i = g(g(f(X.x), Y1.y), Y2.y)


Flow of inherited
attribute values ε
By Bishnu Gautam 26
Eliminating Left Recursion from a
Compiler Construction Semantic Analysis

Translation Scheme
Evaluating attributes
Evaluation of string XYY
A.s = R1.s = g(g(f(X.x), Y1.y), Y2.y)

X R1.s = R2.s = g(g(f(X.x), Y1.y), Y2.y)

Y1 R2.s = R3.s = g(g(f(X.x), Y1.y), Y2.y)

Y2 R3.s = R3.i = g(g(f(X.x), Y1.y), Y2.y)


Flow of synthesized
attribute values ε
By Bishnu Gautam 27
Compiler Construction Semantic Analysis

Bottom-Up Evaluation of Inherited Attributes


While evaluating S-attributed definitions during bottom-up parsing
is straight forward, evaluating inherited attributes is tricky. L-
attributed definitions are a simple subclass of inherited attributes
that can be effectively implemented in most bottom-up parsers.

The method used in Bottom-up Evaluation of S-Attributed


Definitions is extended with conditions:
• All embedding semantic actions in translation scheme should be in the
end of the production rules.
• All inherited attributes should be copied into the synthesized attributes
(most of the time synthesized attributes of new non-terminals).
• Evaluate all semantic actions during reductions

By Bishnu Gautam 28
Compiler Construction Semantic Analysis

Bottom-Up Evaluation of Inherited Attributes


Removing Embedding Semantic Actions
Insert marker nonterminals to remove embedded actions from
translation schemes, that is A → X { actions } Y is rewritten with
marker nonterminal N into
A→XNY
N → ε { actions }
Problem: inserting a marker nonterminal may introduce a conflict in the
parse table

E→TR E→TR
R → + T { print(“+”) } R1 R → + T M R1
R→ε R→ε
T → id { print(id.name) } T → id { print(id.name) }
M → ε { print(“+”) }
By Bishnu Gautam 29
Compiler Construction Semantic Analysis

Bottom-Up Evaluation of Inherited Attributes


Consider type declarations of variables in C: float a,b;
Given a string of the form float id,id, a
bottom-up evaluation can be traced as
Grammar. follows:
E → T {L.type = T.val} L Input Stack
L → L, id {settype(id.entry, L.type)} float id, id$ $
L → id {settype(id.entry, L.type)} id, id$ $float
T → int{T.val = integer}| id, id$ $T {T.val = float}
, id$ $T id
float{T.val = float}
, id$ $T L {L:type = float}
id$ $T L,
$ $T L, id
$ $T L {L.type = T.val}
$ $E

With L-attributed definitions, the required value is always below in the stack.
By Bishnu Gautam 30
Compiler Construction Semantic Analysis

Exercise
1. For the following grammar:
E → E + E | E * E | (E) | id
Annotate the grammar with syntax directed definitions using
synthesized attributes. Remove left recursion from the grammar
and rewrite the attributes correspondingly.

2. For the expression 2 * (3 + 5), construct an annotated parse tree


using the modified grammar (that has no left recursion) from the
previous question. Trace the path in which semantic attributes are
evaluated.

3. Questions 5.1, 5.2, 5.4, 5.6, 5.8, 5.11, 5.12 & 5.14

By Bishnu Gautam 31
Compiler Construction Semantic Analysis

Assignments
1. Write a bison program that can evaluate prefix expressions with
arbitrary arity. This means, an expression is an operator followed
by any number of expressions (not just two).

2. Write a bison program that converts a prefix expression into its


corresponding infix expression.

3. In an infix expression of the form a = b + c, the type of a is int only


if both b and c are of type int. Even if either b or c is float, then the
type of a should be set to float. Write a grammar with syntax
directed definitions to implement the above requirements.

By Bishnu Gautam 32

You might also like