写一个不引用条件的宏

时间:2017-10-23 13:33:14

标签: macros scheme racket

考虑以下形式:

(call hello 12 12)
(call hello pos len)
(call hello (+ 1 2 3) len)

我想写一个扩展为:

的宏
'(call $hello 12 12)
'(call $hello (world $pos) (world $len))
'(call $hello 6 (world $len))

这意味着:

  • 在标识符之前添加$
  • 围绕参数添加(世界)
  • 正常执行其他任何操作(例如(+ 1 2 3)变为6
  • 然后引用整个结果

这是我到目前为止所做的:

#lang racket

(require
  (for-syntax racket)
  (for-syntax racket/syntax))

(define-syntax (call stx)
  (define ($ name)
    (format-symbol "$~a" name))

  (define (eval-args args)
    (map (lambda (a)
           (if (identifier? a)
             `(world ,($ a)) ;; quote
             a))             ;; don't quote
         (syntax->list args)))

  (syntax-case stx ()
    ((_ name . args)
       #``(call #,($ #'name) #,@(eval-args #'args)))))


(call hello 12 12)         ;; '(call $hello 12 12)
(call hello pos len)       ;; '(call $hello (world $pos) (world $len))
(call hello (+ 1 2 3) len) ;; '(call $hello (+ 1 2 3) (world $len))

如您所见,未评估表格(+ 1 2 3) :(

如何才能使我的语法中不是标识符的元素不加引号,其余部分被引用?换句话说,我只是想阻止一些元素(不是标识符的东西)被我的宏引用。

我尝试在不是标识符的参数上使用syntax->datumsyntax-eeval-syntax甚至eval,但没有人可以做...

1 个答案:

答案 0 :(得分:1)

您需要取消引用简单标识符,但使用,a会调用语法错误,因此您需要将其拼写出来或以不同方式进行引用:

(define (eval-args args)
  (map (lambda (a)
         (if (identifier? a)
           `(world ,($ a))    
           (list 'unquote a))) ;; force unquote
       (syntax->list args)))

扩张:

`(call $hello ,(+ 1 2 3) (world $len))

也许这是更好的风格:

(define (eval-args args)
  (map (lambda (a)
         (if (identifier? a)
             `'(world ,($ a))  ;; quote
             a))               ;; don't quote
       (syntax->list args)))

(syntax-case stx ()
  ((_ name . args)
   #``(call #,($ #'name) ,@(list #,@(eval-args #'args))))))

扩展看起来略有不同,但也是如此:

`(call $hello ,@(list (+ 1 2 3) '(world $len)))