2015-07-25 1 views
6

Я пытаюсь определить новый язык в ракетке, назовем его wibble. Wibble позволит загружать модули, поэтому он должен переводить свои формы в формы, требующие Racket. Но у меня возникают проблемы с требованием работать при использовании в расширении языка. В конечном итоге я отследил свои проблемы до следующего странного поведения.пытается понять необходимость в расширении языка

Вот мой читатель, который переопределяет read и read-syntax

=== wibble/lang/reader.rkt === 
#lang racket/base 

(provide (rename-out (wibble-read read) (wibble-read-syntax read-syntax))) 

(define (wibble-read in) 
    (wibble-read-syntax #f in)) 

(define (wibble-read-syntax src in) 
    #`(module #,(module-name src) wibble/lang 
     #,@(read-all src in))) 

(define (module-name src) 
    (if (path? src) 
     (let-values (((base name dir?) (split-path src))) 
     (string->symbol (path->string (path-replace-suffix name #"")))) 
     'anonymous-module)) 

(define (read-all src in) 
    (let loop ((all '())) 
    (let ((obj (read-syntax src in))) 
     (if (eof-object? obj) 
      (reverse all) 
      (loop (cons obj all)))))) 

и вот мой очень упрощенный языковый модуль, это вводит (require racket/base) в каждый Wibble модуль

=== wibble/lang.rkt === 
#lang racket/base 

(require (for-syntax racket/base)) 

(provide (rename-out (wibble-module-begin #%module-begin)) #%app #%datum #%top) 

(define-syntax wibble-module-begin 
    (lambda (stx) 
    (syntax-case stx() 
     ((_ x ...) #`(#%module-begin (require #,(datum->syntax stx 'racket/base)) x ...))))) 

С кода выше, этот Wibble код ' работ, то есть ошибок нет

#lang wibble 
(cons 1 2) 
(cons 3 4) 

но следующий

#lang wibble 
(cons 1 2) 

дает сообщение об ошибке cons: unbound identifier in module in: cons

Действительно, я просто искал объяснения относительно того, что происходит. Я уверен, что эта разница связана с этим из ракеток документации (Ракетка Reference 3.1)

Если одна форма предусмотрена, то она частично разлагается в контекст модуля-начать. Если расширение приводит к #% plain-module-begin, , тогда тело #% plain-module-begin является телом модуля. Если частичное расширение приводит к любой другой примитивной форме, то форма обернута модулем #% - начните с использования лексического контекста модуля body; этот идентификатор должен быть связан начальным импортом модульного пути, , и его расширение должно создать #% plain-module-begin для подачи тела модуля . Наконец, если предоставлено несколько форм, они завернуты в с #% модулем-begin, как в случае, когда одна форма не развернуть до #% plain-module-begin.

, но даже с этим я не понимаю, почему наличие единой формы имеет какое-либо значение, похоже, что это связано со сроками частичного расширения, но я не уверен. Я также не понимаю, почему Racket рассматривает одну форму как особый случай.

Кстати, я могу решить эту проблему с небольшой модификацией к моему читателю

(define (wibble-read-syntax src in) 
    #`(module #,(module-name src) wibble/lang 
     #,@(read-all src in) (void))) 

жесткого кодирования (void) формы означает, что у меня всегда есть более одной формы и Eveything работает.

Извините за длинный пост, я просто ищу какое-то представление о том, как это работает.

ответ

0

Проблема с require (хотя я не уверен, что на 100% понимаю все поведение).

(require X) импортирует привязки от X с лексическим контекстом #'X. #'X здесь есть контекст stx, который является полным #'(module-begin x ...), который не является контекстом, который вы хотите. Вам нужен контекст одного из выражений cons, то есть одного из #'x.

Что-то, как это должно работать:

(define-syntax wibble-module-begin 
    (lambda (stx) 
    (syntax-case stx() 
     [(_) #'(#%module-begin)] 
     [(m x y ...) 
     #`(#%module-begin 
      (require #,(datum->syntax #'x 'racket/base)) 
      x y ...)]))) 

Хотя, как @belph предупредил, что, вероятно, более идиоматических способ сделать то, что вы хотите.

Поведение вашей оригинальной программы, и, как вы интуитивно поняли, скорее всего связано с различной обработкой отдельных и нескольких подформформ, но я считаю, что «рабочий» случай может быть случайным и может быть ошибка в компиляторе ракетки.

+0

Ну, я думаю, у вас это есть. Я не уверен, что когда-нибудь приеду на этот ответ. Синтаксис связан с этим. Благодарю. – john

4

Хорошо, я думаю, что я понял это.

Ваша интуиция верна в том, что проблема заключается в сроках частичного расширения тела модуля одной формы. Внутри вашего файла reader.rkt вы создаете форму (module ...).Как указано в выдержке из вашего вопроса, часть этого раздела обрабатывается специально, так как существует только одна. Давайте посмотрим на отрывок из документации о частичном расширении:

Как частный случай, когда расширение будет иначе добавить #%app, #%datum или #%top идентификатор выражения, и, когда связывание оказывается примитив #%app, #%datum или #%top, тогда расширение останавливается без добавления идентификатора.

Я почти уверен, что частичное расширение, которое происходит в этой точке, что-то делает для идентификатора cons. Это единственная часть, о которой я до сих пор не знаю ... моя кишка говорит мне, что происходит то, что частичное расширение пытается найти привязку для идентификатора cons (так как это первая часть круглых скобок, идентификатор может быть связанный с макросом, который должен быть расширен, поэтому его нужно проверить), но он не может, поэтому он бросает истерику. Обратите внимание, что даже если cons не имеет привязки к фазе 1 (время синтаксиса расширения), расширитель макросов все еще ожидает, что для идентификатора будет привязка фазы 0 (время выполнения) (среди прочего, это помогает расширителю оставаться гигиеничным). Потому что все это частичное расширение происходит с телом вашей формы (module ...) (что сделано до вашей формы (#%module-begin ...), где вы вводите форму (#%require ...)), cons не имеет привязки во время расширения, поэтому расширение, я считаю, терпит неудачу.

Тем не менее, наивное исправление для вашей проблемы переписать wibble-read-syntax следующим образом:

(define (wibble-read-syntax src in) 
    (let* ((read-in (read-all src in)) 
     (in-stx (and (pair? read-in) (car read-in)))) 
    #`(module #,(module-name src) wibble/lang 
     (require #,(datum->syntax in-stx 'racket/base)) 
     #,@read-in)) 

Вы можете удалить (#%require ...) формы от вашего (#%module-begin ...) макроса.

Это не так, на мой взгляд, best способ исправить проблему. В чистоте жесткое кодирование в форме require, как вы сделали в wibble/lang.rkt, будет make Eli Barzilay and co. cry. Гораздо более простой способ сделать то, что вы пытаетесь сделать, это обновив файл lang.rkt на что-то вроде этого:

=== wibble/lang.rkt === 
#lang racket/base 

(require (for-syntax racket/base)) 

(provide (rename-out (wibble-module-begin #%module-begin)) 
     (except-out (all-from-out racket/base) #%module-begin #%app #%datum #%top) 
    #%app #%datum #%top) 

(define-syntax wibble-module-begin 
    (lambda (stx) 
    (syntax-case stx() 
     ((_ x ...) #`(#%module-begin x ...))))) 

Записи в этой конвенции устраняет необходимость в каких-либо жестких закодированные (require ...) форм и предотвращает тонкие ошибки, такие как один из которых вы обнаружили. Если вы смущены, почему это работает, помните, что вы уже предоставили идентификатор #%module-begin, используя этот файл, который впоследствии связан во всех файлах #lang wibble. В принципе, нет ограничений на то, какие идентификаторы вы можете связать таким образом. Если вы хотите продолжить чтение, вот немного бесстыдная самореклама для blog post I wrote немного назад на эту тему.

Надеюсь, я помог.

+0

Спасибо за ответ. К сожалению, жестко закодированное требование было только для меня, чтобы разобраться с проблемой. В реальном случае я бы расширил код wibble, чтобы потребовать формы. Поэтому второе исправление не работает, поскольку модули Racket, которые я хочу импортировать, зависят от написанного кода wibble. Что-то на первом месте вы можете исправить, но это уродливо. Я считаю, что мой лучший выбор - все же полностью исключить одну форму в модульном случае. Я не думаю, что то, что я пытаюсь сделать, настолько необычно, поэтому мне любопытно, как справляются с подобными реализациями. – john

+0

@john Почему бы просто не выполнять какие-либо расширения, чтобы вывести то, что необходимо для того, чтобы вернуть форму '(module ...)' из 'wibble-read-syntax'? Это нецелесообразно для вашего случая использования? – belph