Racket 如何编写新的#%datum函数来捕获所有字符串?

Racket 如何编写新的#%datum函数来捕获所有字符串?,racket,Racket,我想写一种新的球拍语言,它以某种特殊的方式捕捉和处理字符串。我编写了以下示例代码: #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))) (d

我想写一种新的球拍语言,它以某种特殊的方式捕捉和处理字符串。我编写了以下示例代码:

#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?

首先,让我们看看上述datum版本的问题。 假设程序包含字符串
“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
的目的是使用两种不同的扩展名,即
(添加字符串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
外,我还更改了
添加字符串的结果

注意:如果宏的使用扩展为同一宏的使用, 那么你很可能会遇到这个无限扩张的问题。 找到罪魁祸首的最简单方法是使用宏步进器。 将“宏隐藏:”设置改为“禁用”。然后一直走到你
查看循环。

看起来像是
添加字符串
需要有
x
,因为它是最后一个表单,可以让表单计算为
#
以外的内容。感谢您的全面回答。