2015-08-14 2 views
2

Я работаю с расширением макроса Racket syntax-id-rules, что некоторые другие реализации схемы предоставляют под именем identifier-syntax. Они позволяют указывать расширения макросов, которые произойдут, даже если определенный идентификатор не находится в позиции главы. Так, например:Как контролировать порядок расширения макросов схемы?

(define hidden #f) 
(define-syntax proxy 
    (syntax-id-rules (set!) 
    [(set! proxy v) (set! hidden v)] 
    [proxy hidden])) 

будет создан идентификатор proxy быть прокси для hidden. Это бесполезный пример, но он иллюстрирует использование.

Я нахожусь в ситуации, когда хочу глобальный обычный макрос, назовем его foo, что я хочу переопределить в некоторых случаях, когда я использую макрос идентификатора, например proxy. То есть, я хочу, чтобы иметь возможность сделать что-то вроде этого:

(define-syntax foo 
    (syntax-rules() 
    [(foo arg ...) 'default])) 

(define hidden #f) 
(define-syntax proxy 
    (syntax-id-rules (foo set!) 
    [(foo proxy arg ...) 'special] 
    [(set! proxy v) (set! hidden v)] 
    [proxy hidden])) 

(foo proxy) ; should return 'special 

Но на самом деле последняя строка возвращает 'default, потому что foo макрос получает расширен до proxy один.

Любые идеи, как я мог бы достичь чего-то в этом направлении, но с макросом идентификатора proxy, переопределяющим определение макроса по умолчанию для foo? Я не привержен этой архитектуре.

Добавлено: Это не для использования в реальном мире, а в части демонстрации теоретической точки в формальной семантике.

ответ

3

@soegaard это прекрасно. Вы не можете делать то, что хотите, без изменения макрообмена.

Чтобы продлить ответ @ soegaard, вот способ имитировать то, о чем вы просите. По сути, это макроопределение «двойной отправки». Тем не менее, как отметил соагор, вероятно, существует более идиоматический способ достичь того, чего вы хотите, в зависимости от ваших целей.

#lang racket 
(require (for-syntax syntax/parse)) 

(begin-for-syntax 
    (define (special-condition? id) 
    (and (identifier? id) 
     (regexp-match #rx"^p" ; starts with "p" 
         (symbol->string (syntax->datum id)))))) 

(define-syntax foo 
    (syntax-parser 
    [(_ special-case arg ...) 
    #:when (special-condition? #'special-case) 
    #'(special-case 'hidden-special-case-tag arg ...)] 
    ; else 
    [(_ arg ...) #''default])) 

(define hidden #f) 
(define-syntax proxy 
    (syntax-id-rules (quote set!) 
    [(proxy (quote hidden-special-case-tag) arg ...) 'special] 
    [(set! proxy v) (set! hidden v)] 
    [(proxy arg ...) 'other] 
    [proxy hidden])) 

(foo non-proxy) ; => 'default 
(foo proxy) ; => 'special 
(proxy) ; => 'other 
proxy ; => #f 
(set! proxy #t) 
proxy ; => #t 
+0

Я подозревал, что мне нужно сделать два слоя макро-расширения; спасибо за отображение кода скелета, чтобы это произошло. Это может быть достаточно хорошим для моих целей, и это не похоже на то, что я добьюсь успеха. По сути, я нуждаюсь в чем-то вроде «синтаксиса-id-rules» или 'make-set! -transformer', но там, где мы можем предоставить дополнительные формы, такие как' foo', получим такое же специальное обращение, что 'set!' получает. – dubiousjim

+0

'синтаксис-парсер', похоже, не нужен здесь; можно добиться такого же эффекта, используя необязательные защитные выражения в «синтаксисе-case» Racket: '(define-syntax (foo stx) (syntax-case stx() [(_ special-case arg ...) (особое условие ? # 'специальный случай) #' (специальный случай 'hidden-special-case-tag arg ...)] [(_ arg ...) #' 'default])) ' – dubiousjim

+0

Да, конечно, re право. Я полагаю, что Racketeers обычно предпочитают «синтаксический анализ» для более выразительного языка шаблонов и улучшения возможностей отчетности об ошибках. – stchang

3

Мне кажется, что вам нужно найти альтернативную стратегию. Возможно, мы сможем найти решение, если вы предоставите более подробную информацию об этой ситуации.

В любом случае, вот почему ваша стратегия не работает. Когда вы пишете

(define-syntax proxy ...) 

связывание синтаксического-трансформер с идентификатором proxy. Этот трансформатор вызывается расширителем, когда он видит либо (proxy ...), (set! proxy ...), либо proxy.

Для того, чтобы определить, что должно быть (foo proxy arg ...), уточнить его в синтаксическом трансформаторе, связанном с foo.

Теперь в зависимости от ситуации могут быть трюки, которые можно воспроизвести.

Например, можно представить, что вы завершаете свою программу в новой форме, которая переписывает (foo proxy arg ...) в (proxy 'was-a-foo-originally arg ...), а затем пусть синтаксический трансформатор для proxy обрабатывает остальные.

Удобное решение для перемещения (foo proxy arg ...) в трансформатор для foo, но вы специально запрашиваете решение, в котором foo не изменяется.

+0

Добавлена ​​заметка о моем «прецеденте». Я не могу легко поместить обработку 'proxy' в' foo', так как может быть много 'proxy', определенных на разных уровнях, но должна существовать только одна реализация по умолчанию 'foo', где аргументы, необходимые для «прокси». – dubiousjim

Смежные вопросы