1 ;;; -*- Mode: Lisp; Package: Maxima; Syntax: Common-Lisp; Base: 10 -*- ;;;;
2 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
3 ;;; The data in this file contains enhancments. ;;;;;
5 ;;; Copyright (c) 1984,1987 by William Schelter,University of Texas ;;;;;
6 ;;; All rights reserved ;;;;;
7 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
8 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
9 ;;; (c) Copyright 1980 Massachusetts Institute of Technology ;;;
10 ;;; Maintained by GJC ;;;
11 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
15 (macsyma-module trans3
)
17 ;;; The translation of macsyma LAMBDA into lexicaly scoped closures.
18 ;;; Two cases [1] the downward transmission of variable binding environment,
19 ;;; e.g. MAP(LAMBDA([U],F(U,X)),EXP)
20 ;;; [2] downward and upward, requiring a full closure, e.g.
21 ;;; MAP(LAMBDA([U],SUM:SUM+U),EXP);
23 ;;; LAMBDA([U],F(U,X)) =>
24 ;;; (DOWN-CLOSE (LAMBDA (U) (F U X)) (X))
26 ;;; TBIND, TBOUNDP, and TUNBIND and TUNBINDS hack lexical scoping.
28 ;;; A function to determine free vars from a lisp expression.
29 ;;; It returns a <var-set> which is a list of pairs
30 ;;; (<var> . <side-effectp>)
32 ;;; N.B. This code does a veritable storm of consing, it need not
33 ;;; do any if it used the lambda-bound plist scheme of GJC;UTRANS >
34 ;;; a compiler is allowed to cons though, isn't it?
36 (defun free-lisp-vars (exp &aux prop
)
38 (cond ((or (null exp
)(eq t exp
)) nil
)
39 ((symbolp exp
) `((,exp . nil
)))
42 (cond ((setq prop
(get (car exp
) 'free-lisp-vars
))
44 ((setq prop
(get (car exp
) 'macro
))
45 (free-lisp-vars (funcall prop exp
)))
46 ((getl (car exp
) '(fsubr fexpr
))
48 "environment may fail to be correct.")
49 (free-lisp-vars-of-argl (cdr exp
)))
51 (free-lisp-vars-of-argl (cdr exp
)))))
52 ((eq (caar exp
) 'lambda
)
53 (sum-var-sets (free-lisp-vars (car exp
))
54 (free-lisp-vars-of-argl (cdr exp
))))
56 (barfo (intl:gettext
"encountered an unrecognized Lisp expression in FREE-LISP-VARS.")))))
59 (defun free-lisp-vars-of-argl (argl)
60 (union-var-set (mapcar #'free-lisp-vars argl
)))
62 ;;; (REDUCE-VAR-SET '((A . NIL) NIL (B . T) (B . NIL))) => ((A . NIL) (B . T))
63 ;;; mult-set reduction.
65 (defun reduce-var-set&op
(var-set op
)
66 (do ((var-set var-set
(cdr var-set
))
70 ((null var-set
) reduced-var-set
)
71 (setq var1
(car var-set
))
73 ((setq var2
(assoc (car var1
) reduced-var-set
:test
#'eq
))
74 (rplacd var2
(funcall op
(cdr var1
) (cdr var2
))))
76 (push var1 reduced-var-set
)))))
78 (defun reduce-var-set (var-set)
79 (reduce-var-set&op var-set
#'(lambda (p1 p2
)(or p1 p2
))))
81 ;;; S1 - S2. S1 reduced, minus any vars that are in S2.
83 (defun difference-var-sets (s1 s2
)
84 (setq s1
(reduce-var-set s1
))
87 (cond ((assoc (caar s1
) s2
:test
#'eq
)) ;;; is the first elem of S1 a member of S2?
89 (push (car s1
) s
))) ;;; yes. shove it in.
92 ;;; N.B. union of var sets is defined classicaly ala G.F.
94 (defun union-var-set (set-of-var-sets)
95 (reduce-var-set (apply #'append set-of-var-sets
)))
97 ;;; SUM-VAR-SETS is the usual convention.
99 (defun sum-var-sets (&rest l
)
100 (reduce-var-set (apply #'append l
))) ; consing up a storm aren't we?
102 (defun make-var-set (vars)
103 (loop for v in vars collect
(ncons v
)))
105 (macrolet ((empty-free-lisp-vars (name)
106 (let ((form (gensym)))
107 `(defun-prop (,name free-lisp-vars
) (,form
)
108 (declare (ignore ,form
))
110 (empty-free-lisp-vars declare
)
111 (empty-free-lisp-vars function
)
112 (empty-free-lisp-vars go
)
113 (empty-free-lisp-vars quote
))
115 ;;; (LAMBDA <BVL> . <BODY>)
117 (defun-prop (lambda free-lisp-vars
) (form)
118 (difference-var-sets (free-lisp-vars-of-argl (cddr form
))
119 (cond ((null (cadr form
))
122 (make-var-set (list (cadr form
))))
124 (make-var-set (cadr form
))))))
126 ;;; (PROG <BVL> . <BODY>)
128 (defun-prop (prog free-lisp-vars
) (form)
129 (difference-var-sets (union-var-set
130 (mapcar #'(lambda (u)
131 (cond ((atom u
) nil
) ;; go tag.
133 (free-lisp-vars u
))))
135 (make-var-set (cadr form
))))
137 ;;; (LET <BVL> . <BODY>)
139 ;; Take the union of the free variables from the init-forms
140 ;; and the free variables of the body (less the variables bound by LET).
142 (defun-prop (let free-lisp-vars
) (form)
145 ;; extract (FOO BAR NIL NIL) from (LET ((A FOO) (B BAR) C D) ...)
146 ;; and apply FREE-LISP-VARS to each.
147 (union-var-set (mapcar #'free-lisp-vars
(mapcar #'(lambda (e) (if (consp e
) (cadr e
))) (cadr form
))))
149 ;; cargo-cult programming: copy this next bit from (DEFUN-PROP (PROG ...)) above.
151 (mapcar #'(lambda (u)
152 (cond ((atom u
) nil
) ;; go tag.
154 (free-lisp-vars u
))))
156 ;; extract A B C D from (LET ((A FOO) (B BAR) C D) ...)
157 (make-var-set (mapcar #'(lambda (e) (if (atom e
) e
(car e
))) (cadr form
)))))))
159 ;;; (DO ((<V> <V> <V>) ...) ((<in-scope>) ..) ...)
161 (defun-prop (do free-lisp-vars
) (form)
163 (sum-var-sets (free-lisp-vars-of-argl (cdddr form
))
164 (free-lisp-vars-of-argl (caddr form
))
165 (union-var-set (mapcar #'(lambda (do-iter)
166 (free-lisp-vars-of-argl
169 (make-var-set (mapcar #'car
(cadr form
)))))
171 ;;; (COND (<I> ..) (<J> ..) ...)
173 (defun-prop (cond free-lisp-vars
) (form)
174 (union-var-set (mapcar #'free-lisp-vars-of-argl
(cdr form
))))
176 ;;; (SETQ ... ODD AND EVENS...)
178 (defun-prop (setq free-lisp-vars
) (form)
179 (do ((free-vars nil
(sum-var-sets `((,(car form
) . t
))
180 (free-lisp-vars (cadr form
))
182 (form (cdr form
) (cddr form
)))
183 ((null form
) free-vars
)))
185 ;;; uhm. LAMBDA, PROG, GO, DO, COND, QUOTE, SETQ.
187 (defun-prop (and free-lisp-vars
)(form)(free-lisp-vars-of-argl (cdr form
)))
188 (defun-prop (or free-lisp-vars
)(form)(free-lisp-vars-of-argl (cdr form
)))
190 ;;; these next forms are generated by TRANSLATE.
192 (defprop $piece t sort-of-lexical
)
194 (defun-prop (trd-msymeval free-lisp-vars
) (form)
195 (if (get (cadr form
) 'sort-of-lexical
)
196 ;; acts like a lexical variable because of the $SUBSTPART translator.
197 (list (list (cadr form
)))
200 (defun-prop (mfunction-call free-lisp-vars
) (form)
201 ;; it is not strictly known if the name of the function being called
202 ;; is a variable or not. lets say its not.
203 (free-lisp-vars-of-argl (cddr form
)))
205 ;;; (FUNGEN&ENV-FOR-MEVAL () () EXP)
206 (defun-prop (fungen&env-for-meval free-lisp-vars
) (form)
207 (free-lisp-vars (car (cdddr form
))))
209 ;;; the various augmented lambda forms.
211 (defun free-lisp-vars-m-tlambda (form)
212 (difference-var-sets (free-lisp-vars-of-argl (cddr form
))
213 (free-lisp-vars-of-argl (cadr form
))))
215 (mapc #'(lambda (u) (putprop u
'free-lisp-vars-m-tlambda
'free-lisp-vars
))
216 '(m-tlambda m-tlambda
&))
218 (defun free-lisp-vars-m-tlambda&env
(form)
219 (difference-var-sets (free-lisp-vars-of-argl (cddr form
))
220 (free-lisp-vars-of-argl (car (cadr form
)))))
222 (defprop m-tlambda
&env free-lisp-vars-m-tlambda
&env free-lisp-vars
)
223 (defprop m-tlambda
&env
& free-lisp-vars-m-tlambda
&env free-lisp-vars
)
225 ;;; Other entry points:
227 (defun tbound-free-vars (free-varl)
228 ;; Takes a FREE-VAR list and returns a list of two lists.
229 ;; the tbound free vars and the tbound free vars that are
230 ;; side effected also.
233 ((null free-varl
) (list free free
&s
))
234 (let ((v (pop free-varl
)))
235 (cond ((and (tboundp (car v
))
236 (not (tr-get-special (car v
))))
239 (push (car v
) free
&s
))))))))
241 (defun side-effect-free-check (varl form
)
242 (cond ((null varl
) t
)
244 (tr-format (intl:gettext
"error: unsupported side effects on ~:M in expression ~M~%") `((mlist) ,@varl
) form
)
248 ;;; O.K. here is the translate property for LAMBDA.
249 ;;; given catch and throw we don't know where a funarg lambda
253 ;;; I. No side effects on free variables.
254 ;;; A. one funarg only, not reconsed. e.g.
255 ;;; F(N,L):=MAP(LAMBDA([U],Q(N,U)),L)$
256 ;;; (PROGN (SET-ENV <*LINK*> N)
257 ;;; (FUNCTION (LAMBDA (U) (LET ((N (GET-ENV *LINK*))) (f* U N)))))
258 ;;; B. need new instance of the environment each time,
259 ;;; F(N):=LAMBDA([U],N*U);
260 ;;; `(LAMBDA (U) (gen-func U 'N)) without extend loaded.
261 ;;; II. side effects.
262 ;;; A. Those since effects need to be propogated to the environment
263 ;;; where the LAMBDA was made. This is difficult to do in the
264 ;;; present translator. e.g.
265 ;;; F(L):=BLOCK([SUM:0],FULLMAP(LAMBDA([U],SUM:SUM+U),L),SUM);
266 ;;; every function which guarantees the order of argument evalation
267 ;;; (MPROG and MPROGN), must translate and expression and get information
268 ;;; about environment propagation.
269 ;;; (PROGN (FULLMAP (PROGN (SET-ENV) '(LAMBDA ...)) L)
270 ;;; (GET-ENV)), uhm. this is pretty tricky anyway.
271 ;;; B. side effects only have to be maintained inside the LAMBDA.
272 ;;; this is easier, and if you have it, you really don't need II.A.
273 ;;; since you can always ask the LAMBDA for its environment by
274 ;;; calling it on the proper message {If the LAMBDA is written that way}.
277 ;;; ((LAMBDA) ((MLIST) X Y ((MLIST Z))) . <BODY>)
278 ;;; must also handle the &REST arguments. N.B. MAPPLY correctly handles
279 ;;; the application of a lisp lambda form.
282 ;;; Some forms know that the lambda is not going to
283 ;;; be an upward funarg, that it is not possible (wanted)
284 ;;; have two different lambda's generated from the same
285 ;;; place. e.g. INTERPOLATE(SIN(X^2)=A,X,0,N) (implied lambda
286 ;;; which is contructed by the translation property for
287 ;;; interpolate. MAP(LAMBDA([U],...),L) is another example)
288 ;;; these forms will be called I-LAMBDA's, and will be generated
289 ;;; from LAMBDA's by the functions that want to. All this
290 ;;; is meaningless in the present macsyma evaluator of course, since
291 ;;; it uses dynamic binding and just hopes for the best.
293 (def%tr lambda
(form)
294 (gen-tr-lambda form
))
296 ;;; we keep a pointer to the original FORM so that we can
297 ;;; generate messages with it if need be.
299 (defun gen-tr-lambda (form &aux arg-info frees t-form dup
)
300 (unless ($listp
(cadr form
))
301 (tr-format (intl:gettext
"error: first argument of lambda expression must be a list; found ~M") (cadr form
))
303 (return-from gen-tr-lambda nil
))
304 (when (null (cddr form
))
305 (tr-format (intl:gettext
"error: empty body in lambda expression.~%"))
307 (return-from gen-tr-lambda nil
))
308 (setq arg-info
(mapcar #'(lambda (v)
309 (cond ((mdefparam v
) nil
)
310 ((and (op-equalp v
'mlist
)
316 (cond ((or (member '*bad
* arg-info
:test
#'eq
)
317 (and (member t arg-info
:test
#'eq
)
318 (cdr (member t arg-info
:test
#'eq
)))) ;;; the &REST is not the last one.
319 (tr-format (intl:gettext
"error: unsupported argument list ~:M in lambda expression.~%") (cadr form
))
322 ((setq dup
(find-duplicate (cdadr form
) :test
#'eq
:key
#'mparam
))
323 (tr-format (intl:gettext
"error: ~M occurs more than once in lambda expression parameter list") (mparam dup
))
327 (setq arg-info
(member t arg-info
:test
#'eq
) ;; &RESTP
329 (tr-lambda `((lambda)
330 ((mlist) ,@(mapcar #'(lambda (v)
336 frees
(tbound-free-vars (free-lisp-vars t-form
)))))
337 ; with this info we now dispatch to the various macros forms.
338 ; (cadr t-form) is a lambda list. (cddr t-form) is a progn body.
339 (cond ((null (car frees
)) ; woopie.
340 (cond ((null arg-info
)
341 `($any .
(m-tlambda ,@(cdr t-form
))))
343 `($any .
(m-tlambda& ,@(cdr t-form
))))))
345 `($any .
(,(cond ((null arg-info
) 'm-tlambda
&env
)
347 (,(cadr t-form
) ,(car frees
))
351 (side-effect-free-check (cadr frees
) form
)
352 `($any .
(meval ',form
)))))