如何避免此编译错误?

时间:2018-04-19 12:17:53

标签: common-lisp eval sbcl

我有生成代码的代码(如say-hello),我发现使用生成的代码生成函数的最简单方法是将defun放在eval中,如下所示:

(defun say-hello ()
  `(princ "test"))

(eval
 `(defun test ()
    ,(say-hello)))

(test)

此文件在命令行上运行正常:

sbcl --script test.lisp

但是当我尝试在SBCL中编译它时抱怨:

* (compile-file "test.lisp")

; compiling file "test.lisp" (written 19 APR 2018 01:05:19 PM):
; compiling (DEFUN SAY-HELLO ...)
; compiling (EVAL (SB-INT:QUASIQUOTE #))
; compiling (TEST)
; file: test.lisp
; in: TEST
;     (TEST)
;
; caught STYLE-WARNING:
;   undefined function: TEST
;
; compilation unit finished
;   Undefined function:
;     TEST
;   caught 1 STYLE-WARNING condition

; test.fasl written
; compilation finished in 0:00:00.031
#P"test.fasl"
T
NIL
*

有没有办法可以编写我的代码来避免编译错误?是否有更好的习惯用于从生成代码的函数创建代码?

2 个答案:

答案 0 :(得分:4)

函数与宏 - 文件编译器

文件编译器对函数和宏定义略有不同:

  • 函数:文件编译器编译它们并可以使用它们的相关信息。它将生成的代码转储到已编译的fasl文件中。但它不会在编译时环境中创建可执行文件定义。

  • 宏:文件编译器编译它们并使它们在编译时环境中可用。因此,编译器可以使用相同文件定义宏中的较早版本来扩展该宏在同一文件中的使用。

编译文件时

表单的情况

  • :compile-toplevel - >文件编译在编译文件并且表单位于 toplevel

  • 时会使其失效
  • :load-toplevel - >文件编译器生成代码,以便在加载期间执行 toplevel 表单

您的代码

如果要在定义它的同一文件中使用函数,则需要告诉编译器在编译时实际执行定义:

(eval-when (:compile-toplevel :load-toplevel :execute)
  (defun fn-say-hello ()
    `(princ "test")))

稍后在文件中,宏可以使用该函数。编译器自动了解宏定义,但不了解函数。

(defmacro say-hello ()
  (fn-say-hello))  ; this is a function call, not returned code

然后您可以在文件中使用此宏。然后编译器将扩​​展该宏并运行函数fn-say-hello - 然后它起作用,因为我们之前告诉编译器有关该函数的信息。

(defun test ()
  (say-hello))

(test)

答案 1 :(得分:0)

在玩EVAL-WHEN之后,我也意识到我可以使用#.阅读器宏,而不是定义一次性宏:

;; EVAL-WHEN makes say-hello available at compile time
(eval-when (:compile-toplevel :load-toplevel :execute) 
  ;; Define the function we want to use
  (defun say-hello ()
    `(princ "test")))

(defun test ()
  #.(say-hello)) ; Evals say-hello in-place at compile time

(test)