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

Compiler Design

Abstract syntax trees (ASTs) are a condensed representation of parse trees that are useful for representing language constructs. ASTs collapse chains of single productions and move operators to parent nodes. Each node in an AST can be represented as a record with fields for operators and operands. An AST for an expression can be constructed by making function calls that create leaf and node records. Bottom-up evaluation of syntax-directed definitions occurs while parsing by extending the parser stack to hold attribute values.
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
43 views

Compiler Design

Abstract syntax trees (ASTs) are a condensed representation of parse trees that are useful for representing language constructs. ASTs collapse chains of single productions and move operators to parent nodes. Each node in an AST can be represented as a record with fields for operators and operands. An AST for an expression can be constructed by making function calls that create leaf and node records. Bottom-up evaluation of syntax-directed definitions occurs while parsing by extending the parser stack to hold attribute values.
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 38

Abstract Syntax Tree

• Condensed form of parse tree,


• useful for representing language constructs.
• The production S → if B then s1 else s2
may appear as

if-then-else

B s1 s2

1
Abstract Syntax tree …
• Chain of single productions may be collapsed, and
operators move to the parent nodes

E +

E + T * id3

T F id1 id2

T * F id3

F id2

id1 2
Constructing Abstract Syntax Tree
for expression
• Each node can be represented as a
record
• operators: one field for operator,
remaining fields ptrs to operands
mknode(op,left,right )
• identifier: one field with label id and
another ptr to symbol table
mkleaf(id,entry)
• number: one field with label num and
another to keep the value of the number
mkleaf(num,val)
3
Example
the following
sequence of function
calls creates a parse P5

tree for a- 4 + c
+
P3 P4
P1 = mkleaf(id, entry.a)
- id
P2 = mkleaf(num, 4) P1
P2 entry of c
P3 = mknode(-, P1, P2)
id num 4
P4 = mkleaf(id, entry.c)
P5 = mknode(+, P3, P4) entry of a

4
A syntax directed definition for
constructing syntax tree
E → E1 + T E.ptr = mknode(+, E1.ptr, T.ptr)
E →T E.ptr = T.ptr
T → T1 * F T.ptr := mknode(*, T1.ptr, F.ptr)
T →F T.ptr := F.ptr
F → (E) F.ptr := E.ptr
F → id F.ptr := mkleaf(id, entry.id)
F → num F.ptr := mkleaf(num,val)

5
DAG for Expressions
Expression a + a * ( b – c ) + ( b - c ) * d
make a leaf or node if not present,
otherwise return pointer to the existing node
P1 = makeleaf(id,a) +
P13

P2 = makeleaf(id,a)
P7
P3 = makeleaf(id,b)
P4 = makeleaf(id,c) +
P5 = makenode(-,P3,P4)
P6 = makenode(*,P2,P5) P6 P12
P7 = makenode(+,P1,P6) * *
P8 = makeleaf(id,b) P1 P2
P5 P10 P11
P9 = makeleaf(id,c)
P10 = makenode(-,P8,P9) a - d
P11 = makeleaf(id,d) P3 P8
P4 P9
P12 = makenode(*,P10,P11)
b c
P13 = makenode(+,P7,P12)
6
Bottom-up evaluation of S-attributed
definitions
• Can be evaluated while parsing
• Whenever reduction is made, value of
new synthesized attribute is computed
from the attributes on the stack
• Extend stack to hold the values also
• The current top of stack is indicated by
top pointer
state value
top stack stack

7
Bottom-up evaluation of S-attributed
definitions
• Suppose semantic rule
A.a = f(X.x, Y.y, Z.z)
is associated with production
A → XYZ
• Before reducing XYZ to A, value of Z is in
val(top), value of Y is in val(top-1) and
value of X is in val(top-2)
• If symbol has no attribute then the
entry is undefined
• After the reduction, top is decremented
by 2 and state covering A is put in
val(top) 8
Example: desk calculator
LE$ Print (E.val)
EE+T E.val = E.val + T.val
ET E.val = T.val
TT*F T.val = T.val * F.val
TF T.val = F.val
F  (E) F.val = E.val
F  digit F.val = digit.lexval

10
Example: desk calculator
L → E$ print(val(top))
E→E+T val(ntop) = val(top-2) + val(top)
E→T
T→T*F val(ntop) = val(top-2) * val(top)
T→F
F → (E) val(ntop) = val(top-1)
F → digit

Before reduction ntop = top - r +1


