COS 441 - Lexical Scope - Feb 13, 1996

Context Sensitive Syntax

In most programming languages the set of valid programs is not context free. To ensure that a program is valid we must restrict the context free grammar in certain ways. For example, in Scheme Recall that we built a parser for a subset of Scheme using the following representations of abstract syntax.
(define-record Var (name))
(define-record Const (value))
(define-record Lam (formals body))
(define-record Ap (fun args))
Let us define a procedure check that determines if the parameters of every lambda expression in a program are distinct.
(define check
  (lambda (e)
    (cond ((Const? e) #t)
          ((Var? e) #t)
          ((Lam? e) (and (check-unique (Lam->formals e))
                         (check (Lam->body e))))
          ((Ap? e) (and (check (Ap->fun e))
                        (andmap check (Ap->args e)))))))

(define andmap
  (lambda (f l)
    (if (null? l)
        #t
        (and (f (car l))
             (andmap f (cdr l))))))

(define check-unique
  (lambda (names)
    (if (null? names)
        #t
        (and (not (member (car names) (cdr names)))
             (check-unique (cdr names))))))
We can use the syntactic form variant case to furhter simplify check.
(define check
  (lambda (e)
    (variant-case e
      (Const (value) #t)
      (Var (name) #t)
      (Lam (formals body) (and (check-unique formals) (check body)))
      (Ap (fun args) (and (check fun) (andmap check args))))))

Free Vars, Bound Vars, and Lexical Scope

In Scheme the expression (+ 1 2) is not necessarily equal to 3. The reason is that + may be locally bound. For example, consider
((lambda (+) (+ 1 2)) -)
This expression appears to yield -1, but again - might be locally bound. Now what about:
(lambda (+) +)
This is an identity function, no matter where it appears in a program.

Certain constructs of any programming language are binders. The Scheme contructs that are binders are:

A binding occurence of a variable is an occurence positions.
(lambda (x) e)
         ^
(let ((x e)) e)
       ^
A bound occurence is any use of a bound variable. Consider the following.
(lambda (a) (a (lambda (a) a) a))
         1   2          3  4  5         
Occurrences 1 and 3 are binding occurrences, while 2, 4, 5 are bound occurrences. Uses 2 and 5 refer to binding 1, while use 4 refers to binding 3.

The scope of a binding is the region of text within its binder minus any regions which bind the same name. The bound variables of an expression are those variables that occur bound in an expression. The free variables of an expression are those variables that occur but are not bound. The previous example has no free variables. Since English is rather imprecise, let's make these definitions in math.

x occurs free in e if and only if

e = x
 OR x = (lambda (y[1] ... y[n]) e') and x occurs free in e' and x not in any y[i]
 OR x = (e[0] e[1] ... e[n]) and x occurs free in at least one of e[i]
Based on this definition we can write occurs-free.
(define occurs-free?
   (lambda (e x)
      (variant-case e
         (Var (name) (equal? name x))
         (Lam (formals body) (and (occurs-free? body x)
                                  (not (member x formals))))
         (Ap (fun args) (or (occurs-free? fun x)
                            (ormap (occurs-free? (lambda (a) (occurs-free? a x))
                                                 args)))))))

x occurs bound in e if and only if

e = (lambda (y[1] ... y[n]) e') and x = y[i] for some i or x occurs bound in e'
 OR (e[0] e[1] ... e[n]) and x occurs bound in at least one e[i]

Exercise