【问题标题】:reading/parsing common lisp files from lisp without all packages available or loading everything从 lisp 读取/解析常见的 lisp 文件,而没有所有可用的包或加载所有内容
【发布时间】:2019-10-05 18:08:32
【问题描述】:

我正在做一个涉及解析常见 lisp 存储库历史的项目。我需要将它们解析为列表列表或类似的东西。理想情况下,我想以某种方式尽可能多地保留原始源文件语法。例如,对于文本 #+sbcl <something>,我认为它的意思是“如果我们当前的 lisp 是 sbcl,请阅读 <something>,否则跳过它”,我想得到类似 (#+ 'sbcl <something>) 的内容。

我最初用 Python 编写了一个 LALR 解析器,它有点工作,但由于许多原因它并不理想。我很难获得正确的输出,而且我还有很多特殊情况要添加。

我认为我真正应该做的是使用 lisp 本身,因为它已经内置了一个 lisp 解析器。如果我可以将一个文件读入 sexps,我可以将它转储到一些东西中(cl-json 可以)以便进一步处理。

不幸的是,当我尝试阅读 https://github.com/fukamachi/woo/blob/master/src/woo.lisp 时,我得到了错误

There is no package with the name WOO.EV.TCP

这当然来自该文件的第 80 行,因为该包是在 src/ev/tcp.lisp 中定义的,我们还没有阅读它。

基本上,是否可以只将文件读入 sexps 而无需关心包是否已定义或是否包含相关符号?如果是这样,怎么做?我尝试查看 hyperspec 阅读器文档,但没有看到任何听起来相关的内容。

我没有实际编写通用 lisp,但似乎有可能可能通过创建一个具有该名称的空白包来处理未定义的包条件,并处理 no -symbol-of-that-name-in-package 条件仅通过实习给定符号。我认为。我不知道如何真正做到这一点,我不知道它是否会起作用,我不知道会涉及多少特殊情况。顺便说一句,第一个条件称为no-such-package,但第二个(至少在sbcl中)称为simple-error,所以我什至不知道如何确定这个特定的simple-error是否是no-such-symbol -in-that-package 错误,更不用说如何从条件中提取相关名称、修复它并重新启动。我真的很想听听一位普通的 lisp 专家说,在我尝试这样做之前,这是正确的做法,因为这需要大量的学习。

我还想到,我可以通过在读取文件之前对文件进行 sed-ing 来解决这个问题。例如。将woo.ev.tcp:start-listening-socket 变成,比如说,woo.ev.tcp===start-listening-socket。我不是特别喜欢这个解决方案,也不清楚我是否会遇到更多丑陋的特殊情况,但如果没有更好的答案,它可能会起作用。

【问题讨论】:

  • 我很确定你不能(通常)在不执行 Lisp 的一部分的情况下解析 Lisp,因为你可以在解析时运行任意代码,然后可以修改阅读器,以便解析以下所有代码不同。 (另请参阅:如果不实现完整的 Perl 解释器,解析 Perl 是不可能的,这也是同样的问题。)
  • 我知道这一点,但它不必是一个完全通用的解析器。这就是我解释用例的原因。
  • 这里有两个非常相似的问题:stackoverflow.com/questions/52405865/reading-qualified-symbolsstackoverflow.com/questions/53127961/…我很犹豫是否将它们标记为重复,因为意图似乎不同。
  • 我敢打赌,有专门的阅读器实现可以做到这一点。普通的 CL 阅读器不支持它。在 CL 中,需要先定义一个包,然后才能读取带有包前缀的符号。
  • @Svante 这正是我正在寻找的 w/r/t 包装符号,尽管我还有其他问题。我想这有点重复。

标签: parsing lisp common-lisp reader


【解决方案1】:

出于多种原因,我几乎可以肯定没有简单的便携式方法可以做到这一点。

(现在只限于不存在的包问题。)

首先,没有可移植访问读取器的位,它决定令牌将成为符号,然后查找包标记 &c: 这只是根据2.3 中的规则发生的。所以你不能轻易干预。

其次,在读者可能发出信号的任何情况下,都没有足够的便携信息来处理它们。

解决这个问题有几种可能的方法。

如果你觉得自己足够英勇,你可以告诉读者所有的标记起始字符实际上都是你控制的东西,然后编写一个标记阅读器,它通过返回一些对象以某种方式处理整个包的事情不是一个符号。但要做到这一点,您需要处理数字,如果您认为这很简单,那么事实并非如此。

如果您觉得不那么英勇,您可以编写一个更原始的令牌阅读器,它甚至不尝试处理任何事情,除了抓取所有需要的字符并返回某种包装字符串的对象。这会以损失大量信息为代价来避免整数问题。

如果您不关心可移植性,请找到一个实现,了解它的阅读器是如何实现的,然后随意使用它。开源或可用源代码的实现比我可以轻松计算的要多(也许我不太擅长计算),所以这是一个非常好的方法。这当然是我会做的。


但这只是问题的开始。 CL 阅读器毛茸茸的,在其标准配置中(用于compile-file 之类的配置,除非人们另有安排)可以在阅读时运行完全任意代码,包括修改阅读器本身的代码,其中一些可能以依赖于实现的方式这样做。人们使用它:Lisp 被称为“可编程编程语言”是有原因的,而且人们对其进行编程。

【讨论】:

    【解决方案2】:

    我决定使用 sed(实际上是 Python 的 re.sub,但谁在数呢?)来解决这个问题,因为它适用于我的实际用例,而且很简单。

    对于未来的读者:各种人说这通常是不可能的,这可能是对的。 @Svante 发布的其他问题看起来像是解决部分问题的好方法。通过将#.#+#- 等的阅读器宏替换为仅列出列表的阅读器宏,可能会更优雅地解决问题的其他部分,这听起来不像@tfb 的建议那么英勇,但我没时间玩这些。

    【讨论】:

    • 鉴于#+ 中的内容可能根本无法被忽略它们的实现读取,因为缺少包然后“列出”您需要解决标记化问题。这个问题没有简单的解决方案,如果你想要一些真正能正常工作的东西。如果您不在乎(这很好),那么您正在做的事情可能很好。
    猜你喜欢
    • 2020-08-20
    • 2011-11-02
    • 1970-01-01
    • 2011-09-10
    • 1970-01-01
    • 1970-01-01
    • 2012-12-10
    • 2023-02-02
    • 2011-03-23
    相关资源
    最近更新 更多