0% found this document useful (0 votes)
21 views43 pages

Chapter 4

Chapter 4 discusses semantic analysis in programming languages, focusing on ensuring that programs have well-defined meanings and verifying properties not caught in earlier phases, such as type consistency and variable declarations. It introduces Syntax-Directed Translation (SDT) and its components, including synthesized and inherited attributes, and explains the importance of semantic analysis in detecting deeper errors beyond syntax. The chapter also covers the implementation of attribute evaluation through dependency graphs and the distinction between S-Attributed and L-Attributed Definitions.

Uploaded by

Tesfalegn Yakob
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
21 views43 pages

Chapter 4

Chapter 4 discusses semantic analysis in programming languages, focusing on ensuring that programs have well-defined meanings and verifying properties not caught in earlier phases, such as type consistency and variable declarations. It introduces Syntax-Directed Translation (SDT) and its components, including synthesized and inherited attributes, and explains the importance of semantic analysis in detecting deeper errors beyond syntax. The chapter also covers the implementation of attribute evaluation through dependency graphs and the distinction between S-Attributed and L-Attributed Definitions.

Uploaded by

Tesfalegn Yakob
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 43

Chapter 4: Semantic analysis

By Birku L.
 Introduction
 Semantic analysis
 Syntax-Directed Translation (SDT)
 Syntax Directed Definitions
 Synthesized attributes
 Inherited attributes
 Implementing Attribute
 Dependency Graph
 S-Attributed Definitions
 L-Attributed Definitions
 Translation Schemes
Introduction
Analysis of input program Synthesis of output program
(front-end) (back-end)
character
Character
stream
Intermediate Code Generation
Lexical Analysis

token
Token intermediate
Intermediate
stream form

Syntactic Analysis Optimization

abstract
Abstract intermediate
Intermediate
syntax tree form

Semantic Analysis Code Generation

annotated
Annotated target
Target
AST language
Introduction…
 Program is lexically well-formed:
 Identifiers have valid names.
 Strings are properly terminated.
 No stray/lost characters.

 Program is syntactically well-formed:


 Class declarations have the correct structure.
 Expressions are syntactically valid.

 Does this mean that the program is legal?


Semantic analysis
class MyClass implements MyInterface {
string myInteger;
void doSomething() {
int[] x = new string;
x[5] = myInteger * y;
}
void doSomething() {
}
int fibonacci(int n) {
return doSomething() + fibonacci(n – 1);
}
}
Semantic analysis…
 Program is lexically well-formed:
 Identifiers have valid names.
 Strings are properly terminated.
 No stray characters.
 Program is syntactically well-formed:
 Class declarations have the correct structure.
 Expressions are syntactically valid.
 Does this mean that the program is legal?
Semantic analysis…
 Ensure that the program has a well-defined meaning.
 Verify properties of the program that aren't caught during the
earlier phases:
 Variables are declared before they're used.
 Expressions have the right types.
 Classes don't inherit from nonexistent base classes
 Type consistence;
 Inheritance relationship is correct;
 A class is defined only once;
 A method in a class is defined only once;
 Reserved identifiers are not misused;
 Once we finish semantic analysis, we know that the user's input
program is legal.
What Does Semantic Analysis Involve?
 Semantic analysis typically involves:
 Type checking – Data types are used in a manner that is
consistent with their definition (i.e., only with compatible
data types, only with operations that are defined for them,
etc.)
 Label Checking – Labels references in a program must exist
 Flow control checks – control structures must be used in their
proper fashion (e.g. no breaks outside a loop or switch
statement, etc.)
 Uniqueness checks - Certain names must be unique, Many
languages require variable declarations
 Logical checks - Program is syntactically and semantically
correct, but does not do the “correct” thing
What Does Semantic Analysis Involve?…
 Semantic analysis also gathers useful information about program
for later phases:
 E.g. count how many variables are in scope at each point.
 Why can't we just do this during parsing?
 Limitations of CFGs
 How would you prevent duplicate class definitions?
 How would you differentiate variables of one type from
