330 likes | 539 Views
Names, Scopes and Bindings. Aaron Bloomfield CS 415 Fall 2005 1. Binding. A binding is an association between two things, such as a name and the thing it represents Example: int x When this statement is compiled, x is bound to a memory space. Binding Time.
E N D
Names, Scopes and Bindings Aaron Bloomfield CS 415 Fall 20051
Binding • A binding is an association between two things, such as a name and the thing it represents • Example: int x • When this statement is compiled, x is bound to a memory space
Binding Time • The time at which the association between two items is created • There are many different binding times that can be implemented: • Language design time • Language implementation time • Program writing time • Compile time • Link time • Load time • Run time
Binding Times • Language design time • These are control flow constructs, primitives, and other semantics • Think of reserved words: i.e. if, for, else, while, etc. • Language implementation time • These include I/O couplings • System dependent things such as max heap and stack sizes • Think of constants: MAX_INT, etc. • In Algol, this included the I/O procedure names
Binding Times • Program writing time • When you the programmer choose data structures and names • Compile time • The compiler maps high level constructs to machine code • Think of the assembly for a compiled procedure • Link time • Separate compilation means not every part of a program has to be compiled at the same time • Libraries are linked to your program and bound
Binding Times • Load time • For primitive operating systems • Binds physical addresses • Most OSes do this during link time with virtual addresses • Run time • Very broad term covering the span of execution • Values to variable bindings occur here
Binding Times • Very important to design and implementation of programming languages • Early bindings = greater efficiency • Later bindings = greater flexibility
Scope rules • The textual region of the program in which a binding is active is its scope • Static scoping is more common in modern programming langauges • Found in C, C++, Pascal
Static scope • All scoping can be determined at compile time • Most often this is done with a textual top-to-bottom scan • Most basic static scope rule: everything has global scope
Fortran Implementation • Global scopes • Local scopes limited to their subroutines • Local variable scope extends through the execution of the subroutine unless the variable is saved
Nested Subroutines • Introduced in Algol 60 and is found in some modern languages • Pascal, Ada • Subroutines can be declared inside other subroutines • This confuses the scope rules even more
Closest Nested Scope Rule • A variable declared has its scope in the current subroutine, and any internally nested subroutines, unless there is a definition with the same name more locally
Closest Nested Scope Rule Example procedure level1() variable x; procedure level2() variable x; begin (* level 2 code *) end begin (* level 1 code *) end procedure outside() begin (*outside code*) end
Dynamic Scope Rules • Generally less complicated to implement • The “current” binding for a name is the one most recently encountered during execution, and not yet destroyed by returning from its scope • APL, Snobol, early dialects of Lisp, and Perl have dynamic scoping
Implications • Semantic rules are dynamic rather than static • Type checking is generally deferred until run time • To accommodate these rules, dynamically scoped languages are most often interpreted • Very easy for an interpreter to look up the meaning of a name • All that is needed is a stack of declarations • However, it makes it harder to understand programs • The modern consensus seems to be that dynamic scoping is usually a bad idea
Dynamic scope example int print_base = 10; print_integer (int n) { switch (print_base) { case 10: ... case 8: ... case 16: ... case 2: ... } } foo { print_integer (10); int print_base = 16; print_integer (10); print_base = 2; print_integer (4); }
Alternatives to achieve the same result • Overloading and optional parameters • print_integer takes an optional parameter (one with a default value) that specifies the base • print_integer is overloaded: one form prints in decimal, the other form takes an additional parameter that specifies the base • Better or worse than dynamic scoping?
More implications • With dynamic scoping, no program fragment that makes use of non-local names is guaranteed a predictable referencing environment
Referencing Environments • A referencing environment is the complete set of bindings in effect at a given point in a program • Static scope rules: • Referencing environment dependent on lexical nesting of program blocks • Dynamic scope rules: • Referencing environment dependent on the order in which declarations are reached
What about references to functions? • When are dynamic scope rules applied? • When the function is called? • Shallow binding • When the reference is created? • Deep binding
int max_score; float scale_score (int raw_score) { return (float) raw_score / (float) max_score; } float highest_score (int[] scores, function_ptr scaling_function) { float max_score = 0; foreach score in scores { float percent = scaling_function (score); if ( percent > max_score ) max_score = percent; } return max_score; } main() { max_score = 50; int[] scores = ... print highest_score (scores, scale_score); } function is called reference is created
Deep Binding • Generally the default in lexically scoped languages • Dynamically scoped languages tend to use shallow binding
Implementing Deep Binding • Subroutine Closure
The Symbol Table • During compilation, keeps track of all the identifiers • Only matters in a statically scoped language! • Why? • Basic functions: lookup() and insert() • Must keep track of scope as well • Often by enter_scope() and leave_scope() functions
Activation records • When a subroutine starts, a number of things have to be stored: • A copy of the parameters (pass by value) • The return address when the subroutine exits • Local variables in the subroutine • Etc. • This data is called the activation record • Fortran (up until 90) only had space for one activation record • Thus, you couldn’t call more than one subroutine at a time
Recursive vs. Iterative • Ocaml will recognize tail recursion and convert it to a while loop: let foo x = if x = 0 then 0 else 1 + foo (x-1);; • A call to foo 1000000 would require one million activation records if called recursively • That’s many, many megs of memory! • In addition to the time spent creating them and branching to the next recursive call of the subroutine • An iterative solution avoids all that • Although there is still the branch for the loop
Lifetimes Lifetime = time from creation to deletion • Bindings • Objects
Storage Management • Static - absolute address, retained throughout program • Stack - LIFO, subroutine calls • Heap – allocation/deallocation at arbitrary times
Heap Management • Free list
Who Manages the Heap? • User • System
Garbage Collection • Mark and Sweep • Reference Counting