編譯是Common Lisp中的定義:CLHS Section 3.2.3 File Compilation
雖然編譯:使用讀取宏使用的一種形式,你不得不做出這樣的閱讀提供給編譯器宏實現。
通常,這樣的依賴關係是通過defsystem
工具來處理的,其中描述了系統各種文件(如項目)之間的依賴關係。爲了編譯某個文件,必須將另一個文件(最好是編譯後的版本)加載到編譯Lisp中。
現在,如果您想要定義讀取宏並在同一個文件中使用其表示法,那麼您需要確保編譯器知道讀取的宏及其實現。文件編譯器具有編譯環境。它不會將默認編譯的相同文件的函數加載到此環境中。
爲了讓編譯器知道文件中的某些代碼,它編譯Common Lisp提供了EVAL-WHEN
。
讓我們看一個讀宏示例:
(set-syntax-from-char #\] #\))
(defun reader-example (stream char)
(declare (ignore char))
(let ((class (read stream t nil t))
(args (read-delimited-list #\] stream t)))
(apply #'make-instance
class
args)))
(set-macro-character #\[ 'reader-example)
(defclass example()
((name :initarg :name)))
(defvar *examples*
(list [example :name e1]
[example :name e2]
[example :name e3]))
如果加載上面的源代碼,一切都很好。但是如果我們使用文件編譯器,它不會在沒有首先加載的情況下編譯。例如文件編譯器通過調用具有路徑名的函數COMPILE-FILE
來調用。
現在編譯文件:
(set-syntax-from-char #\] #\))
上面會不會在編譯時被執行。新的語法更改在編譯時不可用。
(defun reader-example (stream char)
(declare (ignore char))
(let ((class (read stream t nil t))
(args (read-delimited-list #\] stream t)))
(apply #'make-instance
class
args)))
上述函數被編譯但未加載。它的實現在後面的步驟中不可用於編譯器。
(set-macro-character #\[ 'reader-example)
再一次,上述表單不會被執行 - 只是它的代碼被生成。
(defclass example()
((name :initarg :name)))
編譯器記錄該類,但稍後不能創建它的實例。
(defvar *examples*
(list [example :name e1]
[example :name e2]
[example :name e3]))
上面的代碼觸發錯誤,因爲讀取宏在編譯時不可用 - 除非它之前已經加載。
現在有兩個簡單的解決方案:
實施例:
(EVAL-WHEN (:compile-toplevel :load-toplevel :execute)
(do-something-also-at-compile-time))
上面會被編譯器和可見也執行即可。現在,您必須確保代碼在編譯時具有所調用的所有內容(所有必需的定義)。不用說:儘可能減少這種編譯依賴性是一種很好的風格。通常將所需的功能放在單獨的文件中,並確保在編譯使用該文件的文件之前,將此文件編譯並加載到編譯Lisp中。
實際上,SBCL具有解釋模式,它只是默認關閉:http://www.sbcl.org/manual/Interpreter.html – 2012-07-19 05:48:26
Common Lisp編譯是增量式的,但文件編譯的定義略有不同。 – 2012-07-19 05:55:39
@Rainer能否詳細說明一下?我不熟悉它 – 2012-07-19 06:02:45