790 likes | 806 Views
Basic Semantics. Content. Names, attributes, and bindings Declarations, scope and the symbol table Overloading Allocation, lifetimes, and the environment Variables and constants Aliases, dangling references, and garbage. Names.
E N D
Content • Names, attributes, and bindings • Declarations, scope and the symbol table • Overloading • Allocation, lifetimes, and the environment • Variables and constants • Aliases, dangling references, and garbage
Names • A fundamental abstraction mechanism in a programming language is the use of names or identifiers to denote language entities or constructs • In most languages, constants, variables, and procedures can have names assigned by the programmer
Attributes • The meaning of a name is determined by the attributes associated with the name const int n = 5; /* n is a constant */ int x; /* x is a variable */ double f(int n) { … } /* f is a function */
Binding • The process of associating an attribute to a name is called binding const int n = 2; /* static binding */ int x; /* static binding */ x = 2; /* dynamic binding */ int *y; /* static binding */ y = new int; /* dynamic binding */
Binding Time • Language definition time: int, char, float • Language implementation time: sizeof(int) • Translation time: types of variables • Link time: code body of external functions • Load time: locations of global variables • Execution time: values of variables, locations of local variables
Symbol Table • Bindings can be maintained by a data structure called the symbol table • For an interpreter, the symbol table is a function that maps names to attributes Symbol Table Names Attributes
Symbol Table • For a compiler, the symbol table is a function that maps names to static attributes Symbol Table Names Static attributes
Environment & Memory • The dynamic attributes locations and values can be maintained by two functions environment (memory allocation) and memory(assignment)environment Names Locationsmemory Locations Values
Declarations • Declarations are a principal method for establishing bindings • Declarations are commonly associated with a block. These declarations are called local declarations of that block. • Declarations associated with an enclosing block are called nonlocal declarations • Declarations can also be external to any block. These declarations are called global declarations
An Example int x; /* global */ main() { int i, n; /* nonlocal */ i = 1; n = 10; while (i < n) { int m; /* local */ m = i++; f(m); } }
Scope • The scope of a binding is the region of the program over which the binding is maintained • We can also refer to the scope of a declaration if all the bindings established by the declaration have identical scopes • In a block-structured language, the scope of a binding is limited to the block in which its associated declaration appears. Such a scope rule is called static (or lexical) scope
int x; void p(void) { int i; … } void q(void) { int j; … } main() { int k;… } i x p j q main k An Example
Visibility • The local binding of a name will take precedence over the global and nonlocal binding of the name. This causes a scope hole for the global and nonlocal binding of the name • The visibility of a binding includes only those regions of a program where the binding applies
An Example int i; void p(void) { int i; … } void q(void) { int j; … } main() { int i;… } i i scope hole i i j scope hole i scope visibility
Scope Resolution Operators • Scope resolution operators can be used to access the bindings hidden by scope holes
An Example /* An example in C++ */ int x; class ex { int x; void f() { int x; x = 1; /* local variable x */ ex::x = 2; /* x field of class ex */ ::x = 3; /* global variable x */ } void g() { x = 4; /* x field of class ex */ } };
Scope Across Files • In C, global variable declarations can actually be accessed across filesFile 1: extern int x; /* use x in other files */File 2: int x; /* x can be used by other files */File 2: static int x; /* x can only be used in this file */
The Symbol Table • A symbol table is like a dictionary for names: It must support insertion, lookup, and deletion of names with associated attributes • The maintenance of scope information in a statically scoped language with block structure requires that declarations be processed in a fashion like stack
An Example int x; char y; void p(void) { double x; … { int y[10]; …} } void q(void) { int y; … } main() { char x;… } int global x
An Example int x; char y; void p(void) { double x; … { int y[10]; …} } void q(void) { int y; … } main() { char x;… } int global x char global y
An Example int x; char y; void p(void) { double x; … { int y[10]; …} } void q(void) { int y; … } main() { char x;… } int global x char global y void function p
An Example int x; char y; void p(void) { double x; … { int y[10]; …} } void q(void) { int y; … } main() { char x;… } double local to p int global x char global y void function p
An Example int x; char y; void p(void) { double x; … { int y[10]; …} } void q(void) { int y; … } main() { char x;… } double local to p1 int global x int array local to p2 char global y void function p
An Example int x; char y; void p(void) { double x; … { int y[10]; …} } void q(void) { int y; … } main() { char x;… } int global x char global y void function p
An Example int x; char y; void p(void) { double x; … { int y[10]; …} } void q(void) { int y; … } main() { char x;… } int global x char global y void function p void function q
An Example int x; char y; void p(void) { double x; … { int y[10]; …} } void q(void) { int y; … } main() { char x;… } int global x int local to q char global y void function p void function q
An Example int x; char y; void p(void) { double x; … { int y[10]; …} } void q(void) { int y; … } main() { char x;… } int global x char global y void function p void function q
An Example int x; char y; void p(void) { double x; … { int y[10]; …} } void q(void) { int y; … } main() { char x;… } int global x char global y void function p void function q void function main
An Example int x; char y; void p(void) { double x; … { int y[10]; …} } void q(void) { int y; … } main() { char x;… } char local to main int global x char global y void function p void function q void function main
An Example int x; char y; void p(void) { double x; … { int y[10]; …} } void q(void) { int y; … } main() { char x;… } int global x char global y void function p void function q void function main
Symbol Tables for Structures struct { int a; char b; double c; } x = {1,'a',2.5}; void p(void) { struct { double a; int b; char c; } y = {1.2,2,'b'}; printf("%d, %c, %g\n",x.a,x.b,x.c); printf("%f, %d, %c\n",y.a,y.b,y.c); } main() { p(); return 0; } struct global a int x char b c double void function p a double int b struct local to p y c char
Nested Procedures procedure ex is x: integer := 1; y: character := ‘a’; procedure p is x: float := 2.5; begin put(y); new_line; A: declare y: array (1..10) of integer; begin y(1) := 2; put(y(1)); new_line; put(ex.y); new_line; end A; end p; procedure q is y: integer := 42; begin put(x); new_line; p; end q; begin B: declare x: character := ‘b’; begin q; put(ex.x); new_line; end B; end ex;
Nested Procedures ex procedure x integer x float y character block A procedure p y array of integer procedure y q integer block x B character
Dynamic Scope • Using dynamic scope, the binding of a nonlocal name is determined according to the calling ancestors during the execution instead of the static program text
An Example: Static Scope int x = 1; char y = ‘a’; void p(void) { double x = 2.5; printf(“%c\n”, y); { int y[10]; …} } void q(void) { int y = 42; printf(“%d\n”, x); p(); } main() { char x = ‘b’;q(); return 0; } a 1
An Example: Dynamic Scope int = 1 global int x = 1; char y = ‘a’; void p(void) { double x = 2.5; printf(“%c\n”, y); { int y[10]; …} } void q(void) { int y = 42; printf(“%d\n”, x); p(); } main() { char x = ‘b’;q(); return 0; } x char = ‘a’ global y void function p void function q void function main
An Example char = ‘b’ in main int = 1 global int x = 1; char y = ‘a’; void p(void) { double x = 2.5; printf(“%c\n”, y); { int y[10]; …} } void q(void) { int y = 42; printf(“%d\n”, x); p(); } main() { char x = ‘b’;q(); return 0; } x char = ‘a’ global y void function p void function q void function main
An Example char = ‘b’ in main int = 1 global int x = 1; char y = ‘a’; void p(void) { double x = 2.5; printf(“%c\n”, y); { int y[10]; …} } void q(void) { int y = 42; printf(“%d\n”, x); p(); } main() { char x = ‘b’;q(); return 0; } x int = 42 in q char = ‘a’ global y void function p void function q 98 void function main
An Example double = 2.5 in p char = ‘b’ in main int = 1 global int x = 1; char y = ‘a’; void p(void) { double x = 2.5; printf(“%c\n”, y); { int y[10]; …} } void q(void) { int y = 42; printf(“%d\n”, x); p(); } main() { char x = ‘b’;q(); return 0; } x int = 42 in q char = ‘a’ global y * void function p void function q void function main
Issue of Dynamic Scope • When a nonlocal name is used in an expression or statement, the declaration that applies to that name cannot be determined by simply reading the program • Static typing and dynamic scoping are inherently incompatible • Languages that use dynamic scope are Lisp, APL, Snobol, Perl
Overloading • An operator or function name is overloaded if it is used to refer to two or more different things in the same scope 2 + 3 integer addition 2.1 + 3.2 floating-point addition 2 + 3.2 error or floating-point addition by automatically converting 2 to 2.0
Overload Resolution • Overloading of operators or function names can be resolved by checking the number of parameters (or operands) and the data types of the parameters (or operands)int max(int x, int y); max(2, 3);double max(double x, double y); max(2.1, 3.2);int max(int x, int y, int z); max(1, 3, 2);
Overloading & Automatic Conversion • Automatic conversions complicate the process of overload resolutionmax(2.1, 3); C++: error, both conversions are allowed Ada: error, no conversion is allowed Java: only int to double is allowed
Overloading & Automatic Conversion • Since there are no automatic conversions in Ada, the return type of a function can also be used to resolve overloadingfunction max(x:integer; y:integer) return intfunction max(x:integer; y:integer) return floata: integer;b: float;a := max(2, 3); -- call to max #1b := max(2, 3); -- call to max #2
Operator Overloading • Ada and C++ also allow built-in operators to be extended by overloading • We must accept the syntactic properties of the operator, i.e., we cannot change their associativity or precedence
An Example typedef struct { int i; double d; } IntDouble; bool operator < (IntDouble x, IntDouble y) { return x.i < y.i && x.d < y.d; } IntDouble operator + (IntDouble x, IntDouble y) { IntDouble z; z.i = x.i + y.i; z.d = x.d + y.d; return z; } int main() { IntDouble x = {1, 2.1}, y = {5, 3.4}; if (x < y) x = x + y; else y = x + y; cout << x.i << " " << x.d << endl; return 0; }
Name Overloading • A name can be used to refer to completely different things class A { AA(AA) { A: for(;;) { if (A.A(A) == A) break A; } return A; } }
Environment • Depending on the language, the environment may be constructed statically (at load time), dynamically (at execution time), or a mixture of the two • FORTRAN uses a complete static environment • LISP uses a complete dynamic environment • C, C++, Ada, Java use both
Location Allocation • Typically, global variables are allocated statically • Local variables are usually allocated automatically and dynamically in stack-based fashion • The lifetime or extent of an allocated location is the duration of its allocation in the environment