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

时间:2016-05-05 07:43:47

标签: 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)))

(define-syntax (datum stx)
  (syntax-case stx ()
    [(_ . x)
     #'(if (string? x)
           (#%datum . (add-string x))
           (#%datum . x))]))

当我尝试使用目标语言时,它会给我一个内存不足的错误。是递归调用自己吗?我本以为卫生会阻止这种情况。

问题可能是#%datum返回语法而不是基准?

1 个答案:

答案 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后果。

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