CS308 Compiler Principles Syntax-Directed Translation Fan Wu Department of Computer Science and Engineering Shanghai Jiao Tong University Fall 2012.

Slides:



Advertisements
Similar presentations
Chapter 2-2 A Simple One-Pass Compiler
Advertisements

SYNTAX DIRECTED TRANSLATION 11CS Types of Attributes There are two types of attributes for non- terminals :- Synthesized Attributes : For a non-terminal.
Chapter 5 Syntax-Directed Translation. Translation of languages guided by context-free grammars. Attach attributes to the grammar symbols. Values of the.
Chapter 5 Syntax Directed Translation. Outline Syntax Directed Definitions Evaluation Orders of SDD’s Applications of Syntax Directed Translation Syntax.
1 Error detection in LR parsing Errors are discovered when a slot in the action table is blank. Canonical LR(1) parsers detect and report the error as.
1 Beyond syntax analysis An identifier named x has been recognized. Is x a scalar, array or function? How big is x? If x is a function, how many and what.
9/27/2006Prof. Hilfinger, Lecture 141 Syntax-Directed Translation Lecture 14 (adapted from slides by R. Bodik)
1 Pertemuan 15, 16, 17 Syntax Directed Translation Matakuliah: T0174 / Teknik Kompilasi Tahun: 2005 Versi: 1/6.
Abstract Syntax Tree (AST)
Syntax Directed Translation
CH4.1 CSE244 Syntax Directed Translation Aggelos Kiayias Computer Science & Engineering Department The University of Connecticut 371 Fairfield Road, Unit.
Yu-Chen Kuo1 Chapter 2 A Simple One-Pass Compiler.
Syntax-Directed Translation
Chapter 2 Chang Chi-Chung rev.1. A Simple Syntax-Directed Translator This chapter contains introductory material to Chapters 3 to 8  To create.
BİL 744 Derleyici Gerçekleştirimi (Compiler Design)1 Syntax-Directed Translation Grammar symbols are associated with attributes to associate information.
2.2 A Simple Syntax-Directed Translator Syntax-Directed Translation 2.4 Parsing 2.5 A Translator for Simple Expressions 2.6 Lexical Analysis.
Chapter 5 Syntax-Directed Translation Section 0 Approaches to implement Syntax-Directed Translation 1、Basic idea Guided by context-free grammar (Translating.
Syntax Directed Definitions Synthesized Attributes
Topic #5: Translations EE 456 – Compiling Techniques Prof. Carl Sable Fall 2003.
Syntax-Directed Translation
1 Semantic Analysis Aaron Bloomfield CS 415 Fall 2005.
Topic: Syntax Directed Translations
COP4020 Programming Languages Semantics Prof. Xin Yuan.
Lesson 11 CDT301 – Compiler Theory, Spring 2011 Teacher: Linus Källberg.
Syntax Directed Translation. Tokens Parser Semantic checking TAC Peephole, pipeline, …… TAC  assembly code/mc Cmm subexpression,……
Overview of Previous Lesson(s) Over View  An ambiguous grammar which fails to be LR and thus is not in any of the classes of grammars i.e SLR, LALR.
# 1 CMPS 450 Syntax-Directed Translations CMPS 450 J. Moloney.
Chapter 5: Syntax directed translation –Use the grammar to direct the translation The grammar defines the syntax of the input language. Attributes are.
Scribe Sumbission Date: 28 th October, 2013 By M. Sudeep Kumar.
Chapter 5. Syntax-Directed Translation. 2 Fig Syntax-directed definition of a simple desk calculator ProductionSemantic Rules L  E n print ( E.val.
Review: Syntax directed translation. –Translation is done according to the parse tree. Each production (when used in the parsing) is a sub- structure of.
Overview of Previous Lesson(s) Over View  In syntax-directed translation 1 st we construct a parse tree or a syntax tree then compute the values of.
1 Syntax-Directed Translation Part I Chapter 5 COP5621 Compiler Construction Copyright Robert van Engelen, Florida State University, 2007.
1 Syntax-Directed Translation We associate information with the programming language constructs by attaching attributes to grammar symbols. 2.Values.
國立台灣大學 資訊工程學系 薛智文 98 Spring Syntax-Directed Translation (textbook ch#5.1–5.6, 4.8, 4.9 )
Syntax Directed Definition and Syntax directed Translation
UNIT – 5 SYNTAX-DIRECTED TRANSLATION
1 A Simple Syntax-Directed Translator CS308 Compiler Theory.
Overview of Previous Lesson(s) Over View 3 Model of a Compiler Front End.
Chapter 8: Semantic Analyzer1 Compiler Designs and Constructions Chapter 8: Semantic Analyzer Objectives: Syntax-Directed Translation Type Checking Dr.
Syntax-Directed Translation
SDTs used to implement SDDs A non-cyclic SDD (having definitions of attributes) can always be implemented by a SDT (having actions that assign values to.
CS416 Compiler Design1. 2 Course Information Instructor : Dr. Ilyas Cicekli –Office: EA504, –Phone: , – Course Web.
Semantic Analysis Attribute Grammar. Semantic Analysis  Beyond context free grammar  Is x declared before it is used?  Is x declared but never used?
CSE 420 Lecture Program is lexically well-formed: ▫Identifiers have valid names. ▫Strings are properly terminated. ▫No stray characters. Program.
LECTURE 10 Semantic Analysis. REVIEW So far, we’ve covered the following: Compilation methods: compilation vs. interpretation. The overall compilation.
Chapter4 Syntax-Directed Translation Introduction : 1.In the lexical analysis step, each token has its attribute , e.g., the attribute of an id is a pointer.
CS 404Ahmed Ezzat 1 CS 404 Introduction to Compiler Design Lecture 9 Ahmed Ezzat Semantic Analysis.
Lecture 9 Symbol Table and Attributed Grammars
Syntax-Directed Translation
Semantic analysis Jakub Yaghob
Semantics Analysis.
Syntax-Directed Translation
A Simple Syntax-Directed Translator
Compiler Construction
Chapter 5 Syntax Directed Translation
Syntax-Directed Translation Part I
CS 3304 Comparative Languages
Syntax-Directed Translation
Chapter 5. Syntax-Directed Translation
Syntax-Directed Translation Part I
Syntax-Directed Translation Part I
Syntax-Directed Definition
SYNTAX DIRECTED TRANSLATION
Compilers Principles, Techniques, & Tools Taught by Jing Zhang
Syntax-Directed Translation Part I
SYNTAX DIRECTED DEFINITION
Syntax-Directed Translation Part I
Chapter 5 Syntax Directed Translation
Presentation transcript:

CS308 Compiler Principles Syntax-Directed Translation Fan Wu Department of Computer Science and Engineering Shanghai Jiao Tong University Fall 2012

Compiler Principles Phases of Compilation Lexical Analyzer Syntax Analyzer Semantic Analyzer Intermediate Code Generator Code Optimizer Code Generator Source Language Target Language Intermediate Language Analysis SynthesisSymbol Table

Compiler Principles A Model of A Compiler Font End Lexical analyzer reads the source program character by character and returns the tokens of the source program. Parser creates the tree-like syntactic structure of the given program. Intermediate-code generator translates the syntax tree into three- address codes.

Compiler Principles Syntax-Directed Translation Associate semantic meanings with the grammar. –generate intermediate codes –put information into the symbol table –perform type checking –issue error messages –perform some other activities –in fact, they may perform almost any activities.

Compiler Principles Syntax-Directed Translation Cont’d Syntax-Directed Definitions: –associate a production rule with a set of semantic rules –give high-level specifications for translations –hide many implementation details such as order of evaluation of semantic rules Translation Schemes: –embed program fragments within production bodies –indicate the order of evaluation of semantic actions associated with a production

Compiler Principles Syntax-Directed Definition (SDD) A syntax-directed definition is a generalization of a context-free grammar: –Each grammar symbol is associated with a set of attributes. –Each production is associated with a set of semantic rules. Attributes are divided into two kinds: –Synthesized attribute is defined only in terms of attribute values at the node’s children and itself. –Inherited attribute is defined in terms of attribute values the node’s parent, itself, and siblings.

Compiler Principles SDD Cont’d In a syntax-directed definition, each production A→α is associated with a set of semantic rules of the form: b=f(c 1,c 2,…,c n )where f is a function,  b is a synthesized attribute of A and c 1,c 2,…,c n are attributes of the grammar symbols in the production ( A→α ).  b is an inherited attribute of one of the grammar symbols in α, and c 1,c 2,…,c n are attributes of the grammar symbols in the production ( A→α ).

Compiler Principles Attribute Grammar A semantic rule b=f(c 1,c 2,…,c n ) indicates that the attribute b depends on attributes c 1,c 2,…,c n. In a syntax-directed definition, a semantic rule may not only evaluate the value of an attribute, but also have some side effects such as printing values. An attribute grammar is a syntax-directed definition without side effects.

Compiler Principles SDD Example1 Production Semantic Rules L → E returnprint(E.val) E → E 1 + TE.val = E 1.val + T.val E → TE.val = T.val T → T 1 * FT.val = T 1.val * F.val T → FT.val = F.val F → ( E )F.val = E.val F → digitF.val = digit.lexval Symbols E, T, and F are associated with a synthesized attribute val. The token digit has a synthesized attribute lexval (an integer value returned by the lexical analyzer).

Compiler Principles SDD Example2 Production Semantic Rules E → E 1 + TE.loc=newtemp(), E.code = E 1.code || T.code || add E 1.loc,T.loc,E.loc E → TE.loc = T.loc, E.code=T.code T → T 1 * FT.loc=newtemp(), T.code = T 1.code || F.code || mult T 1.loc,F.loc,T.loc T → FT.loc = F.loc, T.code=F.code F → ( E )F.loc = E.loc, F.code=E.code F → idF.loc = id.name, F.code=“” Symbols E, T, and F are associated with synthesized attributes loc and code. The token id has a synthesized attribute name. || is the string concatenation operator.

Compiler Principles Annotated Parse Tree 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.

Compiler Principles Annotated Parse Tree Example Input: 5+3*4 L E.val=17 E.val=5 + T.val=12 T.val=5 T.val=3 * F.val=4 F.val=5 F.val=3 digit.lexval=4 digit.lexval=5 digit.lexval=3

Compiler Principles Dependency Graph Semantic rules set up dependencies among attributes. Dependency graph determines the evaluation order of the semantic rules. –An edge from one attribute to another indicates that the value of the former one is needed to compute the later one.

Compiler Principles Dependency Graph Example Input: 5+3*4 L E.val=17 E.val=5 T.val=12 T.val=5 T.val=3 F.val=4 F.val=5 F.val=3 digit.lexval=4 digit.lexval=5 digit.lexval=3

Compiler Principles Inherited Attributes Example Production Semantic Rules D → T LL.in = T.type T → intT.type = integer T → realT.type = real L → L 1 idL 1.in = L.in, addtype(id.entry,L.in) L → idaddtype(id.entry,L.in) Symbol T is associated with a synthesized attribute type. Symbol L is associated with an inherited attribute in.

Compiler Principles A Dependency Graph with Inherited Attributes Input: real p q DL.in=real T LT.type=real L 1.in=real, addtype(q,real) real L idaddtype(p,real)id.entry=q id id.entry=p parse treedependency graph

Compiler Principles S & L-Attributed Definitions We will look at two sub-classes of the syntax-directed definitions: –S-Attributed Definitions: only synthesized attributes are used in the syntax-directed definitions. –L-Attributed Definitions: both synthesized and inherited attributes are used in a restricted fashion. dependency-graph edges can go from left to right, but not from right to left

Compiler Principles S-Attributed Definitions S-Attributed Definitions: only synthesized attributes are used in the syntax-directed definitions –each rule computes an attribute for the nonterminal at the head of a production from attributes taken from the body of the production –the attributes can be evaluated by performing a postorder traversal of the parse tree –can be implemented naturally with an LR parser –can also be implemented with an LL parser

Compiler Principles Bottom-Up Evaluation of S-Attributed Definitions Put the values of the synthesized attributes of the grammar symbols into a parallel stack Evaluate the values of the attributes during reductions Example: A  XYZ A.a=f(X.x,Y.y,Z.z) (all attributes are synthesized) stack parallel-stack top   top  Z Y X. A. A.a. Z.z Y.y X.x.

Compiler Principles SDD Example Recall Production Semantic Rules L → E returnprint(E.val) E → E 1 + TE.val = E 1.val + T.val E → TE.val = T.val T → T 1 * FT.val = T 1.val * F.val T → FT.val = F.val F → ( E )F.val = E.val F → digitF.val = digit.lexval Symbols E, T, and F are associated with a synthesized attribute val. The token digit has a synthesized attribute lexval (an integer value returned by the lexical analyzer).

Compiler Principles Bottom-Up Eval. of S-Attributed Definitions Production Semantic Rules L → E returnprint(val[top-1]) E → E 1 + Tval[ntop] = val[top-2] + val[top] E → T T → T 1 * Fval[ntop] = val[top-2] * val[top] T → F F → ( E )val[ntop] = val[top-1] F → digitpush digit.lexval At each shift of digit, we also push digit.lexval into val-stack. At all other shifts, we do not put anything into val- stack because other terminals do not have attribute (but we increment the stack pointer for val-stack).

Compiler Principles Canonical LR(0) Collection for The Grammar L’→. L L →. Er E →. E+T E →. T T →. T*F T →. F F →. (E) F →. d L’→L. L →E. r E →E. +T E →T. T →T. *F T →F. F → (. E) E →. E+T E →. T T →. T*F T →. F F →. (E) F →. d F →d. L →Er. E →E+. T T →. T*F T →. F F →. (E) F →. d T →T*. F F →. (E) F →. d F →(E. ) E →E. +T E →E+T. T →T. *F T →T*F. F →(E). I0:I0: I1:I1: I2:I2: I4:I4: I3:I3: I5:I5: I6:I6: I7:I7: I 12 : I 11 : I 10 : I9:I9: I 13 : I8:I8: r L E E T T T F F F F ( ( ( ( d d d d ) * + + *

Compiler Principles Bottom-Up Evaluation Example At each shift of digit, we also push digit.lexval into val-stack. stackval-stackinputactionsemantic rule 05+3*4rs6d.lexval(5) into val-stack 0d65+3*4rF→dF.val=d.lexval – do nothing 0F45+3*4rT→FT.val=F.val – do nothing 0T35+3*4rE→TE.val=T.val – do nothing 0E25+3*4rs8push empty slot into val-stack 0E2+85-3*4rs6d.lexval(3) into val-stack 0E2+8d65-3*4rF→dF.val=d.lexval – do nothing 0E2+8F45-3*4rT→FT.val=F.val – do nothing 0E2+8T115-3*4rs9push empty slot into val-stack 0E2+8T11*95-3-4rs6d.lexval(4) into val-stack 0E2+8T11*9d65-3-4rF→d F.val=d.lexval – do nothing 0E2+8T11*9F rT→T*FT.val=T 1.val*F.val 0E2+8T115-12rE→E+TE.val=E 1.val+T.val 0E217rs7push empty slot into val-stack 0E2r717-$L→Erprint(17), pop empty slot from val-stack 0L117$acc

Compiler Principles Top-Down Eval. of S-Attributed Definitions ProductionsSemantic Rules A → Bprint(B.n0), print(B.n1) B → 0 B 1 B.n0=B 1.n0+1, B.n1=B 1.n1 B → 1 B 1 B.n0=B 1.n0, B.n1=B 1.n1+1 B →  B.n0=0, B.n1=0 B has two synthesized attributes (n0 and n1).

Compiler Principles Top-Down Eval. of S-Attributed Definitions In a recursive predictive parser, each non- terminal corresponds to a procedure. procedure A() { call B(); A → B } procedure B() { if (currtoken=0) { consume 0; call B(); } B → 0 B else if (currtoken=1) { consume 1; call B(); } B → 1 B else if (currtoken=$) {} // $ is end-marker B →  else error(“unexpected token”); }

Compiler Principles Top-Down Eval. of S-Attributed Definitions procedure A() { int n0,n1;Synthesized attributes of non-terminal B call B(&n0,&n1);are the output parameters of procedure B. print(n0); print(n1); } All the semantic rules can be evaluated procedure B(int *n0, int *n1) { at the end of parsing of production rules if (currtoken=0) { int a,b; consume 0; call B(&a,&b); *n0=a+1; *n1=b; } else if (currtoken=1) { int a,b; consume 1; call B(&a,&b); *n0=a; *n1=b+1; } else if (currtoken=$) {*n0=0; *n1=0; } // $ is end-marker else error(“unexpected token”); }

Compiler Principles L-Attributed Definitions L-Attributed Definitions: both synthesized and inherited attributes are used in a restricted fashion. –can always be evaluated by a depth first traversal of the parse tree –can also be evaluated during the parsing

Compiler Principles L-Attributed Definitions A syntax-directed definition is L-attributed if each inherited attribute of X j, where 1  j  n, on the right side of A → X 1 X 2...X n depends only on: 1.the inherited attribute of A 2.the attributes of the symbols X 1,...,X j-1 to the left of X j in the production 3.attributes associated with X j itself, under the condition that there is no cycle in the dependency graph involving the attributes of X j Every S-attributed definition is L-attributed, the restrictions only apply to the inherited attributes (not to synthesized attributes).

Compiler Principles A L-Attributed SDD ProductionsSemantic Rules T → F T’T’.inh = F.val T’ → * F T’ 1 T’ 1.inh=T’.inh * F.val

Compiler Principles A Definition that is NOT L-Attributed ProductionsSemantic Rules A → L ML.in=l(A.i), M.in=m(L.s), A.s=f(M.s) A → Q R R.in=r(A.in), Q.in=q(R.s), A.s=f(Q.s) This syntax-directed definition is not L- attributed because the semantic rule Q.in=q(R.s) violates the restrictions of L- attributed definitions.

Compiler Principles Syntax-Directed Translation Schemes (SDT) A syntax-directed translation scheme is a context-free grammar in which: –attributes are associated with the grammar symbols –semantic actions enclosed between braces {} are inserted within the body of productions. Example:A → {... } X {... } Y {... } Semantic Actions

Compiler Principles SDT Cont’d In translation schemes, we use semantic action instead of semantic rule used in syntax-directed definitions. Restrictions in designing a translation scheme: –The position of the semantic action on the right side indicates when that semantic action will be evaluated. –These restrictions (motivated by L-attributed definitions) ensure that a semantic action does not refer to an attribute that has not yet computed.

Compiler Principles When to evaluate the sematic action? For 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 parsing stack. –If the parse is top-down, we perform a just before we attempt to expand this occurrence of Y (if Y is a nonterminal) or check for Y on the input (if Y is a terminal).

Compiler Principles A SDT Example A simple translation scheme that converts infix expressions to the corresponding postfix expressions. E → T R R → + T { print(“+”) } R 1 R →  T → id { print(id.name) } a+b+c  ab+c+ infix expression postfix expression

Compiler Principles A SDT Example Cont’d E T R id{print(“a”)} + T {print(“+”)} R id {print(“b”)} + T {print(“+”)} R id {print(“c”)}  A depth first traversal of the parse tree will produce the postfix representation of the infix expression.

Compiler Principles SDT for S-Attributed Definition For each associated semantic rule in a S- attributed SDD, append a semantic action to the end of the production body. ProductionSemantic Rule E → E 1 + TE.val = E 1.val + T.val  E → E 1 + T { E.val = E 1.val + T.val }

Compiler Principles Bottom-Up Eval. of S-Attributed Definitions Production Actions L → E returnprint(val[top-1]) E → E 1 + Tval[ntop] = val[top-2] + val[top] E → T T → T 1 * Fval[ntop] = val[top-2] * val[top] T → F F → ( E )val[ntop] = val[top-1] F → digitpush digit.lexval At each shift of digit, we also push digit.lexval into val-stack. At all other shifts, we do not put anything into val- stack because other terminals do not have attribute (but we increment the stack pointer for val-stack).

Compiler Principles SDT for L-Attributed Definition Conversion 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 (this semantic action is placed at the end of the production body). Any L-attributed definition can always be converted to a corresponding translation scheme satisfying these three rules.

Compiler Principles A SDT with Inherited Attributes D → T id { addtype(id.entry,T.type), L.in = T.type } L T → int { T.type = integer } T → real { T.type = real } L → id { addtype(id.entry,L.in), L 1.in = L.in } L 1 L →  This is a translation scheme for an L-attributed definitions.

Compiler Principles Implementing SDT Using Recursive-Descent Parsing –Decide the production used to expand A. –Match each terminal appears on the input. –Preserve, in local variables, the values of all attributes needed to compute inherited and synthesized attributes. –Call functions corresponding to nonterminals in the body, and provide them with the proper arguments.

Compiler Principles Recursive-Descent Parsing of SDT procedure D() { int Ttype,Lin,identry; call T(&Ttype); consume(id,&identry); addtype(identry,Ttype); Lin=Ttype; call L(Lin);a synthesized attribute (an output parameter) } procedure T(int *Ttype) { if (currtoken is int) { consume(int); *Ttype=TYPEINT; } else if (currtoken is real) { consume(real); *Ttype=TYPEREAL; } else { error(“unexpected type”); } }an inherited attribute (an input parameter) procedure L(int Lin) { if (currtoken is id) { int L1in,identry; consume(id,&identry); addtype(identry,Lin); L1in=Lin; call L(L1in); } else if (currtoken is endmarker) { } else { error(“unexpected token”); } }

Compiler Principles Eliminating Left Recursion from SDT A translation scheme with a left recursive grammar. E → E 1 + T { E.val = E 1.val + T.val } E → E 1 - T { E.val = E 1.val - T.val } E → T { E.val = T.val } T → T 1 * F { T.val = T 1.val * F.val } T → F{ T.val = F.val } F → ( E ) { F.val = E.val } F → digit { F.val = digit.lexval } When we eliminate the left recursion from the grammar (to get a suitable grammar for the top- down parsing) we also have to change semantic actions

Compiler Principles Eliminating Left Recursion A → A 1 Y { A.a = g(A 1.a,Y.y) }a left recursive grammar with A → X { A.a=f(X.x) }synthesized attributes (a,y,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 } R → Y { R 1.in=g(R.in,Y.y) } R 1 { R.syn = R 1.syn } R →  { R.syn = R.in }

Compiler Principles Eliminating Left Recursion Cont’d A parse tree of left recursive grammar AY A.a=g(f(X.x),Y.y) parse tree of non-left-recursive grammar X X.x=f(X.x) A X R.in=f(X.x) R A.a=g(f(X.x,Y.y) Y R 1.in=g(f(X.x),Y.y) R 1 R.syn=g(f(X.x),Y.y)  R 1.syn=R 1.in

Compiler Principles Eliminating Left Recursion from SDT A translation scheme with a left recursive grammar. E → E 1 + T { E.val = E 1.val + T.val } E → E 1 - T { E.val = E 1.val - T.val } E → T { E.val = T.val } T → T 1 * F { T.val = T 1.val * F.val } T → F{ T.val = F.val } F → ( E ) { F.val = E.val } F → digit { F.val = digit.lexval } When we eliminate the left recursion from the grammar (to get a suitable grammar for the top- down parsing) we also have to change semantic actions

Compiler Principles Eliminating Left Recursion Example inherited attribute synthesized attribute E → T { A.in=T.val } A { E.val=A.syn } A → + T { A 1.in=A.in+T.val } A 1 { A.syn = A 1.syn } A → - T { A 1.in=A.in-T.val } A 1 { A.syn = A 1.syn } A →  { A.syn = A.in } T → F { B.in=F.val } B { T.val=B.syn } B → * F { B 1.in=B.in*F.val } B 1 { B.syn = B 1.syn} B →  { B.syn = B.in } F → ( E ) { F.val = E.val } F → digit { F.val = digit.lexval }

Compiler Principles Test Yourself Textbook page 337, Exercise 5.4.3

Compiler Principles Intermediate Code Generation with SDT E → T { A.in=T.loc } A { E.loc=A.loc } A → + T { A 1.in=newtemp(); emit(add,A.in,T.loc,A 1.in) } A 1 { A.loc = A 1.loc} A →  { A.loc = A.in } T → F { B.in=F.loc } B { T.loc=B.loc } B → * F { B 1.in=newtemp(); emit(mult,B.in,F.loc,B 1.in) } B 1 { B.loc = B 1.loc} B →  { B.loc = B.in } F → ( E ) { F.loc = E.loc } F → id { F.loc = id.name }

Compiler Principles Intermediate Code Generation with Predictive Parsing procedure E(char **Eloc) { char *Ain, *Tloc, *Aloc; call T(&Tloc); Ain=Tloc; call A(Ain,&Aloc); *Eloc=Aloc; } procedure A(char *Ain, char **Aloc) { if (currtok is +) { char *A1in, *Tloc, *A1loc; consume(+); call T(&Tloc); A1in=newtemp(); emit(“add”,Ain,Tloc,A1in); call A(A1in,&A1loc); *Aloc=A1loc; } else { *Aloc = Ain } }

Compiler Principles Intermediate Code Generation with Predictive Parsing procedure T(char **Tloc) { char *Bin, *Floc, *Bloc; call F(&Floc); Bin=Floc; call B(Bin,&Bloc); *Tloc=Bloc; } procedure B(char *Bin, char **Bloc) { if (currtok is *) { char *B1in, *Floc, *B1loc; consume(+); call F(&Floc); B1in=newtemp(); emit(“mult”,Bin,Floc,B1in); call B(B1in,&B1loc); Bloc=B1loc; } else { *Bloc = Bin } } procedure F(char **Floc) { if (currtok is “(“) { char *Eloc; consume(“(“); call E(&Eloc); consume(“)”); *Floc=Eloc } else { char *idname; consume(id,&idname); *Floc=idname } }

Compiler Principles Bottom-Up Evaluation of L-Attributed SDD In bottom-up evaluation, the semantic actions are evaluated during the reductions. During the bottom-up evaluation of S-attributed definitions, we have a parallel stack to hold synthesized attributes. Problem: Where do we hold inherited attributes? Solution: –Convert the grammar to guarantee the followings: All embedding semantic actions in the translation scheme is moved to the end of the production rules. All inherited attributes is copied into the synthesized attributes.

Compiler Principles Moving Embedding Semantic Actions Translation rules: 1.Remove an embedding semantic action S i, and put a new non-terminal M i in the place of the semantic action. 2.Put that semantic action S i to the end of a new production rule M i  for the new non- terminal M i. The semantic action S i will be evaluated when this new production rule is reduced. The evaluation order of the semantic rules are not changed by this transformation.

Compiler Principles Removing Embedding Semantic Actions Example1 A  {S 1 } X 1 {S 2 } X 2... {S n } X n  remove embedding semantic actions A  M 1 X 1 M 2 X 2... M n X n M 1  {S 1 } M 2  {S 2 } … M n  {S n }

Compiler Principles Removing Embedding Semantic Actions Example2 E → T R R → + T { print(“+”) } R 1 R →  T → id { print(id.name) }  remove embedding semantic actions E → T R R → + T M R 1 R →  T → id { print(id.name) } M →  { print(“+”) }

Compiler Principles Copying Inherited Attributes Assume every non-terminal A has an inherited attribute A.i, and every symbol X has a synthesized attribute X.s. For every production rule A  X 1 X 2... X n, –Introduce new marker non-terminals M 1,M 2,...,M n –Replace the production rule with A  M 1 X 1 M 2 X 2... M n X n –Synthesized attributes of X i will be not changed. –Inherited attributes of X i will be copied into the synthesized attribute of M i by the new semantic action added to the end of the new production rule M i. –The inherited attribute of X i can be found in the synthesized attribute of M i (which is immediately available in the stack).

Compiler Principles Copying Inherited Attributes Cont’d A  {B.i=f 1 (...)} B {C.i=f 2 (...)} C {A.s= f 3 (...)}  A  {M 1.i=f 1 (...)} M 1 {B.i=M 1.s} B {M 2.i=f 2 (...)} M 2 {C.i=M 2.s} C {A.s= f 3 (...)} M 1  {M 1.s=M 1.i} M 2  {M 2.s=M 2.i}

Compiler Principles Translation with Inherited Attributes S  {A.i=1} A {S.s=k(A.i,A.s)} A  {B.i=f(A.i)} B {C.i=g(A.i,B.i,B.s)} C {A.s= h(A.i,B.i,B.s,C.i,C.s)} B  b {B.s=m(B.i,b.s)} C  c {C.s=n(C.i,c.s)} S  {M 1.i=1} M 1 {A.i=M 1.s} A {S.s=k(M 1.s,A.s)} A  {M 2.i=f(A.i)} M 2 {B.i=M 2.s} B {M 3.i=g(A.i,M 2.s,B.s)} M 3 {C.i=M 3.s} C {A.s= h(A.i, M 2.s,B.s, M 3.s,C.s)} B  b {B.s=m(B.i,b.s)} C  c {C.s=n(C.i,c.s)} M 1  {M 1.s=M 1.i} M 2  {M 2.s=M 2.i} M 3  {M 3.s=M 3.i}

Compiler Principles Actual Translation Scheme S  {M 1.i=1} M 1 {A.i=M 1.s} A {S.s=k(M 1.s,A.s)} A  {M 2.i=f(A.i)} M 2 {B.i=M 2.s} B {M 3.i=g(A.i,M 2.s,B.s)} M 3 {C.i=M 3.s} C {A.s= h(A.i, M 2.s,B.s, M 3.s,C.s)} B  b {B.s=m(B.i,b.s)} C  c {C.s=n(C.i,c.s)} M 1  {M 1.s= M 1.i} M 2  {M 2.s=M 2.i} M 3  {M 3.s=M 3.i} S  M 1 A { s[ntop]=k(s[top-1],s[top]) } M 1   { s[ntop]=1 } A  M 2 B M 3 C{ s[ntop]=h(s[top-4],s[top-3],s[top-2],s[top-1],s[top]) } M 2   { s[ntop]=f(s[top]) } M 3   { s[ntop]=g(s[top-2],s[top-1],s[top])} B  b { s[ntop]=m(s[top-1],s[top]) } C  c { s[ntop]=n(s[top-1],s[top]) }

Compiler Principles Evaluation of Attributes S S.s=k(1,h(..)) A.i=1 A A.s=h(1,f(1),m(..),g(..),n(..)) B.i=f(1) C.i=g(1,f(1),m(..)) BC B.s=m(f(1),b.s) C.s=n(g(..),c.s) bc

Compiler Principles Evaluation of Attributes Cont’d stackinput l-attribute stack bc$ M 1 bc$1 M 1 M 2 bc$1 f(1) M 1 M 2 b c$1 f(1) b.s M 1 M 2 B c$1 f(1) m(f(1),b.s) M 1 M 2 B M 3 c$1 f(1) m(f(1),b.s) g(1,f(1),m(f(1),b.s)) M 1 M 2 B M 3 c $1 f(1) m(f(1),b.s) g(1,f(1),m(f(1),b.s)) c.s M 1 M 2 B M 3 C $1 f(1) m(f(1),b.s) g(1,f(1),m(f(1),b.s)) n(g(..),c.s) M 1 A $1 h(f(1),m(..),g(..),n(..)) S $k(1,h(..))

Compiler Principles Problems All L-attributed definitions based on LR grammars cannot be evaluated during bottom-up parsing. S  { L.i=0 } L  this translations scheme cannot be implemented L  { L 1.i=L.i+1 } L 1 1 during the bottom-up parsing L   { print(L.i) } S  M 1 L L  M 2 L 1 1  But since L   will be reduced first by the bottom- up L   { print(s[top]) } parser, the translator cannot know the number of 1s. M 1   { s[ntop]=0 } M 2   { s[ntop]=s[top]+1 }

Compiler Principles Problems The modified grammar cannot be LR grammar anymore. L  L bL  M L b L  a  L  aNOT LR-grammar M   S’ . L, $ L . M L b, $ L . a, $ M .,a  shift/reduce conflict