Symbol Tables Louden gives a description of symbol
Symbol Tables • Louden gives a description of symbol tables and discusses both static and dynamic scope rules. • On the next slide, I present a brief program example to show dynamic scope rules work. • You can think of dynamic scope as introducing a binding of attributes to a name dynamically, as an executable statement. Thus, to understand the bindings of a variable at any time, you must know the history of computation that led to the execution of the current code. (This is complicated. ) • There are two traditional ways to represent a symbol table for dynamic scope: – Deep binding The symbol table is a LIFO list. A new binding is pushed for each declaration to associate the name with its new attributes) – Shallow binding (the symbol table is a hash from names to LIFO lists of definition. When a new declaration is encountered, it is pushed on the list associated with that single variable. Lecture #16 PLP Spring 2004, UF CISE 1
Possible Symbol Table Representations (Dynamic Scope) Shallow Binding Deep Binding int x; char y; xx : : int char, double, intchar, int y : int[10] void p(void) { double x; yy : : char int, int[10], charint, char x : double. . . { int y[10]; y : int p : void(*)(void). . . x : char q : void(*)(void) } } main : (*)() void q(void) q : void(*)(void) { int y; p(); p : void(*)(void) } main() y : char { char x; x : int q(); } What is the type of variable x within q? What would x’s type be if we called q from within p? Lecture #16 PLP Spring 2004, UF CISE 2
Static Binding • To implement static binding, the symbols declared in a given block are associated with that block lexically (when it is read) rather than dynamically (when it is executed). • The referencing environment is composed of a frame for each block. That frame provides a complete picture of the name bindings for that block (including names defined in outer blocks). • The structure of the frames for a program written in a block-structured language can be determined at compile time. Lecture #16 PLP Spring 2004, UF CISE 3
Static Symbol Table Construction procedure ex is x: integer; y: character; ex procedure p is x: float; A: declare y: array (1. . 10) of integer; … end p; procedure q is y: integer; … end q; x integer y character p procedure begin declare x: character; … end ex x float A block y Lecture #16 PLP Spring 2004, UF CISE array 4
Name Resolution and Overloading • We often encounter names that are overloaded, that is, they have multiple meanings. For example, in this class, there are two men named Patel. If I say, Mr. Patel, then its not clear which one I’m describing unless I give some contextual information. • Consider the operation + in Java on int values and on Strings. • Overload resolution is the activity of determining which of the possible meanings of a name is meant in a given context. Pay close attention to the example provided on page 153 that shows three max functions. • Overload resolution is closely related to coercions (automatic type conversions) which will be discussed later. • If we have double max(double, double) and int max(int, int), then in C++ we cannot determine what is meant by max(1. 2, 1) because the coercions from double to int and int to double are equally acceptable. Other languages may prefer widening (avoiding loss of precision) conversions to narrowing conversions (which lose precision). Lecture #16 PLP Spring 2004, UF CISE 5
User Defined Overloadings • If programmers can overload functions, it’s important they be able to understand the overload resolution rules. • Some languages restrict overloadings just to those functions whose names are given by identifiers (as opposed to token symbols). I think this limitation is needless and serves little purpose. Overloading confusion will arise in either case. • In most cases, only the argument types are used to determine the correct overloading to select, but in some languages (Ada) the result type is employed as well. Lecture #16 PLP Spring 2004, UF CISE 6
Allocation, Lifetime, Environment • We saw how the structure of the frames in the referencing environment for a block structured language can be determined statically. • When we execute a sequential block-structured program, we must create run-time representations of the frames (called activation records). These can be allocated on a stack. • The lifetime of a variable binding is determined by how long that binding remains active in this stack. • Pointer values can be allocated dynamically and pointers to such storage may be maintained in variables with different lifetimes. Thus, we cannot statically determine the lifetime of a pointer variable and a stack cannot suffice for maintaining such variables. Lecture #16 PLP Spring 2004, UF CISE 7
Storage Layout of a Typical Program static (global) data activation record stack available space heap Lecture #16 PLP Spring 2004, UF CISE 8
Variables and Constants • I don’t like Louden’s box and circle diagrams. They make it seem as if the name of a variable must be stored at run time. However, in most compiled programming language implementations, variables names do not appear anywhere at run time. • I much prefer simply to represent a variable’s location by labeling a box (the storage associated with the variable) with the name of the variable followed by a colon. • Louden makes the statement that a sharing assignment (such as Java uses) of the value of variable y to variable x (that is, x = y) has the same effect as *x = *y in C. This is completely wrong. Write a test program to verify this. • Constants are names associated with values that do not change during the execution of a program. Some constant values (such as strings and other large structures) may appear stored in data areas at run time. Other constants may appear only as immediate operands of instructions. • Some languages support constants whose values are initialized upon entry to a program unit (at the beginning of their lifetimes). This allows constant values to depend on the program’s execution. Lecture #16 PLP Spring 2004, UF CISE 9
Aliases, Dangling References, Garbage • An alias occurs when there are two names (selection paths) that denote the same object. Aliases can arise numerous ways: – reference parameter and global variable – array elements subscripted by different but equal expressions – pointers pointing at the same object. • Aliases are closely related to dangling references. A dangling reference occurs when two selection paths reach the same dynamically allocated storage and one of those paths frees the storage. The other path’s reference to the storage is a dangling reference that may become invalid through storage reallocation. • Garbage is created when a selection path to dynamically allocated storage is modified without first freeing the referenced storage. Programmer management of storage allocation is quite difficult. The only way to be certain to avoid problems due to garbage is to have a garbage collector. Lecture #16 PLP Spring 2004, UF CISE 10
- Slides: 10