1 ;;; -*- lexical-binding: t -*-
2 ;;; cconv.el --- Closure conversion for statically scoped Emacs lisp.
4 ;; licence stuff will be added later(I don't know yet what to write here)
8 ;; This takes a piece of Elisp code, and eliminates all free variables from
9 ;; lambda expressions. The user entry points are cconv-closure-convert and
10 ;; cconv-closure-convert-toplevel(for toplevel forms).
11 ;; All macros should be expanded.
13 ;; Here is a brief explanation how this code works.
14 ;; Firstly, we analyse the tree by calling cconv-analyse-form.
15 ;; This function finds all mutated variables, all functions that are suitable
16 ;; for lambda lifting and all variables captured by closure. It passes the tree
17 ;; once, returning a list of three lists.
19 ;; Then we calculate the intersection of first and third lists returned by
20 ;; cconv-analyse form to find all mutated variables that are captured by
23 ;; Armed with this data, we call cconv-closure-convert-rec, that rewrites the
24 ;; tree recursivly, lifting lambdas where possible, building closures where it
25 ;; is needed and eliminating mutable variables used in closure.
27 ;; We do following replacements :
28 ;; (lambda (v1 ...) ... fv1 fv2 ...) => (lambda (v1 ... fv1 fv2 ) ... fv1 fv2 .)
29 ;; if the function is suitable for lambda lifting (if all calls are known)
31 ;; (function (lambda (v1 ...) ... fv ...)) =>
32 ;; (curry (lambda (env v1 ...) ... env ...) env)
33 ;; if the function has only 1 free variable
36 ;; (function (lambda (v1 ...) ... fv1 fv2 ...)) =>
37 ;; (curry (lambda (env v1 ..) .. (aref env 0) (aref env 1) ..) (vector fv1 fv2))
38 ;; if the function has 2 or more free variables
40 ;; If the function has no free variables, we don't do anything.
42 ;; If the variable is mutable(updated by setq), and it is used in closure
43 ;; we wrap it's definition with list: (list var) and we also replace
44 ;; var => (car var) wherever this variable is used, and also
45 ;; (setq var value) => (setcar var value) where it is updated.
47 ;; If defun argument is closure mutable, we letbind it and wrap it's
48 ;; definition with list.
49 ;; (defun foo (... mutable-arg ...) ...) =>
50 ;; (defun foo (... m-arg ...) (let ((m-arg (list m-arg))) ...))
59 (eval-when-compile (require 'cl
))
61 (defconst cconv-liftwhen
3
62 "Try to do lambda lifting if the number of arguments + free variables
63 is less than this number.")
65 "List of mutated variables in current form")
66 (defvar cconv-captured
67 "List of closure captured variables in current form")
68 (defvar cconv-captured
+mutated
69 "An intersection between cconv-mutated and cconv-captured lists.")
70 (defvar cconv-lambda-candidates
71 "List of candidates for lambda lifting")
75 (defun cconv-freevars (form &optional fvrs
)
76 "Find all free variables of given form.
78 -- FORM is a piece of Elisp code after macroexpansion.
79 -- FVRS(optional) is a list of variables already found. Used for recursive tree
82 Returns a list of free variables."
83 ;; If a leaf in the tree is a symbol, but it is not a global variable, not a
84 ;; keyword, not 'nil or 't we consider this leaf as a variable.
85 ;; Free variables are the variables that are not declared above in this tree.
86 ;; For example free variables of (lambda (a1 a2 ..) body-forms) are
87 ;; free variables of body-forms excluding a1, a2 ..
88 ;; Free variables of (let ((v1 ..) (v2) ..)) body-forms) are
89 ;; free variables of body-forms excluding v1, v2 ...
92 ;; a list of free variables already found(FVRS) is passed in parameter
93 ;; to try to use cons or push where possible, and to minimize the usage
96 ;; This function can contain duplicates(because we use 'append instead
97 ;; of union of two sets - for performance reasons).
99 (`(let ,varsvalues .
,body-forms
) ; let special form
101 (dolist (exp body-forms
)
102 (setq fvrs-1
(cconv-freevars exp fvrs-1
)))
103 (dolist (elm varsvalues
)
105 (setq fvrs-1
(delq (car elm
) fvrs-1
))
106 (setq fvrs-1
(delq elm fvrs-1
))))
107 (setq fvrs
(append fvrs fvrs-1
))
108 (dolist (exp varsvalues
)
109 (when (listp exp
) (setq fvrs
(cconv-freevars (cadr exp
) fvrs
))))
112 (`(let* ,varsvalues .
,body-forms
) ; let* special form
115 (dolist (exp varsvalues
)
118 (setq fvrs-1
(cconv-freevars (cadr exp
) fvrs-1
))
119 (dolist (elm vrs
) (setq fvrs-1
(delq elm fvrs-1
)))
120 (push (car exp
) vrs
))
122 (dolist (elm vrs
) (setq fvrs-1
(delq elm fvrs-1
)))
124 (dolist (exp body-forms
)
125 (setq fvrs-1
(cconv-freevars exp fvrs-1
)))
126 (dolist (elm vrs
) (setq fvrs-1
(delq elm fvrs-1
)))
127 (append fvrs fvrs-1
)))
129 (`((lambda .
,_
) .
,_
) ; first element is lambda expression
130 (dolist (exp `((function ,(car form
)) .
,(cdr form
)))
131 (setq fvrs
(cconv-freevars exp fvrs
))) fvrs
)
133 (`(cond .
,cond-forms
) ; cond special form
134 (dolist (exp1 cond-forms
)
136 (setq fvrs
(cconv-freevars exp2 fvrs
)))) fvrs
)
138 (`(quote .
,_
) fvrs
) ; quote form
140 (`(function .
((lambda ,vars .
,body-forms
)))
141 (let ((functionform (cadr form
)) (fvrs-1 '()))
142 (dolist (exp body-forms
)
143 (setq fvrs-1
(cconv-freevars exp fvrs-1
)))
144 (dolist (elm vars
) (setq fvrs-1
(delq elm fvrs-1
)))
145 (append fvrs fvrs-1
))) ; function form
147 (`(function .
,_
) fvrs
) ; same as quote
149 (`(condition-case ,var
,protected-form .
,conditions-bodies
)
151 (setq fvrs-1
(cconv-freevars protected-form
'()))
152 (dolist (exp conditions-bodies
)
153 (setq fvrs-1
(cconv-freevars (cadr exp
) fvrs-1
)))
154 (setq fvrs-1
(delq var fvrs-1
))
155 (append fvrs fvrs-1
)))
157 (`(,(and sym
(or `defun
`defconst
`defvar
)) .
,_
)
158 ;; we call cconv-freevars only for functions(lambdas)
159 ;; defun, defconst, defvar are not allowed to be inside
160 ;; a function(lambda)
161 (error "Invalid form: %s inside a function" sym
))
163 (`(,_ .
,body-forms
) ; first element is a function or whatever
164 (dolist (exp body-forms
)
165 (setq fvrs
(cconv-freevars exp fvrs
))) fvrs
)
167 (_ (if (or (not (symbolp form
)) ; form is not a list
168 (special-variable-p form
)
175 (defun cconv-closure-convert (form &optional toplevel
)
176 ;; cconv-closure-convert-rec has a lot of parameters that are
177 ;; whether useless for user, whether they should contain
178 ;; specific data like a list of closure mutables or the list
179 ;; of lambdas suitable for lifting.
181 ;; That's why this function exists.
182 "Main entry point for non-toplevel forms.
183 -- FORM is a piece of Elisp code after macroexpansion.
184 -- TOPLEVEL(optional) is a boolean variable, true if we are at the root of AST
186 Returns a form where all lambdas don't have any free variables."
187 (let ((cconv-mutated '())
188 (cconv-lambda-candidates '())
190 (cconv-captured+mutated
'()))
191 ;; Analyse form - fill these variables with new information
192 (cconv-analyse-form form
'() nil
)
193 ;; Calculate an intersection of cconv-mutated and cconv-captured
194 (dolist (mvr cconv-mutated
)
195 (when (memq mvr cconv-captured
) ;
196 (push mvr cconv-captured
+mutated
)))
197 (cconv-closure-convert-rec
200 '() ; fvrs initially empty
201 '() ; envs initially empty
203 toplevel
))) ; true if the tree is a toplevel form
206 (defun cconv-closure-convert-toplevel (form)
207 "Entry point for toplevel forms.
208 -- FORM is a piece of Elisp code after macroexpansion.
210 Returns a form where all lambdas don't have any free variables."
211 ;; we distinguish toplevel forms to treat def(un|var|const) correctly.
212 (cconv-closure-convert form t
))
214 (defun cconv-closure-convert-rec
215 (form emvrs fvrs envs lmenvs defs-are-legal
)
216 ;; This function actually rewrites the tree.
217 "Eliminates all free variables of all lambdas in given forms.
219 -- FORM is a piece of Elisp code after macroexpansion.
220 -- LMENVS is a list of environments used for lambda-lifting. Initially empty.
221 -- EMVRS is a list that contains mutated variables that are visible
222 within current environment.
223 -- ENVS is an environment(list of free variables) of current closure.
225 -- FVRS is a list of variables to substitute in each context.
227 -- DEFS-ARE-LEGAL is a boolean variable, true if def(un|var|const)
228 can be used in this form(e.g. toplevel form)
230 Returns a form where all lambdas don't have any free variables."
231 ;; What's the difference between fvrs and envs?
232 ;; Suppose that we have the code
233 ;; (lambda (..) fvr (let ((fvr 1)) (+ fvr 1)))
234 ;; only the first occurrence of fvr should be replaced by
236 ;; So initially envs and fvrs are the same thing, but when we descend to
237 ;; the 'let, we delete fvr from fvrs. Why we don't delete fvr from envs?
238 ;; Because in envs the order of variables is important. We use this list
239 ;; to find the number of a specific variable in the environment vector,
240 ;; so we never touch it(unless we enter to the other closure).
241 ;;(if (listp form) (print (car form)) form)
243 (`(,(and letsym
(or `let
* `let
)) ,varsvalues .
,body-forms
)
245 ; let and let* special forms
246 (let ((body-forms-new '())
248 ;; next for variables needed for delayed push
249 ;; because we should process <value(s)>
250 ;; before we change any arguments
251 (lmenvs-new '()) ;needed only in case of let
252 (emvrs-new '()) ;needed only in case of let
253 (emvr-push) ;needed only in case of let*
254 (lmenv-push)) ;needed only in case of let*
256 (dolist (elm varsvalues
) ;begin of dolist over varsvalues
257 (let (var value elm-new iscandidate ismutated
)
258 (if (listp elm
) ; (let (v1) ...) => (let ((v1 nil)) ...)
261 (setq value
(cadr elm
)))
264 ;; Check if var is a candidate for lambda lifting
265 (let ((lcandid cconv-lambda-candidates
))
266 (while (and lcandid
(not iscandidate
))
267 (when (and (eq (caar lcandid
) var
)
268 (eq (caddar lcandid
) elm
)
269 (eq (cadr (cddar lcandid
)) form
))
270 (setq iscandidate t
))
271 (setq lcandid
(cdr lcandid
))))
273 ; declared variable is a candidate
276 (let* ((func (cadr elm
)) ; function(lambda) itself
278 (fv (delete-dups (cconv-freevars func
'())))
279 (funcvars (append fv
(cadadr func
))) ;function args
280 (funcbodies (cddadr func
)) ; function bodies
281 (funcbodies-new '()))
282 ; lambda lifting condition
283 (if (or (not fv
) (< cconv-liftwhen
(length funcvars
)))
288 ,(cconv-closure-convert-rec
289 func emvrs fvrs envs lmenvs nil
)))
292 (dolist (elm2 funcbodies
)
293 (push ; convert function bodies
294 (cconv-closure-convert-rec
295 elm2 emvrs nil envs lmenvs nil
)
297 (if (eq letsym
'let
*)
298 (setq lmenv-push
(cons var fv
))
299 (push (cons var fv
) lmenvs-new
))
300 ; push lifted function
306 ,(reverse funcbodies-new
)))))))))
308 ;declared variable is not a function
310 ;; Check if var is mutated
311 (let ((lmutated cconv-captured
+mutated
))
312 (while (and lmutated
(not ismutated
))
313 (when (and (eq (caar lmutated
) var
)
314 (eq (caddar lmutated
) elm
)
315 (eq (cadr (cddar lmutated
)) form
))
317 (setq lmutated
(cdr lmutated
))))
319 (progn ; declared variable is mutated
321 `(,var
(list ,(cconv-closure-convert-rec
323 fvrs envs lmenvs nil
))))
324 (if (eq letsym
'let
*)
326 (push var emvrs-new
)))
331 ,(cconv-closure-convert-rec
332 value emvrs fvrs envs lmenvs nil
)))))))
334 ;; this piece of code below letbinds free
335 ;; variables of a lambda lifted function
336 ;; if they are redefined in this let
338 ;; (let* ((fun (lambda (x) (+ x y))) (y 1)) (funcall fun 1))
339 ;; Here we can not pass y as parameter because it is
340 ;; redefined. We add a (closed-y y) declaration.
341 ;; We do that even if the function is not used inside
342 ;; this let(*). The reason why we ignore this case is
343 ;; that we can't "look forward" to see if the function
344 ;; is called there or not. To treat well this case we
345 ;; need to traverse the tree one more time to collect this
346 ;; data, and I think that it's not worth it.
348 (when (eq letsym
'let
*)
349 (let ((closedsym '())
352 (dolist (lmenv lmenvs
)
353 (when (memq var
(cdr lmenv
))
356 (concat "closed-" (symbol-name var
))))
357 (setq new-lmenv
(list (car lmenv
)))
358 (dolist (frv (cdr lmenv
)) (if (eq frv var
)
359 (push closedsym new-lmenv
)
360 (push frv new-lmenv
)))
361 (setq new-lmenv
(reverse new-lmenv
))
362 (setq old-lmenv lmenv
)))
364 (setq lmenvs
(remq old-lmenv lmenvs
))
365 (push new-lmenv lmenvs
)
366 (push `(,closedsym
,var
) varsvalues-new
))))
367 ;; we push the element after redefined free variables
368 ;; are processes. this is important to avoid the bug
369 ;; when free variable and the function have the same
371 (push elm-new varsvalues-new
)
373 (when (eq letsym
'let
*) ; update fvrs
374 (setq fvrs
(remq var fvrs
))
375 (setq emvrs
(remq var emvrs
)) ; remove if redefined
377 (push emvr-push emvrs
)
378 (setq emvr-push nil
))
379 (let (lmenvs-1) ; remove var from lmenvs if redefined
380 (dolist (iter lmenvs
)
381 (when (not (assq var lmenvs
))
382 (push iter lmenvs-1
)))
383 (setq lmenvs lmenvs-1
))
385 (push lmenv-push lmenvs
)
386 (setq lmenv-push nil
)))
387 )) ; end of dolist over varsvalues
388 (when (eq letsym
'let
)
390 (let (var fvrs-1 emvrs-1 lmenvs-1
)
391 ;; Here we update emvrs, fvrs and lmenvs lists
394 (when (not (assq vr varsvalues-new
)) (push vr fvrs-1
)))
398 (when (not (assq vr varsvalues-new
)) (push vr emvrs-1
)))
401 (setq emvrs
(append emvrs emvrs-new
))
403 (when (not (assq (car vr
) varsvalues-new
))
405 (setq lmenvs
(append lmenvs lmenvs-new
)))
407 ;; Here we do the same letbinding as for let* above
408 ;; to avoid situation when a free variable of a lambda lifted
409 ;; function got redefined.
415 (fvrs-new)) ; list of (closed-var var)
416 (dolist (elm varsvalues
)
421 (let ((lmenvs-1 lmenvs
)) ; just to avoid manipulating
422 (dolist (lmenv lmenvs-1
) ; the counter inside the loop
423 (when (memq var
(cdr lmenv
))
424 (setq closedsym
(make-symbol
428 (setq new-lmenv
(list (car lmenv
)))
429 (dolist (frv (cdr lmenv
)) (if (eq frv var
)
430 (push closedsym new-lmenv
)
431 (push frv new-lmenv
)))
432 (setq new-lmenv
(reverse new-lmenv
))
433 (setq lmenvs
(remq lmenv lmenvs
))
434 (push new-lmenv lmenvs
)
435 (push `(,closedsym
,var
) letbinds
)
437 (setq varsvalues-new
(append varsvalues-new letbinds
))))
439 (dolist (elm body-forms
) ; convert body forms
440 (push (cconv-closure-convert-rec
441 elm emvrs fvrs envs lmenvs nil
)
443 `(,letsym
,(reverse varsvalues-new
) .
,(reverse body-forms-new
))))
444 ;end of let let* forms
446 ; first element is lambda expression
447 (`(,(and `(lambda .
,_
) fun
) .
,other-body-forms
)
449 (let ((other-body-forms-new '()))
450 (dolist (elm other-body-forms
)
451 (push (cconv-closure-convert-rec
452 elm emvrs fvrs envs lmenvs nil
)
453 other-body-forms-new
))
456 (cconv-closure-convert-rec
457 (list 'function fun
) emvrs fvrs envs lmenvs nil
))
458 (reverse other-body-forms-new
))))
460 (`(cond .
,cond-forms
) ; cond special form
461 (let ((cond-forms-new '()))
462 (dolist (elm cond-forms
)
463 (push (let ((elm-new '()))
466 (cconv-closure-convert-rec
467 elm-2 emvrs fvrs envs lmenvs nil
)
472 (reverse cond-forms-new
))))
474 (`(quote .
,_
) form
) ; quote form
476 (`(function .
((lambda ,vars .
,body-forms
))) ; function form
477 (let (fvrs-new) ; we remove vars from fvrs
478 (dolist (elm fvrs
) ;i use such a tricky way to avoid side effects
479 (when (not (memq elm vars
))
480 (push elm fvrs-new
)))
481 (setq fvrs fvrs-new
))
482 (let* ((fv (delete-dups (cconv-freevars form
'())))
483 (leave fvrs
) ; leave = non nil if we should leave env unchanged
489 ;; Here we form our environment vector.
490 ;; If outer closure contains all
491 ;; free variables of this function(and nothing else)
492 ;; then we use the same environment vector as for outer closure,
493 ;; i.e. we leave the environment vector unchanged
494 ;; otherwise we build a new environmet vector
495 (if (eq (length envs
) (length fv
))
497 (while (and fv-temp leave
)
498 (when (not (memq (car fv-temp
) fvrs
)) (setq leave nil
))
499 (setq fv-temp
(cdr fv-temp
))))
506 (cconv-closure-convert-rec
507 elm
(remq elm emvrs
) fvrs envs lmenvs nil
)
508 envector
)) ; process vars for closure vector
509 (setq envector
(reverse envector
))
511 (setq envector
`(env))) ; leave unchanged
512 (setq fvrs fv
)) ; update substitution list
514 ;; the difference between envs and fvrs is explained
515 ;; in comment in the beginning of the function
516 (dolist (elm cconv-captured
+mutated
) ; find mutated arguments
517 (setq mv
(car elm
)) ; used in inner closures
518 (when (and (memq mv vars
) (eq form
(caddr elm
)))
519 (progn (push mv emvrs
)
520 (push `(,mv
(list ,mv
)) letbind
))))
521 (dolist (elm body-forms
) ; convert function body
522 (push (cconv-closure-convert-rec
523 elm emvrs fvrs envs lmenvs nil
)
527 (if letbind
`((let ,letbind .
,(reverse body-forms-new
)))
528 (reverse body-forms-new
)))
531 ;if no freevars - do nothing
533 `(function (lambda ,vars .
,body-forms-new
)))
534 ; 1 free variable - do not build vector
535 ((null (cdr envector
))
537 (function (lambda (env .
,vars
) .
,body-forms-new
))
539 ; >=2 free variables - build vector
542 (function (lambda (env .
,vars
) .
,body-forms-new
))
543 (vector .
,envector
))))))
545 (`(function .
,_
) form
) ; same as quote
548 (`(,(and sym
(or `defconst
`defvar
)) ,definedsymbol .
,body-forms
)
551 (let ((body-forms-new '()))
552 (dolist (elm body-forms
)
553 (push (cconv-closure-convert-rec
554 elm emvrs fvrs envs lmenvs nil
)
556 (setq body-forms-new
(reverse body-forms-new
))
557 `(,sym
,definedsymbol .
,body-forms-new
))
558 (error "Invalid form: %s inside a function" sym
)))
560 ;defun, defmacro, defsubst
561 (`(,(and sym
(or `defun
`defmacro
`defsubst
))
562 ,func
,vars .
,body-forms
)
564 (let ((body-new '()) ; the whole body
565 (body-forms-new '()) ; body w\o docstring and interactive
567 ; find mutable arguments
568 (let ((lmutated cconv-captured
+mutated
) ismutated
)
571 (while (and lmutated
(not ismutated
))
572 (when (and (eq (caar lmutated
) elm
)
573 (eq (cadar lmutated
) form
))
575 (setq lmutated
(cdr lmutated
)))
579 ;transform body-forms
580 (when (stringp (car body-forms
)) ; treat docstring well
581 (push (car body-forms
) body-new
)
582 (setq body-forms
(cdr body-forms
)))
583 (when (and (listp (car body-forms
)) ; treat (interactive) well
584 (eq (caar body-forms
) 'interactive
))
586 (cconv-closure-convert-rec
588 emvrs fvrs envs lmenvs nil
) body-new
)
589 (setq body-forms
(cdr body-forms
)))
591 (dolist (elm body-forms
)
592 (push (cconv-closure-convert-rec
593 elm emvrs fvrs envs lmenvs nil
)
595 (setq body-forms-new
(reverse body-forms-new
))
598 ; letbind mutable arguments
599 (let ((varsvalues-new '()))
600 (dolist (elm letbind
) (push `(,elm
(list ,elm
))
602 (push `(let ,(reverse varsvalues-new
) .
603 ,body-forms-new
) body-new
)
604 (setq body-new
(reverse body-new
)))
605 (setq body-new
(append (reverse body-new
) body-forms-new
)))
607 `(,sym
,func
,vars .
,body-new
))
609 (error "Invalid form: defun inside a function")))
611 (`(condition-case ,var
,protected-form .
,conditions-bodies
)
612 (let ((conditions-bodies-new '()))
613 (setq fvrs
(remq var fvrs
))
614 (dolist (elm conditions-bodies
)
615 (push (let ((elm-new '()))
616 (dolist (elm-2 (cdr elm
))
618 (cconv-closure-convert-rec
619 elm-2 emvrs fvrs envs lmenvs nil
)
621 (cons (car elm
) (reverse elm-new
)))
622 conditions-bodies-new
))
625 ,(cconv-closure-convert-rec
626 protected-form emvrs fvrs envs lmenvs nil
)
627 .
,(reverse conditions-bodies-new
))))
629 (`(setq .
,forms
) ; setq special form
630 (let (prognlist sym sym-new value
)
632 (setq sym
(car forms
))
633 (setq sym-new
(cconv-closure-convert-rec
635 (remq sym emvrs
) fvrs envs lmenvs nil
))
637 (cconv-closure-convert-rec
638 (cadr forms
) emvrs fvrs envs lmenvs nil
))
640 (push `(setcar ,sym-new
,value
) prognlist
)
641 (if (symbolp sym-new
)
642 (push `(setq ,sym-new
,value
) prognlist
)
643 (push `(set ,sym-new
,value
) prognlist
)))
644 (setq forms
(cddr forms
)))
646 `(progn .
,(reverse prognlist
))
649 (`(,(and (or `funcall
`apply
) callsym
) ,fun .
,args
)
650 ; funcall is not a special form
651 ; but we treat it separately
652 ; for the needs of lambda lifting
653 (let ((fv (cdr (assq fun lmenvs
))))
657 ;; All args (free variables and actual arguments)
658 ;; should be processed, because they can be fvrs
659 ;; (free variables of another closure)
661 (push (cconv-closure-convert-rec
663 fvrs envs lmenvs nil
)
665 (setq processed-fv
(reverse processed-fv
))
667 (push (cconv-closure-convert-rec
668 elm emvrs fvrs envs lmenvs nil
)
670 (setq args-new
(append processed-fv
(reverse args-new
)))
671 (setq fun
(cconv-closure-convert-rec
672 fun emvrs fvrs envs lmenvs nil
))
673 `(,callsym
,fun .
,args-new
))
675 (dolist (elm (cdr form
))
676 (push (cconv-closure-convert-rec
677 elm emvrs fvrs envs lmenvs nil
)
679 `(,callsym .
,(reverse cdr-new
))))))
681 (`(,func .
,body-forms
) ; first element is function or whatever
682 ; function-like forms are:
683 ; or, and, if, progn, prog1, prog2,
685 (let ((body-forms-new '()))
686 (dolist (elm body-forms
)
687 (push (cconv-closure-convert-rec
688 elm emvrs fvrs envs lmenvs defs-are-legal
)
690 (setq body-forms-new
(reverse body-forms-new
))
691 `(,func .
,body-forms-new
)))
694 (if (memq form fvrs
) ;form is a free variable
695 (let* ((numero (position form envs
))
698 (if (null (cdr envs
))
702 (setq var
`(aref env
,numero
)))
703 (if (memq form emvrs
) ; form => (car (aref env #)) if mutable
706 (if (memq form emvrs
) ; if form is a mutable variable
707 `(car ,form
) ; replace form => (car form)
710 (defun cconv-analyse-form (form vars inclosure
)
712 "Find mutated variables and variables captured by closure. Analyse
713 lambdas if they are suitable for lambda lifting.
714 -- FORM is a piece of Elisp code after macroexpansion.
715 -- MLCVRS is a structure that contains captured and mutated variables.
716 (first MLCVRS) is a list of mutated variables, (second MLCVRS) is a
717 list of candidates for lambda lifting and (third MLCVRS) is a list of
718 variables captured by closure. It should be (nil nil nil) initially.
719 -- VARS is a list of local variables visible in current environment
721 -- INCLOSURE is a boolean variable, true if we are in closure.
725 (`(,(and (or `let
* `let
) letsym
) ,varsvalues .
,body-forms
)
727 (when (eq letsym
'let
)
728 (dolist (elm varsvalues
) ; analyse values
730 (cconv-analyse-form (cadr elm
) vars inclosure
))))
736 (dolist (elm varsvalues
)
740 (setq value
(cadr elm
)))
742 (setq var elm
) ; treat the form (let (x) ...) well
745 (when (eq letsym
'let
*) ; analyse value
746 (cconv-analyse-form value vars inclosure
))
748 (let (vars-new) ; remove the old var
750 (when (not (eq (car vr
) var
))
752 (setq vars vars-new
))
754 (setq varstruct
(list var inclosure elm form
))
755 (push varstruct vars
) ; push a new one
757 (when (and (listp value
)
758 (eq (car value
) 'function
)
759 (eq (caadr value
) 'lambda
))
760 ; if var is a function
761 ; push it to lambda list
762 (push varstruct cconv-lambda-candidates
))))
764 (dolist (elm body-forms
) ; analyse body forms
765 (cconv-analyse-form elm vars inclosure
))
768 (`(,(or `defun
`defmacro
) ,func
,vrs .
,body-forms
)
771 (push (list vr form
) vars
))) ;push vrs to vars
772 (dolist (elm body-forms
) ; analyse body forms
773 (cconv-analyse-form elm vars inclosure
))
776 (`(function .
((lambda ,vrs .
,body-forms
)))
777 (if inclosure
;we are in closure
778 (setq inclosure
(+ inclosure
1))
780 (let (vars-new) ; update vars
781 (dolist (vr vars
) ; we do that in such a tricky way
782 (when (not (memq (car vr
) vrs
)) ; to avoid side effects
785 (push (list vr inclosure form
) vars-new
))
786 (setq vars vars-new
))
788 (dolist (elm body-forms
)
789 (cconv-analyse-form elm vars inclosure
))
792 (`(setq .
,forms
) ; setq
793 ; if a local variable (member of vars)
794 ; is modified by setq
795 ; then it is a mutated variable
797 (let ((v (assq (car forms
) vars
))) ; v = non nil if visible
799 (push v cconv-mutated
)
800 ;; delete from candidate list for lambda lifting
801 (setq cconv-lambda-candidates
(delq v cconv-lambda-candidates
))
803 ;; test if v is declared as argument for lambda
804 (let* ((thirdv (third v
))
805 (isarg (if (listp thirdv
)
806 (eq (car thirdv
) 'function
) nil
)))
808 (when (> inclosure
(cadr v
)) ; when we are in closure
809 (push v cconv-captured
)) ; push it to captured vars
810 ;; FIXME more detailed comments needed
811 (push v cconv-captured
))))))
812 (cconv-analyse-form (cadr forms
) vars inclosure
)
813 (setq forms
(cddr forms
)))
816 (`((lambda .
,_
) .
,_
) ; first element is lambda expression
817 (dolist (exp `((function ,(car form
)) .
,(cdr form
)))
818 (cconv-analyse-form exp vars inclosure
))
821 (`(cond .
,cond-forms
) ; cond special form
822 (dolist (exp1 cond-forms
)
824 (cconv-analyse-form exp2 vars inclosure
)))
827 (`(quote .
,_
) nil
) ; quote form
829 (`(function .
,_
) nil
) ; same as quote
831 (`(condition-case ,var
,protected-form .
,conditions-bodies
)
833 (cconv-analyse-form protected-form vars inclosure
)
834 (dolist (exp conditions-bodies
)
835 (cconv-analyse-form (cadr exp
) vars inclosure
))
838 (`(,(or `defconst
`defvar
`defsubst
) ,value
)
839 (cconv-analyse-form value vars inclosure
))
841 (`(,(or `funcall
`apply
) ,fun .
,args
)
842 ;; Here we ignore fun because
843 ;; funcall and apply are the only two
844 ;; functions where we can pass a candidate
845 ;; for lambda lifting as argument.
846 ;; So, if we see fun elsewhere, we'll
847 ;; delete it from lambda candidate list.
849 ;; If this funcall and the definition of fun
850 ;; are in different closures - we delete fun from
851 ;; canidate list, because it is too complicated
852 ;; to manage free variables in this case.
853 (let ((lv (assq fun cconv-lambda-candidates
)))
855 (when (not (eq (cadr lv
) inclosure
))
856 (setq cconv-lambda-candidates
857 (delq lv cconv-lambda-candidates
)))))
860 (cconv-analyse-form elm vars inclosure
))
863 (`(,_ .
,body-forms
) ; first element is a function or whatever
864 (dolist (exp body-forms
)
865 (cconv-analyse-form exp vars inclosure
))
869 (when (and (symbolp form
)
870 (not (memq form
'(nil t
)))
871 (not (keywordp form
))
872 (not (special-variable-p form
)))
873 (let ((dv (assq form vars
))) ; dv = declared and visible
876 ;; test if v is declared as argument of lambda
877 (let* ((thirddv (third dv
))
878 (isarg (if (listp thirddv
)
879 (eq (car thirddv
) 'function
) nil
)))
881 ;; FIXME add detailed comments
882 (when (> inclosure
(cadr dv
)) ; capturing condition
883 (push dv cconv-captured
))
884 (push dv cconv-captured
))))
886 (setq cconv-lambda-candidates
; if it is found here
887 (delq dv cconv-lambda-candidates
)))))
891 ;;; cconv.el ends here