Lisp s表达式是一种将代码表示为抽象语法树的简洁灵活的方法。但是,相对于其他语言的编译器使用的更专业的数据结构,它们具有一个缺点:难以跟踪与代码中任何特定点相对应的文件和行号。至少有一些Lisps最终只是解决问题。发生错误时,它们仅报告源位置的函数名称,而不报告文件和行号。
Scheme的某些方言通过不使用普通的con单元,而是使用语法对象来表示代码,该对象与con单元是同构的,但是还可以携带其他信息,例如源位置。 / p>
Common Lisp的任何实现都解决了此问题吗?如果可以,怎么办?
答案 0 :(得分:3)
Common Lisp标准几乎没有说明这些事情。例如,它提到函数ed
可以采用函数名,然后使用相应的源代码打开编辑器。但是没有指定机制,此功能完全由开发环境提供,可能与Lisp系统结合使用。
一种典型的处理方法是编译文件,编译器将记录所定义对象(函数,变量,类等)的源位置。例如,源位置可以放在符号的属性列表(定义的事物的名称)上,或记录在其他位置。同样,作为列表结构的实际源代码也可以与Lisp符号关联。请参见函数FUNCTION-LAMBDA-EXPRESSION
。
某些实现对源位置进行更复杂的记录。例如,LispWorks可以定位当前执行的功能的特定部分。它还指出定义何时来自编辑器或侦听器。参见Dspecs: Tools for Handling Definitions。然后,调试器可以例如定位某个堆栈帧的代码在源中的位置。
SBCL还具有locate source code的功能。
还请注意,Common Lisp中的实际“源代码”并不总是文本文件,而是读取的s表达式。 eval
和compile
-两个标准函数-不要将字符串或文件名作为参数。他们使用实际的表达式:
CL-USER 26 > (compile 'foo (lambda (x) (1+ x)))
FOO
NIL
NIL
CL-USER 27 > (foo 41)
42
作为代码的S表达式未绑定到任何特定的文本格式。可以通过漂亮的打印机功能pprint
重新设置格式,并且可能会考虑可用宽度以生成布局。
因此,注意结构可能有用,而记录源代码行则没那么有用。
答案 1 :(得分:2)
我的理解是,Scheme存储在AST中的任何数据都是可以与CL环境中的表达式相关联的数据。
False
例如:
(defun my-simple-scheme-reader (stream)
(let ((char (read-char stream)))
(or (position char "0123456789")
(and (member char '(#\newline #\space #\tab)) :space)
(case char
(#\) :closing-paren)
(#\( (loop
with beg = (file-position stream)
for x = (my-simple-scheme-reader stream)
until (eq x :closing-paren)
unless (eq x :space)
collect x into items
finally (return (list :beg beg
:end (file-position stream)
:items items))))))))
返回:
(with-input-from-string (in "(0(1 2 3) 4 5 (6 7))")
(my-simple-scheme-reader in))
丰富的树表示语法对象。
(:BEG 1 :END 20 :ITEMS
(0 (:BEG 3 :END 9 :ITEMS (1 2 3)) 4 5 (:BEG 15 :END 19 :ITEMS (6 7))))
测试:
(defun make-environment ()
(make-hash-table :test #'eq))
(defun my-simple-lisp-reader (stream environment)
(let ((char (read-char stream)))
(or (position char "0123456789")
(and (member char '(#\newline #\space #\tab)) :space)
(case char
(#\) :closing-paren)
(#\( (loop
with beg = (file-position stream)
for x = (my-simple-lisp-reader stream environment)
until (eq x :closing-paren)
unless (eq x :space)
collect x into items
finally
(setf (gethash items environment)
(list :beg beg :end (file-position stream)))
(return items)))))))
返回两个值:
(let ((env (make-environment)))
(with-input-from-string (in "(0(1 2 3) 4 5 (6 7))")
(values
(my-simple-lisp-reader in env)
env)))
给出一个cons单元格,您可以追溯其原始位置。您可以根据需要添加更精确的信息。例如,一旦评估了(0 (1 2 3) 4 5 (6 7))
#<HASH-TABLE :TEST EQL :COUNT 3 {1010524CD3}>
,就可以将源信息附加到函数对象或作为符号属性,这意味着该信息在重新定义时被垃圾回收。
请注意,在两种情况下都没有可跟踪的源文件,除非系统能够追溯到调用读取器的源文件中的原始字符串。