After code reduction top = ntop
r is the #symbols on RHS
11
INPUT STATE Val PROD
3*5+4$
*5+4$ digit 3
*5+4$ F 3 F → digit
*5+4$ T 3 T→F
5+4$ T* 3□
+4$ T*digit 3□5
+4$ T*F 3□5 F → digit
+4$ T 15 T→T*F
+4$ E 15 E→ T
4$ E+ 15 □
$ E+digit 15 □ 4
$ E+F 15 □ 4 F → digit
$ E+T 15 □ 4 T→F
$ E 19 E → E +T
12
YACC Terminology
E→E+T val(ntop) = val(top-2) + val(top)

In YACC
E→E+T $$ = $1 + $3

$$ maps to val[top – r + 1]
$k maps to val[top – r + k]
r=#symbols on RHS ( here 3)
$$ = $1 is the default action in YACC
L-attributed definitions
• When translation takes place during
parsing, order of evaluation is linked to
the order in which nodes are created
• In S-attributed definitions parent’s
attribute evaluated after child’s.
• A natural order in both top-down and
bottom-up parsing is depth first-order
• L-attributed definition: where attributes
can be evaluated in depth-first order
14
L attributed definitions …
• A syntax directed definition is L-
attributed if each inherited attribute of
Xj (1 ≤ j ≤ n) at the right hand side of
A→X1 X2…Xn depends only on
– Attributes of symbols X1 X2…Xj-1 and
– Inherited attribute of A
• Examples (i inherited, s synthesized)
A → LM L.i = f1(A.i) A → QR R.i = f4(A.i)
M.i = f2(L.s) Q.i = f5(R.s)
A.s = f3(M.s) A.s = f6(Q.s)
15
Translation schemes
• A CFG where semantic actions occur
within the rhs of production

• Example: A translation scheme to map


infix to postfix
E→ T R
R→
R →addop
addopTT{print(addop)}
R|ε R|ε
T→ num {print(num)}
addop → + | –
Exercise: Create Parse Tree for 9 – 5 + 2
16
Parse tree for 9-5+2
E

T R

num print(num) addop T Print(addop) R


(9) (-)

num print(num) addop T print(addop) R


(5) (+)

num print(num) Є
(2)

17
Evaluation of Translation Schemes
• Assume actions are terminal symbols
• Perform depth first order traversal to
obtain 9 5 – 2 +

• When designing translation scheme,


ensure attribute value is available
when referred to
• In case of synthesized attribute it is
trivial (why ?)

18
• An inherited attribute for a symbol on RHS
of a production must be computed in an
action before that symbol S
S → A1 A2 {A1.in = 1,A2.in = 2}
A → a {print(A.in)} A1 A2 A1.in=1
A2.in=2

a print(A1.in) a print(A2.in)

depth first order traversal gives error (undef)


• A synthesized attribute for the non terminal
on the LHS can be computed after all
attributes it references, have been
computed. The action normally should be
placed at the end of RHS.
19
Bottom up evaluation of inherited
attributes
• Remove embedded actions from
translation scheme
• Make transformation so that
embedded actions occur only at the
ends of their productions
• Replace each action by a distinct
marker non terminal M and attach
action at end of M → ε

27
ETR
R  + T {print (+)} R
R  - T {print (-)} R
RЄ
T  num {print(num.val)}
transforms to
E→TR
R→+TMR
R→-TNR
R→Є
T → num {print(num.val)}
M→Є {print(+)}
N→Є {print(-)}
28
Inheriting attribute on parser stacks
• bottom up parser reduces rhs of A →
XY by removing XY from stack and
putting A on the stack
• synthesized attributes of Xs can be
inherited by Y by using the copy rule
Y.i=X.s

29
Inherited Attributes: SDD
DTL L.in = T.type
T  real T.type = real
T int T.type = int
L  L1, id L1.in = L.in;
addtype(id.entry, L.in)
L  id addtype (id.entry,L.in)
Exercise: Convert to Translation Scheme
30
Inherited Attributes: Translation
Scheme
D  T {L.in = T.type} L
T  int {T.type = integer}
T real {T.type = real}
L → {L1.in =L.in} L1,id {addtype(id.entry,Lin)}
L → id {addtype(id.entry,Lin)}
Example: take string real p,q,r
31
State stack INPUT PRODUCTION
real p,q,r
real p,q,r
T p,q,r T → real
Tp ,q,r
TL ,q,r L → id
TL, q,r
TL,q ,r
TL ,r L → L,id
TL, r
TL,r -
TL - L → L,id
D - D →TL
Every time a string is reduced to L, T.val is
just below it on the stack 32
Example …
• Every time a reduction to L is made value of T
type is just below it
• Use the fact that T.val (type information) is at a
known place in the stack
• When production L  id is applied, id.entry is at
the top of the stack and T.type is just below it,
therefore,
addtype(id.entry,L.in) 
addtype(val[top], val[top-1])
• Similarly when production L  L1 , id is applied
id.entry is at the top of the stack and T.type is
three places below it, therefore,
addtype(id.entry, L.in) 
addtype(val[top],val[top-3])
33
Example …
Therefore, the translation scheme becomes