variables of another type?
 How would you ensure classes implement all interface
methods?
 Actually, semantic analysis is not a separate module within a
compiler.
 It is usually a collection of procedures called at appropriate
times by the parser as the grammar requires it.
Why we need semantic analysis
 Syntax analyzer/ parser is not able to detect all
kinds of error
 Deeper levels of correctness is presented in
semantic than syntax
 Some features of language can’t presented by
CFG
What Does Semantic Analysis Produce?
 Part of semantic analysis is producing some sort of representation
of the program, either object code or an intermediate
representation of the program.
 One-pass compilers will generate object code without using an
intermediate representation;
 Code generation is part of the semantic actions performed
during parsing.
 Other compilers will produce an intermediate representation
during semantic analysis;
 Most often it will be an annotated abstract syntax tree or
quadruples.
 The input for Semantic Analysis is Abstract Syntax tree and its
output is parser tree with some attribute  Anotated parser tree
What Does Semantic Analysis Produce?…
 Annotated Abstract Syntax tree is parse-tree that shows the
values of the attributes at each node.
Syntax-Directed Translation (SDT)
 As for Lexical and Syntax analysis, also for Semantic Analysis we
need both a Representation Formalism and an Implementation
Mechanism
 One of representation formalism of semantic Analysis is Syntax
Directed Translations (SDT)
 The Principle of SDT states that the meaning of an input
sentence is related to its syntactic structure, i.e. to its Parse-Tree.
 By SDTs we indicate those formalisms for specifying translations
for PL constructs guided by CFGs.
 We associate Attributes to the grammar symbols representing the
language constructs.
 Values for attributes are computed by Semantic Rules associated with
grammar productions.
Syntax-Directed Translation (SDT)…
 It is the combination of production and semantic rule
STD=Production+Sematic rule ABC {f(B.V,C.V)}
 Evaluation of Semantic Rules may:
 Generate Code;
 Insert information into the Symbol Table;
 Perform Semantic Check;
 Issue error messages ; etc.
 There are two notations for attaching semantic rules:
 Syntax Directed Definitions
 High-level specification hiding many implementation details (also
called Attribute Grammars).
 Translation Schemes
 More implementation oriented: Indicate the order in which
semantic rules are to be evaluated.
Syntax Directed Definitions(SDD)
 SDD are a generalization of CFGs in which:
 Grammar symbols have an associated set of Attributes;
 Productions are associated with Semantic Rules for
computing the values of attributes.
 Such formalism generates Annotated Parse-Trees where
each node is type record with a field for each attribute
 e.g., X.a indicates the attribute a of the grammar symbol
X.
 Attributes can represent anything we need: a string, a type,
a number, a memory location , … and there are two types
attributes.
Two Types of Attributes
 Synthesized attributes: attribute values of node are
computed from some attribute values of its children
nodes
– P.synthesized_attr = f(C1.attr, C2.attr, C3.attr)
 Inherited attributes: attribute values are computed from
attributes of the siblings and parent of the node
– C2.inherited_attr = f(P1.attr, C1.attr, C3.attr)
S and L attributes
 S attribute only use Synthesized attribute where as L attribute
used both synthesized and inherited attribute. The inherited
attribute only inherit values from either parent or left siblings
only
Example ABCD {B.V=A.V, C.V=B.V, D.V=C.V} but
impossible to describe like {C.V=D.V}
 In S attribute Semantic actions are placed at the end of the
production ABC {…..}where as in L attribute the semantic
are placed at anywhere on RHS like
A{….}BC|B{….}C|BC{…}
 In S attribute attributes are evaluated during bottom up parsing
where as in L attribute by traversing parse tree depth first, left to
right
Syntax-Directed Definitions (SDD): Example
Production Semantic Rules
L → E return { print(E.val) }
E → E1 + T { E.val = E1.val + T.val }
E →T { E.val = T.val }
T → T1 * F { T.val = T1.val * F.val }
T→F { T.val = F.val }
F→(E) { F.val = E.val }
F → digit { 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).
Steps to compute synthesized attribute
 Synthesized (S) attribute:- These are those nodes which
