#| Lexical scope examples ====================== (let ([y z]) (let ([z (+ y w)]) 5) ) - --------------------- { y, w} { z } ------------------------------------ { z, w } (let ([x 5]) (+ 1 x)) ------- { x } -------------------- {} (let ([x y]) (+ 1 x)) - ------- { x } { y} --------------------- {y, x } (let ([x x]) (+ 1 x)) - ------- { x } { x } -------------------- { x } (let ([x 10]) (let ([x x])(+ 1 x))) -- {}-------------------- {x} (from above) ---------------------------------- {} (let ([f (lambda (y) (+ z y))]) (f x)) ------- ----- { f, x } { z, y } ------------------------------------- { z, x } |# ;; ------------------------------------------------------------ ;; Implementing free-vars for mini-Scheme ;; Concrete syntax: ;; ::= ;; ::= ;; ::= (+ ) ;; ::= (let ([ ]) ) ;; ::= (let ([ (lambda () ))) ) ;; ::= ( ) ;; Abstract syntax: put a quote in front of the concrete syntax. ;; For example, the Scheme expression ;; (+ 1 2) ;; will be reprsented as the Scheme value ;; '(+ 1 2) ;; which is shorthand for ;; (list '+ 1 2) ;; i.e., ;; (cons '+ (cons 1 (cons 2 '()))) ;; Abstract syntax: ;; ::= ;; ::= ;; ::= (list '+ ) ;; ::= (list 'let (list (list )) ) ;; ::= (list 'let (list ;; (list ;; (list 'lambda (list ) ))) ;; ) ;; ::= (list ) ;; Template: (define (free-vars e) (cond ;; Number [(number? e) ... e ...] ;; Id [(symbol? e) ... e ...] ;; Addition [(and (pair? e) (eq? '+ (car e))) ... (free-vars (cadr e)) ... ; first arg ... (free-vars (caddr e)) ...] ; second arg ;; Let non-function [(and (pair? e) (eq? 'let (car e)) (or (not (pair? (cadar (cadr e)))) (not (eq? 'lambda (caadar (cadr e)))))) ... (caar (cadr e)) ... ; local id ... (free-vars (cadar (cadr e))) ... ; local value ... (free-vars (caddr e)) ...] ; body ;; Let function [(and (pair? e) (eq? 'let (car e)) (pair? (cadar (cadr e))) (eq? 'lambda (caadar (cadr e)))) ... (caar (cadr e)) ... ; local id ... (caadr (cadar (cadr e))) ... ; func arg ... (free-vars (caddr (cadar (cadr e)))) ... ; func body ... (free-vars (caddr e)) ...] ; body ;; Function call [else ... (car e) ... ; rator ... (free-vars (cadr e)) ... ])) ; rand ;; Completed function: (define (free-vars e) (cond [(number? e) '()] [(symbol? e) (list e)] [(and (pair? e) (eq? '+ (car e))) (append (free-vars (cadr e)) (free-vars (caddr e)))] [(and (pair? e) (eq? 'let (car e)) (or (not (pair? (cadar (cadr e)))) (not (eq? 'lambda (caadar (cadr e)))))) (append (free-vars (cadar (cadr e))) (remove (free-vars (caddr e)) (caar (cadr e))))] [(and (pair? e) (eq? 'let (car e)) (pair? (cadar (cadr e))) (eq? 'lambda (caadar (cadr e)))) (append (remove (free-vars (caddr (cadar (cadr e)))) (caadr (cadar (cadr e)))) (remove (free-vars (caddr e)) (caar (cadr e))))] [else (cons (car e) (free-vars (cadr e)))])) (define (remove l s) (cond [(null? l) '()] [else (cond [(eq? (car l) s) (remove (cdr l) s)] [else (cons (car l) (remove (cdr l) s))])])) (free-vars '5) (free-vars 'x) (free-vars '(let ([x z]) x)) (free-vars '(let ([x 5]) (y x))) (free-vars '(let ([x (lambda (x) (+ x y))]) z)) ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; The code above is UGLY and UNREADABLE. ;; Make it better by lifting out tests and ;; part-extractors: ;; New template: (define (free-vars e) (cond [(number-expr? e) ... e ...] [(id-expr? e) ... e ...] [(plus-expr? e) ... (free-vars (plus-expr-first e)) ... ... (free-vars (plus-expr-second e)) ...] [(let-val-expr? e) ... (let-val-expr-id e) ... ... (free-vars (let-val-expr-val e)) ... ... (free-vars (let-val-expr-body e)) ...] [(let-func-expr? e) ... (let-func-expr-id e) ... ... (let-func-expr-func-id e) ... ... (free-vars (let-func-expr-func-body e)) ... ... (free-vars (let-func-expr-body e)) ...] [(app-expr? e) ... (app-expr-rator e) ... ... (free-vars (app-expr-rand e)) ...])) (define (free-vars e) (cond [(number-expr? e) '()] [(id-expr? e) (list e)] [(plus-expr? e) (append (free-vars (plus-expr-first e)) (free-vars (plus-expr-second e)))] [(let-val-expr? e) (append (free-vars (let-val-expr-val e)) (remove (free-vars (let-val-expr-body e)) (let-val-expr-id e)))] [(let-func-expr? e) (append (remove (free-vars (let-func-expr-func-body e)) (let-func-expr-func-id e)) (remove (free-vars (let-func-expr-body e)) (let-func-expr-id e)))] [(app-expr? e) (cond (app-expr-rator e) (free-vars (app-expr-rand e)))])) (define (number-expr? e) (number? e)) (define (id-expr? e) (symbol? e)) (define (plus-expr? e) (and (pair? e) (eq? '+ (car e)))) (define (let-val-expr? e) (and (pair? e) (eq? 'let (car e)) (or (not (pair? (cadar (cadr e)))) (not (eq? 'lambda (caadar (cadr e))))))) (define (let-func-expr? e) (and (pair? e) (eq? 'let (car e)) (pair? (cadar (cadr e))) (eq? 'lambda (caadar (cadr e))))) (define (app-expr? e) (not (or (number-expr? e) (id-expr? e) (plus-expr? e) (let-val-expr? e) (let-func-expr? e)))) (define (plus-expr-first e) (cadr e)) (define (plus-expr-second e) (caddr e)) (define (let-val-expr-id e) (caar (cadr e))) (define (let-val-expr-val e) (cadar (cadr e))) (define (let-val-expr-body e) (caddr e)) (define (let-func-expr-id e) (caar (cadr e))) (define (let-func-expr-func-id e) (caadr (cadar (cadr e)))) (define (let-func-expr-func-body e) (caddr (cadar (cadr e)))) (define (let-func-expr-body e) (caddr e)) (define (app-expr-rator e) (car e)) (define (app-expr-rand e) (cadr e)) ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; The revised code is now readable, but making ;; all those predicate and extractor functions ;; is painful and error-prone. ;; `define-datatype' does it automatically: (define-datatype expr expr? (number (val number?)) (id (val symbol?)) (plus (first expr?) (second expr?)) (let-val (id symbol?) (val expr?) (body expr?)) (let-func (id symbol?) (func-id symbol?) (func-expr expr?) (body expr?)) (app (rator symbol?) (rand expr?))) ;; New abstract syntax: ;; ::= (number ) ;; ::= (id ) ;; ::= (plus ) ;; ::= (let-val ) ;; ::= (let-func ) ;; ::= (app ) ;; Final template: (define (free-vars e) (cases expr e [number (val) ... val ...] [id (name) ... name ...] [plus (first second) ... (free-vars first) ... ... (free-vars second) ...] [let-val (id val body) ... id ... ... (free-vars val) ... ... (free-vars body) ...] [let-func (id func-id func-body body) ... id ... ... func-id ... ... (free-vars func-body) ... ... (free-vars body) ...] [app (rator rand) ... rator ... ... (free-vars rand) ...])) (define (free-vars e) (cases expr e [number (val) '()] [id (name) (list name)] [plus (first second) (append (free-vars first) (free-vars second))] [let-val (id val body) (append (free-vars val) (remove (free-vars body) id))] [let-func (id func-id func-body body) (append (remove (free-vars func-body) func-id) (remove (free-vars body) id))] [app (rator rand) (cons rator (free-vars rand))])) ;; '5 (free-vars (number 5)) ;; 'x (free-vars (id 'x)) ;; '(let ([x z]) x) (free-vars (let-val 'x (id 'z) (id 'x))) ;; (let ([x 5]) (y x)) (free-vars (let-val 'x (number 5) (app 'y (id 'x)))) ;; (let ([x (lambda (x) (+ x y))]) z) (free-vars (let-func 'x 'x (plus (id 'x) (id 'y)) (id 'z)))