home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Amiga MA Magazine 1998 #6
/
amigamamagazinepolishissue1998.iso
/
coders
/
jËzyki_programowania
/
clisp
/
src
/
archive
/
clisp.faslsp.lha
/
user1.lsp
< prev
next >
Wrap
Lisp/Scheme
|
1996-08-07
|
30KB
|
839 lines
;;;; User-Interface, Teil 1
;;;; Eval-Env, Debugger, Stepper, Errors, Query-User
;;;; Bruno Haible 4.2.1990, 4.11.1991
(in-package "LISP")
(export '(eval-env with-keyboard *keyboard-input*))
(in-package "SYSTEM")
;-------------------------------------------------------------------------------
;; EVAL-ENV
; Das Toplevel-Environment
(defparameter *toplevel-environment* (eval '(the-environment)))
(defparameter *toplevel-denv* (svref *toplevel-environment* 4))
; Evaluiert eine Form in einem Environment
(defun eval-env (form &optional (env *toplevel-environment*))
(evalhook form nil nil env)
)
;-------------------------------------------------------------------------------
;; Debugger
(defvar *break-count* 0) ; Anzahl der aktiven Break-Schleifen (Fixnum >=0)
; Zähler zum Vermeiden von Endlosrekursionen wegen *error-output*
(defvar *recurse-count-error-output* 0)
; Hauptschleife:
; (driver
; #'(lambda () (read-eval-print "> "))
; )
(defvar *prompt-with-package* nil)
(defun prompt-string-package ()
(if (and (packagep *package*) (package-name *package*))
(if (or *prompt-with-package*
(not (find-symbol "T" *package*)) ; Ist *package* eine Package ohne Lisp-Syntax?
)
(string-concat "[" (package-name *package*) "]")
""
)
#L{
DEUTSCH "[*package* ungültig]"
ENGLISH "[*package* invalid]"
FRANCAIS "[*package* invalide]"
}
) )
; Vom Prompt der erste Teil:
(defun prompt-string1 () "")
; Vom Prompt der zweite Teil:
(defun prompt-string2 () (prompt-string-package))
; Vom Prompt der letzte Teil:
(defun prompt-string3 () "> ")
; Help-Funktion:
(defvar *key-bindings* nil) ; Liste von Tasten-Bindungen und Helpstrings
(defun help ()
(dolist (s (reverse (remove-if-not #'stringp *key-bindings*)))
(write-string s #|*debug-io*|#)
) )
; Bausteine der Break-Schleife:
(defvar *debug-frame*)
(defvar *debug-mode*)
(defvar *frame-limit1* nil) ; untere Grenze für frame-down und frame-down-1
(defvar *frame-limit2* nil) ; obere Grenze für frame-up und frame-up-1
(defun frame-limit1 (frames-to-skip)
(let ((frame (the-frame)))
(let ((*frame-limit1* nil)
(*frame-limit2* nil))
(dotimes (i frames-to-skip) (setq frame (frame-up-1 frame 1)))
)
frame
) )
(defun frame-limit2 ()
(let ((frame (the-frame)))
(let ((*frame-limit1* nil)
(*frame-limit2* nil))
(loop
(let ((nextframe (frame-up-1 frame 1)))
(when (or (eq nextframe frame) (driver-frame-p nextframe)) (return))
(setq frame nextframe)
) )
(dotimes (i 2) (setq frame (frame-down-1 frame 1)))
)
frame
) )
(defun debug-help () (help) (throw 'debug 'continue))
(defun debug-unwind () (throw 'debug 'unwind))
(defun debug-mode-1 () (setq *debug-mode* 1) (throw 'debug 'continue))
(defun debug-mode-2 () (setq *debug-mode* 2) (throw 'debug 'continue))
(defun debug-mode-3 () (setq *debug-mode* 3) (throw 'debug 'continue))
(defun debug-mode-4 () (setq *debug-mode* 4) (throw 'debug 'continue))
(defun debug-mode-5 () (setq *debug-mode* 5) (throw 'debug 'continue))
(defun debug-where ()
(describe-frame *standard-output* *debug-frame*)
(throw 'debug 'continue)
)
(defun debug-up ()
(describe-frame *standard-output*
(setq *debug-frame* (frame-up-1 *debug-frame* *debug-mode*))
)
(throw 'debug 'continue)
)
(defun debug-top ()
(describe-frame *standard-output*
(setq *debug-frame* (frame-up *debug-frame* *debug-mode*))
)
(throw 'debug 'continue)
)
(defun debug-down ()
(describe-frame *standard-output*
(setq *debug-frame* (frame-down-1 *debug-frame* *debug-mode*))
)
(throw 'debug 'continue)
)
(defun debug-bottom ()
(describe-frame *standard-output*
(setq *debug-frame* (frame-down *debug-frame* *debug-mode*))
)
(throw 'debug 'continue)
)
(defun debug-backtrace (&optional (mode *debug-mode*))
(let ((frame (frame-down-1 (frame-up-1 *frame-limit1* mode) mode)))
(loop
(describe-frame *standard-output* frame)
(when (eq frame (setq frame (frame-up-1 frame mode))) (return))
) )
(throw 'debug 'continue)
)
(defun debug-backtrace-1 () (debug-backtrace 1))
(defun debug-backtrace-2 () (debug-backtrace 2))
(defun debug-backtrace-3 () (debug-backtrace 3))
(defun debug-backtrace-4 () (debug-backtrace 4))
(defun debug-backtrace-5 () (debug-backtrace 5))
(defun debug-trap-on ()
(trap-eval-frame *debug-frame* t)
(throw 'debug 'continue)
)
(defun debug-trap-off ()
(trap-eval-frame *debug-frame* nil)
(throw 'debug 'continue)
)
(defun debug-redo ()
(redo-eval-frame *debug-frame*)
(throw 'debug 'continue)
)
(defun debug-return ()
(return-from-eval-frame *debug-frame*
(read-form
#L{
DEUTSCH "Werte: "
ENGLISH "values: "
FRANCAIS "Valeurs : "
}
) )
(throw 'debug 'continue)
)
(defun debug-continue () (throw 'debug 'quit))
(defun commands0 ()
(list
#L{
DEUTSCH "
Help = diese Liste
Benutzen Sie die üblichen Editiermöglichkeiten."
ENGLISH "
Help = this list
Use the usual editing capabilities."
FRANCAIS "
Help = cette liste
Éditez de la façon habituelle."
}
(cons "Help" #'debug-help )
) )
(defun commands1 ()
(list
#L{
DEUTSCH "
Help = dieses Menü
Abort = Abbruch, Rücksprung zur nächsthöheren Eingabeschleife
Unwind = Abbruch, Rücksprung zur nächsthöheren Eingabeschleife
Mode-1 = alle Stack-Elemente inspizieren
Mode-2 = alle Frames inspizieren
Mode-3 = nur lexikalische Frames inspizieren
Mode-4 = nur EVAL- und APPLY-Frames inspizieren (Default)
Mode-5 = nur APPLY-Frames inspizieren
Where = diesen Frame inspizieren
Up = nächsthöheren Frame inspizieren
Top = obersten Frame inspizieren
Down = nächstneueren Frame inspizieren
Bottom = neuesten Frame inspizieren
Backtrace-1 = alle Stack-Elemente auflisten
Backtrace-2 = alle Frames auflisten
Backtrace-3 = alle lexikalische Frames auflisten
Backtrace-4 = alle EVAL- und APPLY-Frames auflisten
Backtrace-5 = alle APPLY-Frames auflisten
Backtrace = Stack auflisten im aktuellen Mode
Break+ = Breakpoint im EVAL-Frame setzen
Break- = Breakpoint im EVAL-Frame löschen
Redo = Form im EVAL-Frame erneut auswerten
Return = EVAL-Frame mit gegebenen Werten verlassen"
ENGLISH "
Help = this command list
Abort = abort to the next recent input loop
Unwind = abort to the next recent input loop
Mode-1 = inspect all the stack elements
Mode-2 = inspect all the frames
Mode-3 = inspect only lexical frames
Mode-4 = inspect only EVAL and APPLY frames (default)
Mode-5 = inspect only APPLY frames
Where = inspect this frame
Up = go up one frame, inspect it
Top = go to top frame, inspect it
Down = go down one frame, inspect it
Bottom = go to bottom (most recent) frame, inspect it
Backtrace-1 = list all stack elements
Backtrace-2 = list all frames
Backtrace-3 = list all lexical frames
Backtrace-4 = list all EVAL and APPLY frames
Backtrace-5 = list all APPLY frames
Backtrace = list stack in current mode
Break+ = set breakpoint in EVAL frame
Break- = disable breakpoint in EVAL frame
Redo = re-evaluate form in EVAL frame
Return = leave EVAL frame, prescribing the return values"
FRANCAIS "
Help = ce menu-ci
Abort = arrêt, retour au niveau supérieur
Unwind = arrêt, retour au niveau supérieur
Mode-1 = examiner tous les éléments de la pile
Mode-2 = examiner tous les «frames»
Mode-3 = examiner uniquement les «frames» lexicaux
Mode-4 = examiner uniquement les «frames» EVAL et APPLY (par défaut)
Mode-5 = examiner uniquement les «frames» APPLY
Where = examiner ce «frame»
Up = examiner un «frame» supérieur
Top = examiner le «frame» le plus élevé
Down = examiner un prochain «frame» plus récent (inférieur)
Bottom = examiner le «frame» le plus récent (le plus bas)
Backtrace-1 = montrer tous les éléments de la pile
Backtrace-2 = montrer tous les «frames»
Backtrace-3 = montrer tous les «frames» lexicaux
Backtrace-4 = montrer tous les «frames» EVAL et APPLY
Backtrace-5 = montrer tous les «frames» APPLY
Backtrace = montrer la pile en mode actuel
Break+ = placer un point d'interception dans le «frame» EVAL
Break- = enlever le point d'interception du «frame» EVAL
Redo = réévaluer la forme dans le «frame» EVAL
Return = quitter le «frame» EVAL avec certaines valeurs"
}
(cons "Help" #'debug-help )
(cons "?" #'debug-help )
(cons "Abort" #'debug-unwind)
(cons "Unwind" #'debug-unwind)
(cons "Mode-1" #'debug-mode-1)
(cons "Mode-2" #'debug-mode-2)
(cons "Mode-3" #'debug-mode-3)
(cons "Mode-4" #'debug-mode-4)
(cons "Mode-5" #'debug-mode-5)
(cons "Where" #'debug-where )
(cons "Up" #'debug-up )
(cons "Top" #'debug-top )
(cons "Down" #'debug-down )
(cons "Bottom" #'debug-bottom)
(cons "Backtrace-1" #'debug-backtrace-1)
(cons "Backtrace-2" #'debug-backtrace-2)
(cons "Backtrace-3" #'debug-backtrace-3)
(cons "Backtrace-4" #'debug-backtrace-4)
(cons "Backtrace-5" #'debug-backtrace-5)
(cons "Backtrace" #'debug-backtrace )
) )
(defun commands2 ()
(list
(cons "Break+" #'debug-trap-on )
(cons "Break-" #'debug-trap-off)
(cons "Redo" #'debug-redo )
(cons "Return" #'debug-return)
) )
(defun commands3 ()
(list
#L{
DEUTSCH "
Continue = Rest weiter abarbeiten"
ENGLISH "
Continue = continue evaluation"
FRANCAIS "
Continue = continuer l'évaluation"
}
(cons "Continue" #'debug-continue)
) )
;; um Help-Kommando erweiterte Hauptschleife.
(defun main-loop ()
(setq *break-count* 0)
(driver ; Driver-Frame aufbauen und folgende Funktion (endlos) ausführen:
#'(lambda ()
(catch 'debug ; die (throw 'debug ...) abfangen
(if ; Eingabezeile verlangen
(read-eval-print (string-concat (prompt-string1) (prompt-string2) (prompt-string3))
(copy-list (commands0))
)
; T -> #<EOF>
(exit)
; NIL -> Form bereits ausgewertet und ausgegeben
) ) ) ) )
(setq *driver* #'main-loop)
;; komfortable Break-Schleife. (Läuft nur in compiliertem Zustand!)
(defun break-loop (continuable &optional (condition nil) (print-it nil)
&aux (may-continue
(or continuable
(and condition (find-restart 'continue condition))
) )
(interactive-p (interactive-stream-p *debug-io*))
(commandsr '())
)
(when (and print-it (typep condition (clos:find-class 'condition)))
(symbol-stream '*error-output* :output)
; Ein Zeichen auf *error-output* ausgeben, mit Abfangen von Endlosrekursion:
(let ((*recurse-count-error-output* (1+ *recurse-count-error-output*)))
(when (> *recurse-count-error-output* 3)
(setq *recurse-count-error-output* 0)
(close *error-output*) (symbol-stream '*error-output* :output)
)
(terpri *error-output*)
)
(if may-continue
(progn (write-string "** - Continuable Error" *error-output*) (terpri *error-output*))
(write-string "*** - " *error-output*)
)
;; Output the error message, but don't trap into recursive errors.
(let ((*recursive-error-count* (1+ *recursive-error-count*)))
(if (> *recursive-error-count* 3)
(progn
(setq *recursive-error-count* 0)
(write-string
#L{
DEUTSCH "Unausgebbare Fehlermeldung"
ENGLISH "Unprintable error message"
FRANCAIS "Message inimprimable"
}
*error-output*
) )
(sys::print-condition condition *error-output*)
) )
(symbol-stream '*debug-io* :io)
(when may-continue
(if continuable
(when interactive-p
(terpri *debug-io*)
(write-string
#L{
DEUTSCH "Sie können (mit Continue) fortfahren."
ENGLISH "You can continue (by typing 'continue')."
FRANCAIS "Vous pouvez continuer (tapez «continue» pour cela)."
}
*debug-io*
)
)
(progn
(terpri *debug-io*)
(when interactive-p
(write-string
#L{
DEUTSCH "Wenn Sie (mit Continue) fortfahren: "
ENGLISH "If you continue (by typing 'continue'): "
FRANCAIS "Si vous continuez (en tapant «continue»): "
}
*debug-io*
)
)
(princ may-continue *debug-io*)
) ) ) )
(when condition
(let ((restarts (remove may-continue (compute-restarts condition))))
(when restarts
(when interactive-p
(terpri *debug-io*)
(write-string (if may-continue
#L{
DEUTSCH "Weitere mögliche Optionen:"
ENGLISH "The following restarts are available too:"
FRANCAIS "D'autres rentrées possibles:"
}
#L{
DEUTSCH "Mögliche Optionen:"
ENGLISH "The following restarts are available:"
FRANCAIS "Rentrées possibles:"
}
)
*debug-io*
) )
(let ((counter 0))
(dolist (restart restarts)
(let* ((command (string-concat "R" (sys::decimal-string (incf counter))))
(helpstring (string-concat "
" command " = " (princ-to-string restart))))
; Restart-Möglichkeit ausgeben:
(when interactive-p
(write-string helpstring *debug-io*)
)
(push helpstring commandsr)
; und in die Liste commandsr aufnehmen:
(push (cons command
(let ((restart restart))
#'(lambda () (invoke-restart-interactively restart))
) )
commandsr
) ) )
(setq commandsr (nreverse commandsr))
) ) ) )
(tagbody
(let* ((*break-count* (1+ *break-count*))
(stream (make-synonym-stream '*debug-io*))
(*standard-input* stream)
(*standard-output* stream)
(prompt (with-output-to-string (s)
(write-string (prompt-string1) s)
(write *break-count* :stream s)
(write-string ". Break" s)
(write-string (prompt-string2) s)
(write-string (prompt-string3) s)
) )
(*frame-limit1* (frame-limit1 13))
(*frame-limit2* (frame-limit2))
(*debug-mode* 4)
(*debug-frame* (frame-down-1 (frame-up-1 *frame-limit1* *debug-mode*) *debug-mode*))
)
(driver ; Driver-Frame aufbauen und folgende Funktion (endlos) ausführen:
#'(lambda ()
(case
(catch 'debug ; die (throw 'debug ...) abfangen und analysieren
(same-env-as *debug-frame* ; bei *debug-frame* gültiges Environment aufbauen
#'(lambda ()
(if ; Eingabezeile verlangen
(read-eval-print prompt
(nconc (copy-list (commands1))
(when (eval-frame-p *debug-frame*) (copy-list (commands2)))
(when may-continue (copy-list (commands3)))
commandsr
) )
; T -> #<EOF>
(throw 'debug (if may-continue 'quit 'unwind))
; NIL -> Form bereits ausgewertet und ausgegeben
#|(throw 'debug 'continue)|#
) ) ) )
(unwind (go unwind))
(quit ; nur erreicht, falls may-continue
(if continuable
(go quit)
(invoke-restart-interactively may-continue)
) )
(t ) ; alles andere, insbesondere continue
) ) ) )
unwind (unwind-to-driver)
quit
) )
(setq *break-driver* #'break-loop)
;-------------------------------------------------------------------------------
;; komfortabler Stepper. (Läuft nur in compiliertem Zustand!)
(defvar *step-level* 0) ; momentane Step-Tiefe
(defvar *step-quit* most-positive-fixnum) ; kritische Step-Tiefe:
; sobald diese unterschritten wird, wacht der Stepper wieder auf.
(defvar *step-watch* nil) ; Abbruchbedingung
; (STEP form), CLTL S. 441
(defmacro step (form)
`(let* ((*step-level* 0)
(*step-quit* most-positive-fixnum)
(*step-watch* nil)
(*evalhook* #'step-hook-fn))
,form
)
)
(defun commands4 ()
(list
#L{
DEUTSCH "
Step = Step into form: diese Form im Einzelschrittmodus ausführen
Next = Step over form: diese Form auf einmal ausführen
Over = Step over this level: bis zum Aufrufer auf einmal ausführen
Continue = Einzelschrittmodus abschalten, Rest ausführen
Step-until, Next-until, Over-until, Continue-until:
dito, jedoch mit Angabe einer Abbruchbedingung"
ENGLISH "
Step = step into form: evaluate this form in single step mode
Next = step over form: evaluate this form at once
Over = step over this level: evaluate at once up to the next return
Continue = switch off single step mode, continue evaluation
Step-until, Next-until, Over-until, Continue-until:
same as above, specify a condition when to stop"
FRANCAIS "
Step = step into form: évaluer cette forme petit à petit
Next = step over form: évaluer cette forme en bloc
Over = step over this level: évaluer tout le reste jusqu'au prochain retour
Continue = continue: évaluer tout le reste en bloc
Step-until, Next-until, Over-until, Continue-until:
de même, avec spécification d'une condition d'arrêt"
}
(cons "Step" #'(lambda () (throw 'stepper 'into)))
(cons "Next" #'(lambda () (throw 'stepper 'over)))
(cons "Over" #'(lambda () (throw 'stepper 'over-this-level)))
(cons "Continue" #'(lambda () (throw 'stepper 'continue)))
(cons "Step-until" #'(lambda () (throw 'stepper (values 'into t))))
(cons "Next-until" #'(lambda () (throw 'stepper (values 'over t))))
(cons "Over-until" #'(lambda () (throw 'stepper (values 'over-this-level t))))
(cons "Continue-until" #'(lambda () (throw 'stepper (values 'continue t))))
) )
(defun step-values (values)
(let ((*standard-output* *debug-io*))
(terpri #|*debug-io*|#)
(write-string
#L{
DEUTSCH "Step "
ENGLISH "step "
FRANCAIS "Step "
}
#|*debug-io*|#
)
(write *step-level* #|:stream *debug-io*|#)
(write-string " ==> " #|*debug-io*|#)
(case (length values)
(0 (write-string
#L{
DEUTSCH "Keine Werte"
ENGLISH "no values"
FRANCAIS "Aucune valeur"
}
#|*debug-io*|#
) )
(1 (write-string
#L{
DEUTSCH "Wert: "
ENGLISH "value: "
FRANCAIS "Valeur : "
}
#|*debug-io*|#
)
(write (car values) #|:stream *debug-io*|#)
)
(t (write (length values) #|:stream *debug-io*|#)
(write-string
#L{
DEUTSCH " Werte: "
ENGLISH " values: "
FRANCAIS " Valeurs : "
}
#|*debug-io*|#
)
(do ((L values))
((endp L))
(write (pop L) #|:stream *debug-io*|#)
(unless (endp L) (write-string ", " #|*debug-io*|#))
) )
) )
(values-list values)
)
(defun step-hook-fn (form &optional (env *toplevel-environment*))
(let ((*step-level* (1+ *step-level*)))
(when (>= *step-level* *step-quit*) ; Solange *step-level* >= *step-quit*
(if (and *step-watch* (funcall *step-watch*)) ; und kein Breakpoint,
(setq *step-quit* most-positive-fixnum)
(return-from step-hook-fn ; ist der Stepper passiv
(evalhook form nil nil env) ; (d.h. er evaluiert die Form einfach)
) ) )
(tagbody
(let* ((stream (make-synonym-stream '*debug-io*))
(*standard-input* stream)
(*standard-output* stream)
(prompt (with-output-to-string (s)
(write-string (prompt-string1) s)
(write-string "Step " s)
(write *step-level* :stream s)
(write-string (prompt-string2) s)
(write-string (prompt-string3) s)
) )
(*frame-limit1* (frame-limit1 11))
(*frame-limit2* (frame-limit2))
(*debug-mode* 4)
(*debug-frame* (frame-down-1 (frame-up-1 *frame-limit1* *debug-mode*) *debug-mode*))
)
(fresh-line #|*debug-io*|#)
(write-string
#L{
DEUTSCH "Step "
ENGLISH "step "
FRANCAIS "Step "
}
#|*debug-io*|#
)
(write *step-level* #|:stream *debug-io*|#)
(write-string " --> " #|*debug-io*|#)
(write form #|:stream *debug-io*|# :length 4 :level 3)
(loop
(multiple-value-bind (what watchp)
(catch 'stepper ; die (throw 'stepper ...) abfangen und analysieren
(driver ; Driver-Frame aufbauen und folgende Funktion endlos ausführen:
#'(lambda ()
(case
(catch 'debug ; die (throw 'debug ...) abfangen und analysieren
(same-env-as *debug-frame* ; bei *debug-frame* gültiges Environment aufbauen
#'(lambda ()
(if ; Eingabezeile verlangen
(read-eval-print prompt
(nconc (copy-list (commands1))
(when (eval-frame-p *debug-frame*) (copy-list (commands2)))
(copy-list (commands4))
) )
; T -> #<EOF>
(go continue)
; NIL -> Form bereits ausgewertet und ausgegeben
#|(throw 'debug 'continue)|#
) ) ) )
(unwind (go unwind))
(t ) ; alles andere, insbesondere continue
) ) ) )
(when watchp
(let ((form (read-form
#L{
DEUTSCH "Abbruchbedingung: "
ENGLISH "condition when to stop: "
FRANCAIS "condition d'arrêt : "
}
)) )
(setq *step-watch* ; Funktion, die 'form' bei *debug-frame* auswertet
(eval-at *debug-frame* `(function (lambda () ,form)))
) ) )
(case what
(into (go into))
(over (go over))
(over-this-level (go over-this-level))
(continue (go continue))
)
) ) )
unwind
(unwind-to-driver)
into
(return-from step-hook-fn
(step-values
(multiple-value-list (evalhook form #'step-hook-fn nil env))
) )
over-this-level
(setq *step-quit* *step-level*) ; Stepper in Schlafzustand schalten
over
(return-from step-hook-fn
(step-values
(multiple-value-list (evalhook form nil nil env))
) )
continue
(setq *step-quit* 0)
(go over)
) ) )
;-------------------------------------------------------------------------------
;; Errors
; *ERROR-HANDLER* sollte NIL oder eine Funktion sein, die übergeben bekommt:
; - NIL (bei ERROR) bzw. continue-format-string (bei CERROR),
; - error-format-string,
; - Argumente dazu,
; und die nur zurückkehren sollte, falls das erstere /=NIL ist.
(defvar *error-handler* nil)
; (CERROR continue-format-string error-format-string {arg}*), CLTL S. 430
(defun cerror (continue-format-string error-format-string &rest args)
(if *error-handler*
(apply *error-handler*
(or continue-format-string t) error-format-string args
)
(progn
(terpri *error-output*)
(write-string "** - Continuable Error" *error-output*)
(terpri *error-output*)
(apply #'format *error-output* error-format-string args)
(terpri *debug-io*)
(if (interactive-stream-p *debug-io*)
(progn
(write-string
#L{
DEUTSCH "Wenn Sie (mit Continue) fortfahren: "
ENGLISH "If you continue (by typing 'continue'): "
FRANCAIS "Si vous continuez (en tapant «continue»): "
}
*debug-io*
)
(apply #'format *debug-io* continue-format-string args)
(funcall *break-driver* t)
)
(apply #'format *debug-io* continue-format-string args)
) ) )
nil
)
(defvar *break-on-warnings* nil)
; (WARN format-string {arg}*), CLTL S. 432
(defun warn (format-string &rest args)
(terpri *error-output*)
(write-string
#L{
DEUTSCH "WARNUNG:"
ENGLISH "WARNING:"
FRANCAIS "AVERTISSEMENT :"
}
*error-output*
)
(terpri *error-output*)
(apply #'format *error-output* format-string args)
(when *break-on-warnings* (funcall *break-driver* t))
nil
)
; (BREAK [format-string {arg}*]), CLTL S. 432
(defun break (&optional (format-string "*** - Break") &rest args)
(terpri *error-output*)
(apply #'format *error-output* format-string args)
(funcall *break-driver* t)
nil
)
; (SYSTEM::BATCHMODE-ERRORS {form}*) executes the forms, but handles errors
; just as a batch program should do: continuable errors are signalled as
; warnings, non-continuable errors cause Lisp to exit.
(defmacro batchmode-errors (&body body)
`(LET ((*ERROR-HANDLER* #'BATCHMODE-ERROR-HANDLER))
(PROGN ,@body)
)
)
(defun batchmode-error-handler (continue errorstring &rest args)
(if continue
(warn "~A~%~A" (apply #'format nil errorstring args)
(apply #'format nil continue args)
)
(progn
(terpri *error-output*)
(write-string "*** - " *error-output*)
(apply #'format *error-output* errorstring args)
(exit t) ; exit Lisp with error
) ) )
;-------------------------------------------------------------------------------
;; Querying the user
; (Y-OR-N-P [format-string {arg}*]), CLTL S. 407
(defun y-or-n-p (&optional format-string &rest args)
(when format-string
(fresh-line *query-io*)
(apply #'format *query-io* format-string args)
(write-string
#L{
DEUTSCH " (j/n) "
ENGLISH " (y/n) "
FRANCAIS " (o/n) "
}
*query-io*
) )
(loop
(let ((line (string-left-trim " " (read-line *query-io*))))
(when (plusp (length line))
(case (char-upcase (char line 0))
(#\N (return nil))
((#\J #\Y #\O) (return t))
) ) )
(terpri *query-io*)
(write-string
#L{
DEUTSCH "Bitte mit j oder n antworten: "
ENGLISH "Please answer with y or n : "
FRANCAIS "Répondez par o ou n : "
}
*query-io*
) ) )
; (YES-OR-NO-P [format-string {arg}*]), CLTL S. 408
(defun yes-or-no-p (&optional format-string &rest args)
(when format-string
(fresh-line *query-io*)
(apply #'format *query-io* format-string args)
(write-string
#L{
DEUTSCH " (ja/nein) "
ENGLISH " (yes/no) "
FRANCAIS " (oui/non) "
}
*query-io*
) )
(loop
(clear-input *query-io*)
(let* ((line (string-trim " " (read-line *query-io*)))
(h (assoc line '(("ja" . t) ("nein" . nil)
("yes" . t) ("no" . nil)
("oui" . t) ("non" . nil)
)
:test #'string-equal
)) )
(when h (return (cdr h)))
)
(terpri *query-io*)
(write-string
#L{
DEUTSCH "Bitte mit ja oder nein antworten: "
ENGLISH "Please answer with yes or no : "
FRANCAIS "Répondez par oui ou non : "
}
*query-io*
) ) )
(defvar *keyboard-input*)
#+AMIGA
(defun do-with-keyboard (fn)
;; Funktion wird umdefiniert falls SCREEN geladen ist
(unwind-protect
(progn
(sys::terminal-raw *terminal-io* t)
(funcall fn))
(sys::terminal-raw *terminal-io* nil)))
(defmacro with-keyboard (&body body)
#+(or DOS OS/2 WIN32-DOS) ; *keyboard-input* existiert schon
`(LET () (PROGN ,@body))
#+(or UNIX ACORN-RISCOS WIN32-UNIX)
(let ((mode (gensym)))
`(LET ((,mode NIL))
(UNWIND-PROTECT
(PROGN (SETQ ,mode (SYS::TERMINAL-RAW *TERMINAL-IO* T)) ,@body)
(SYS::TERMINAL-RAW *TERMINAL-IO* ,mode)
) )
)
#+AMIGA
`(DO-WITH-KEYBOARD (FUNCTION (LAMBDA () ,@body)))
)