Chaining module languages
(require chain-module-begin) | package: chain-module-begin |
This package is experimental. Later versions may break backward-compatibility.
syntax
(chain-module-begin lang . body)
As an example here is the definition for a no-op language, which simply takes a (possibly improper) list of languages to chain, and calls the next one:
(module the-meta-lang racket/base (provide (rename-out [new-#%module-begin #%module-begin])) (require chain-module-begin (for-syntax racket/base syntax/parse)) (define-syntax (new-#%module-begin stx) (syntax-parse stx [(_ {~or next-lang:id (next-lang:id . chain₊)} . body) (define maybe-chain₊ (if (attribute chain₊) `(,#'chain₊) '())) (define new-form `(,#'chain-module-begin ,#'next-lang ,@maybe-chain₊ . ,(transform-body #'body))) (datum->syntax stx new-form stx stx)])) (define-for-syntax (transform-body body) ; identity transformation: body))
This language could then be used as follows:
(module b the-meta-lang typed/racket (define x : Number 123))
Given two other meta-language built in the same way and provided by meta-two and meta-three, it would be possible to chain the three languages as follows:
(module b the-lang (meta-two meta-three . typed/racket) (define x : Number 123))
The chain-module-begin macro produces the following syntax:
(#%plain-module-begin (require lang) (continue . internal-args))
where (continue . internal-args) fully expands (#%module-begin . body), where #%module-begin is the one provided by lang, and produces the following syntax:
(begin . expanded-body)
An extra scope is added to the whole (begin . expanded-body) form, so that a #%require form within the expanded-body may shadow bindings provided by lang, just as require forms normally have the possibility to shadow bindings provided by the #lang language.