首页 > 解决方案 > 用“read”解析字符串并忽略包命名空间

问题描述

我正在编写一个程序,它打开一个 lisp 文件,在流上调用“读取”直到流为空,然后对它收集的列表进行处理。

这工作得很好,直到我发现“读取”将执行包查找,例如,如果遇到some-package:foo它会抱怨Package SOME-PACKAGE does not exist.

这是一个显示我的意思的示例:

(read (make-string-input-stream "(list 'foo :foo some-package:foo)"))

所以我现在想要三件事之一:

  1. 让它如此“读取”将忽略包名称空间,因此我可以将任意源文件转换为符号列表。
  2. 使用其他一些具有类似行为的解析库来“读取”,但只能通过修改:或忽略冒号及其前面的所有内容来获取普通符号。
  3. 预处理文件并使用正则表达式等来打包查找并用普通名称替换它们,例如将“some-package:foo”转换为简单的“foo”

首先,所有这些的目的是制作一个函数调用依赖图。我知道存在质量更高的那种性质的东西,但我想自己做这件事是为了好玩/学习。但是,我遇到了这个问题,不知道如何继续。

标签: listparsinglispcommon-lisps-expression

解决方案


最简单的答案是告诉 Lisp 阅读器按#\:原样阅读冒号:

(defun read-standalone-char (stream char)
  (declare (ignore stream))
  char)

(defun make-no-package-prefix-readtable (&optional (rt (copy-readtable)))
  "Return a readtable for reading while ignoring package prefixes."
  (set-syntax-from-char #\: #\Space rt)
  (set-macro-character #\: #'read-standalone-char nil rt)
  rt)

(let ((*readtable* (make-no-package-prefix-readtable)))
  (read-from-string "(list 'foo :foo some-package:foo)"))
==> (LIST 'FOO #\: FOO SOME-PACKAGE #\: FOO) ; 33

明显的问题是它的读取方式FOO:BAR相同FOO :BAR,但您也许可以解决这个问题。


推荐阅读