DTL
T  int val[top] =integer
T  real val[top] =real

L  L,id addtype(val[top], val[top-3])


L  id addtype(val[top], val[top-1])

34
Simulating the evaluation of
inherited attributes
• The scheme works only if grammar allows
position of attribute to be predicted.
• Consider the grammar
S  aAC Ci = As
S  bABC Ci = As
Cc Cs = g(Ci)
• C inherits As
• there may or may not be a B between A
and C on the stack when reduction by rule
Cc takes place
• When reduction by C  c is performed the
value of Ci is either in [top-1] or [top-2]
35
Simulating the evaluation …
• Insert a marker M just before C in the
second rule and change rules to
S  aAC Ci = As
S  bABMC Mi = As; Ci = Ms
Cc Cs = g(Ci)
Mε Ms = Mi
• When production M  ε is applied we have
Ms = Mi = As
• Therefore value of Ci is always at val[top-1]

36
Simulating the evaluation …
• Markers can also be used to simulate
rules that are not copy rules

S  aAC Ci = f(A.s)

• using a marker

S  aANC Ni= As; Ci = Ns


Nε Ns = f(Ni)
37
General algorithm
• Algorithm: Bottom up parsing and translation with
inherited attributes
• Input: L attributed definitions
• Output: A bottom up parser
• Assume every non terminal has one inherited attribute
and every grammar symbol has a synthesized attribute
• For every production A  X1… Xn introduce n markers
M1….Mn and replace the production by
A  M1 X1 ….. Mn Xn
M1 … Mn  Є
• Synthesized attribute Xj,s goes into the value entry of Xj
• Inherited attribute Xj,i goes into the value entry of Mj

38
Algorithm …
• If the reduction is to a marker Mj and
the marker belongs to a production
A  M1 X1… MnXn then
Ai is in position top-2j+2
X1.i is in position top-2j+3
X1.s is in position top-2j+4
• If reduction is to a non terminal A by
production A  M1 X1… MnXn
then compute As and push on the
stack
39
Space for attributes at compile
time
• Lifetime of an attribute begins when it is first
computed

• Lifetime of an attribute ends when all the


attributes depending on it, have been computed

• Space can be conserved by assigning space for an


attribute only during its lifetime

40
Example
• Consider following definition

D T L L.in := T.type
T  real T.type := real
T  int T.type := int
L  L1,I L1.in :=L.in; I.in=L.in
LI I.in = L.in
I  I1[num] I1.in=array(numeral, I.in)
I  id addtype(id.entry,I.in)

41
Consider string int x[3], y[5]
its parse tree and dependence graph
D

T1 2 L

int 6 L , 3I

7 I 5I [ num ]
4 5
I [ num ] id
9 83 y
id
x 42
Resource requirement

1 2 3 4 5 6 7 8 9

Allocate resources using life time information

R1 R1 R2 R3 R2 R1 R1 R2 R1

Allocate resources using life time and copy information

R1 =R1 =R1 R2 R2 =R1 =R1 R2 R1


43
Space for attributes at compiler
Construction time
• Attributes can be held on a single stack. However, lot of
attributes are copies of other attributes

• For a rule like A B C stack grows up to a height of five


(assuming each symbol has one inherited and one
synthesized attribute)

• Just before reduction by the rule A B C the stack


contains I(A) I(B) S(B) I (C) S(C)

• After reduction the stack contains I(A) S(A)


44
Example
• Consider rule B B1 B2 with inherited attribute ps and
synthesized attribute ht

• The parse tree for this string and a snapshot of the stack at
each node appears as

B.ps B B.ht
B.ps
B2.ht
B2.ps
B1.ht
B1.ps
B1.ps B1.ht B2.ps B.ps
B.ps B1.ps B1.ht
B1 B.ps B1.ps B2
B.ps
45
Example …
• However, if different stacks are maintained for the
inherited and synthesized attributes, the stacks will
normally be smaller

B.ps B B.ps B.ht

B2.ht
B.ps B1.ht
B.ps
B1 B.ps B1.ht B.ps B1.ht B2
46

You might also like