;;;----------------------------------------------------------------------------
;;; Update to NODE.SCM abstractions to handle FUNREC:
;;;
;;; (funrec ((I1 (lambda (I*) E_1))
;;; ...
;;; (In (lambda (I*) E_n)))
;;; E_body)
;;;
(define (funrec-node? node)
(eq? (node-type node) 'funrec))
(define (make-funrec names lams body)
`(FUNREC ,(map (lambda (name lam) `(,name ,lam))
names
lams)
,body))
(define (funrec-names node)
(map first (second node)))
(define (funrec-lambdas node)
(map second (second node)))
(define (funrec-body node)
(third node))
(define *special-forms*
'(program lambda call if set! begin quote primop let
define ; Only should be a top-level
define-global global-ref global-set! ; Introduced by globalizing
call-generic call-closure ; Closure conversion
if-zero if-non-zero constant ; Data-conversion
integer boolean char string
unspecific null
error syscall
code call-code
funrec ; ***
))
(define (subnodes node)
(cond
((leaf-node? node) '())
((lambda-node? node) (list (lambda-body node)))
; ((simple-let-node? node)
; (cons (simple-let-body node) (simple-let-defs node)))
((let-node? node)
(cons (let-body node) (let-defs node)))
((application-node? node) (application-subexps node))
((primop-node? node) (primop-args node))
((syscall-node? node) (syscall-args node))
((assignment-node? node) (list (assignment-body node)))
((program-node? node)
(cons (program-body node) (map definition-body node)))
((funrec-node? node)
(cons (funrec-body node) (funrec-lambdas node)))
(else (node-subexps node))))
(define (subnode-map fn node)
;;
;; For compound (non-leaf) nodes, return a new compound node in which
;; FN has been applied to each subnode. Has no effect on leaf nodes.
;;
(cond
((leaf-node? node) node)
((lambda-node? node)
(make-lambda (lambda-formals node)
(fn (lambda-body node))))
((let-node? node)
(make-let (let-names node)
(map fn (let-defs node))
(fn (let-body node))))
((assignment-node? node)
(new-assignment node (fn (assignment-body node))))
((primop-node? node)
(make-primop (primop-op node)
(map fn (primop-args node))))
((syscall-node? node)
(make-syscall (syscall-op node)
(map fn (syscall-args node))))
((program-node? node)
(let ((defs (program-defs node)))
(make-program
(map new-definition defs (map (compose fn definition-body) defs))
(fn (program-body node)))))
;;; *** NEW ***
((funrec-node? node)
(make-funrec (funrec-names node)
(map fn (funrec-lambdas node))
(fn (funrec-body node))))
;; ************
(else (make-node (node-keyword node)
(map fn (node-subexps node))))
))
(define (subnode-map-receive fn node leaf receive)
;;
;; Generalized version of SUBNODE-MAP that allows the return of
;; multiple results in a recursive tree accumulation over a node tree.
;; At any node, applies RECEIVE to:
;;
;; (i) A node-making procedure specialized for the node that
;; expects new subnodes as arguments.
;; (ii) A rest arg that is the result of applying FN to all of the
;; subnodes. In general, FN will return a compound structure only
;; one component of which is the new node.
;;
;; Since leaf nodes have no subnodes, the LEAF procedure is applied
;; to leaf nodes to generate the appropriate base case for the
;; recursive tree accumuation.
;;
(cond
((leaf-node? node)
(receive (lambda (ignore) node)
(leaf node)))
((lambda-node? node)
(receive (lambda (body) (make-lambda (lambda-formals node) body))
(fn (lambda-body node))))
((let-node? node)
(apply receive
(lambda (new-body . new-defs)
(make-let (let-names node) new-defs new-body))
(map fn (cons (let-body node) (let-defs node)))))
((assignment-node? node)
(receive (lambda (body) (new-assignment node body))
(fn (assignment-body node))))
((primop-node? node)
(apply receive
(lambda new-args (make-primop (primop-op node) new-args))
(map fn (primop-args node))))
((syscall-node? node)
(apply receive
(lambda new-args (make-syscall (syscall-op node) new-args))
(map fn (syscall-args node))))
((program-node? node)
(let ((defs (program-defs node)))
(apply receive
(lambda (new-body . new-def-bodies)
(make-program (map new-definition defs new-def-bodies)
new-body))
(map fn (cons (program-body node)
(map define-body defs))))))
;; *** NEW ***
((funrec-node? node)
(apply receive
(lambda (new-body . new-lambdas)
(make-funrec (funrec-names node)
new-lambdas
new-body))
(map fn (cons (funrec-body node) (funrec-lambdas node)))))
;; ***********
(else
(apply receive
(lambda new-subnodes (make-node (node-keyword node) new-subnodes))
(map fn (node-subexps node))))
))
(define (rewrite vars rewrite-ref rewrite-set! node)
;;
;; A simple substitution routine.
;; For each X in the set VARS of variable names:
;; (i) replace every reference to X in NODE by the result of
;; (REWRITE-REF X).
;; (ii) replace every (SET! X
) in NODE by the result of
;; (REWRITE-SET! X )
;;
;; Neither REWRITE-REF and REWRITE-SET! should return nodes with names
;; that might be captured by enclosing lambdas.
;;
(let walk ((vars vars)
(node node))
(cond
((set-empty? vars) node) ;; Optimization
((and (var-node? node) (set-member? (var-name node) vars))
(rewrite-ref (var-name node)))
((and (set!-node? node) (set-member? (set!-name node) vars))
(rewrite-set! (set!-name node)
(walk vars (set!-body node))))
((lambda-node? node)
(let ((formals (lambda-formals node)))
(make-lambda formals
(walk (set-difference vars (list->set formals))
(lambda-body node)))))
((let-node? node)
(let ((names (let-names node)))
(make-let names
(map (lambda (def) (walk vars def))
(let-defs node))
(walk (set-difference vars (list->set names))
(let-body node)))))
((program-node? node)
(let* ((defs (program-defs node))
(names (map definition-names defs))
(new-vars (set-difference vars (list->set names))))
(make-program
(map (lambda (def)
(new-definition def
(walk new-vars (definition-body def))))
defs)
(walk new-vars (program-body node)))))
;; *** NEW ***
((funrec-node? node)
(let ((new-vars (set-difference vars
(list->set (funrec-names node)))))
(make-funrec (funrec-names node)
(map (lambda (def) (walk new-vars def))
(funrec-lambdas node))
(walk new-vars (funrec-body node)))))
;; **********
(else (subnode-map (lambda (n) (walk vars n)) node))
)))
(define (free-vars node)
(cond
((var-node? node) (set-singleton (var-name node)))
((assignment-node? node)
(set-union (set-singleton (assignment-name node))
(free-vars (assignment-body node))))
((lambda-node? node)
(set-difference (free-vars (lambda-body node))
(list->set (lambda-formals node))))
((let-node? node)
(set-union (map-union free-vars (let-defs node))
(set-difference (free-vars (let-body node))
(list->set (let-names node)))))
((program-node? node)
(set-difference
(set-union
(map-union free-vars (map define-body (program-defs node)))
(free-vars (program-body node)))
(list->set (map define-name (program-defs node)))))
;; *** NEW ***
((funrec-node? node)
(set-difference
(set-union (map-union free-vars (funrec-lambdas node))
(free-vars (funrec-body node)))
(list->set (funrec-names node))))
;; ***********
(else (map-union free-vars (subnodes node)))
))
(define (free-mutables node)
;;
;; New function (not in NODE.SCM).
;; Finds all free vars in node that are assigned via SET!
;;
(cond
((var-node? node) the-empty-set)
((assignment-node? node)
(set-union (set-singleton (assignment-name node))
(free-mutables (assignment-body node))))
((lambda-node? node)
(set-difference (free-mutables (lambda-body node))
(list->set (lambda-formals node))))
((let-node? node)
(set-union (map-union free-mutables (let-defs node))
(set-difference (free-mutables (let-body node))
(list->set (let-names node)))))
((program-node? node)
(set-difference
(set-union
(map-union free-mutables (map define-body (program-defs node)))
(free-mutables (program-body node)))
(list->set (map define-name (program-defs node)))))
;; *** NEW ***
((funrec-node? node)
(set-difference
(set-union (map-union free-mutables (funrec-lambdas node))
(free-mutables (funrec-body node)))
(list->set (funrec-names node))))
;; ***********
(else (map-union free-mutables (subnodes node)))
))
;;;----------------------------------------------------------------------------
;;; New implementation of closures supporting %CLOSURE-SHIFT to work
(define closure-tag '(closure))
(define (%closure . elts)
(vector closure-tag 0 (apply vector elts)))
(define (%closure-ref closure index)
(closure-check-index closure index)
(vector-ref (vector-ref closure 2)
(+ index (vector-ref closure 1))))
(define (%closure-set! closure index new)
(closure-check-index closure index)
(vector-set! (vector-ref closure 2)
(+ index (vector-ref closure 1))
new))
(define (closure-check-index closure index)
(let ((elts (vector-ref closure 2))
(real-index (+ index (vector-ref closure 1))))
(if (or (< real-index 0)
(>= real-index (vector-length elts)))
(error "CLOSURE: index out of range -- " index))))
(define (%closure-shift closure offset)
;;
;; Effectively returns a pointer into the middle of the closure.
;; Note that the result shares structure with the input.
;;
(let ((new-offset (+ offset (vector-ref closure 1)))
(elts (vector-ref closure 2)))
(if (or (< new-offset 0)
(>= new-offset (vector-length elts)))
(error "CLOSURE-SHIFT: Offset out of range -- " (list closure offset))
(vector closure-tag new-offset elts))))
(define (%closure? obj)
(and (vector? obj)
(= (vector-length obj) 3)
(eq? (vector-ref obj 0) closure-tag)))
;;;----------------------------------------------------------------------------
;;; Extension to DESUGAR.SCM to catch assignments to FUNREC names
;;; (which are illegal).
(define-sugar 'funrec
(lambda (exp)
(define (lambda-exp? exp)
(and (list? exp)
(>= (length exp) 3)
(eq? (car exp) 'lambda)))
(define (check-lambda exp)
(if (not (lambda-exp? exp))
(error "FUNREC: non-lambda expression" exp)
exp))
(let ((bindings (second exp))
(body-exps (cddr exp)))
(let ((names (map first bindings))
(lams (map (compose check-lambda second) bindings)))
(let ((new-lams (map desugar lams))
(new-body (make-desugared-begin
(map desugar body-exps))))
(let ((illegal-mutables
(set-intersection
(list->set names)
(map-union free-mutables
(cons new-body new-lams)))))
(if (not (set-empty? illegal-mutables))
(error "SYNTAX ERROR: FUNREC contains illegal assignments"
illegal-mutables)
`(FUNREC ,(map (lambda (name lam) `(,name ,lam))
names
new-lams)
,new-body))))))))
;;;----------------------------------------------------------------------------
;;; GLOBALIZE and ASSIGNMENT CONVERSION phases don't need to change.
;;;----------------------------------------------------------------------------
;;; CPS-CONVERSION phase:
;;; Modify CPS to dispatch to CPS-FUNREC (below)
(define (cps node mcont)
;; MCONT here is a "meta-continuation" that maps a lettable value
;; (i.e., syntactic class W) into a syntactic continuation.
(cond
((leaf-node? node) (mcont node))
((lambda-node? node) (cps-lambda node mcont))
((let-node? node) (cps-let node mcont))
((application-node? node) (cps-application node mcont))
((conditional-node? node) (cps-conditional node mcont))
((assignment-node? node) (cps-assignment node mcont))
((primop-node? node) (cps-primop node mcont))
((syscall-node? node) (cps-syscall node mcont))
((program-node? node) (cps-program node mcont))
((funrec-node? node) (cps-funrec node mcont))
;; ((begin-node? node) (cps-begin node mcont)) ; No longer supported
(else (error "CPS: Don't know how to handle node:" node))))
(define (cps-funrec node mcont)
;; Patterned after CPS-PROGRAM:
(cps-list (funrec-lambdas node)
(lambda (Vs) ;; Guaranteed to be Vs because all are lambdas
(make-funrec (funrec-names node)
Vs
(cps (funrec-body node) mcont)))))
;;;----------------------------------------------------------------------------
;;; Extension to RUNTIME.SCM to make FUNREC desugar into a LETREC within Scheme
; The local version
(define-syntax define-syntax-global
(macro (name expander)
`(begin
(define-syntax ,name ,expander)
(syntax-table-define system-global-syntax-table ',name ,expander))))
; The exported version
(syntax-table-define system-global-syntax-table
'define-syntax-global
(macro (name expander)
`(begin
(define-syntax ,name ,expander)
(syntax-table-define system-global-syntax-table ',name ,expander))))
(define-syntax-global define-macro-global
(macro (pattern . body)
`(DEFINE-SYNTAX-GLOBAL ,(car pattern)
(MACRO ,(cdr pattern) ,@body))))
(define-macro-global (funrec bindings . body)
`(LETREC ,bindings ,@body))
;;;----------------------------------------------------------------------------
;;; Names for compiler passes
(define ->desugar (cascade initialize desugar abbreviate pp))
(define ->globalize (cascade initialize desugar globals/wrap abbreviate pp))
(define ->assign (cascade initialize desugar globals/wrap assignment-convert
abbreviate pp))
(define ->cps (cascade initialize desugar globals/wrap assignment-convert
cps-convert abbreviate pp))
;; Note: the following passes don't include an ORDER-CONVERT at the end,
;; but they could.
(define ->closures (cascade initialize desugar globals/wrap assignment-convert
cps-convert closurize abbreviate pp))
(define ->closures/no-cps
(cascade initialize desugar globals/wrap assignment-convert
closurize abbreviate pp))
(define ->lift (cascade initialize desugar globals/wrap assignment-convert
cps-convert closurize lift-convert
abbreviate pp))
(define ->data (cascade initialize desugar globals/wrap assignment-convert
cps-convert closurize lift-convert
data-convert data-unconvert abbreviate pp))
;;;----------------------------------------------------------------------------
;;; Code for the even/odd example:
(define even/odd
'(funrec ((even? (lambda (a) (if (= 0 a)
#t
(odd? (- a 1)))))
(odd? (lambda (b) (if (= 0 b)
#f
(even? (- b 1))))))
(even? 2)))