Merge branch 'master' into bug-4403-remove-polyfill
[maxima.git] / archive / share / trash / defstruct.lisp
blob6b22023862d6df5a12e473432e4eaf2f6f5327f7
1 ;;; from maxima-5.9.0/maxima-5.9.0/src/mlisp.lisp
3 (in-package :maxima)
5 ;;; changes, 8/14/05
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)
25 (defmfun mset (x y)
26 (prog ()
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))))
35 (not (eq x y)))
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
39 (if $setcheckbreak
40 (let (($setval y))
41 (merrbreak t)
42 (setq y $setval)))))
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.
51 (if (mget x '$numer)
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?
64 (optionp x))
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)).
81 ((let*
82 ((x-value (if (boundp (caar x)) (symbol-value (caar x))))
83 (mset-extension-op
84 (cond
85 ((get (caar x) 'mset_extension_operator))
86 ((and (not (atom x-value)) (get (caar x-value) 'mset_extension_operator))))))
87 (if mset-extension-op
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)))))
99 ;;; starting here..
100 (setf (get '$@ 'mset_extension_operator) '$mrecordassign)
102 ;;; new programs by Richard Fateman 8/14/05
103 ;; defstruct(f(x,y,z));
104 ;; myrecord: new(f);
105 ;; myrecord@y:45;
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.
118 ;; k;
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
129 (obj (meval in))
130 (index (if (integerp fn) fn ;;; allow foo@3, also
131 (position fn ($get (caar obj) '$defstruct_template))))
132 ) ;field->integer
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))
137 value))
140 (defmfun $@ (in fn)
141 (if (not (listp in))(list '(%@) in fn) ;noun form
142 (let* ((index
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)
168 (cond((symbolp z) z)
169 ((eq (caar z) 'mequal)(second z))
170 (t (merror "~% Expected record initializer, not ~M." z))))
171 (cdr r))))
172 (defun initializersmostly(r);; f(a=3,b,c=5) -> f(3,b,5)
173 (cons (car r)(mapcar #'(lambda(z)
174 (cond((symbolp z) z)
175 ((eq (caar z) 'mequal)(third z))
176 (t (merror "~% Expected record initializer, not ~M." z))))
177 (cdr r))))
179 (defmspec $new (h)
180 (if (not (= (length (cdr h)) 1))
181 (merror "~% new: expected exactly one argument, not ~M." (length (cdr h))))
183 (let ((recordname (cadr h)))
184 (cond
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
204 ;; infix(@);
207 (defprop $@ %@ verb)
208 (defprop $@ &@ op)
209 (defprop &@ $@ opr)
210 ;;(add2lnc '&@ $props)
211 (define-symbol '&@)
212 (defprop $@ dimension-infix dimension)
213 (defprop $@ (#\space #\@ #\space) dissym)
214 (defprop $@ msize-infix grind)
215 (defprop $@ 180 lbp)
216 (defprop $@ 180 rbp)
217 (defprop $@ parse-infix led)
218 (defprop %@ dimension-infix dimension)
219 (defprop %@ (#\space #\@ #\space) dissym)
220 (defprop %@ $@ noun)
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
230 ;; to a,b,c.
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
241 ;; assignment.
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))
262 vlist)