如何编写一个新的#%基准函数来捕获所有字符串



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

#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")。由于#%datumdatum绑定,定义为:

(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的结果。

注意:如果宏的使用扩展为对同一宏的使用,那么你很可能会遇到这个无限膨胀的问题。找到罪魁祸首的最简单方法是使用宏步进器。将"宏隐藏:"设置设置为"禁用"。然后逐步直到请参阅循环。

最新更新