derive there values from their child nodes
 To compute the synthesized attribute from the given
grammar you must follow the steps listed below
 Write the SDD using semantic rule for each production
 The annotated parser tree is generated and attribute
value computed with bottom up manner
 Start from left most bottom up node
 Add data left completely then right
 Value obtained from semantic analyzer
Depth-First Traversals: Example
LEn
EE+T/T
TF
Fid L print (16)
Input: 9+5+2n
E.val=16

E.val=14 T.val=2

E.val=9 F.val=2
T.val=5
Note: all attributes
T.val=9 F.val=5 in this example
are of synthesized
F.val=9 attributes

9 + 5 + 2 n
20
Annotated Parse Tree Example
S-Attributed Definitions
 An S-Attributed Definition is a SDD that uses only
Synthesized attributes
 Evaluation Order:
 Semantic rules in a S-Attributed Definition can be
evaluated by a bottom-up, or post-order traversal of the
parse-tree.
 An S-attributed SDD can be implemented naturally in
conjunction with an LR parser.
 Example – The arithmetic grammar on the above is an
example of an S-Attributed.
Compute the attribute
Compute the S attribute for the given production
by the following the sequence of steps .
The given string is 2+3*4
EE+T/T
TT*F/F
Fid
Inherited Attributes
 Inherited Attributes are useful for expressing the dependence of
a construct on the context in which it appears.
 It is always possible to rewrite a syntax directed definition to use
only synthesized attributes, but it is often more natural to use
both synthesized and inherited attributes.
 Evaluation Order: Inherited attributes can be evaluated by a pre-
order traversal of the parse-tree, but
 Unlike synthesized attributes, the order in which the inherited
attributes of the children are computed is important.
 Inherited attributes of the children can depend from both left
and right siblings!
Dependency Graphs for
Attributed Parse Trees Direction of value
dependence
Synthesized A.a
attributes A.a= (X.x, Y.y)
X.x Y.y

A.a
A  XY X.x= (A.a, Y.y)
X.x Y.y

Inherited A.a
attributes
X.x Y.y Y.y= (A.a, X.x)

25
Steps to compute L attribute
 These are attributes which drive their value their parent
or siblings
 To drive the L attribute from the given grammar we
must follow steps
 Step 1 construct the SDD using semantic action
 The annotated parser tree generated and attribute value
computed in top down manner
 Top down approach
 Left tree value obtained from child node
 Value obtained from sibling node
 Right tree value obtained from parent
Inherited Attributes: Example
Production Semantic Rules
D →T L {L.in = T.type }
T → int { T.type = integer }
T → real { T.type = real }
L → L1, id { L1.in = L.in, addtype(id.entry, L.in) }
L → id { addtype(id.entry, L.in) }

Symbol T is associated with a synthesized attribute type.


 Symbol L is associated with an inherited attribute in.
 The production L  L1, id distinguishes the two occurrences
of L .
Annotated Parse Tree Example
 The annotated parse-tree for the input real id1, id2,
id3 is
Dependency Graph
 Implementing a SDD consists primarily in finding an order for the
evaluation of attributes
 Dependency Graphs(one of implementation mechanisms of
semantic analysis) are the most general procedure to evaluate
syntax directed translations with both synthesized and inherited
attributes.
 A Dependency Graph shows the interdependencies among the
attributes of the various nodes of a parse-tree.
 There is a node for each attribute;
 If attribute b depends on an attribute c there is a link from the
node for c to the node for b (b  c)
 If an attribute b depends from another attribute c then we need to
fire the semantic rule c for first and then the semantic rule for b
Dependency Graph…
L
Input: 5+3*4 Synthesized Attributes

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

A dependency graph suggests possible evaluation orders for an


annotated parse-tree.
Dependency Graph…

Input: real p, q Inherited Attributes


D L.in=real

