490 likes | 702 Views
Topic #5: Translations. EE 456 – Compiling Techniques Prof. Carl Sable Fall 2003. Syntax-Directed Translations. Translation of languages guided by CFGs Information associated with programming language constructs Attributes attached to grammar symbols
E N D
Topic #5: Translations EE 456 – Compiling Techniques Prof. Carl Sable Fall 2003
Syntax-Directed Translations • Translation of languages guided by CFGs • Information associated with programming language constructs • Attributes attached to grammar symbols • Values of attributes computed by “semantic rules” associated with grammar productions • Two notations for associating semantic rules • Syntax-directed definitions • Translation schemes
Semantic Rules • Semantic rules perform various activities: • Generation of code • Save information in a symbol table • Issue error messages • Other activities • Output of semantic rules is the translation of the token stream
Conceptual View • Implementations do not need to follow outline literally • Many “special cases” can be implemented in a single pass
Attributes • Each grammar symbol (node in parse tree) has attributes • Synthesized attributes based on children • Inherited attributes based on siblings and parent • A dependency graph represents dependencies between attributes • A parse tree showing the values of attributes at each node is an annotated parse tree
Semantic Rules • Each semantic rule for production A ->α has the form b := f(c1, c2, …, ck) • f is a function • b may be a synthesized attribute of A • b may be an inherited attribute of a grammar symbol on the right side of the production • c1, c2, …, ck are attributes belonging to grammar symbols of production • An attribute grammar is one in which the functions do not have side effects
S-attributed Definitions • Synthesized attributes are used extensively in practice • S-attributed definition: A syntax-directed definition using only synthesized attributes • Parse tree can be annotated by evaluation nodes during a single bottom up pass
Inherited Attributes • Inherited Attributes: • Value at a node in a parse tree depends on attributes of parent and/or siblings • Convenient for expressing dependencies of programming language constructs on context • It is always possible to avoid inherited attributes, but they are often convenient
Dependency Graphs • Dependency graph: • Depicts interdependencies among synthesized and inherited attributes • Includes dummy nodes for procedure calls • Numbered with a topological sort • If mi mj is an edge from mi to mj, then mi appears before mj in the ordering • Gives valid order to evaluate semantic rules
Creating a Dependency Graph for each node n in parse tree for each attribute a of grammar symbol at n construct a node in dependency graph for a for each node n in parse tree for each semantic rule b := f(c1, c2, …, ck) associated with production used at n for i := 1 to k construct edge from node for ci to node for b
Syntax Trees • (Abstract) Syntax Trees • Condensed form of parse tree • Useful for representing language constructs • Operators and keywords appear as internal nodes • Syntax-directed translation can be based on syntax trees as well as parse trees
Implementing Syntax Trees • Each node can be represented by a record with several fields • Example: node representing an operator used in an expression: • One field indicates the operator and others point to records for nodes representing operands • The operator is referred to as the “label” of the node • If being used for translation, records can have additional fields for attributes
Syntax Trees for Expressions • Functions will create nodes for the syntax tree • mknode (op, left, right) – creates an operator node with label op and pointers left and right which point to operand nodes • mkleaf(id, entry) – creates an identifier node with label id and a pointer to the appropriate symbol table entry • Mkleaf(num, val) – creates a number node with label num and value val • Each function returns pointer to created node
Example: a - 4 + c p1 := mkleaf(id, pa); P2 := mkleaf(num, 4); p3 := mknode('-', p1, p2); p4 := mkleaf(id, pc); p5 := mknode('+', p3, p4);
Directed Acyclic Graphs • Called a dag for short • Convenient for representing expressions • As with syntax trees: • Every subexpression will be represented by a node • Interior nodes represent operators, children represent operands • Unlike syntax trees, nodes may have more than one parent • Can be created automatically (discussed in textbook)
Bottom-Up Evaluation • Synthesized attributes can be evaluated with single bottom-up pass • Parser keeps values of synthesized attributes on stack • With each reduction, new synthesized attributes computed based on those at top of stack
Evaluating Attributes • Possible evaluation orders depend on order that nodes are created by parser • Depth-first search is very common evaluation order • L-attributed definitions use this technique • Information appears to flow left-to-right • Can handle all synthesized and some inherited attributes
Depth-First Evaluation procedure dfvisit(n: node); begin for each child m of n, from left to right begin evaluate inherited attributes of m dfvisit(m) end; evaluate synthesized attributes of n end
L-attributed Definitions • A syntax-directed definition is L-attributed: • If each inherited attribute of Xj, for production A X1X2…Xn(1 <= j <= n), depends on: • X1, X2, …, Xj-1 in the production • The inherited attributes of A • Any synthesized attribute is OK • All S-attributed definitions are, by this definition, L-attributed
Translation Schemes • Semantic actions are inserted within the right side of productions • Placement indicates order of evaluation • If we are dealing with both inherited and synthesized attributes: • Each inherited attribute must be computed by action before symbol appears on right side of production • No action may refer to a synthesized attribute of a symbol to the right of the action • Any synthesized attribute of nonterminal on left must be computed after computing all referenced attributes
Typesetting Example (2) S {B.ps := 10} B {S.ht := B.ht} B {B1.ps := B.ps} B1 {B2.ps := B.ps} B2 {B.ht := max(B1.ht, B2.ht)} B {B1.ps := B.ps} B1 sub {B2.ps := shrink(B.ps)} B2 {B.ht := disp(B1.ht, B2.ht)} B text {B.ht := text.h * B.ps}
Eliminating Left Recursion • Have seen simple and general solution for CFGs • Now we must take semantic actions and attributes into account as well • First we will examine synthesized attributes A A1 Y {A.a := g(A1.a, Y.y) A X {A.a := f(X.x)} A X {R.i := f(X.x)} R {A.a := R.s} R Y {R1.i := g(R.i, Y.y)} R1 {R.s := R1.s} | ε {R.s := R.i}
Evaluating Expressions Example 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 (E) {T.val := E.val} T num {T.val := num.val} E T {R.i := T.val} R {E.val := R.s} R + T {R1.i := R.i + T.val} R1 {R.s := R1.s} | - T {R1.i := R.i + T.val} R1 {R.s := R1.s} | ε {R.s := R.i} T (E) {T.val := E.val} T num {T.val := num.val}
Creating Syntax Tree Example E E1 + T {E.np := mknode('+', E1.np, T.np)} E E1 – T {E.np := mknode('-', E1.np, T.np)} E T {E.np := T.np} T (E) {T.np := E.np} T id {T.np := mkleaf(id, id.entry)} T num {T.np := mkleaf(num, value)} E T {R.i := T.np} R {E.np R.S} R + T {R1.i := mknode('+', R.i, T.np)}R1 {R.s := R1.s} R - T {R1.i := mknode(‘-', R.i, T.np)}R1 {R.s := R1.s} R ε {R.s := R.i} T (E) {T.np := E.np} T id {T.np := mkleaf(id, id.entry)} T num {T.np := mkleaf(num, value)}
Designing a Predictive Parser • LL(1) Grammars can be implemented using relatively simple top-down parsing techniques • For each nonterminal A, construct function: • Parameter for each inherited attribute • Returns synthesized attribute (or attributes) • Code decides which production to use based on next input symbol • Right side of production considered left to right: • For token X with synthesized attribute x, store X.x • For nonterminal B, generate c := B(b1,b2,…,bk) with call to function for B • Copy other actions into the parser
Syntax Tree Code Example function R(i:↑syntax_tree_node):↑syntax_tree_node; var np, i1, s1, s: ↑syntax_tree_node; begin if lookahead = '+' then begin /* Case for R + T R */ match('+'); np := T; i1 := mknode('+', i, np); s1 := R(il) s := s1; end else if lookahead = '-' then begin /* Case for R - T R */ … else s := i; /* Case for R ε */ return s end
Generalized Bottom-Up Evaluation • Can handle: • All synthesized attributes • All L-attributed definitions based on an LL(1) grammar • Can handle some L-attributed definitions based on LR(1) grammars • Relies on use of copy rules and markers
Copy Rules • Consider reduction: A X Y • Suppose X has synthesized attribute X.s • X.s will already be on stack before any reductions take place in subtree below Y • Therefore, this value can be inherited by Y • Define attribute Y.i using a copy rule: Y.i = X.s
Copy Rule Example (1) 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, L.in)} L id {addtype(id.entry, L.in)}
Limitation of Copy Rules • Reaching into stack for an attribute value only works if the position of the value is predictable • Here, C inherits synthesized attribute A.s • There may or may not be a B between A and C • C.i may be in either val[top-1] or val[top-2]
Markers • Marker nonterminals generating ε are inserted into the grammar • Each embedded action is replaced by a marker with the action attached • Actions in the transformed translation scheme terminate productions • Markers can often be used to move all actions to the right side of productions
Markers Example E T R R + T {print('+')} R | - T {print('-')} R | ε T num {print(num.val)} E T R R + T M R | - T N R | ε M ε {print('+')} N ε {print('+')} T num {print(num.val)}
Avoiding Inherited Attributes • It is sometimes possible to avoid inherited attributes by rewriting the underlying grammar • The goal is to replace inherited attributes with synthesized attributes D L : T T integer | real L L, id | id D id L L , id L | : T T integer | real