我想写一种新的球拍语言,以某种特殊的方式捕捉和处理字符串。我写了以下示例代码:
#lang racket
(provide #%top #%app #%top-interaction #%module-begin
(rename-out [datum #%datum]))
(define big-string "")
(define (add-string x)
(set! big-string (string-append big-string x)))
(define-syntax (datum stx)
(syntax-case stx ()
[(_ . x)
#'(if (string? x)
(#%datum . (add-string x))
(#%datum . x))]))
当我尝试使用目标语言时,它会给我一个记忆不足的错误。它是递归地调用自己吗?我本以为卫生会阻止这种情况的发生。
也许问题是#%datum返回语法,而不是datums?
首先让我们看看上面数据版本的问题。假设程序包含字符串"a"
。扩展器看到字符串"a"
并将其转换为(#%datum . "a")
。由于#%datum
与datum
绑定,定义为:
(define-syntax (datum stx)
(syntax-case stx ()
[(_ . x)
#'(if (string? x)
(#%datum . (add-string x))
(#%datum . x))]))
语法(#%datum . "a")
将扩展到
(if (string? "a")
(#%datum . (add-string "a"))
(#%datum . "a"))
然后,扩展器将开始展开上述表达式。当涉及到第一个"a"
时,它将把它扩展为(#%datum . "a")
它变成CCD_ 9,然后变成的另一个副本
(if (string? "a")
(#%datum . (add-string "a"))
(#%datum . "a"))
等等。
datum
的目的是使用两种不同的展开式(add-string x)
或(#%datum . x)
。然而,由于datum
的输出是#'(if (string? x) ...)
,所以if
不是在编译时评估的,而是在运行时评估的。
解决方案是移动if
。
#lang racket
(provide #%top #%app #%top-interaction #%module-begin
(rename-out [datum #%datum]))
(define big-string "")
(define (add-string x)
(set! big-string (string-append big-string x))
x)
(define-syntax (datum stx)
(syntax-case stx ()
[(_ . x)
(if (string? (syntax-e #'x))
#'(add-string (#%datum . x))
#'(#%datum . x))]))
除了移动if
,我还改变了add-string
的结果。
注意:如果宏的使用扩展为对同一宏的使用,那么你很可能会遇到这个无限膨胀的问题。找到罪魁祸首的最简单方法是使用宏步进器。将"宏隐藏:"设置设置为"禁用"。然后逐步直到请参阅循环。