我想写一种新的球拍语言,以某种特殊方式捕捉和处理字符串。我编写了以下示例代码:
#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返回语法而不是基准?
答案 0 :(得分:2)
首先让我们看看上面的基准版本的问题。
我们假设该程序包含字符串"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")
变为(datum . "a")
,然后成为
(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
后果。
注意:如果宏使用扩展为使用同一个宏, 那你很可能遇到这个无限扩展问题。 找到罪魁祸首的最简单方法是使用宏步进器。 转动"宏隐藏:"设置为"禁用"。然后一步到你 看到循环。