1 ;;; -*- Mode: Lisp; Package: Maxima; Syntax: Common-Lisp; Base: 10 -*- ;;;;
2 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
3 ;;; The data in this file contains enhancements. ;;;;;
5 ;;; Copyright (c) 1984,1987 by William Schelter,University of Texas ;;;;;
6 ;;; All rights reserved ;;;;;
7 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
8 ;;; (c) Copyright 1982 Massachusetts Institute of Technology ;;;
9 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
13 (macsyma-module merror
)
15 ;;; Macsyma error signalling.
16 ;;; 2:08pm Tuesday, 30 June 1981 George Carrette.
18 (defmfun $error
(&rest l
)
19 "Signals a Maxima user error."
20 (apply #'merror
(fstringc l
)))
22 (defmfun $warning
(&rest l
)
23 "Signals a Maxima warning."
26 (defmvar $error_size
60.
27 "Expressions greater in SOME size measure over this value
28 are replaced by symbols {ERREXP1, ERREXP2,...} in the MAXIMA-ERROR
29 display, the symbols being set to the expressions, so that one can
30 look at them with expression editing tools. The default value of
31 this variable may be determined by factors of terminal speed and type.")
33 (defun error-size (exp)
34 ; Call SPECREPCHECK on the argument in case it's a specrep. Ugh.
35 ; But this simplifies its argument, which is a no-no if we got here
36 ; because some simplification code is complaining, so inhibit simplification. Double ugh.
38 (declare (special $simp
))
39 (setq exp
(specrepcheck exp
)))
43 (do ((l (cdr exp
) (cdr l
))
44 (n 1 (1+ (+ n
(error-size (car l
))))))
46 ;; no need to go any further, and this will save us
47 ;; from circular structures. (Which the display
48 ;; package would have a hell of a time with too.)
52 ;;; Problem: Most macsyma users do not take advantage of break-points
53 ;;; for debugging. Therefore they need to have the error variables
54 ;;; SET (as the old ERREXP was), and not PROGV bound. The problem with
55 ;;; this is that recursive errors will bash the old value of the
56 ;;; error variables. However, since we do bind the value of the
57 ;;; variable $ERROR, calling the function $ERRORMSG will always
58 ;;; set things back. It would be better to bind these variables,
59 ;;; for, among other things, then the values could get garbage
62 (define-condition maxima-$error
(error)
63 ((message :initform $error
:reader the-$error
))
64 (:documentation
"Muser error, to be signalled by MERROR, usually.")
65 (:report
(lambda (c stream
)
67 (let ((*standard-output
* stream
))
70 (defvar *merror-signals-$error-p
* nil
71 "When T, MERROR will signal a MAXIMA-$ERROR condition.")
75 ;; (merror "hi there ~:M and ~:M" he he))
76 ;; This will signal a MAXIMA-$ERROR condition:
77 ;; (with-$error (h '$you))
79 (defmacro with-$error
(&body body
)
80 "Let MERROR signal a MAXIMA-$ERROR condition."
81 `(let ((*merror-signals-$error-p
* t
))
82 (declare (special *merror-signals-$error-p
*))
85 (defun merror (sstring &rest l
)
86 (setq $error
`((mlist simp
) ,sstring
,@ l
))
87 (cond (*merror-signals-$error-p
*
88 (error 'maxima-$error
))
90 ; Go immediately into the lisp debugger
91 (let ((*debugger-hook
* nil
))
92 (invoke-debugger (make-condition 'maxima-$error
))))
94 (let ((dispflag t
) ret
)
95 (declare (special dispflag
))
98 (format t
(intl:gettext
" -- an error. Entering the Maxima debugger.~%~
99 Enter ':h' for help.~%"))
101 (setq ret
(break-dbm-loop nil
))
102 (cond ((eql ret
:resume
)
107 (error 'maxima-$error
))
111 (fresh-line *standard-output
*)
113 (format t
(intl:gettext
"~& -- an error. To debug this try: debugmode(true);~%"))
115 (throw 'macsyma-quit
'maxima-error
))))
117 (defun mwarning (&rest l
)
118 (format t
"Warning: ~{~a~^ ~}~%" (mapcar #'$sconcat l
)))
120 (defmvar $error_syms
'((mlist) $errexp1 $errexp2 $errexp3
)
121 "Symbols to bind the too-large `maxima-error' expressions to"
122 :properties
((assign 'assign-symbols
)))
124 (defun assign-symbols (var val
)
125 (if (not (and ($listp val
)
126 (do ((l (cdr val
) (cdr l
)))
127 ((null l
) (return t
))
128 (if (not (symbolp (car l
))) (return nil
)))))
129 (merror (intl:gettext
"assignment: assignment to ~M must be a list of symbols; found: ~M")
132 (defun process-error-argl (l)
133 ;; This returns things so that we could set or bind.
134 (do ((error-symbols nil
)
139 (list (nreverse error-symbols
)
140 (nreverse error-values
)
141 (nreverse new-argl
)))
142 (let ((form (pop l
)))
143 (cond ((> (error-size form
) $error_size
)
145 (let ((sym (nthcdr symbol-number $error_syms
)))
147 (setq sym
(car sym
)))
149 (setq sym
(intern (format nil
"~A~D" '$errexp
151 (tuchus $error_syms sym
)))
152 (push sym error-symbols
)
153 (push form error-values
)
154 (push sym new-argl
)))
156 (push form new-argl
))))))
158 (defmfun $errormsg
()
159 "errormsg() redisplays the maxima-error message while in a `maxima-error' break."
160 ;; Don't optimize out call to PROCESS-ERROR-ARGL in case of
161 ;; multiple calls to $ERRORMSG, because the user may have changed
162 ;; the values of the special variables controlling its behavior.
163 ;; The real expense here is when MFORMAT calls the DISPLA package.
164 (let ((the-jig (process-error-argl (cddr $error
))))
165 (mapc #'(lambda (v x
) (setf (symbol-value v
) x
)) (car the-jig
) (cadr the-jig
))
170 (cadr $error
) (caddr the-jig
))))
171 (mtell (intl:gettext
"~%** error while printing error message **~%~A~%")
177 ;; RAT-ERROR (function)
179 ;; Throw to the nearest enclosing RAT-ERR tag (set by IGNORE-RAT-ERROR or
180 ;; RAT-ERROR-TO-MERROR). If ERROR-ARGS is nonzero, they are thrown. The
181 ;; RAT-ERROR-TO-MERROR form applies the MERROR function to them.
183 ;; The obvious way to make RAT-ERROR work is to raise a condition. On the lisp
184 ;; implementations we support other than CMUCL, this runs perfectly
185 ;; fast. Unfortunately, on CMUCL there's a performance bug which turns out to be
186 ;; very costly when you raise lots of the condition. There are lots and lots of
187 ;; rat-error calls running the test suite (10s of thousands), and this turns out
188 ;; to be hilariously slow.
190 ;; Thus we do the (catch .... (throw .... )) thing instead. Other error handling
191 ;; should be able to use conditions with impunity: the only reason that the
192 ;; performance was so critical with rat error is the sheer number of them that
194 (defun rat-error (&rest error-args
)
195 (throw 'rat-err error-args
))
199 ;; Evaluate BODY with the RAT-ERR tag set. If something in BODY throws to
200 ;; RAT-ERR (happens upon calling the RAT-ERROR function), this form evaluates to
202 (defmacro ignore-rat-err
(&body body
)
203 (let ((result (gensym)) (error-p (gensym)))
204 `(let ((,result
) (,error-p t
))
206 (setf ,result
(progn ,@body
))
208 (unless ,error-p
,result
))))
210 (defmacro rat-error-to-merror
(&body body
)
211 (let ((result (gensym)) (error-args (gensym)) (error-p (gensym)))
212 `(let ((,result
) (,error-p t
))
215 (setf ,result
(progn ,@body
))
216 (setf ,error-p nil
))))
218 (apply #'merror
,error-args
)))
221 ;;; The user-error function is called on "strings" and expressions.
222 ;;; Cons up a format string so that $ERROR can be bound.
223 ;;; This might also be done at code translation time.
224 ;;; This is a bit crude.
227 (do ((sl nil
) (s) (sb)
230 (setq sl
(maknam sl
))
231 (cons sl
(nreverse se
)))
234 (setq sb
(mapcan #'(lambda (x)
241 (setq sb
(list #\~
#\M
))))
242 (setq sl
(nconc sl sb
(if (null l
) nil
(list #\space
))))))