Names, Scopes and Bindings Aaron Bloomfield CS 415 Fall 20051 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 2 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 3 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 4 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 5 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 6 Binding Times • Very important to design and implementation of programming languages – Early bindings = greater efficiency – Later bindings = greater flexibility 7 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 8 Static scope • All scoping can be determined at compile time • Most often this is done with a textual top-tobottom scan • Most basic static scope rule: everything has global scope 9 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 10 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 11 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 12 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 13 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 14 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 15 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); } 16 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? 17 More implications • With dynamic scoping, no program fragment that makes use of non-local names is guaranteed a predictable referencing environment 18 19 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 nesting of program blocks dependent on lexical • Dynamic scope rules: – Referencing environment dependent on the order in which declarations are reached 20 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 21 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; function is called } return max_score; } main() { reference is created max_score = 50; int[] scores = ... print highest_score (scores, scale_score); } 22 Deep Binding • Generally the languages default in lexically scoped • Dynamically scoped languages tend to use shallow binding 23 Implementing Deep Binding • Subroutine Closure 24 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 25 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 26 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 27 Lifetimes Lifetime = time from creation to deletion • Bindings • Objects 28 Storage Management • Static - absolute address, retained throughout program • Stack - LIFO, subroutine calls • Heap – allocation/deallocation at arbitrary times 29 Heap Management • Free list 30 Who Manages the Heap? • User • System 31 Garbage Collection • Mark and Sweep • Reference Counting 32 Aliases 33