Perhaps the simplest way is to just evaluate expressions, e.g., if you were experimenting with the small machine and issued the following commands:
(include-book "small-machine") (in-package "SMALL-MACHINE") (defun demo-state nil (st :pc '(time . 0) :stk nil :mem '(7 11 3 4 5) :halt nil :code (list (times-program)))) (sm (demo-state) 1)It seems like sm did nothing. Why didn't it increment the program counter? Here is what I mean by evaluating expressions. We just take the definition of sm:
(defun sm (s n) (if (zp n) s (sm (step s) (+ n -1))))and replace the first line with a let* to get:
(let* ((s (demo-state)) (n 1)) (if (zp n) s (sm (step s) (+ n -1))))This is equivalent to the original call, but now by cutting and pasting we can easily check subexpressions, e.g., we can check (zp n)
(let* ((s (demo-state)) (n 1)) (zp n))(after typing (set-ignore-ok t) to bypass the ACL2 warning generated) we get nil for the above, which is what we expect, so now we decide to check (step s)
(let* ((s (demo-state)) (n 1)) (step s))which is just the original state, so the problem is in step. We open the definition of step.
(let* ((s (demo-state)) (n 1)) (if (halt s) s (execute (current-instruction s) s)))We now check halt
(let* ((s (demo-state)) (n 1)) (halt s))This returns nil, as expected so we check
(let* ((s (demo-state)) (n 1)) (current-instruction s))which is nil. That is a surprise, since this should return the first instruction in times, so we open up current-instruction:
(let* ((s (demo-state)) (n 1)) (fetch (pc s) (code s)))We check pc first.
(let* ((s (demo-state)) (n 1)) (pc s))Seems ok. So let's check code.
(let* ((s (demo-state)) (n 1)) (code s))Seems ok, so let's open up code's definition. Here is why we were using let*, so that we can use earlier bindings.
(let* ((s (demo-state)) (n 1) (pc (pc s)) (code (code s))) (nth (cdr pc) (cdr (assoc (car pc) code))))Let's look at the first argument to nth.
(let* ((s (demo-state)) (n 1) (pc (pc s)) (code (code s))) (cdr pc))Looks good. What about the 2nd argument? It should be the code for times.
(let* ((s (demo-state)) (n 1) (pc (pc s)) (code (code s))) (cdr (assoc (car pc) code)))We get nil, so this is the problem. Let's look at the first argument to assoc:
(let* ((s (demo-state)) (n 1) (pc (pc s)) (code (code s))) (car pc))Aha! We wrote "time" instead of "times".
If you want more powerful mechanisms, then you have to use the debugging facilities provided by the underlying lisp.
For example, if you are using GCL on Linux, then in then in the directory ~manolios/public/gcl-2.4.1/info you will find info files for GCL. The node user-interfaces describes (briefly) how to use step, trace, etc. to debug.
First, note that you do not want to debug compiled functions, so you would not use include-book, but would rather submit each of the function definitions to ACL2. Then when you are ready, get into the underlying lisp by typing :q and then you can call GCL's step function (note that you have to give the full names of the SMALL-MACHINE functions)
(step (small-machine::sm (small-machine::demo-state) 1))Alternatively, you can enter the SMALL-MACHINE package:
(in-package "SMALL-MACHINE")and then issue the command
(lisp::step (sm (demo-state) 1))Note, above you have to tell GCL where the step function is (otherwise, you would call small-machine::step!). A this point, follow the online instructions and experiment.