gpt4 book ai didi

macros - Common Lisp 中阅读器宏的局限性是什么

转载 作者:太空宇宙 更新时间:2023-11-03 18:52:13 28 4
gpt4 key购买 nike

我有自己的 JavaScript Lisp 解释器,我已经工作了一段时间,现在我想像在 Common Lisp 中一样实现阅读器宏。

我已经创建了 Streams(除了 、@、` ' 等特殊符号外,几乎可以正常工作)但是当它加载包含脚本(lisp 文件)的页面时,浏览器会卡住几秒钟有 400 行代码)。这是因为我的流是基于子串函数的。如果我先拆分 token ,然后使用迭代 token 的 TokenStream,它就可以正常工作。

所以我的问题是,字符串流真的是 Common Lisp 中的东西吗?你能添加阅读器宏来创建像 CL 中的 Python 这样的全新语法吗,这简化了我可以实现 """ 宏的问题(不确定你是否可以有 3 个字符作为阅读器宏)或其他字符例如,将在 lisp 中实现模板文字:

(let ((foo 10) (bar 20))
{lorem ipsum ${baz} and ${foo}})

(let ((foo 10) (bar 20))
""lorem ipsum ${baz} and ${foo}"")

(let ((foo 10) (bar 20))
:"lorem ipsum ${baz} and ${foo}")

会产生字符串

"lorem ipsum 10 and 20"

在 Common Lisp 中是否可能实现类似的功能?将 #\{#\: 作为阅读器宏实现有多难?

我能想到的在 Lisp 中使用模板文字的唯一方法是这样的:

  (let ((foo 10) (bar 20))
(tag "lorem ipsum ${baz} and ${foo}")))

其中 tag 是一个宏,它返回带有 ${} 作为自由变量的字符串。阅读器宏是否也可以返回已评估的 lisp 代码?

还有一个问题,你能不能像这样实现阅读器宏:

(list :foo:bar)


(list foo:bar)

其中:是阅读器宏,如果它在符号之前,它将符号转换为

foo.bar

如果它在里面就会抛出错误。我问这个是因为基于标记的宏 :foo:barfoo:bar 将是符号,不会被我的阅读器宏处理。

还有一个问题,阅读器宏可以放在一行,第二行使用吗?这肯定只有在字符串流中才有可能,而且根据我的测试,在用 JavaScript 编写的解释器中这是不可能的。

最佳答案

例如,除了“从头开始实现您自己的 token 解释器”之外,很难以任何方式干预 token 的解释,这在某种意义上存在一些局限性。但是,好吧,如果你只想这样做,你可以这样做:问题是你的代码需要像现有代码一样处理数字和事物,而像浮点解析这样的事情是非常繁琐的。

但是与宏字符关联的宏函数获取正在读取的流,它们可以自由地读取尽可能多或尽可能少的流,并返回任何类型的对象(或不返回任何对象,这是注释是如何实现的)。

我强烈建议阅读章节 2 & 23 hyperspec,然后玩一个实现。当您使用该实现时,请注意通过与读者混在一起非常容易地完全楔入事物。至少我会建议这样的代码:

(defparameter *my-readtable* (copy-readtable nil))

;;; Now muck around with *my-readtable*, *not* the default readtable
;;;

(defun experimentally-read ((&key (stream *standard-input*)
(readtable *my-raedtable*)))
(let ((*readtable* readtable))
(read stream)))

这至少给了你一些从灾难中恢复的机会:如果你可以一次中止 experimentally-read 那么你就回到了 *readtable* 的位置明智的。

这是一个相当无用的例子,它显示了你可以用宏字符破坏多少语法:一个宏字符定义将导致 ( ...) 被读取为一个字符串。这可能没有完全调试,正如我所说,我看不出它有什么用。

(defun mindless-parenthesized-string-reader (stream open-paren)
;; Cause parenthesized groups to be read as strings:
;; - (a b) -> "a b"
;; - (a (b c) d) -> "a (b c) d"
;; - (a \) b) -> "a ) b"
;; This serves no useful purpose that I can see. Escapes (with #\))
;; and nested parens are dealt with.
;;
;; Real Programmers would write this with LOOP, but that was too
;; hard for me. This may well not be completely right.
(declare (ignore open-paren))
(labels ((collect-it (escaping depth accum)
(let ((char (read-char stream t nil t)))
(if escaping
(collect-it nil depth (cons char accum))
(case char
((#\\)
(collect-it t depth accum))
((#\()
(collect-it nil (1+ depth) (cons char accum)))
((#\))
(if (zerop depth)
(coerce (nreverse accum) 'string)
(collect-it nil (1- depth) (cons char accum))))
(otherwise
(collect-it nil depth (cons char accum))))))))
(collect-it nil 0 '())))

(defvar *my-readtable* (copy-readtable nil))

(set-macro-character #\( #'mindless-parenthesized-string-reader
nil *my-readtable*)

(defun test-my-rt (&optional (stream *standard-input*))
(let ((*readtable* *my-readtable*))
(read stream)))

现在

> (test-my-rt)
12
12

> (test-my-rt)
x
x

> (test-my-rt)
(a string (with some parens) and \) and the end)
"a string (with some parens) and ) and the end"

关于macros - Common Lisp 中阅读器宏的局限性是什么,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56306111/

28 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com