Symbol Tables Map Identifiers To Their Attributes
Symbol Tables Map Identifiers To Their Attributes
1
Symbol Table
A compiler or interpreter maintains a symbol table containing
information about known identifiers (and maybe other symbols).
Each new scope is given a number. Scope 1 (this file)
Name Scope Cat. Type Other
float f(float x) { f 2 func. float (param)
float y = x*x + 1; main 3 func. int (void)
return y; Scope 2
}
Name Scope Cat. Type Other
x 2 param float -
y 2 local float -
int main( ) {
float sum; Scope 3
sum = f(1.0) + f(2.5); Name Scope Cat. Type Other
} sum 3 local float -
2
Predefined Symbols Table
The outer most symbol table (0) contains predefined identifiers, such
as "int", "float". Each new scope needs its own symbol table.
3
Implementing symbol table structure
One strategy is to use a stack of symbol tables.
When a scope is entered, its symbol table is put on top of stack.
/* Java */ /* C++ */
class who { public int who( ) {
private String who; int n = 10;
for(int k=1;;k<10) {
public int who( ) { int n = 10*k;
... sum = sum + n;
} }
7
Symbol table structure evaluated
Which organization is better?
Table of linked lists is simpler (C, Pascal).
8
Ada example
(global symbol table:)
name bindings
ex procedure
symtab
name bindings
x integer
y character
procedure
p
symtab
name bindings
x fl oat
block
A
symtab
name bindings
y array (1..10) of
integer
9
Overloading
Overloading is a property of symbol tables that allows
them to successfully handle declarations that use the
same name within the same scope.
int max( int a, int b ) { return ( a > b ) ? a : b; }
float max ( int a, int b ) { return (a > b ) ? (float) a : (float)b; }
int max( int a, int b, int c ) { return ( max(a,b) > max(b,c) ) ? max(a,b) : ...
}
float max ( float a, float b ) { return (a > b ) ? a : b; }
int main( ) {
float y = max( 3, 4);
max( 2.5, 7 );
10
Overloading
It is the job of the symbol table to pick the correct
choice from among the declarations for the same name
in the same scope.
This is called overload resolution.
It must do so by using extra information, typically the
data type of each declaration, which it compares to the
probable type at the use site, picking the best match.
If it cannot successfully do this, a static semantic error
occurs.
11
Overloading (2)
Overloading typically applies only to functions or methods.
Overloading must be distinguished from dynamic binding in an
OO language.
Overloading is made difficult by weak typing, particularly
automatic conversions.
In the presence of partially specified types, such as in ML,
overload resolution becomes even more difficult, which is why
ML disallows it.
Scheme disallows it for a different reason: there are no types on
which to base overload resolution, even during execution.
12
Overloading (3)
An example in Java:
public class Overload {
public static int max(int x, int y)
{ return x > y ? x : y;}
public static double max(double x, double y)
{ return x > y ? x : y;}
public static int max(int x, int y, int z)
{ return max(max(x,y),z);}
public static void main(String[] args)
{ System.out.println(max(1,2));
System.out.println(max(1,2,3));
System.out.println(max(4,1.3));
}
}
Adding more max functions that mix double and int parameters is
ok.
Adding functions that mix double and int return values is not!
13
Overloading (4)
C++ and Ada are even more challenging for overload resolution:
C++ allows many more automatic conversions,
In Ada the return type is also used to resolve overloading (Ada
can do this only because it allows no automatic conversions).
It is possible for languages to also keep different symbol tables for
different kinds of declarations.
In Java these are called "name spaces," and they also
represent a kind of overloading.
Java uses different name spaces for classes, methods,
variables, labels, and even packages.
14