Lisp S表达式和跟踪源位置

Lisp S表达式和跟踪源位置,lisp,common-lisp,Lisp,Common Lisp,Lisp s表达式是将代码表示为抽象语法树的一种简洁灵活的方法。然而,相对于其他语言的编译器使用的更专业的数据结构,它们有一个缺点:很难跟踪与代码中任何特定点对应的文件和行号。至少有一些口齿不清的人最终只是在讨论这个问题;如果发生错误,它们只报告函数名的源位置,而不报告文件和行号 Scheme的一些方言解决了这个问题,它们不是用普通的cons单元格来表示代码,而是用语法对象来表示代码,语法对象与cons单元格同构,但也可以携带诸如源位置等附加信息 公共Lisp的任何实现都解决了这个问题吗?如果是

Lisp s表达式是将代码表示为抽象语法树的一种简洁灵活的方法。然而,相对于其他语言的编译器使用的更专业的数据结构,它们有一个缺点:很难跟踪与代码中任何特定点对应的文件和行号。至少有一些口齿不清的人最终只是在讨论这个问题;如果发生错误,它们只报告函数名的源位置,而不报告文件和行号

Scheme的一些方言解决了这个问题,它们不是用普通的cons单元格来表示代码,而是用语法对象来表示代码,语法对象与cons单元格同构,但也可以携带诸如源位置等附加信息


公共Lisp的任何实现都解决了这个问题吗?如果是,怎么做?

我的理解是,AST中存储的任何数据方案都是可以与CL环境中的表达式关联的数据

计划 例如:

(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))))
扩展树表示语法对象

公共口齿不清 测试:

返回两个值:

(0 (1 2 3) 4 5 (6 7))
#<HASH-TABLE :TEST EQL :COUNT 3 {1010524CD3}>
(0(123)45(67))
#
给定一个cons单元格,您可以跟踪其原始位置。如果愿意,可以添加更精确的信息。例如,计算
defun
后,源信息可以附加到函数对象,或者作为符号属性,这意味着在重新定义时对信息进行垃圾收集

评论
请注意,在这两种情况下,都没有要跟踪的源文件,除非系统能够跟踪到调用读卡器的源文件中的原始字符串。

公共Lisp标准对这些内容几乎没有说明。例如,它提到函数
ed
可以使用函数名,然后使用相应的源代码打开编辑器。但是没有指定任何机制,这个特性完全由开发环境提供,可能与Lisp系统结合使用

一种典型的处理方法是编译一个文件,编译器将记录所定义对象(函数、变量、类等)的源位置。例如,源位置可以放在符号的属性列表上(定义的对象的名称),或者记录在其他地方。另外,作为列表结构的实际源代码可以与Lisp符号相关联。请参阅函数
function-LAMBDA-EXPRESSION

一些实现进行更复杂的源位置记录。例如,LispWorks可以定位当前执行的函数的特定部分。它还注意到定义何时来自编辑器或侦听器。看见例如,调试器可以定位某个堆栈帧的代码在源代码中的位置

SBCL还具有一个功能,可用于

还要注意,Common Lisp中的实际“源代码”并不总是文本文件,而是读取的s表达式
eval
compile
-两个标准函数-不接受字符串或文件名作为参数。它们使用实际表达式:

CL-USER 26 > (compile 'foo (lambda (x) (1+ x)))
FOO
NIL
NIL

CL-USER 27 > (foo 41)
42
作为代码的S表达式不绑定到任何特定的文本格式。它们可以通过pretty printer函数
pprint
重新格式化,这可能会考虑可用宽度以生成布局

因此,注意结构可能有用,而记录源行则不太有用

(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)))
(0 (1 2 3) 4 5 (6 7))
#<HASH-TABLE :TEST EQL :COUNT 3 {1010524CD3}>
CL-USER 26 > (compile 'foo (lambda (x) (1+ x)))
FOO
NIL
NIL

CL-USER 27 > (foo 41)
42