From: Teemu L. <tli...@ik...> - 2010-06-26 18:33:38
|
There is something in Common Lisp's (or SBCL's) conditions and restarts which I don't understand. Here's some background and an example code. I made a simple tool "sbcl-eval" which evaluates its command-line arguments as Lisp expressions and prints the results: $ sbcl-eval "" '(* 123 2)' blah pi '(foo)' ; No value 246 ; The variable BLAH is unbound. 3.141592653589793d0 ; undefined function: FOO The code is here: --8<---------------cut here---------------start------------->8--- #!/home/dtw/local/bin/sbcl --script (defun handler-eof (condition) (declare (ignore condition)) (invoke-restart 'continue-eval "No value")) (defun handler-t (condition) (invoke-restart 'continue-eval condition)) (handler-bind ((end-of-file #'handler-eof) (t #'handler-t)) (dolist (arg (rest sb-ext:*posix-argv*)) (restart-case (format *standard-output* "~&~S~%" (eval (read-from-string arg))) (continue-eval (msg) (format *error-output* "~&; ~A~%" msg))))) --8<---------------cut here---------------end--------------->8--- The code does what I want but then I tried to test more its error handling. At least this works: $ sbcl-eval "(error 'error)" ; Condition ERROR was signalled. The following expression with SIMPLE-ERROR condition doesn't work. My restart is not active any more and I don't understand why. $ sbcl-eval "(error 'simple-error)" [See the backtrace below] There is some "unbound condition slot" error in the backtrace. What is happening? I'm using SBCL 1.0.39.22. --8<---------------cut here---------------start------------->8--- unhandled SB-INT:SIMPLE-CONTROL-ERROR in thread #<SB-THREAD:THREAD "initial thread" RUNNING {AA00A91}>: No restart CONTINUE-EVAL is active. 0: (SB-DEBUG::MAP-BACKTRACE #<CLOSURE (LAMBDA #) {AAAA88D}>)[:EXTERNAL] 1: (BACKTRACE 128 #<SYNONYM-STREAM :SYMBOL SB-SYS:*STDERR* {90BF8F9}>) 2: (SB-DEBUG::DEBUGGER-DISABLED-HOOK #<SB-INT:SIMPLE-CONTROL-ERROR "No restart ~S is active~@[ for ~S~]." {AAA9131}> #<unavailable argument>) 3: (SB-DEBUG::RUN-HOOK *INVOKE-DEBUGGER-HOOK* #<SB-INT:SIMPLE-CONTROL-ERROR "No restart ~S is active~@[ for ~S~]." {AAA9131}>) 4: (INVOKE-DEBUGGER #<SB-INT:SIMPLE-CONTROL-ERROR "No restart ~S is active~@[ for ~S~]." {AAA9131}>) 5: (ERROR SB-INT:SIMPLE-CONTROL-ERROR)[:EXTERNAL] 6: (SB-INT:FIND-RESTART-OR-CONTROL-ERROR CONTINUE-EVAL NIL) 7: (INVOKE-RESTART CONTINUE-EVAL)[:EXTERNAL] 8: (SIGNAL #<SIMPLE-ERROR "unbound condition slot: ~S" {AAA8EB9}>)[:EXTERNAL] 9: (ERROR "unbound condition slot: ~S")[:EXTERNAL] 10: (SB-KERNEL::FIND-SLOT-DEFAULT #<SB-KERNEL::CONDITION-CLASSOID SIMPLE-ERROR> #S(SB-KERNEL::CONDITION-SLOT :NAME SB-KERNEL:FORMAT-CONTROL :INITARGS (:FORMAT-CONTROL) :READERS (SIMPLE-CONDITION-FORMAT-CONTROL) :WRITERS NIL :INITFORM-P NIL :INITFORM NIL :ALLOCATION :INSTANCE :CELL NIL :DOCUMENTATION NIL)) 11: (SB-KERNEL:CONDITION-READER-FUNCTION #<SIMPLE-ERROR {AAA8981}> SB-KERNEL:FORMAT-CONTROL) 12: (SB-KERNEL::SIMPLE-CONDITION-PRINTER #<SIMPLE-ERROR {AAA8981}> #<SYNONYM-STREAM :SYMBOL SB-SYS:*STDERR* {90BF8F9}>) 13: (PRINC #<SIMPLE-ERROR {AAA8981}> #<SYNONYM-STREAM :SYMBOL SB-SYS:*STDERR* {90BF8F9}>) 14: ((LAMBDA (MSG)) #<SIMPLE-ERROR {AAA8981}>) 15: ((LAMBDA ())) 16: (SB-INT:SIMPLE-EVAL-IN-LEXENV (HANDLER-BIND ((END-OF-FILE #'HANDLER-EOF) (T #'HANDLER-T)) (DOLIST (ARG (REST *POSIX-ARGV*)) (RESTART-CASE (FORMAT *STANDARD-OUTPUT* "~&~S~%" (EVAL (READ-FROM-STRING ARG))) (CONTINUE-EVAL (MSG) (FORMAT *ERROR-OUTPUT* "~&; ~A~%" MSG))))) #<NULL-LEXENV>) 17: (SB-FASL::LOAD-AS-SOURCE #<SB-SYS:FD-STREAM for "file /home/dtw/tmp/sbcl-eval" {AA047A1}> NIL NIL) 18: ((FLET SB-FASL::LOAD-STREAM) #<SB-SYS:FD-STREAM for "file /home/dtw/tmp/sbcl-eval" {AA047A1}> NIL) 19: (LOAD #<SB-SYS:FD-STREAM for "file /home/dtw/tmp/sbcl-eval" {AA047A1}>)[:EXTERNAL] 20: (SB-IMPL::PROCESS-SCRIPT "./sbcl-eval") 21: (SB-IMPL::TOPLEVEL-INIT) 22: ((LABELS SB-IMPL::RESTART-LISP)) unhandled condition in --disable-debugger mode, quitting --8<---------------cut here---------------end--------------->8--- |
From: Teemu L. <tli...@ik...> - 2010-06-26 19:06:54
|
* 2010-06-26 21:33 (+0300), Teemu Likonen wrote: > The following expression with SIMPLE-ERROR condition doesn't work. My > restart is not active any more and I don't understand why. > > $ sbcl-eval "(error 'simple-error)" > [See the backtrace below] > > There is some "unbound condition slot" error in the backtrace. What is > happening? OK, I figured it out that the condition object has no slot named CONDITION and therefore the object can't be pretty-printed with FORMAT. It's unclear to me why the slot doesn't exist, though. Anyway, I added a SLOT-EXISTS-P test and now SIMPLE-ERROR condition is handled too: --8<---------------cut here---------------start------------->8--- #!/home/dtw/local/bin/sbcl --script (defun handler-eof (condition) (declare (ignore condition)) (invoke-restart 'continue-eval "No value")) (defun handler-t (condition) (invoke-restart 'continue-eval (if (slot-exists-p condition 'condition) condition "Unknown condition"))) (handler-bind ((end-of-file #'handler-eof) (t #'handler-t)) (dolist (arg (rest sb-ext:*posix-argv*)) (restart-case (format *standard-output* "~&~S~%" (eval (read-from-string arg))) (continue-eval (msg) (format *error-output* "~&; ~A~%" msg))))) --8<---------------cut here---------------end--------------->8--- |
From: Tobias C R. <tc...@fr...> - 2010-06-27 03:54:32
|
In article <87h...@mi...da>, Teemu Likonen <tli...@ik...> wrote: > * 2010-06-26 21:33 (+0300), Teemu Likonen wrote: > > > The following expression with SIMPLE-ERROR condition doesn't work. My > > restart is not active any more and I don't understand why. > > > > $ sbcl-eval "(error 'simple-error)" > > [See the backtrace below] > > > > There is some "unbound condition slot" error in the backtrace. What is > > happening? > > OK, I figured it out that the condition object has no slot named > CONDITION and therefore the object can't be pretty-printed with FORMAT. > It's unclear to me why the slot doesn't exist, though. Are you sure the name of the slot is CONDITION? It should complain about a missing slot named FORMAT-CONTROL. > Anyway, I added a SLOT-EXISTS-P test and now SIMPLE-ERROR condition is > handled too: > > --8<---------------cut here---------------start------------->8--- > #!/home/dtw/local/bin/sbcl --script > > (defun handler-eof (condition) > (declare (ignore condition)) > (invoke-restart 'continue-eval "No value")) > > (defun handler-t (condition) > (invoke-restart 'continue-eval (if (slot-exists-p condition 'condition) > condition > "Unknown condition"))) > > (handler-bind ((end-of-file #'handler-eof) > (t #'handler-t)) > (dolist (arg (rest sb-ext:*posix-argv*)) > (restart-case (format *standard-output* "~&~S~%" > (eval (read-from-string arg))) > (continue-eval (msg) > (format *error-output* "~&; ~A~%" msg))))) > --8<---------------cut here---------------end--------------->8--- N.B. This will /seem/ to work even in case I'm right and the slot in question is FORMAT-CONTROL and not CONDITION. -T. |
From: Tobias C R. <tc...@fr...> - 2010-06-27 03:49:24
|
In article <87l...@mi...da>, Teemu Likonen <tli...@ik...> wrote: > There is something in Common Lisp's (or SBCL's) conditions and restarts > which I don't understand. Here's some background and an example code. I > made a simple tool "sbcl-eval" which evaluates its command-line > arguments as Lisp expressions and prints the results: > > $ sbcl-eval "" '(* 123 2)' blah pi '(foo)' > ; No value > 246 > ; The variable BLAH is unbound. > 3.141592653589793d0 > ; undefined function: FOO > > The code is here: > > --8<---------------cut here---------------start------------->8--- > #!/home/dtw/local/bin/sbcl --script > > (defun handler-eof (condition) > (declare (ignore condition)) > (invoke-restart 'continue-eval "No value")) > > (defun handler-t (condition) > (invoke-restart 'continue-eval condition)) > > (handler-bind ((end-of-file #'handler-eof) > (t #'handler-t)) > (dolist (arg (rest sb-ext:*posix-argv*)) > (restart-case (format *standard-output* "~&~S~%" > (eval (read-from-string arg))) > (continue-eval (msg) > (format *error-output* "~&; ~A~%" msg))))) > --8<---------------cut here---------------end--------------->8--- > > The code does what I want but then I tried to test more its error > handling. At least this works: > > $ sbcl-eval "(error 'error)" > ; Condition ERROR was signalled. > > The following expression with SIMPLE-ERROR condition doesn't work. My > restart is not active any more and I don't understand why. > > $ sbcl-eval "(error 'simple-error)" > [See the backtrace below] > > There is some "unbound condition slot" error in the backtrace. What is > happening? If you M-. on simple-error in Slime, you'll be brought into src/code/condition.lisp; unfortunately not to the right toplevel-form, the definition of SIMPLE-ERROR, but the macro definition of DEFINE-CONDITION -- my memory currently fails for the exact reason for that mismatch, but iirc it's due to some ordering issues during compilation of SBCL itself. Anyway, you can just search for "simple-error" in that file, and you'll see: SIMPLE-ERROR is a SIMPLE-CONDITION which consists of two slots, in particular its FORMAT-CONTROL slot has no initform. If you know look into its reporter, SIMPLE-CONDITION-PRINTER, you'll see that printing SIMPLE-ERRORs depends on the presence of a FORMAT-CONTROL value. Indeed, if you try (ERROR 'SIMPLE-ERROR) at the REPL, you'll see SBCL complain for Unable to display error condition: unbound condition slot: SB-KERNEL:FORMAT-CONTROL As SIMPLE-ERROR and SIMPLE-CONDITION are condition specified by the standard (they're external symbols of the CL package), you can actually look up their specifications via C-c C-d h in Slime. Arguably, the definition of SIMPLE-CONDITION should have a :INITFORM (SB-INT:MISSING-ARGUMENT :FORMAT-CONTROL) in the slot definition of FORMAT-CONTROL to bail out early. -T. |
From: Teemu L. <tli...@ik...> - 2010-06-27 07:28:21
|
* 2010-06-27 11:48 (+0800), Tobias C. Rittweiler wrote: > If you M-. on simple-error in Slime, you'll be brought into > src/code/condition.lisp; unfortunately not to the right toplevel-form, > the definition of SIMPLE-ERROR, but the macro definition of > DEFINE-CONDITION -- my memory currently fails for the exact reason for > that mismatch, but iirc it's due to some ordering issues during > compilation of SBCL itself. > > Anyway, you can just search for "simple-error" in that file, and > you'll see: SIMPLE-ERROR is a SIMPLE-CONDITION which consists of two > slots, in particular its FORMAT-CONTROL slot has no initform. If you > know look into its reporter, SIMPLE-CONDITION-PRINTER, you'll see that > printing SIMPLE-ERRORs depends on the presence of a FORMAT-CONTROL > value. Thanks. It seems that I presumed wrong things and thought that PRINT-OBJECT method (called by FORMAT "~A" in my code) should always print something when given a condition. The HyperSpec says that PRINT-OBJECT method is required for class STANDARD-OBJECT but it seems that conditions are not inherited from that. And the second message I wrote was just some more misunderstanding on the top of misunderstanding. :-) One of the goals with my simple "sbcl-eval" tool is to learn to handle errors without the debugger and ugly backtraces. Now I think I understand that the handler wasn't active because the error was signalled in the handler+restart code itself (because of the missing PRINT-OBJECT method called by FORMAT "~A"). I added HANDLER-CASE form in the handler function and now my code catches those SIMPLE-ERRORs and -CONDITIONs too. --8<---------------cut here---------------start------------->8--- #!/home/dtw/local/bin/sbcl --script (defun handler-eof (c) (declare (ignore c)) (invoke-restart 'continue-eval "No value")) (defun handler-condition (c) (invoke-restart 'continue-eval (handler-case (princ-to-string c) (condition () (type-of c))))) (defun interrupt (c) (declare (ignore c)) (terpri *error-output*) (sb-ext:quit :unix-status 1)) (handler-bind ((sb-sys:interactive-interrupt #'interrupt) (end-of-file #'handler-eof) (condition #'handler-condition)) (dolist (arg (rest sb-ext:*posix-argv*)) (restart-case (format *standard-output* "~&~S~%" (eval (read-from-string arg))) (continue-eval (msg) (format *error-output* "~&; ~A~%" msg))))) --8<---------------cut here---------------end--------------->8--- |