我正在努力更好地理解如何在不同的lisps中评估S表达式,并希望看到它们将处理有趣的错误表达式。我认为Common Lisp和Scheme是完全不同的语言,但它们的语义是否存在特定的差异,这解释了行为的差异。例如,Lisp-1s和Lisp-2s的行为存在明显的差异,卫生与非卫生的宏观系统也是如此。
我有一个程序,在Scheme和Common Lisp中包含一个无法访问的格式错误的 if 表达式。
;; foo.scm
(if #t 1 (if))
(display "12")
Common Lisp版本
;; foo.lisp
(if t 1 (if))
(display "12")
chicken
和guile
都会产生语法错误。
鸡:
% chicken foo.scm
Syntax error: (foo.scm:1) in `if' - pair expected
(if)
Expansion history:
<syntax> (##core#begin (if #t 1 (if)))
<syntax> (if #t 1 (if))
<syntax> (##core#if #t 1 (if))
<syntax> (if) <--
狡诈:
% guile foo.scm
...
.../foo.scm:1:9: source expression failed to match any pattern in form (if)
sbcl
和clisp
都打印12并且不发出任何警告。
SBCL:
% sbcl --load foo.lisp
This is SBCL 1.3.11, an implementation of ANSI Common Lisp.
...
12
0]^D
CLISP
% clisp foo.lisp
"12"
答案 0 :(得分:8)
Common Lisp的实现:解释器和编译器
在Common Lisp中,代码执行的类型取决于Lisp系统实现的内容以及如何使用它。 Common Lisp实现通常有多种方法来执行代码: interpreter 和一个或多个编译器。即使是单个运行的实现也可能具有该功能,并且允许用户在这些实现之间切换。
解释器:直接从Lisp数据结构执行。它不是像JVM一样的虚拟机代码的解释器。人们不希望解释器在执行期间验证完整的代码树/图。解释器通常只查看它执行的当前顶层表单。
编译器:将Lisp代码编译为C,一些字节代码或机器代码。由于编译器在代码运行之前生成代码,因此它会对所看到的所有代码(可能的异常,请参见底部的注释)进行语法检查。
Toplevel评估:可能使用解释器,编译器或两者兼而有之。
GNU CLISP既有解释器又有编译器
GNU CLISP中的示例:
文本文件的加载通常使用解释器:
[1]> (load "test.lisp")
;; Loading file test.lisp ...
;; Loaded file test.lisp
T
解释器不会检测到错误,因为它不检查整个表达式的语法正确性。由于从未使用过具有语法错误的else子句,因此解释器永远不会查看它。
CLISP也有一个编译器:
[2]> (compile-file "test.lisp")
;; Compiling file /tmp/test.lisp ...
** - Continuable Error
in #:|1 1 (IF T 1 ...)-1| in line 1 : Form too short, too few arguments: (IF)
If you continue (by typing 'continue'): Ignore the error and proceed
The following restarts are also available:
ABORT :R1 Abort main loop
如您所见,CLISP编译器检测到语法错误并提供明确的错误消息。
SBCL使用编译器,但也有解释器
默认情况下,SBCL使用编译器,它将检测错误。对于顶级表单,它在某些表单中使用更简单的评估机制。也可以切换到翻译。
如果您在SBCL中编写一个简单的IF表单,评估程序不会使用完整编译,也不会捕获错误:
CL-USER> (if t 1 (if))
1
如果在函数定义中编写相同的代码,则会检测到错误,因为默认情况下将编译函数定义:
CL-USER> (defun foo () (if t 1 (if)))
; in: DEFUN FOO
; (IF)
;
; caught ERROR:
; error while parsing arguments to special operator IF:
; too few elements in
; ()
; to satisfy lambda list
; (SB-C::TEST SB-C::THEN &OPTIONAL SB-C::ELSE):
; between 2 and 3 expected, but got 0
;
; compilation unit finished
; caught 1 ERROR condition
WARNING: redefining COMMON-LISP-USER::FOO in DEFUN
FOO
如果要切换到完整的SBCL解释器,则在定义时不会检测到错误:
CL-USER> (setf *evaluator-mode* :interpret)
:INTERPRET
CL-USER> (defun foo () (if t 1 (if)))
WARNING: redefining COMMON-LISP-USER::FOO in DEFUN
FOO
优化和语法检查
请注意,某些优化编译器可能无法检查无法访问代码的语法。
<强>摘要强>
在大多数Common Lisp实现中,您需要使用编译器来获取完整的语法警告/错误。
答案 1 :(得分:3)
看起来CL部分由Rainer很好地处理。我只想指出R5RS不需要像你的两个实现那样行事。
R5RS指标非常不明确,if
只指定使用时我们有this passage about error handling正在做什么:
在谈到错误情况时,此报告使用短语“an 错误信号“表示实现必须检测和 报告错误。 如果这样的措辞没有出现在讨论中 如果出现错误,则不需要实现检测或报告 错误,但鼓励他们这样做。错误情况 通常会提到不需要检测的实现 简单地称为“错误。”
因此,要将其包含在R5RS中,字符串"banana"
与Guile和Chickens对(if #t 1 (if))
评估的响应一样正确。
if
应该引发异常,但它并不表示它必须发生编译时间,因为语言可能正在解析和解释。