Common Lisp hackery: writing fasls from non-file input
Common Lisp provides compile-file whose purposes is to convert a CL source file into an (implementation-defined) format which usually has precompiled code and is designed for faster loading into a lisp system.
compile-file takes the pathname of a textual lisp source file. But what if you want to compile some Lisp code that's not in a file already, perhaps because you translated/compiled it from some not-amenable-to-the-Lisp-reader input syntax, or because it contains unREADable literals? You can use a "universal Lisp file", which I know two ways to create (use whichever you find cleaner):
or(cl:in-package :mypackage) #.mypackage::*program*
(cl:in-package :mypackage) (macrolet ((it () *program*)) (it))
Suppose this is in "universal.lisp". Then to use it:
(defvar *program*) (defun compile-to-file (form output-file) (let ((*program* form)) (compile-file #p"universal.lisp" :output-file output-file)))
This is just a minimal example; you'll also want to appropriately handle the return value from compile-file, provide an appropriate pathname to the universal file, etc. For example, here's an excerpt of the relevant code from E-on-CL, where I have used this technique to compile non-CL sources (emakers) into fasls:
(defparameter +the-asdf-system+ (asdf:find-system :e-on-cl)) (defvar *efasl-program*) (defvar *efasl-result*) ... (defun compile-e-to-file (expr output-file fqn-prefix opt-scope) ... (let* (... (*efasl-program* `(setf *efasl-result* (lambda (...) ...)))) (multiple-value-bind (truename warnings-p failure-p) (compile-file (merge-pathnames #p"lisp/universal.lisp" (asdf:component-pathname +the-asdf-system+)) :output-file output-file :verbose nil :print nil) (declare (ignore truename warnings-p)) (assert (not failure-p) () "Compilation for ~A failed." output-file)))) (defun load-compiled-e (file env) (let ((*efasl-result* nil) ...) (load file :verbose nil :print nil) (funcall *efasl-result* env)))
Note that the pathname is computed relative to the ASDF system containing the universal file; also note the use of a variable *efasl-result* to simulate a "return value" from the compiled file, and the use of a lambda to provide a nonempty lexical environment, both of which are features not directly provided by the CL compiled file facility.