我有一段时间在JavaScript中使用自己的Lisp解释器,现在我想像在Common Lisp中那样实现阅读器宏。
我已经创建了Streams(除了,@ , ` '
之类的特殊符号以外,几乎可以正常工作),但是当它用包含的脚本(具有400行代码的lisp文件)加载页面时,它将冻结浏览器几秒钟。这是因为我的流基于子字符串函数。如果我先拆分令牌,然后使用遍历令牌的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}")))
其中标记是宏,该宏返回以$ {}作为自由变量的字符串。阅读器宏还可以返回经过评估的Lisp代码吗?
还有另一个问题,您可以实现这样的阅读器宏吗:
(list :foo:bar)
(list foo:bar)
其中:是阅读器宏,如果在符号之前,它将符号转换为
foo.bar
,如果在其中,则抛出错误。我之所以这样问,是因为基于令牌的宏:foo:bar
和foo:bar
将是符号,不会被我的阅读器宏处理。
还有一个问题可以将阅读器宏放在一行中,而第二行可以使用它吗?肯定只有字符串流才有可能,而根据我的测试,用JavaScript编写的解释器是不可能的。
答案 0 :(得分:2)
从某种意义上说,存在一定的局限性,例如,除了“从头实现自己的令牌解释器”之外,很难以任何方式干预令牌的解释。但是,好吧,如果您只想这样做:问题是您的代码将需要像现有代码一样处理数字和事物,并且浮点解析之类的事情很容易就可以解决。 / p>
但是与宏字符关联的宏函数获取正在读取的流,并且它们可以自由读取任意多或少的流,并返回任何类型的对象(或不返回任何对象,即评论的实现方式。)
我强烈建议您阅读hyperspec的第2和23章,然后尝试实现。当您使用实现时,请注意,通过与读者混为一谈,完全楔入东西是非常容易的。至少我会建议这样的代码:
var xPositions = Xaxis.getVerticesData(BABYLON.VertexBuffer.PositionKind);
xPositions[1] = xPositions[4] = (vy/100)*newVal, xPositions[2] = xPositions[5] = (vz/100)*newVal;
Xaxis.updateVerticesData(BABYLON.VertexBuffer.PositionKind, xPositions);
这至少使您有机会从灾难中恢复过来:如果您一次可以放弃(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)))