схема - неверный синтаксис после переопределения вспомогательного ключевого слова - PullRequest
0 голосов
/ 26 февраля 2019

Пожалуйста, обратите внимание на следующие макросы.

(let ()
  (define-syntax minus
    (syntax-rules (from)
      [(_ e1 from e2) (- e2 e1)]))

  ; a  
  (minus 1 from 2)
  ;; => 1

  ; b
  (define from 3)
  (minus from from 2) 
  ;; => -1

  ; c
  (minus 1 from 2)
  ;; => 1

  ; d
  #; 
  (let ([from 1])
    (minus 1 from 2))
  ;; bad syntax
  )

Я заметил что-то неожиданное с вспомогательным синтаксисом from, включенным в макрос minus.

Мне кажется, from может быть переопределено везде, где мы хотим.Однако похоже, что case d не будет работать.

Это можно легко воспроизвести с помощью Racket.

Есть идеи, почему существует плохой синтаксис?

Спасибо,

Редактировать:

Обратите внимание: если я немного изменю определение, оно снова будет действительным.

(let ()
  (define-syntax minus
    (syntax-rules (from)
      [(_ e1 from e2) (- e2 e1)]
      [(_ e1 e2 _) (- e2 e1)]))

  ; a
  (minus 1 from 2)
  ;; => 1

  ; b
  (let ([from 1])
    (minus 1 from 2))
  ;; => 0
  )

Поскольку free-identifier=? используется для неявной защиты вспомогательных ключевых слов, from в случае b не рассматривается как ключевое слово, тогда первый случай не будет сопоставлен.

1 Ответ

0 голосов
/ 26 февраля 2019

Как следует из документации , syntax-rules расширяется до syntax-case.И действительно, syntax-case имеет ту же проблему:

#lang racket

(define-syntax (minus stx)
  (syntax-case stx (from)
    [(_ e1 from e2) #'(- e2 e1)]))

(let ([from 1])
  (minus 1 from 2))
;; => minus: bad syntax in: (minus 1 from 2)

Однако есть вариант syntax-case с именем syntax-case*.В документации говорится, что:

Как и в случае с синтаксисом, но id-compare-expr должен создать процедуру, которая принимает два аргумента.Литеральный идентификатор в шаблоне совпадает с идентификатором, для которого процедура возвращает истину, если ему присвоен соответствующий идентификатор (в качестве первого аргумента), и идентификатором в шаблоне (в качестве второго аргумента).

Другими словами, синтаксис-регистр похож на синтаксис-регистр * с идентификатором сравнения-сравнения, который создает free-identifier =?.

Так что мы можем попробовать это:

#lang racket

(define-for-syntax (my-comparator a b)
  (println a)
  (println b)
  (println (free-identifier=? a b))
  (free-identifier=? a b))

(define-syntax (minus stx)
  (syntax-case* stx (from) my-comparator
    [(_ e1 from e2) #'(- e2 e1)]))

(minus 1 from 2)
;; => .#<syntax:unsaved-editor:13:9 from>
;; => .#<syntax:unsaved-editor:11:11 from>
;; => #t

(let ([from 1])
  (minus 1 from 2))

;; => .#<syntax:unsaved-editor:19:11 from>
;; => .#<syntax:unsaved-editor:11:11 from>
;; => #f
;; => minus: bad syntax in: (minus 1 from 2)

КакВы видите, проблема в том, что free-identifier=? обнаруживает, что оба from не равны в последнем случае.

Итак, чтобы сделать эту работу, просто предоставьте свой собственный компаратор, которыйне использует free-identifier=?:

#lang racket

(define-for-syntax (my-comparator a b)
  (eq? (syntax->datum a) (syntax->datum b)))

(define-syntax (minus stx)
  (syntax-case* stx (from) my-comparator
    [(_ e1 from e2) #'(- e2 e1)]))

(minus 1 from 2)
;; => 1

(let ([from 1])
  (minus 1 from 2))
;; => 1

Если вы не можете использовать syntax-case*, вы также можете сделать это:

(define-syntax (minus stx)
  (syntax-case stx () 
    [(_ e1 from e2)
     (eq? (syntax->datum #'from) 'from) ; a guard
     #'(- e2 e1)]))

, хотя это становится утомительнымкогда у вас есть несколько буквальных идентификаторов.

...