Common lisp 保留注释的公共Lisp解析器

Common lisp 保留注释的公共Lisp解析器,common-lisp,Common Lisp,Common Lisp附带了一个解析器(读取器),可以将其文本语法转换为s表达式。但是,它会丢弃注释,这使得它不适合用于往返Lisp代码的工具 现有的公共Lisp解析器是否保留注释?如注释中所述,您应该能够修改可读表以绑定宏字符到另一个读卡器宏函数。例如,如果您定义: (defun semicolon-reader (stream char) (list 'my-comment (concatenate 'string (string char)

Common Lisp附带了一个解析器(读取器),可以将其文本语法转换为s表达式。但是,它会丢弃注释,这使得它不适合用于往返Lisp代码的工具


现有的公共Lisp解析器是否保留注释?

如注释中所述,您应该能够修改可读表以绑定宏字符
到另一个读卡器宏函数。例如,如果您定义:

(defun semicolon-reader (stream char)
  (list 'my-comment
        (concatenate 'string (string char)
                     (read-line stream nil #\Newline t))))
然后在顶层运行:

> (set-macro-character #\; #'semicolon-reader)
> (read)
用户输入:

(a b ; is b
c ; is c
)
将产生:

(A B (MY-COMMENT "; is b") C (MY-COMMENT "; is c"))

然而,真正的往返处理也需要保留空白。我对读者了解不多,不知道你是否可以为空白字符定义一些巧妙的宏函数,或者,如果您必须编写某种预处理函数,用另一个宏字符对空白行进行预引用,然后像上面的
分号读取器那样对其进行处理。

因为您正在制作一个代码格式化程序,所以您可能不需要只支持标准的公共Lisp语法(
使用标准io语法
)并修改标准可读表以保留注释和空白(
设置宏字符
)。可读表是CL阅读器的主要数据结构,告诉它在遇到源代码中的特定字符时调用哪个函数来读取不同类型的对象(例如,当列表遇到左括号时,如何读取列表)

您必须使用gensym或structs/Class来表示注释和空白,因为Lisp读取器可以使用标准IO语法从源文件读入其他类型的对象(例如列表和非gensym符号)

下面是一个概念的快速证明。读卡器工作正常,但我无法让打印机工作(即重新打印我们从读卡器获得的内容,以生成一个与输入足够接近的源文件)-它在空白周围打印额外的空白,可能是因为它认为我们的空白对象类似于普通的Lisp对象(符号、列表等),并且当您在一行中打印多个时,应使用空格分隔(例如,如果您打印
1
2
3
,则应打印
1 2 3
而不是
123
)。深入普通Lisp打印机的内部,找出如何覆盖此行为,留给读者作为练习:p

此外,仔细阅读常见的Lisp HyperSpec.会列出所有以
开头的语法。尤其要注意
+
-
.

如果您在实际代码中能很好地工作,请考虑将其发布为开源包。

(defstruct注释样式字符串)
(defstruct空白字符串)
(defcontent+空格字符+'(#\Space#\Tab#\Return#\Newline))
(反常量+eof+(gensym“eof”))
(定义读取分号注释(流分号)
(声明(忽略分号))
(进行注释:样式:分号:字符串)
(输出为字符串(注释)
(循环(let((字符(读取字符流nil+eof+t)))
(cond((相等字符+eof+)(返回))
((相等字符#\Newline)
(未读字符流)
(返回)
(t(写字符注释(()()())()))(写字符注释)
(定义读取空白(流第一个字符)
(使用空格:string)
(输出为字符串(空白)
(写入字符第一个字符空白)
(循环(let((字符(读取字符流nil+eof+t)))
(除非(成员字符+空白字符+)
(除非(相等字符+eof+(未读字符流))
(返回)
(写入字符空白(()())))
(卸载读取流(流)
(使用标准io语法;例如,这里有一条注释。
(let((*readtable*(copy readtable)))
(设置宏字符“read-分号-comment”)
(数字列表(字符+空格字符+)
(设置宏字符char#'读取空格))
(x的循环=(读取流nil+eof+)直到(等于x+eof+)
收集(x)))
(defmethod打印对象((x注释)流)
(断言(等于:分号(注释样式x)))
(写入字符流)
(写入字符串(注释字符串x)流)
十)
(defmethod打印对象((x空格)流)
(写入字符串(空白字符串x)流)
十)
(mapc#prin1(读取流*标准输入*)

阅读器是可定制的。您可以在
|
上使用自己的阅读器宏创建一个可读表。您要解决的最终问题是什么?正确地说,像CL这样的完整语言的语法非常复杂。您几乎肯定会有模糊的错误。我只会通过调用成熟的ell测试的开放源码库专门用于此项工作。如果您只需要CL语法的一小部分,可以尝试从头开始编写您自己的解析器。还要注意的是,CL阅读器依赖于许多不容易保存或复制的状态。例如,数基(10或16)、符号大小写(默认情况下,符号转换为大写,因此您需要保留原始符号的大小写,但请记住,用户没有使用“符号周围的管道”)、插入符号的默认包等。在一般情况下,任何CL代码本身都可以更改可读表:-)@Lassi我试图解决的最终问题是编写一个代码格式化程序。我知道没有办法保留所有状态;至少会有一些功能,如大小写折叠。保存文件偏移量可以通过一个自定义流类来完成,该类记录要读取的元素的位置。谢谢!是的,目前正在进行的工作是