Мой макрос lisp перестает работать в последней версии - PullRequest
2 голосов
/ 15 апреля 2019

У меня есть макрос, который я написал в 2010 году, он был для управления структурами, как в Common Lips, используя Alists (здесь весь файл, включая функции https://jcubic.pl/struct.txt).

(define-macro (defstruct name . fields)
  "Macro implementing structures in guile based on assoc list."
  (let ((names (map (lambda (symbol) (gensym)) fields))
        (struct (gensym))
        (field-arg (gensym)))
    `(if (not (every-unique ',fields))
        (error 'defstruct "Fields must be unique")
        (begin
          (define (,(make-name name) ,@names)
        (map cons ',fields (list ,@names)))
          ,@(map (lambda (field)
               `(define (,(make-getter name field) ,struct)
              (cdr (assq ',field ,struct)))) fields)
          ,@(map (lambda (field)
               `(define (,(make-setter name field) ,struct ,field-arg)
              (assq-set! ,struct ',field ,field-arg)
              ,field-arg)) fields)
          (define (,(make-predicate name) ,struct)
        (and (struct? ,struct)
             (let ((result #t))
               (for-each (lambda (x y)
                   (if (not (eq? x y)) (set! result #f)))
                 ',fields
                 (map car ,struct))
               result)))))))

Работало нормально. Я недавно обновил этот макрос для моих губ в JavaScript (он основан на схеме), и когда я его вызывал, он возвращал false и хотел знать, так ли это будет работать в guile. Но оказывается, что это не работает в коварстве вообще. Это показывает эту ошибку:

При компиляции выражения: ОШИБКА: синтаксическая ошибка: неизвестное местоположение: определение в контексте выражения, где определения не допускаются, в форме (определить (make-point # {g746} # # {g747} #) (минусы карты (цитата (x у)) (список # {g746} # # {g747} #))

Почему у меня есть эта ошибка и как ее исправить, чтобы она снова работала в guile? Я был давно, я не помню, как я тестировал этот код, но открытие guile с помощью функции load или копирование, вставка кода в интерпретатор выдает ту же ошибку.

Я использую guile 2.0.14 в GNU / Linux.

PS: Я предпочитаю использовать макросы lisp ИМО, они превосходят гигиенические макросы странной схемы.

1 Ответ

2 голосов
/ 15 апреля 2019

Похоже, что современная схема guile не видит начала в if в качестве допустимой опции для запуска нового определения контекста. Возможно, это ошибка или лучшее выравнивание схемы дону. Но в следующем примере кода показан метод исправления вашего кода для более поздней версии guile (вам может потребоваться создать define-values, так как это более свежее дополнение к guile. PS использование макросов lisps в guile - это обман, и вы попадете в беда, если вы планируете много спланировать, макросы похожи на парены, если вы привыкнете, они будут выглядеть естественно.

Вот код,

(define-macro (defstruct name . fields)
   "Macro implementing structures in guile based on assoc list."
   (let* ((names (map (lambda (symbol) (gensym)) fields))
          (struct    (gensym))
          (field-arg (gensym))
          (sname     (make-name name))
          (predname  (make-predicate name))
          (getnames  (map (lambda (f) (make-getter name f)) fields))
          (setnames  (map (lambda (f) (make-setter name f)) fields)))

      `(define-values (,sname ,predname ,@getnames ,@setnames)
         (if (not (every-unique ',fields))
             (error 'defstruct "Fields must be unique")
             (let ()
               (define (,sname ,@names)
                 (map cons ',fields (list ,@names)))
               ,@(map (lambda (field)
                  `(define (,(make-getter name field) ,struct)
                      (cdr (assq ',field ,struct)))) fields)
               ,@(map (lambda (field)
                  `(define (,(make-setter name field) ,struct ,field-arg)
                      (assq-set! ,struct ',field ,field-arg)
                  ,field-arg)) fields)
               (define (,predname ,struct)
                  (and (struct? ,struct)
                       (let ((result #t))
                          (for-each (lambda (x y)
                             (if (not (eq? x y)) (set! result #f)))
                           ',fields
                          (map car ,struct))
                          result)))

                 (values ,sname ,predname ,@getnames ,@setnames))))))

Вот версия define-values (посмотрите код после #', чтобы увидеть, что он делает)

(define-syntax define-values
   (lambda (x)
      (syntax-case x ()
        ((_ (f ...) code ...)
         (with-syntax (((ff ...) (generate-temporaries #'(f ...))))
           #'(begin
               (define f #f)
                ...
               (call-with-values (lambda () code ...)
                  (lambda (ff ...)
                     (set! f ff)
                     ...))))))))
...