#lang scribble/doc @(require "common.ss" (for-syntax scheme/base) scribble/scheme) @(begin (define-syntax-rule (dbs id) (begin (require (for-label lang/htdp-intermediate)) (define id @scheme[lambda]))) (dbs intm-lambda)) @(define-syntax library ;; R6RS `library' isn't actually a binding, but typeset it as a ;; syntactic form: (make-element-id-transformer (lambda (stx) #'@schemekeywordfont{library}))) @title[#:tag "foundations"]{Modules and Bindings} As an embedded domain-specific language, Scribble follows a long tradition of using Lisp- and Scheme-style macros to implement little languages. In particular, Scribble relies heavily on the Scheme notion of @deftech{syntax objects}@~cite[R6RS], which are fragments of code that have lexical-binding information attached. Besides using syntax objects in the usual way to implement macros, Scribble uses syntax objects to carry lexical information all the way through document rendering. For example, @scr:code-elem|{@scheme[lambda]}| expands to roughly @scheme[(typeset-id #'lambda)], where @scheme[#'lambda] is similar to @scheme['lambda] but produces a syntax object (with its lexical information intact) instead of a symbol. At the same time, many details of Scribble's implementation rely on PLT Scheme extensions to Scheme macros. Continuing the above example, the @scheme[typeset-id] function applies PLT Scheme's @scheme[identifier-label-binding] function to the given syntax object to determine the source module of its binding. The @scheme[typeset-id] function can then construct a cross-reference key based on the identifier and the source module; the documentation for the binding pairs the same identifier and source module to define the target of the cross-reference. A deeper dependence of Scribble on PLT Scheme relates to @hash-lang[] parsing. The @hash-lang[] notation organizes @deftech{reader extensions} of Scheme (i.e., changes to the way that raw text is converted to S-expressions) to allow new forms of surface syntax. The identifier after @hash-lang[] in the original source act as the ``language'' of a module. To parse a @hash-lang[] line, the identifier after @hash-lang[] is used as the name of a library collection that contains a @scheme["lang/reader.ss"] module. The collection's @scheme["lang/reader.ss"] module must export a @schemeidfont{read-syntax} function, which takes an input stream and produces a syntax object. The @scheme["lang/reader.ss"] module for @schememodname[scribble/doc] parses the given input stream in @lit["@"]-notation text mode, and then wraps the result in a @scheme[module] form. For example, @; @code-block|{ #lang scribble/doc @(require scribble/manual) It was a @bold{dark} and @italic{stormy} night. }| @; in a file named @filepath{hello.scrbl} reads as @; @code-block|{ (module hello scribble/doclang doc () "\n" (require scribble/manual) "\n" "It was a " (bold "dark") " and " (italic "stormy") "night." "\n") }| @; where @scheme[doc] is inserted by the @schememodname[scribble/doc] reader as the identifier to export from the module, and the @scheme[()] is a convenience explained below. The @scheme[module] form is PLT Scheme's core module form, and it generalizes the standard @scheme[library] form@~cite[R6RS] to give macros more control in transforming the body of a module. Within a @scheme[module], the first identifier is the relative name of the module, and the second identifier indicates a module to supply initial bindings for the module body. In particular, the initial import of a module is responsible for supplying a @scheme[#%module-begin] macro that is implicitly applied to the entire content of the module. In the case of @schememodname[scribble/doclang], the @scheme[#%module-begin] macro lifts out all import and definitions forms in the body, passes all remaining content to the @scheme[decode] function, and binds the result to an exported @scheme[doc] identifier. Thus, macro expansion converts the @scheme[hello] module to the following: @code-block|{ (module hello scheme/base (require scribble/doclang scribble/manual) (provide doc) (define doc (decode "\n" "\n" "It was a " (bold "dark") " and " (italic "stormy") "night." "\n"))) }| A subtlety in the process of lifting out import and definition forms is that they might not appear directly, but instead appear in the process of macro expansion. For example, @scheme[include-section] expands to a @scheme[require] of the included document plus a reference to the document. The @scheme[#%module-begin] macro of @schememodname[scribble/doclang] therefore relies on a PLT Scheme facility for forcing the expansion of sub-forms. Specifically, @scheme[#%module-begin] uses @scheme[local-expand] to expand each sub-form just far enough to determine whether it is an import form, definition form, or expression. If the sub-form is an import or definition, then @scheme[#%module-begin] suspends further work and lifts out the import or definition immediately; the import or definition can then supply bindings for further expansion of the module body. The need to suspend and continue lifting explains the @scheme[()] inserted in the body of a module by the @schememodname[scribble/doc] reader; @scheme[#%module-begin] uses that position to track the sub-forms that have been expanded already to expressions. Aside from (1) the ability to force the expansion of nested forms and (2) the ability of macros to expand into new imports, macro expansion of a module body is essentially the same as for libraries in the current Scheme standard@~cite[R6RS]. Where the standard allows choice in the separation of phases, we have chosen maximal separation in PLT Scheme, so that compilation and expansion as consistent as possible@~cite[macromod]. That is, bindings and module instantiations needed during the compilation of a module are kept separate from the bindings and instantiations needed when executing a module for rendering. Furthermore, to support the connection between documentation and library bindings, PLT Scheme introduces a new phase that is orthogonal to compile time or run time: the @deftech{label phase level}. As noted in @Secref["code"], a @scheme[for-label] import introduces bindings for documentation without triggering the execution of the imported module. In PLT Scheme, the same identifier can have different bindings in different phases. For example, when documenting the Intermediate Scheme pedagogical language, a document author would like uses of @|intm-lambda| to link to the @|intm-lambda| specification for Intermediate Scheme, while procedures used to implement the document itself will more likely use the full PLT Scheme language, which is a different @scheme[lambda]. The two different uses of @schemeidfont{lambda} are kept straight naturally and automatically by separate bindings in separate phases.