![]() | ![]() | ![]() | Symbol Table Details |
Now, to complete the discussion of our scheme for representing STApL programs, we must discuss more details of the types used in the symbol table.
As explained in the overview presented above, the symbol table is composed
of identifier descriptors and declaration descriptors. Identifier
descriptors are actually quite simple. There is one such descriptor
for each distinct identifier used in the program.3 A C language
structure specification for the type identdesc
used to store
identifier descriptors is shown in figure *.
typedef struct iddesc {
char *name; /* The character string for the identifier */
struct iddesc *hashlink; /* Link for hash chains used by scanner */
union dcldesc *declstack; /* Head pointer for stack of declarations */
/* in open scopes. */
} identdesc;
The name
field is just a pointer to the characters that form
the identifier. The hashlink
field is used to maintain lists of
identifier with the same hash value when building the hash table used
by the scanner. It will not be of concern to you when doing semantic
processing. The declstack
component is to be used as a pointer
to the head of the linked list representing the stack of declarations
of the identifier found in scopes that are still open. The scanner
initializes this field to NULL.
/* Enumeration type used to label the various type of declaration */
/* descriptors that can occur in the symbol table. */
typedef enum {
funcdecl, /* function declarations */
valdecl, /* names bound by value bindings */
formaldecl, /* Formal parameter names */
listdecl, /* Type names for list types */
recddecl, /* Type names for record types */
fielddecl, /* Component names of record type */
integertype /* Label for pseudo-declaration of integer */
} decltype;
/* All declartion descriptors contain the following components */
/*(although record component descriptors don't use them all.) */
#define COMMONFIELDS \
decltype type; /* Type of this declaration descriptor */ \
identdesc *ident; /* pointer to associated ident. descriptor */ \
int line; /* Line number at which decl. occurred. */ \
union dcldesc *levellink; /* list of decl.s made at nesting level */ \
int level; /* nesting level of this declaration */ \
union dcldesc * decllink; /* stack of active declarations of this ident */
/* Generic structure used to access common fields of decl. descriptors. */
struct unkdesc {
COMMONFIELDS
};
Declaration descriptors are more complex than identifier descriptors. Depending on the type of declaration involved ( a type definition or a function definition or a value declaration, etc.) different structures must be used. Accordingly, as with tree nodes, the type used to describe declaration descriptors is a union type. The C declaration for this union type and the declarations of the record types from which it is formed are shown in figure *. First, however, we must discuss some related definitions shown in figure *.
The most important portion of figure * is the
#define
for COMMONFIELDS
. While many distinct structure
types are used as declaration descriptors they all share several
common fields. This #define
specifies all these fields and is
in fact used to include the fields in each of the distinct structure
type definitions. As in the syntax tree definitions, a structure type
unkdesc
is provided to allow one to reference the common fields
of a declaration descriptor before the actual type of the descriptor
involved can be determined.
The first of the common fields is the type
field which holds an
element of the enumeration type decltype
(whose definition also
appears in the figure). The field ident
is used by all
declaration descriptors to hold a pointer back to the identifier
descriptor for the identifier associated with the declaration. The
line
component is used to hold the line number on which the declaration
occurred.
/* Structure used for value name declatation descriptors. */
struct valdesc { COMMONFIELDS
union dcldesc * valtype; /* decl. descriptor for the value's type */
};
/* Stucture used for function declaration descriptors. */
struct funcdesc { COMMONFIELDS
union dcldesc * formallist; /* head of list of this proc's formals. */
union dcldesc * retntype ; /* descriptor for type returned by func */
};
/* Structure used for formal parameter declaration descriptors. */
struct formaldesc { COMMONFIELDS
union dcldesc * valtype; /* decl. descriptor for the formal's type */
union dcldesc * formallink; /* link for list of func's formals. */
};
/* Structure used for list type declaration descriptors. */
struct listdesc { COMMONFIELDS
union dcldesc * elmnttype; /* decl. descriptor for list's element type */
};
/* Structure used for record type declaration descriptors. */
struct recorddesc { COMMONFIELDS
};
/* Structure used for record field name declartion descriptors. */
struct fielddesc { COMMONFIELDS
union dcldesc * hashlink; /* link pointer for hash chain */
union dcldesc * comptype; /* decl. descriptor for component's type */
union dcldesc * recdtype; /* decl. descriptor for type to which the */
/* component belong */
};
/* This union describes the type of all declaration descriptors */
typedef union dcldesc {
struct unkdesc unk;
struct funcdesc func;
struct valdesc val;
struct formaldesc formal;
struct listdesc list;
struct recorddesc record;
struct fielddesc field
} decldesc;
The next two common fields are not used in all declaration
descriptors. In particular, they are not used in descriptors for
record component names. The first of these two fields is
levellink
. This is used to link all of the declarations
found in an open scope together in a linked list. The second is
level
which holds the nesting level of the scope in which the
declaration occurred.
The last component in COMMONFIELDS
is decllink
. During
declaration processing, this field is used as the "next" pointer
for the linked list that is used to implement the stack of
declaration descriptors associated with a given identifier descriptor.
The head pointers for these stacks are stored in the declstack
components of identifier descriptors.
With this understanding of the common fields of all declaration descriptors, we can consider the various kinds of declaration descriptors used. Definitions of structure types for all these different sorts of declaration descriptors are shown in figure *.
These definitions are not complete. Later in the semester we will add additional fields. The fields included at this point are sufficient to enable you to complete this phase and to justify the distinct types of declaration descriptors used.
The six structure types used are:
valtype
field
is used to hold a pointer to the declaration descriptor of the
type associated with the expression used in the value binding.
formallist
component
points to a list of declaration descriptors stored as structures
of type formaldesc
. This list should contain the declaration
descriptors for all of the formal parameters associated with the
function. This list will be used to type check calls made to the
function. The retntype
field points to the declaration descriptor
for the function's return type.
valtype
component is used to
specify the type of the formal as in a valdesc
declaration
descriptor. The formallink
field is used to hold the `next'
pointers needed to maintain the list of all formal parameter
declarations associated with the function.
elmnttype
component is used
to hold a pointer to the declaration descriptor for the element type
of the list.
comptype
field is simply
used to indicate the type of the component as is the valtype
field of value and formal parameter declaration descriptors.
The recdtype
component is used to hold a back pointer to
the descriptor for the record type to which the component belongs.
Finally, the hashlink
field is used to build the chains of
descriptors that form the hash table used to look up the descriptor
associated with a reference to a record field name.
![]() | ![]() | ![]() | Symbol Table Details |