假设我想在Racket / Scheme中定义一个工具"定义"的宏。具体来说,它计算一个定义体中的数字文字,然后将这个数字添加到所有这些文字中(这应该在宏扩展阶段发生)。
这是以普通函数样式定义的宏(在语法树上作为列表运行):
#lang racket
(require
(for-syntax racket/syntax)
(rename-in racket [define define/racket]))
(define-for-syntax (count-and-transform e)
(define tree (syntax->datum e))
; count number literals
(define (count t)
(if (list? t)
(apply + (map count t))
(if (number? t) 1 0)))
(define n (count tree))
; transform number literals
(define (transform t)
(if (list? t)
(map transform t)
(if (number? t) (+ t n) t)))
(values n (datum->syntax e (transform tree))))
; rewrite defines
(define-syntax (define stx)
(syntax-case stx ()
[(_ signature body)
(let-values ([(n new-body)
(count-and-transform #'body)])
#`(begin
(display "number of literals in function ")
(display 'signature) (display ": ") (displayln #,n)
(define/racket signature #,new-body)))]))
(define (add-some x) (if (= x 0) (+ x 1) 2))
我想用普通的Racket / Scheme模板宏样式重写它。这是我的(不成功)尝试:
#lang racket
(require
(for-syntax racket/syntax)
(rename-in racket [define define/racket]))
(define-for-syntax n 0)
; rewrite defines
(define-syntax-rule
(define signature body)
(begin
(display "number of literals in function ")
(display 'signature) (display ": ") (display-counted-n)
(define/racket signature (descent body))))
; descent the syntax tree and mark all nodes
(define-syntax descent
(syntax-rules (f-node a-node)
[(_ (f etc ...)) (mark (f (descent etc) ...))]
[(_ a etc ...) (mark a (descent etc) ...)]))
; count number literals
(define-syntax (mark stx)
(syntax-case stx ()
[(_ node)
(begin
;(display n) (display " : ") (displayln (syntax->datum #'node))
(if (number? (syntax-e #'node))
(begin
(set! n (add1 n))
#'(transform node))
#'node))]))
; transform number literals
(define-syntax (transform stx)
(syntax-case stx ()
[(_ node)
(let* ([i (syntax->datum #'node)]
[i+n (+ i n)])
(begin
;(display i) (display " -> ") (displayln i+n)
(datum->syntax stx i+n)))]))
(define-syntax (display-counted-n stx)
(syntax-case stx ()
[(_) #`(displayln #,n)]))
(define (add-some x) (if (= x 0) (+ x 11) 13))
我们的想法是分阶段测试代码:首先在语法树中标记所有节点,然后计算文字,用" transform"替换标记。宏,如果需要......好吧,正如评论"显示"会显示,宏观"标记"在所有"下降之前开始扩张"已完成(因此它们仍然在宏代码中捕获)。甚至"显示计数n"过快地扩张,而" n"仍然是0。
有没有办法改变宏扩展的顺序?我希望Racket / Scheme分阶段进行扩展:首先" descent"然后"标记"然后"转换"然后"显示计数 - N'#34;
我已经阅读了How to control order of Scheme macro expansion?的答案 - 似乎用模板宏实现这样一项任务的唯一方法就是使用"秘密文字"并在一个大宏定义中定义所有内容。但是,我想这会使代码更难写入和读取。还有其他方式吗?
答案 0 :(得分:2)
这是我的宏syntax-case
版本的版本:
(define-syntax (lambda-fun stx)
(define (count-numeric-literals stx2)
(syntax-case stx2 ()
(num (number? (syntax->datum #'num)) 1)
((first rest ...) (+ (count-numeric-literals #'first)
(count-numeric-literals #'(rest ...))))
(_ 0)))
(define (instrument-numeric-literals stx3 n)
(syntax-case stx3 ()
(num (number? (syntax->datum #'num))
(datum->syntax #'num (+ (syntax->datum #'num) n)))
((first rest ...)
(with-syntax ((a (instrument-numeric-literals #'first n))
((b ...) (instrument-numeric-literals #'(rest ...) n)))
#'(a b ...)))
(x #'x)))
(syntax-case stx ()
((_ params . body)
(let ((count (count-numeric-literals #'body)))
(with-syntax ((instrumented (instrument-numeric-literals #'body count)))
#'(lambda params . instrumented))))))
(define-syntax define-fun
(syntax-rules ()
((_ (f . params) . body)
(define f (lambda-fun params . body)))
((_ . passthrough)
(define . passthrough))))
这使用语法保护(也称为挡泥板)来确定语法数据是否为数字。对于更容易阅读的内容,您可以使用syntax-parse
,它允许您指定语法类,如number
,而不是使用语法保护:
(require (for-syntax syntax/parse))
(define-syntax (lambda-fun stx)
(define (count-numeric-literals stx2)
(syntax-parse stx2
(num:number 1)
((first rest ...) (+ (count-numeric-literals #'first)
(count-numeric-literals #'(rest ...))))
(_ 0)))
(define (instrument-numeric-literals stx3 n)
(syntax-parse stx3
(num:number (datum->syntax #'num (+ (syntax->datum #'num) n)))
((first rest ...)
(with-syntax ((a (instrument-numeric-literals #'first n))
((b ...) (instrument-numeric-literals #'(rest ...) n)))
#'(a b ...)))
(x #'x)))
(syntax-parse stx
((_ params . body)
(let ((count (count-numeric-literals #'body)))
(with-syntax ((instrumented (instrument-numeric-literals #'body count)))
#'(lambda params . instrumented))))))
示例:
> (define-fun (fun) (+ 1 2 3 4))
> (fun)
26