• Static checking and symbol table

advertisement
• Static checking and symbol table
• Chapter 6, Chapter 7.6 and Chapter 8.2
• Static checking: check whether the program follows
both the syntactic and semantic conventions at
compile time (versus dynamic checking -- check at
run time).
• Examples of static checking
– Type checks:
– Flow of control checks
int a, b[10], c;
…
a = b + c;
main {
int I
….
I++;
break;
}
– Examples of static checks
– uniqueness check:
– defined before use:
– name related check:
main() {
int i, j;
double i, j;
….
}
main() {
int i;
i1 = 0;
….
}
LOOPA:
LOOP
EXIT WHEN I=N
I=I+1;
END LOOP LOOPB;
– Some checks can only be done at runtime:
• Array-bound checking in java:
a[i] = 0;
– To perform static checks, semantic information
must be recorded in some place -- symbol table.
• Grammar specifies the syntax, additional (semantic)
information, sometimes called attributes, must be
recorded in symbol table for all identifiers.
• Typically attributes in a symbol table entry include
type and offset (where in the memory can I find this
variable?).
– Struct {int id; int type; int offset;} stentry;
• Organization of a symbol table:
– basic requirement: must be able to find the information
associated with a symbol (identifier) quickly.
– Example: array, link list, hash table.
– Provides two functions: enter(table, name, type, offset)
and lookup(name);
– Dealing with nested scope:
Program sort(input, output)
var a: array [0..10] of integers;
x: integer;
procedure readarray
var x : real;
begin …. x …. End
procedure quicksort(i, j)
begin … x … end
begin … x … end
main() {
int a, b;
a = 0;
{
int a;
a = 1;
}
printf(“a = %d\n”, a);
}
– How to organize the symbol table?
– How to do lookup and enter?
• One symbol table for each scope (procedure, blocks)?
• Maintain a stack of symbol tables for lookup/enter
• Symbol tables for sort:
nil header
a ...
x ...
readarray
quicksort
header
x ….
Symbol table for readarray
Symbol table for sort
header
Symbol table for quicksort
• How does the compiler created the symbol table?
– First let us consider the simple case: no nested scope, every thing
entered into one symbol table: table by using
• enter (table, id, type, offset)
– grammar:
P ->D
D ->D; D
D ->id : T
T -> integer
T ->real
T ->array [num] of T
T ->^T
I : array [10] of integer;
j : real;
k : integer
I array(10, integer)
j real
k integer
0
40
48
P -> {offset = 0;} D
D ->D; D
D ->id : T {enter(table, id.name, T.type, offset);
offset:= offset + T.width}
T -> integer {T.type = integer; T.width = 4}
T ->real {T.type = real; T.width = 8;}
T ->array [num] of T1 {T.type = array(num.val, T1.type);
T.width = num.val * T1.width}
T ->^T1 {T.type = pointer(T1.type); T.width = 4;}
– Now consider the case when you have nested
procedures (blocks can be considered as special
procedures)
• must maintain a stack of symbol tables, create new ones when
entering new procedure
• must reset offset when entering new procedures (a stack of
offsets)
• Let us also compute the total size of a table
– Grammar:
P->D
D ->D; D
D->id : T
D->proc id; D; S
T ->integer | real | array[num] of T | ^T
• mktable(previous): make a new table, properly set all links and
related information.
• Enter(table, name, type, offset).
• Addwidth(table, width): compute all memory needed by the
symbol table.
• Enterproc(table, name, newtable): enter the procedure name
with its symbol table into the old table.
– Grammar:
P->{t=mktable(nil); push(t, tblptr);push(0, offset);}D
{addwidth(top(tblptr), top(offset))}
D ->D; D
D->id : T {enter(top(tblptr), id.name, T.type, top(offset));
top(offset) = top(offset) + T.width;}
D->proc id; {t:=mktable(top(tblptr));push(t, tblptr); push(0,
offset);}D; S {t:= top(tblptr);addwidth(t, top(offset));
pop(tblptr); pop(offset);enterproc(top(tblptr), id.name, t)}
• Dealing with structure (record):
– T ->record D end
– Make a new symbol table for all the fields in the record.
T->record
{
t=mktable(nil);
push(t, tblptr);
push(0, offset);
}
D end
{
T.type = record(top(tblptr));
T.width = top(offset);
pop(tblptr);
pop(offset);
}
Question:
How does allowing variable declaration at anywhere in a program (like
in C++, java) affect the maintenance of the symbol tables?
– Type checking
• Make sure operations can be performed on
operands.
• Make sure the types of actual arguments matches the
types of formal arguments.
• Need a type system to do the job.
– A type system is a collection of rules for assigning type
expression to the various parts of a program.
– The type system for a practical language can be complicated.
• Type checking of expressions:
P->D;E
D->D;D | id : T
T->char | integer | array[num] of T | ^T
E->literal | num | id | E mod E | E[E] | E^
P->D;E
D->D;D
D->id : T {enter(id.val, T.type);}
T->char {T.type = char;} | integer {T.type = integer;}
| array[num] of T1 {T.type = array(num.val, T1.type);}
| ^T1 {T.type = pointer(T1.type);}
E->literal {E.type = char;}
| num {E.type = integer;}
| id {E.type = lookup(id.val);}
| E1 mod E2
{if E1.type == integer && E2.type ==integer then E.type = integer;
else E.type =error;}
| E1[E2]
{if E1.type == array(s, t) && E2.type == integer then E.type = t;
else E.type =error;}
| E1^ {if E1.type == pointer(t) then E.type = t; else E.type =error;}
• Type checking for statements
S -> id := E
S -> if E then S1
S ->while E do S1
S->S1;S2
• Type checking for statements
S -> id := E
{if id.type == E.type then S.type = void;
else S.type = error;}
S -> if E then S1
{if E.type == boolean then S.type = S1.type;
else S.type = error;}
S ->while E do S1
{ if E.type == boolean then S.type = S1.type;
else S.type = error;}
S->S1;S2
{if S1.type == void and S2.type == void then
S.type = void;
else S.type = type_error;
}
• Type checking for functions:
T->T1->T2 /* function declaration */
{T.type = T1.type ->T2.type}
E->E1(E2) /* function call */
{if E1.type == t1.type->t2.type && E2.type == t1.type then
T.type = t2.type;
else T.type - error;
}
• Equivalence of type expressions
• Name equivalence - each type with different name is
different
• structural equivalence - names are replaced by the
type expressions they define
• Example:
type link = ^cell;
var next : link
last : link
p: ^cell
• Is structural equivalence good for C++?
– Other things related to type.
• coercion: implicit type conversion:
– e.g. double x; ….x = 1;
• overloading:
– a function or operator can represent different operations in
different contexts.
• polymorphic functions:
– the body of a polymorphic function can be executed with
arguments of different types.
Download