1 ;;; from maxima-5.9.0/maxima-5.9.0/src/mlisp.lisp
6 ;;; 1. Improved error messages for mset
7 ;;; 2. Allows setting of record fields e.g. XX@YY:45 if mset_extension_operators set up
9 ;;; author Richard Fateman
11 ;; modifications 2005/08/28 Robert Dodier
12 ;; (0) cut matrix assignment code from previous rev (but kept parallel multiple assignment)
13 ;; (1) $NEW barfs if #arguments != 1, or argument has no defstruct, or wrong number of initializers.
14 ;; (2) $DEFSTRUCT allows 1 or more arguments, returns a list of defstructs.
15 ;; (3) use $PUT and $GET to maintain defstruct properties
16 ;; (renamed to $DEFSTRUCT_DEFAULT and $DEFSTRUCT_TEMPLATE).
17 ;; This makes defstruct properties visible to user via get and propvars.
18 ;; Also, this makes `kill' kill defstructs.
20 ;; If this is the last def'n in file of mset_extension_operators,
21 ;; it will disable the $@ defstruct features
23 (defparameter mset_extension_operators nil
)
27 ;; first see if we are supposed to report this assignment
28 ;; to the user. Is $setcheck set to a list containing x?
29 (cond ((or (null $setcheck
)
30 (eq $setcheck
'$setcheck
))) ;setcheck "not set"
31 ((and (or (atom $setcheck
)
32 (memalike x
(cdr $setcheck
)) ;; setcheck set to x?
33 (and (not (atom x
)) ;; setcheck set a list with x?
34 (memalike (caar x
) (cdr $setcheck
))))
36 ;; the conditions for printing out the trace on set are fulfilled.
37 (displa (list '(mtext) (disp2 x
) '| set to | y
))
38 ;; now, check to see if we are supposed to wait in a break at this point
44 (cond ((atom x
) ;; typical case is setting an atom to a value
45 (when (or (not (symbolp x
))
46 (member x
'(t nil
) :test
#'eq
) ;can't set t or nil, boolean constants
47 (mget x
'$numer
) ;can't set a numeric constant like $%pi or $%e
48 (char= (getcharn x
1) #\
&)) ;can't set a string (begins with &)
49 (if munbindp
(return nil
)) ;dunno what this does. see mlisp.lisp file.
50 ;; an unsettable atom. Signal an error.
52 (merror "~:M is a constant. Attempt to reassign it." x
)
53 (merror "~:M is an improper left-hand side for an assignment" x
))
55 ;; a settable atom. Let's get on with it.
56 (let ((f (get x
'assign
)))
57 (if (and f
(or (not (eq x y
))
58 (member f
'(neverset read-only-assign
) :test
#'eq
)))
59 (if (eq (funcall f x y
) 'munbindp
) (return nil
))))
60 (cond ((and (not (boundp x
))
61 (not dsksetp
)) ;? something about disk?
62 (add2lnc x $values
)) ;not previously bound, make a note.
63 ((and (not (eq x y
)) ; something about macsyma options?
65 (if $optionset
(mtell "~:M option is being set.~%" x
))
66 (if (not (eq x
'$linenum
)) (add2lnc x $myoptions
))))
68 (return (set x y
))) ;; actually put a value in lisp value cell!
70 ;; ADDITION 8/17/05 RJF thnx to suggestions by S. Macrakis, R. Dodier,;;;;;;;;;;;;
71 ;;check to see if the operator has an mset_extension_operator.
72 ;; If so, this says how to do assignments. Examples, a@b:x. Put mset_extension_operator
73 ;; of $mrecordassign on the atom $@. To allow [a,b]:[3,4] put op on mlist.
74 ;; arguably we could use mget, mfuncall, and $mset_extension_operator and
75 ;; allow this to be done at the maxima level instead of lisp.
77 ;; X is could be something like (($FOO ARRAY) 42), in which case it is meaningful
78 ;; to look for an assignment operator associated either with $FOO itself or with
79 ;; $FOO's object type, with "object type" = (CAAR (SYMBOL-VALUE '$FOO)).
82 ((x-value (if (boundp (caar x
)) (symbol-value (caar x
))))
85 ((get (caar x
) 'mset_extension_operator
))
86 ((and (not (atom x-value
)) (get (caar x-value
) 'mset_extension_operator
))))))
88 (return-from mset
(funcall mset-extension-op x y
)))))
90 ;; x is not an atom, but something like (($M array simp) 12)
91 ((member 'array
(cdar x
) :test
#'eq
)
92 (return (arrstore x y
))) ;; do the array store
94 ;; ((and $subscrmap (member (caar x) '(mlist $matrix) :test #'eq));; deprecated.
95 ;; (return (outermap1 'mset x y)))
97 (t (merror "Improper left-hand side for an assignment:~%~M" x
)))))
100 (setf (get '$
@ 'mset_extension_operator
) '$mrecordassign
)
102 ;;; new programs by Richard Fateman 8/14/05
103 ;; defstruct(f(x,y,z));
106 ;; myrecord; ==> f(x,45,z)
109 ;; initializers are possible
110 ;; defstruct(f(x,y=3.14159, z));
111 ;; ff:new(f) ==> f(x,3.14159,z)
112 ;; ff@y:2.71828 ==> ff is f(x,2.71828,z).
114 ;; the @ syntax can also be used instead of substinpart.
116 ;; k: h(g(aa,bb),cc);
117 ;; k@1@2:dd; change aa to dd.
120 ;;; This definition and the ones following are needed to get the @ stuff going
121 ;;(defparameter mset_extension_operators '(($@ . $mrecordassign)))
124 (defmfun $mrecordassign
(atted value
)
125 ;; assume atted is (($@..) instance-name field-name)
126 ;; should insert some checking code here
127 (let* ((in (cadr atted
)) ;instance
128 (fn (caddr atted
)) ;field
130 (index (if (integerp fn
) fn
;;; allow foo@3, also
131 (position fn
($get
(caar obj
) '$defstruct_template
))))
134 (if (null index
) (merror "Unknown field in record:~%~M" fn
))
135 (if (< 0 index
(length obj
)) (setf (elt obj index
) value
)
136 (merror "Illegal instance:~%~M @ ~M" in fn
))
141 (if (not (listp in
))(list '(%
@) in fn
) ;noun form
143 (if (integerp fn
) fn
;;; allow foo@3, also
144 (position fn
($get
(caar in
) '$defstruct_template
))))) ;field->integer
145 (if (null index
) (merror "Unknown field in record:~%~M" fn
))
146 (if (< 0 index
(length in
))
147 (elt in index
) (merror "Illegal instance:~%~M @ ~M" in fn
))
151 ;; This will not work for compiled code.
153 ;; L looks like defstruct (foo(...), bar(...), baz(...)).
154 ;; Process each argument and return a list of declared structures.
156 (defmspec $defstruct
(L)
157 `((mlist) ,@(mapcar 'defstruct1
(cdr L
))))
159 (defun defstruct1 (z) ;; z will look like (($whatever) $a $b $c)
160 ;; store the template on $whatever
161 ($put
(caar z
) (namesonly z
) '$defstruct_template
)
162 ;; set the initialization on $whatever
163 ($put
(caar z
) (initializersmostly z
) '$defstruct_default
)
166 (defun namesonly(r) ; f(a,b,c) unchanged, f(a=3,b=4,c=5) -> f(a,b,c)
167 (cons (car r
)(mapcar #'(lambda(z)
169 ((eq (caar z
) 'mequal
)(second z
))
170 (t (merror "~% Expected record initializer, not ~M." z
))))
172 (defun initializersmostly(r);; f(a=3,b,c=5) -> f(3,b,5)
173 (cons (car r
)(mapcar #'(lambda(z)
175 ((eq (caar z
) 'mequal
)(third z
))
176 (t (merror "~% Expected record initializer, not ~M." z
))))
180 (if (not (= (length (cdr h
)) 1))
181 (merror "~% new: expected exactly one argument, not ~M." (length (cdr h
))))
183 (let ((recordname (cadr h
)))
185 ((symbolp recordname
) ;; the case of, e.g. new(f);
186 (if (null ($get recordname
'$defstruct_default
))
187 (merror "~% new: don't know anything about `~M'." recordname
))
189 (copy-tree ($get recordname
'$defstruct_default
)))
191 ;; assume there is some initialization here e.g. new (f(5,6,7))
193 (let ((recordop (caar recordname
)) (recordargs (cdr recordname
)))
194 (if (null ($get recordop
'$defstruct_default
))
195 (merror "~% new: don't know anything about `~M'." recordop
))
197 (if (not (= (length recordargs
) (length (cdr ($get recordop
'$defstruct_default
)))))
198 (merror "~% new: wrong number of arguments in initializer; expected ~M, not ~M."
199 (length (cdr ($get recordop
'$defstruct_default
))) (length recordargs
)))
201 (copy-tree recordname
))))))
203 ;; this is the lisp code equivalent to executing the command
210 ;;(add2lnc '&@ $props)
212 (defprop $
@ dimension-infix dimension
)
213 (defprop $
@ (#\space
#\
@ #\space
) dissym
)
214 (defprop $
@ msize-infix grind
)
217 (defprop $
@ parse-infix led
)
218 (defprop %
@ dimension-infix dimension
)
219 (defprop %
@ (#\space
#\
@ #\space
) dissym
)
222 ;;;;;;;;;;;;;8/15/05 RJF
223 ;; after reading in the redefinition of mset and mset_extension_operators
224 ;; (not necessarily the @ stuff)
225 ;; the follow code implements PARALLEL LIST assignment.
226 ;; it is consistent with commercial macsyma. [a,b,c]:[x,y,z] means
227 ;; about the same as a:x, b:y, c:z. Actually it
228 ;; evaluates x,y,z BEFORE any assignments to a,b,c, hence parallel.
229 ;; Also implemented is [a,b,c]:x which evaluates x once and assigns
231 ;; value returned is (evaluated x to ex) [ex,ex,ex].
234 ;; quiz . [a,b]:[b,2*a]. produces values a=b, b= 2*a.
235 ;; re-execute the statement 4 times. what do you get? [4b, 8a]
237 ;; a neat application of parallel assignment is this version of
238 ;; a gcd algorithm (for integers)...
239 ;; kgcd(a,b):=(while b#0 do [a,b]:[b,remainder(a,b)], abs(a));
240 ;; The extended euclidean algorithm looks even better with parallel
243 ;; add MLIST to possible operators on the left hand side of
244 ;; an assignment statement.
246 (setf (get 'mlist
'mset_extension_operator
) '$mlistassign
)
248 (defmfun $mlistassign
(tlist vlist
)
249 ;; tlist is ((mlist..) var[0]... var[n]) of targets
250 ;; vlist is either((mlist..) val[0]... val[n]) of values
251 ;; or possibly just one value.
252 ;; should insert some checking code here
253 (if (and (listp vlist
)
254 (eq (caar vlist
) 'mlist
)
255 (not (= (length tlist
)(length vlist
))))
256 (merror "Illegal list assignment: different lengths of ~M and ~M." tlist vlist
))
257 (unless (and (listp vlist
)
258 (eq (caar vlist
) 'mlist
))
259 (setf vlist
(cons (car tlist
) ;; if [a,b,c]:v then make a list [v,v,v]
260 (make-sequence 'list
(1-(length tlist
)) :initial-element vlist
))))
261 (map nil
#'mset
(cdr tlist
)(cdr vlist
))