DOC-20250419-WA0025.
DOC-20250419-WA0025.
postorder(N) {
for (each child C of N, from the left) postorder(C);
evaluate the attributes associated with node N;
}
⚫ S-Attributed definitions can be implemented during
bottom-up parsing without the need to explicitly create
parse trees
L-Attributed definitions
⚫ A SDD is L-Attributed if the edges in dependency graph
goes from Left to Right but not from Right to Left.
⚫ More precisely, each attribute must be either
⚫ Synthesized
⚫ Inherited, but if there us a production A->X1X2…Xn and there
is an inherited attribute Xi.a computed by a rule associated
with this production, then the rule may only use:
⚫ Inherited attributes associated with the head A
⚫ Either inherited or synthesized attributes associated with the
occurrences of symbols X1,X2,…,Xi-1 located to the left of Xi
⚫ Inherited or synthesized attributes associated with this occurrence of
Xi itself, but in such a way that there is no cycle in the graph
Application of Syntax Directed
Translation
⚫ Type checking and intermediate code generation
(chapter 6)
⚫ Construction of syntax trees
⚫ Leaf nodes: Leaf(op,val)
⚫ Interior node: Node(op,c1,c2,…,ck)
⚫ Example:
Production Semantic Rules
1) E -> E1 + T E.node=new node(‘+’, E1.node,T.node)
2) E -> E1 - T E.node=new node(‘-’, E1.node,T.node)
3) E -> T E.node = T.node
4) T -> (E) T.node = E.node
5) T -> id T.node = new Leaf(id,id.entry)
6) T -> num T.node = new Leaf(num,num.val)
Syntax tree for L-attributed
definition
Production Semantic Rules
1) E -> TE’ E.node=E’.syn +
E’.inh=T.node
2) E’ -> + TE1’ E1’.inh=new node(‘+’, E’.inh,T.node)
E’.syn=E1’.syn
3) E’ -> -TE1’ E1’.inh=new node(‘+’, E’.inh,T.node)
E’.syn=E1’.syn
4) E’ -> ∈ E’.syn = E’.inh
1) L -> E n {print(E.val);}
2) E -> E1 + T {E.val=E1.val+T.val;}
3) E -> T {E.val = T.val;}
4) T -> T1 * F {T.val=T1.val*F.val;}
5) T -> F {T.val=F.val;}
6) F -> (E) {F.val=E.val;}
7) F -> digit {F.val=digit.lexval;}
Parse-Stack implementation of
postfix SDT’s
⚫ In a shift-reduce parser we can easily implement
semantic action using the parser stack
⚫ For each nonterminal (or state) on the stack we can
associate a record holding its attributes
⚫ Then in a reduction step we can execute the semantic
action at the end of a production to evaluate the
attribute(s) of the non-terminal at the leftside of the
production
⚫ And put the value on the stack in replace of the
rightside of production
Example
L -> E n {print(stack[top-1].val);
top=top-1;}
E -> E1 + T {stack[top-2].val=stack[top-2].val+stack.val;
top=top-2;}
E -> T
T -> T1 * F {stack[top-2].val=stack[top-2].val+stack.val;
top=top-2;}
T -> F
F -> (E) {stack[top-2].val=stack[top-1].val
top=top-2;}
F -> digit
SDT’s with actions inside
productions
⚫ For a production B->X {a} Y
⚫ If the parse is bottom-up then we
perform action “a” as soon as this
occurrence of X appears on the
top of the parser stack
⚫ If the parser is top down we
perform “a” just before we expand
Y 1) L -> E n
2) E -> {print(‘+’);} E1 + T
⚫ Sometimes we cant do things as 3) E -> T
easily as explained above 4) T -> {print(‘*’);} T1 * F