2013-06-12 3 views
1

Cette macro R5RS est ce que j'ai essayé et est à peu près ce que je veux faire. Racket ou d'autres implémentations n'aiment pas cette macro exactement où je voulais que la magie se produise.Est-il possible de faire correspondre des motifs répétés dans des macro R5RS define-syntax/syntax-rules?

(define-syntax quote-unique 
    (syntax-rules (magic end) 

    ;; end case 
    ((quote-unique magic processed end) 
    'processed) 

    ;; finished iteration 
    ((quote-unique magic (processed ...) sym1 end rest ...) 
    (quote-unique magic (processed ... sym1) rest ... end)) 

    ;; match (doesn't work since racket doesn't like sym1 twice in template) 
    ;; but I'm looking for the same expression twice 
    ((quote-unique magic processed sym1 sym1 . rest) 
    (quote-unique magic processed sym1 . rest)) 

    ;; rotate 
    ((quote-unique magic processed sym1 sym2 rest ...) 
    (quote-unique magic processed sym1 rest ... sym2)) 

    ;; start iteration 
    ((quote-unique rest ...) 
    (quote-unique magic() rest ... end)))) 

Cela aurait été facile en Common Lisp:

(defmacro quote-unique (&rest xs) 
    (labels ((remove-duplicates (lis) 
     (if lis 
     (if (member (car lis) (cdr lis)) 
       (remove-duplicates (cdr lis)) 
      (cons (car lis) (remove-duplicates (cdr lis))))))) 

    (list 'quote (remove-duplicates xs)))) 

J'ai également lu Define syntax primer et pense que la mise en œuvre est-eq? m'aurait indiqué dans les bonnes directions, mais il semble que ce n'est pas une macro qui y est définie.

Si ce n'est pas possible dans la compilation R5RS, comment cela peut-il être fait avec R6RS?

Répondre

2

Vous ne pouvez pas faire cela en utilisant syntax-rules, mais vous pouvez le faire en utilisant syntax-case, en utilisant un protecteur qui utilise free-identifier=?. Voici un exemple:

(define-syntax (remove-id stx) 
    (syntax-case stx() 
    ((_ head()) 
    #''()) 
    ((_ head (next tail ...)) (free-identifier=? #'head #'next) 
    #'(remove-id head (tail ...))) 
    ((_ head (next tail ...)) 
    #'(cons 'next (remove-id head (tail ...)))))) 

> (remove-id foo (foo bar baz qux foo bar)) 
; => (bar baz qux bar) 

Mais bien sûr, si vous allez utiliser syntax-case, il y a une beaucoup plus simple à mettre en œuvre votre quote-unique (cette implémentation utilise les tables de hachage personnalisées de Racket):

(require (for-syntax racket/dict)) 
(define-syntax (quote-unique stx) 
    (define (id-dict ids) 
    (foldl (lambda (id d) 
      (dict-set d id #t)) 
      (make-immutable-custom-hash free-identifier=? (compose eq-hash-code syntax-e)) 
      (syntax-e ids))) 
    (syntax-case stx() 
    ((_ ids ...) 
    (with-syntax ((unique (dict-keys (id-dict #'(ids ...))))) 
     #''unique)))) 
3

Le remove-id exemple de la réponse de Chris Jester-Young est exprimable dans r5rs:

(define-syntax remove-id 
    (syntax-rules() 
    ((remove-id s (t ...)) 
    (letrec-syntax ((aux (syntax-rules (s) 
          ((aux p*()) 
          'p*) 
          ((aux p* (s . rest)) 
          (aux p* rest)) 
          ((aux (p (... ...)) (u . rest)) 
          (aux (p (... ...) u) rest))))) 
     (aux() (t ...)))))) 

(Notez que citer les points de suspension par (... ...) n'est pas strictement R5RS (seulement R7RS), mais est seulement utilisé pour avoir la séquence résultante dans l'ordre donné et non inversé. Ainsi, en ajoutant une autre macro, vous pouvez même abandonner l'ellipse.)

J'espère que cet exemple montre clairement comment résoudre votre problème d'origine. Si quelque chose peut être résolu avec des macros hygiéniques, il faut réfléchir à deux fois avant d'utiliser une macro procédurale ou une macro qui ne sera probablement pas standardisée après R6RS.

Questions connexes