2016-10-02 77 views
4

我有不上表達一些處理功能:從宏觀球拍Unquoting

(struct sym (s) #:transparent) 

(define (foo-fn ex) 
    (match ex 
    [(? symbol? s) `(sym (quote ,s))] 
    [(? list? xs) (cons (car xs) (map foo-fn (cdr xs)))] 
    [x x])) 

此功能按預期工作:

> (foo-fn '(cons x y)) 
'(cons (sym 'x) (sym 'y)) 

現在我想做一個宏以管理給出它的表達並用foo-fn的結果代替它。我設法得到了一些存在的方式與

(define-syntax-rule (foo ex) 
    (foo-fn (quote ex))) 

然而,這個宏還給出了報價的表情,我想表達本身。也就是說,雖然我當前的代碼給

> (foo (cons x y)) 
'(cons (sym 'x) (sym 'y)) 

我寧願結果是

> (foo (cons x y)) 
(cons (sym 'x) (sym 'y)) 

我已經設法通過使用eval找到一個解決辦法,但我敢肯定,這不是多麼宏是爲了使用(糾正我,如果我錯了)

(define-syntax-rule (foo ex) 
    (eval (foo-fn (quote ex)))) 

雖然上述作品,這是我的信念,這是使用宏的不正確的方法。什麼是首選方法?

回答

4

這裏的根本問題不在foo宏中,而是在foo-fn中。 foo-fn對數據而不是語法對象進行操作,因此會丟棄所有詞彙上下文和源位置信息。這是一個不可逆轉的操作,所以沒有辦法從引用的表達式「回溯」到語法塊。

而不是使用foo-fn來執行您的源轉換,它看起來像您可能希望foo宏自己執行語法分析。使用syntax/parse/define,這是很簡單的:

(require syntax/parse/define) 

(struct sym (s) #:transparent) 

(define-syntax-parser foo 
    [(_ s:id) #'(sym 's)] 
    [(_ (f x ...)) #'(f (foo x) ...)] 
    [(_ x) #'x]) 

通過實施這個宏,你有一個語法對語法轉換,而不是數據到數據之一,適當保留詞彙的上下文。 (這種防護是不可或缺的被稱爲「宏生」的概念。)

現在,您可以使用foo宏,讓你期望的結果:

> (foo (cons x y)) 
(cons (sym 'x) (sym 'y))