Search code examples
common-lispread-eval-print-loopsbclslime

How to step in SBCL like this?


I am new to Common Lisp and I am using SBCL, Slime and Emacs to learn.

While reading the book Common Lisp: A Gentle Introduction to Symbolic Computation, the author mentions the STEP tool which is helpful for debugging and able to do this:

enter image description here

It is not 100% clear if the italic text is coming from the author or the tool itself. Probably, just the author's comments.

However, even if I do not consider the italic, I am unable to generate descriptive info like this.

If I use just SBCL's REPL, I get:

* (step (if (oddp 5) 'yes 'no))            
YES

If I use the REPL inside Emacs with Slime on, I get:

CL-USER> (step (if (oddp 5) 'yes 'no))
YES

The author says that:

Each implementation of Common Lisp provides its own version of this tool; only the name has been standardized.

If I try the same thing in Emacs/Slime with a function, I get more info:

(defun my-abs (x)
  (cond ((> x 0) x)
    ((< x 0) (- x))
    (t 0)))

Using the definition above and the command bellow on the REPL:

CL-USER> (step (my-abs 10))

I get:

Evaluating call:
  (MY-ABS 10)
With arguments:
  10
   [Condition of type STEP-FORM-CONDITION]

Restarts:
 0: [STEP-CONTINUE] Resume normal execution
 1: [STEP-OUT] Resume stepping after returning from this function
 2: [STEP-NEXT] Step over call
 3: [STEP-INTO] Step into call
 4: [RETRY] Retry SLIME REPL evaluation request.
 5: [*ABORT] Return to SLIME's top level.
 --more--

Backtrace:
  0: ((LAMBDA ()))
  1: (SB-INT:SIMPLE-EVAL-IN-LEXENV (LET ((SB-IMPL::*STEP-OUT* :MAYBE)) (UNWIND-PROTECT (SB-IMPL::WITH-STEPPING-ENABLED #))) #S(SB-KERNEL:LEXENV :FUNS NIL :VARS NIL :BLOCKS NIL :TAGS NIL :TYPE-RESTRICTIONS ..
  2: (SB-INT:SIMPLE-EVAL-IN-LEXENV (STEP (MY-ABS 10)) #<NULL-LEXENV>)
  3: (EVAL (STEP (MY-ABS 10)))
 --more--

Unfortunately, none of those options seem to give me what I want (which could be an error of interpretation on my side).

I would like to see something like:

enter image description here

SLIME seems to be a thorough tool. I might be missing something.

Is there a way to generate the same output described by the book using SLIME or SBCL?


Solution

  • It looks like the author is using LispWorks' stepper.

    With LispWorks

    Here's my step session, using :s to step the current form and all its subforms.

    CL-USER 5 > (step (my-abs -5))
    (MY-ABS -5) -> :s
       -5 -> :s
       -5 
       (COND ((> X 0) X) ((< X 0) (- X)) (T 0)) <=> (IF (> X 0) (PROGN X) (IF (< X 0) (- X) (PROGN 0)))
       (IF (> X 0) (PROGN X) (IF (< X 0) (- X) (PROGN 0))) -> :s
          (> X 0) -> :s
             X -> :s
             -5 
             0 -> :s
             0 
          NIL 
          (IF (< X 0) (- X) (PROGN 0)) -> :s
             (< X 0) -> :s
                X -> :s
                -5 
                0 -> :s
                0 
             T 
             (- X) -> :s
                X -> :s
                -5 
             5 
          5 
       5 
    5 
    5
    

    Help is on :?:

     :?
    
    :s       Step this form and all of its subforms (optional +ve integer arg)
    :st      Step this form without stepping its subforms
    :si      Step this form without stepping its arguments if it is a function call
    :su      Step up out of this form without stepping its subforms
    :sr      Return a value to use for this form
    :sq      Quit from the current stepper level
    :bug-form <subject> &key <filename>
             Print out a bug report form, optionally to a file.
    :get <variable> <command identifier>
             Get a previous command (found by its number or a symbol/subform within it) and put it in a variable.
    :help    Produce this list.
    :his &optional <n1> <n2>
             List the command history, optionally the last n1 or range n1 to n2.
    :redo &optional <command identifier> 
             Redo a previous command, found by its number or a symbol/subform within it.
    :use <new> <old> &optional <command identifier> 
             Do variant of a previous command, replacing old symbol/subform with new symbol/subform.
    

    For compiled code it also has a visual stepper, where you can press a red button to set a breakpoints, see the intermediate variables changing etc. It looks like this:

    LispWorks is a proprietary implementation and IDE that has a free but limited version. I just wrote a review that should be merged on the Cookbook.

    trace and printv

    Do you know trace? printv, an external library, is a trace on steroids. They resemble the output you appreciate.

    (defun factorial (n)
      (if (plusp n)
        (* n (factorial (1- n)))
        1))
    
    (trace factorial)
    
    (factorial 2)
      0: (FACTORIAL 3)
        1: (FACTORIAL 2)
          2: (FACTORIAL 1)
            3: (FACTORIAL 0)
            3: FACTORIAL returned 1
          2: FACTORIAL returned 1
        1: FACTORIAL returned 2
      0: FACTORIAL returned 6
    6
    
    (untrace factorial)
    

    printv prints the code and the returned values.

    (printv:printv
               (+ 2 3)              
               *print-case*
               *package*
               'symbol
               (let* ((x 0) (y (1+ x)) (z (1+ y)))
                 (values x y z)))
    ;;;   (+ 2 3) => 5
    ;;;   *PRINT-CASE* => :UPCASE
    ;;;   *PACKAGE* => #<PACKAGE "ISSR-TEST">
    ;;;   'SYMBOL => SYMBOL
    ;;;   (LET* ((X 0) (Y (1+ X)) (Z (1+ Y)))
            (VALUES X Y Z)) =>
               [ [X=0]  [Y=1]  [Z=2] ]
    ;;;   => 0, 1, 2