Lec6 - SemanticAnalysis 3
Lec6 - SemanticAnalysis 3
Outline
• The role of semantic analysis in a compiler
– A laundry list of tasks
• Scope
– Static vs. Dynamic scoping
– Implementation: symbol tables
• Types
– Static analyses that detect type errors
– Statically vs. Dynamically typed languages
2
The Compiler Front-End
Lexical analysis: program is lexically well-formed
– Tokens are legal
• e.g. identifiers have valid names, no stray characters, etc.
– Detects inputs with illegal tokens
Parsing: program is syntactically well-formed
– Declarations have correct structure, expressions are
syntactically valid, etc.
– Detects inputs with ill-formed syntax
Semantic analysis:
– Last “front end” compilation phase
– Catches all remaining errors
3
Semantic analyzer
The syntax analyzer will just create a parse tree.
Semantic analyzer will check the actual meaning
of the statement parsed in the parsed tree.
4
Semantic analysis is used for the following
Maintaining the symbol table for each block.
5
The semantic analyzer will check:
8
What Does Semantic Analysis Do?
Performs checks beyond syntax of many kinds ...
Examples:
1.All used identifiers are declared
2.Identifiers declared only once
3.Types
4.Procedures and functions defined only once
5.Procedures and functions used with the right
number and type of arguments
And others . . .
Example 1
let string y "abc" in y + 42
Example 2
let integer y in x + 42
10
Semantic Processing: Syntax-Directed
Translation
Basic idea: Associate information with language
constructs by attaching attributes to the
grammar symbols that represent these constructs
– Values for attributes are computed using semantic
rules associated with grammar productions
– An attribute can represent anything (reasonable)
that we choose; e.g. a string, number, type, etc.
– A parse tree showing the values of attributes at
each node is called an annotated parse tree
11
Attributes of an Identifier
name: character string (obtained from scanner)
scope: program region in which identifier is valid
type:
- integer
- array:
• number of dimensions
• upper and lower bounds for each dimension
• type of elements
– function:
• number and type of parameters (in order)
• type of returned value
• size of stack frame
10
Scope
• The scope of an identifier (a binding of a name
to the entity it names) is the textual part of
the program in which the binding is active
• Scope matches identifier declarations with uses
– Important static analysis step in most languages
let integer x 0
{ in
x;
let integer x 1
x; in
x;
}
Example
g(y) = let integer a 42 in f(3);
f(x) = a;
– When invoking g(54) the result will be
42
16
Static vs. Dynamic Scope
Program scopes (input, output);
var a: integer;
procedure first; With static scope
begin a := 1; end; rules, it prints 1
procedure second;
var a: integer; With dynamic scope
begin first; end; rules, it prints 2
begin
a := 2; second; write(a);
end.
17
Dynamic Scope (Cont.)
• With dynamic scope, bindings cannot always
be resolved by examining the program
because they are dependent on calling
sequences
• Dynamic scope rules are usually encountered
in interpreted languages
• Also, usually these languages do not
normally have static type checking:
– type determination is not always possible
when dynamic rules are in effect
18
Scope of Identifiers
• In most programming languages
identifier bindings are introduced by
– Function declarations (introduce function
names)
– Procedure definitions (introduce procedure
names)
– Identifier declarations (introduce
identifiers)
– Formal parameters (introduce identifiers)
19
Scope of Identifiers (Cont.)
• Not all kinds of identifiers follow the most-
closely nested scope rule
20
Example: Use Before Definition
foo (int x)
{
int y
y =
bar(x)
} ...
int bar (int i)
{
...
}
Flow-of-Control Checks
myfunc()
{ …
break; // ERROR
} myfunc()
{ …
switch (a)
myfunc() { case 0:
{ … …
while (n) break; // OK
{ … case 1:
if (i>10) …
break; // OK }
} }
}
Uniqueness Checks
myfunc()
{ int i, j, i; // ERROR
…
}
struct myrec
{ int name;
};
struct myrec // ERROR
{ int id;
};
Symbol Tables
Purpose: To hold information about identifiers
that is computed at some point and looked up
at later times during compilation
Examples:
– type of a variable
– entry point for a function
• Operations
add_symbol(x) push x and associated info, such as
x’s type, on the stack
find_symbol(x) search stack, starting from top, for
x. Return first x found or NULL if none found
remove_symbol() pop the stack
26
Limitations
• The simple symbol table works for variable
declarations
– Symbols added one at a time
– Declarations are perfectly nested
• Other problems?
27
A Fancier Symbol Table
• enter_scope() start/push a new nested scope
• find_symbol(x) finds current x (or null)
• add_symbol(x) add a symbol x to the table
• check_scope(x) true if x defined in current
scope
• exit_scope() exits/pops the current
scope
28
Function/Procedure Definitions
• Function names can be used prior to their
definition
• We can’t check that for function names
– using a symbol table
– or even in one pass
• Solution
– Pass 1: Gather all function/procedure names
– Pass 2: Do the checking
• Semantic analysis requires multiple
passes
– Probably more than two
29
Types
• What is a type?
– This is a subject of some debate
– The notion varies from language to language
• Consensus
– A type is a set of values and
– A set of operations on those values
31
Types and Operations
Certain operations are legal for values of
each type
32
Type Systems
• A language’s type system specifies which
operations are valid for which types
33
What Can Types do For Us?
• Allow for a more efficient compilation of
programs
– Allocate right amount of space for variables
• Use fewer bits when possible
– Select the right machine operations
34
Type Checking Overview
Three kinds of languages:
35
The Type Wars
• Competing views on static vs. dynamic typing
36
The Type Wars (Cont.)
• In practice, most code is written in statically
typed languages with an “escape” mechanism
– Unsafe casts in C, Java
37
Questions???