If we are using a Lisp interpreter, which executes s-expressions, we can do similar things, here an example using LispWorks, which has both a compiler and a source-level Lisp interpreter.
The function FOO has a BREAK.
CL-USER 76 > (defun foo (a)
(+ (break) (sin a)))
FOO
CL-USER 77 > (foo 10)
Break.
1 (continue) Return from break.
2 (abort) Return to top loop level 0.
Type :b for backtrace or :c <option number> to proceed.
Type :bug-form "<subject>" for a bug report template or :? for other options.
We are in the break lets get a quick backtrace and move to the right stack frame:
CL-USER 78 : 1 > :bq
INVOKE-DEBUGGER <- BREAK <- FOO <- EVAL <- CAPI::CAPI-TOP-LEVEL-FUNCTION <- CAPI::INTERACTIVE-PANE-TOP-LOOP <- MP::PROCESS-SG-FUNCTION
CL-USER 79 : 1 > :n
Call to INVOKE-DEBUGGER
CL-USER 80 : 1 > :n
Call to BREAK
CL-USER 81 : 1 > :n
Interpreted call to FOO
What is the current source?
CL-USER 82 : 1 > :lambda
(LAMBDA (A) (DECLARE (SYSTEM::SOURCE-LEVEL #<EQ Hash Table{0} 8220104A8B>)) (DECLARE (LAMBDA-NAME FOO)) (+ (BREAK) (SIN A)))
Let's change the code:
CL-USER 83 : 1 > (setf (third (fifth *)) '(* a 4))
(* A 4)
Let's see the new code.
CL-USER 84 : 1 > :lambda
(LAMBDA (A) (DECLARE (SYSTEM::SOURCE-LEVEL #<EQ Hash Table{0} 8220104A8B>)) (DECLARE (LAMBDA-NAME FOO)) (+ (BREAK) (* A 4)))
Move to the BREAK stack frame and return 2 from there:
CL-USER 85 : 1 > :p
Call to BREAK
CL-USER 86 : 1 > :ret 2
42
As you can see, we have changed the running source code, without the need to restart anything.