T L T.type=real L1.in=real addtype(q,real)

real L , id addtype(p,real) id.entry=q

id id.entry=p

parse tree dependency graph


Implementing Attribute Evaluation: General Remark
 Attributes can be evaluated by building a dependency graph
at compile-time and then finding a topological sort
 Disadvantages
 fails if the dependency graph has a cycle: We need a test for non-
circularity;
 is time consuming due to the construction of the dependency graph.
 Alternative Approach - design the SDD in such away that
attributes can be evaluated with a fixed order (method followed
by many compilers).
 Formalisms for which an attribute evaluation order can be fixed
at compiler construction time are known as Strongly Non-
Circular Syntax Directed Definitions.
 S-Attributed and L-Attributed Definitions examples.
Evaluation of S-Attributed Definitions
 Synthesized Attributes can be evaluated by a bottom-up parser
as the input is being analyzed avoiding the construction of a
dependency graph.

 The parser keeps the values of the synthesized attributes in its


stack.

 When ever a reduction A   is made, the attribute for A is


computed from the attributes of  which appear on the stack.

 Thus, a translator for an S-Attributed Definition can be


implemented by extending the stack of an LR-Parser
L-Attributed Definitions
 L-Attributed Definitions contain both synthesized and inherited
attributes but do not need to build a dependency graph to evaluate
them
 This means that they can also be evaluated during the parsing
 A syntax-directed definition is L-attributed if each inherited
attribute of Xj, where 1jn, on the right side of A →
X1X2...Xn depends only on:
1. The attributes of the symbols X1,...,Xj-1 to the left of Xj in the
production and
2. the inherited attribute of A
 Note that: Every S-attributed definition is L-attributed, the
restrictions only apply to inherited attribute
L-Attributed Definitions…
 L-Attributed Definitions are a class of syntax directed
definitions whose attributes can always be evaluated by single
traversal of the parse-tree of the inherited attributes (not to
synthesized attributes)
 The following procedure evaluate L-Attributed Definitions by
mixing PostOrder (synthesized) and PreOrder (inherited)
traversal
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 { ... }
f
Semantic Actions
 Translation schemes indicate the order in which semantic rules and
attributes are to be evaluated
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 Semantic Rule
E → E1+ T E.val = E1.val + T.val  a production
of a syntax directed definition


E → E1+ T { E.val = E1.val + T.val }  the
production of the corresponding translation scheme
A Translation Scheme Example
 A simple translation scheme that converts infix
expressions to the corresponding postfix expressions.

E →T R
R → + T { print(“+”) } R1
R→
T → id { print(id.name) }

a+b+c  ab+c+
infix expression postfix expression
A Translation Scheme Example (cont.)
E

T R

id{print(“a”)} + T {print(“+”)} R

id {print(“b”)} + T {print(“+”)} R

id {print(“c”)} 

The depth first traversal of the parse tree (executing the semantic
actions in that order) will produce the postfix representation of the
infix expression.
Convert infix to post fix
 SDT for converting infix to postfix expression
String 2+5*3 253*+

EE+T {printf(“+”);}
ET { }
TT*F {printf(“*”);}
TF { }
Fnum {printf(num.lval);}
Inherited Attributes in Translation Schemes
 If a translation scheme has to contain both synthesized and
inherited attributes, we have to observe the following rules:
 An inherited attribute of a symbol on the RHS of a
production must be computed in a semantic action before that
symbol.
 A semantic action must not refer to a synthesized attribute of a
symbol to the right of that semantic action.
 A synthesized attribute for the non-terminal on the left can
only be computed after all attributes it references have been
computed
 With a L-attributed SDD, it is always possible to construct a
corresponding translation scheme which satisfies these three
conditions
Designing translation schemes

 Every attribute value must be available when referenced


 S-attribute of left-hand symbol computed at end of production
 L-attribute of right-hand symbol computed before the symbol
 S-attribute of right-hand symbol referenced after the symbol
Reading Assignment

Type Checking

You might also like