The tricky part is that Common Lisp (and some other Lisps) gets rid of the source code. Especially when it comes to the compiler. By default, the source code is gone, and everything remains, then this is machine code. How to restore a Lisp source and in what form?
The reason for this: why should CL be required to store the source? It can be fully compiled for machine code or C code and does not have a / EVAL compiler at runtime. A program can work without much of a development environment (without a compiler, etc.). The Common Lisp environment also should not be required in order to be able to "compile" the code with some restored source code.
And itโs generally difficult.
(let ((n 0) (step 2)) (defun foo () (incf n step)))
What is the source above and how can you change STEP? The function depends on the lexical binding.
Another complication:
(defun foo (n) (+ n #.(random 1.0)))
How to recover it? Each time Lisp reads the source, a random number will be read.
Another complication:
(setf (symbol-function 'foo) (compute-function))
You can set the value of a function using some arbitrary function or a predefined function (for example, SIN). How to restore them if they are compiled for machine code, loaded as machine code, etc.?
If the Common Lisp implementation stores the source code, FUNCTION-LAMBDA-EXPRESSION retrieves it.
There are two ways:
a) Tell Lisp the source code or remember the source.
Indicate the source.
(let* ((fs (copy-list '(lambda (n) (+ n 3)))) (fc (compile nil fs))) (print (funcall fc 6)) (setf (third (third fs)) 5) (setf fc (compile nil fs)) (funcall fc 6))
Extended example:
Write a DEFINE macro that both remembers the source and defines the function.
(defmacro define (&rest source) `(progn (setf (get ',(first source) :source) (list* 'defun ',source)) (defun ,@source)))
Displays the source code in the list of character properties in: SOURCE.
Now we can write a function that modifies the source and compiles it:
(defun modify (fname modifier) (let ((source (get fname :source))) (when source (setf (get fname :source) (funcall modifier source)) (eval (get fname :source)) (compile fname))))
Definition example:
(define addx (n) (+ n 3))
Rewrite example:
(modify 'addx (lambda (source) (setf (third (fourth source)) 6) source))
b) Some Common Lisp implementations implement the FUNCTION-LAMBDA-EXPRESSION function (defined in ANSI Common Lisp).
This function returns three values: source as Lisp data, closure-p, and name. This will allow you to change the source, compile it, and set the name to a new function using COMPILE. Sample code is left as an exercise.
Problem: In Common Lisp, the DEFUN macro defines functions. What the macro does behind the scenes (accounting for the IDE, code rewriting, ...) depends on the implementation. Thus, the code returned by the FUNCTION-LAMBDA-EXPRESSION function (if the implementation returns the source code) may look different for each implementation.
This is an example of LispWorks:
CL-USER 12 > (function-lambda-expression #'addx) (LAMBDA (N) (DECLARE (SYSTEM::SOURCE-LEVEL #<EQ Hash Table{0} 217874D3>)) (DECLARE (LAMBDA-NAME ADDX)) (+ N 3)) NIL ADDX
So you can manipulate the original expression and modify it.