Semantic Phase SDTSyntax Directed Translation Type Checking 652021























- Slides: 23
Semantic Phase • SDT(Syntax Directed Translation) • Type Checking 6/5/2021 Babita Tiwari 1
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 • An attribute may hold almost any thing. – a string, a number, a memory location, a complex record. 6/5/2021 Babita Tiwari 2
Syntax-Directed Definitions and Translation Schemes • When we associate semantic rules with productions, we use two notations: – Syntax-Directed Definitions – Translation Schemes • Syntax-Directed Definitions: – give high-level specifications for translations – hide many implementation details such as order of evaluation of semantic actions. – We associate a production rule with a set of semantic actions, and we do not say when they will be evaluated. • Translation Schemes: – indicate the order of evaluation of semantic actions associated with a production rule. – In other words, translation schemes give a little bit information about implementation details. 6/5/2021 Babita Tiwari 3
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 called synthesized and inherited attributes of that grammar symbol. – Each production rule is associated with a set of semantic rules. • Semantic rules set up dependencies between attributes which can be represented by a dependency graph. • This dependency graph determines the evaluation order of these semantic rules. • Evaluation of a semantic rule defines the value of an attribute. But a semantic rule may also have some side effects such as printing a value. 6/5/2021 Babita Tiwari 4
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. • Of course, the order of these computations depends on the dependency graph induced by the semantic rules. 6/5/2021 Babita Tiwari 5
Syntax-Directed Definition • 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, …, cn) where f is a function, and b can be one of the followings: b is a synthesized attribute of A and c 1, c 2, …, cn are attributes of the grammar symbols in the production ( A→α ). OR b is an inherited attribute one of the grammar symbols in α (on the right side of the production), and c 1, c 2, …, cn are attributes of the grammar symbols in the production ( A→α ). 6/5/2021 Babita Tiwari 6
Syntax-Directed Definition – Synthesized Attribute Production Semantic Rules E → E 1 + T E→T T → T 1 * F T→F F→(E) F → digit E. val = E 1. val + T. val E. val = T 1. val * F. val T. val = F. val = E. val 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). 6/5/2021 Babita Tiwari 7
Annotated Parse Tree -- Example Input: 5+3*4 E. val=17 E. val=5 6/5/2021 + T. val=12 T. val=5 T. val=3 F. val=5 F. val=3 digit. lexval=5 digit. lexval=3 Babita Tiwari * F. val=4 digit. lexval=4 8
Dependency Graph Input: 5+3*4 E. val=17 E. val=5 6/5/2021 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 Babita Tiwari 9
Syntax-Directed Definition – Inherited Attributes Production Semantic Rules D→TL T → int T → real L → L , id L → id L. in = T. type = integer T. type = real L 1. in = L. in, addtype(id. entry, L. in) • Symbol T is associated with a synthesized attribute type. • Symbol L is associated with an inherited attribute in. 6/5/2021 Babita Tiwari 10
A Dependency Graph – Inherited Attributes Input: real p q D L. in=real T real L L , id parse tree 6/5/2021 T. type=real id L 1. in=real addtype(q, real) addtype(p, real) id. entry=q id. entry=p dependency graph Babita Tiwari 11
S-Attributed Definitions • Syntax-directed definitions are used to specify syntax-directed translations. • To create a translator for an arbitrary syntax-directed definition can be difficult. • We would like to evaluate the semantic rules during parsing (i. e. in a single pass, we will parse and we will also evaluate semantic rules during the parsing). • We will look at two sub-classes of the syntax-directed definitions: – S-Attributed Definitions: only synthesized attributes used in the syntax-directed definitions. – L-Attributed Definitions: in addition to synthesized attributes, we may also use inherited attributes in a restricted fashion. • To implement S-Attributed Definitions and L-Attributed Definitions are easy (we can evaluate semantic rules in a single pass during the parsing). • Implementations of S-attributed Definitions are a little bit easier than implementations of L-Attributed Definitions 6/5/2021 Babita Tiwari 12
Translation Schemes • In a syntax-directed definition, we do not say anything about the evaluation times of the semantic rules (when the semantic rules associated with a production should be evaluated? ). • 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 6/5/2021 Babita Tiwari 13
Translation Schemes for S-attributed Definitions • If our syntax-directed definition is S-attributed, the construction of the corresponding translation scheme will be simple. • Each associated semantic rule in a S-attributed syntax-directed definition will be inserted as a semantic action into the end of the right side of the associated production. Production E → E 1 + T Semantic Rule E. val = E 1. val + T. val a production of a syntax directed definition E → E 1 + T { E. val = E 1. val + T. val } the production of the corresponding translation scheme 6/5/2021 Babita Tiwari 14
A Translation Scheme Example • A simple translation scheme that converts infix expressions to the corresponding postfix expressions. E→TR R → + T { print(“+”) } R R→ T → id { print(id. name) } a+b+c infix expression 6/5/2021 ab+c+ postfix expression Babita Tiwari 15
A Translation Scheme Example (cont. ) E T id {print(“a”)} R + T {print(“+”)} R id {print(“b”)} + T {print(“+”)} id {print(“c”)} R The depth first traversal of the parse tree (executing the semantic actions in that order) will produce the postfix representation of the infix expression. 6/5/2021 Babita Tiwari 16
Type Checking • A compiler has to do semantic checks in addition to syntactic checks. • Semantic Checks – Static – done during compilation – Dynamic – done during run-time • Type checking is one of these static checking operations. – we may not do all type checking at compile-time. – Some systems also use dynamic type checking too. • A type system is a collection of rules for assigning type expressions to the parts of a program. • A type checker implements a type system. • A sound type system eliminates run-time type checking for type errors. • A programming language is strongly-typed, if every program its compiler accepts will execute without type errors. – In practice, some of type checking operations are done at run-time (so, most of the programming languages are not strongly-typed). – Ex: int x[100]; … x[i] most of the compilers cannot guarantee that i will be between 0 and 99 6/5/2021 Babita Tiwari 17
Type Expression • The type of a language construct is denoted by a type expression. • A type expression can be: – A basic type • a primitive data type such as integer, real, char, boolean, … • type-error to signal a type error • void : no type – A type name • a name can be used to denote a type expression. – A type constructor applies to other type expressions. • arrays: If T is a type expression, then array(I, T) is a type expression where I denotes index range. Ex: array(0. . 99, int) • products: If T 1 and T 2 are type expressions, then their cartesian product T 1 x T 2 is a type expression. Ex: int x int • pointers: If T is a type expression, then pointer(T) is a type expression. Ex: pointer(int) • functions: We may treat functions in a programming language as mapping from a domain type D to a range type R. So, the type of a function can be denoted by the type expression D→R where D are R type expressions. Ex: int→int represents the type of a function which takes an int value as parameter, and its return type is also int. 6/5/2021 Babita Tiwari 18
A Simple Type Checking System P → D; E D → D; D D → id: T { addtype(id. entry, T. type) } T → char { T. type=char } T → int { T. type=int } T → real { T. type=real } T → ↑T 1 { T. type=pointer(T 1. type) } T → array[intnum] of T 1 { T. type=array(1. . intnum. val, T 1. type) } 6/5/2021 Babita Tiwari 19
Type Checking of Expressions E → id { E. type=lookup(id. entry) } E → charliteral { E. type=char } E → intliteral { E. type=int } E → realliteral { E. type=real } E → E 1 + E 2 { if (E 1. type=int and E 2. type=int) then E. type=int else if (E 1. type=int and E 2. type=real) then E. type=real else if (E 1. type=real and E 2. type=int) then E. type=real else if (E 1. type=real and E 2. type=real) then E. type=real else E. type=type-error } E → E 1 [E 2] { if (E 2. type=int and E 1. type=array(s, t)) then E. type=t else E. type=type-error } E → E 1 ↑ { if (E 1. type=pointer(t)) then E. type=t else E. type=type-error } 6/5/2021 Babita Tiwari 20
Type Checking of Statements S id = E { if (id. type=E. type then S. type=void else S. type=type-error } S if E then S 1 { if (E. type=boolean then S. type=S 1. type else S. type=type-error } S while E do S 1 { if (E. type=boolean then S. type=S 1. type else S. type=type-error } 6/5/2021 Babita Tiwari 21
Type Checking of Functions E E 1 ( E 2 ) { if (E 2. type=s and E 1. type=s t) then E. type=t else E. type=type-error } Ex: int f(double x, char y) {. . . } f: double x char int argument types 6/5/2021 return type Babita Tiwari 22
Structural Equivalence of Type Expressions • How do we know that two type expressions are equal? • As long as type expressions are built from basic types (no type names), we may use structural equivalence between two type expressions Structural Equivalence Algorithm (sequiv): if (s and t are same basic types) then return true else if (s=array(s 1, s 2) and t=array(t 1, t 2)) then return (sequiv(s 1, t 1) and sequiv(s 2, t 2)) else if (s = s 1 x s 2 and t = t 1 x t 2) then return (sequiv(s 1, t 1) and sequiv(s 2, t 2)) else if (s=pointer(s 1) and t=pointer(t 1)) then return (sequiv(s 1, t 1)) else if (s = s 1 s 2 and t = t 1 t 2) then return (sequiv(s 1, t 1) and sequiv(s 2, t 2)) else return false 6/5/2021 Babita Tiwari 23























