Literate programming in style
#lang scribble/lp2/manual | package: scribble-lp2-manual |
The scribble/lp2 language with scribble/manual style. You must call title for the new style to be applied, but really, what program document doesn’t have a title?
1 About this document
This document is written in scribble/lp2/manual and thus serves as both test and example for the language. This document also embeds an implementation of the language, which the main sources use to expose the language.
If it renders at all, then it successfully integrates with Scribble: The Racket Documentation Tool.
If it renders in the style of scribble/manual, then it successfuly applies the style.
If it can be run as a racket program and reports 2, then it successfully integrates with scribble/lp2. See <main-module>.
This chunk is the main-module that produces 2 when this code is run with racket <source>.scrbl.
2 Building scribble/lp2/manual
Let’s walkthrough how the language is built as an exercise in using the language.
First, we have to figure out how to apply the scribble/manual style to an existing scribble/lp2 document. Since title takes #:style to control the document’s style, we’ll use that with manual-doc-style to get what we want:
#lang scribble/lp2 @(require scribble/manual) @title[#:style manual-doc-style]{My program}
Thankfully, scribble/manual provides manual-doc-style, which we know by looking at Manual Rendering Style.
This works, but we have to do it for any scribble/lp2 program we want to render with style. Let’s make a language to fix this.
2.1 Reader
All #langs need a reader, so here’s ours:
(module reader syntax/module-reader ; Name of the expander module scribble/lp2/manual #:read read-inside #:read-syntax read-syntax-inside #:whole-body-readers? #t #:language-info (scribble-base-language-info) #:info (scribble-base-info) (require scribble/reader (only-in scribble/base/reader scribble-base-info scribble-base-language-info)) ; cf. https://github.com/racket/scribble/blob/master/scribble-lib/scribble/lp2.rkt)
We base it on the existing scribble readers, such as that of scribble/lp2, but we use our own expander module.
2.2 Expander
Next we provide a module implementing the expander:
(module expander racket/base <expander-provides> <expander-impl>)
The expander needs to provide base bindings for the language, including a #%module-begin syntax. We’ll re-use bindings from scribble/lp/lang/lang2, since that what scribble/lp2 uses, but we’ll supplement with our own #%module-begin:
(provide (except-out (all-from-out scribble/lp/lang/lang2) lp2:mb) (rename-out [mb #%module-begin]))
Our #%module-begin needs to force title to use manual-doc-style, then invoke #%module-begin from scribble/lp/lang/lang2:
(require (rename-in scribble/lp/lang/lang2 [#%module-begin lp2:mb]) syntax/parse/define) (define-syntax-parse-rule (mb body ...) (lp2:mb (define title ; Avoid polluting the module-body with require's by using dynamic-require. ; ; But it's odd that we still have to bind title, since we know title will ; be bound by the (require scribble/manual) in the expansion of lp2:mb. ; Perhaps hygiene is in the way? (require scribble/manual) in this module ; doesn't work either, though I suspect the strip-context call of being ; at fault at that point. (let ([curryr (dynamic-require 'racket/function 'curryr)] [title (dynamic-require 'scribble/manual 'title)]) (curryr title #:style manual-doc-style))) body ...))
3 Putting it all together
That’s it! The actual implementation uses files that provide exactly this code. Because this is a literate program, we could require its submodules to get a reader or expander. If the actual language implementation did that, though, there would be a circular dependency! In order to understand the source of this document, we consult the language definition, which requires this document to compile, etc., ad infinitum. So we won’t do that, though it is a neat trick (e.g., pollen-tfl implements part of its codebase as a literal program).
Put the code in the following order: