scribble/manual: make `defmodule' more flexible

original commit: eb924d75b638f10780fad46126072311e54d88ed
This commit is contained in:
Matthew Flatt 2013-04-30 07:56:09 -06:00
parent 9f4dba7886
commit e2578af0ef
4 changed files with 331 additions and 149 deletions

View File

@ -6,7 +6,8 @@
"manual-ex.rkt"
"manual-style.rkt"
"manual-scheme.rkt"
(for-syntax scheme/base)
(for-syntax scheme/base
syntax/parse)
(for-label scheme/base))
(provide defmodule defmodule*
@ -39,91 +40,134 @@
(define spacer (hspace 1))
(begin-for-syntax
(define-splicing-syntax-class link-target?-kw
#:description "#:link-target? keyword"
(pattern (~seq #:link-target? expr))
(pattern (~seq)
#:with expr #'#t)))
(define-syntax (defmodule stx)
(syntax-parse stx
[(_ (~or (~seq #:require-form req)
(~seq))
(~or (~seq #:multi (name2 ...))
name)
(~or (~optional (~seq #:link-target? link-target-expr)
#:defaults ([link-target-expr #'#t]))
(~optional (~seq #:use-sources (pname ...)))
(~optional (~seq #:module-paths (modpath ...)))
(~optional (~and #:no-declare no-declare))
(~optional (~or (~and #:lang language)
(~and #:reader readr))))
...
. content)
(with-syntax ([(name2 ...) (if (attribute name)
#'(name)
#'(name2 ...))]
[(pname ...) (if (attribute pname)
#'(pname ...)
#'())])
(with-syntax ([(decl-exp ...)
(if (attribute no-declare)
#'()
(if (attribute modpath)
#'((declare-exporting modpath ... #:use-sources (pname ...)))
#'((declare-exporting name2 ... #:use-sources (pname ...)))))]
[kind (cond
[(attribute language) #'#t]
[(attribute readr) #''reader]
[else #'#f])]
[modpaths (if (attribute modpath)
#'(list (racketmodname modpath) ...)
#'#f)]
[req (if (attribute req)
#'req
#'(racket require))]
[(show-name ...)
(if (attribute modpath)
#'(name2 ...)
#'((racketmodname name2) ...))])
#'(begin
decl-exp ...
(*defmodule (list show-name ...)
modpaths
link-target-expr
kind
(list . content)
req))))]))
;; ----------------------------------------
;; old forms for backward compatibility:
(define-syntax defmodule*/no-declare
(syntax-rules ()
[(_ #:require-form req (name ...) . content)
(*defmodule (list (racketmodname name) ...)
#f
#f
(list . content)
req)]
(defmodule #:require-form req
#:names (name ...)
#:no-declare
. content)]
[(_ (name ...) . content)
(defmodule*/no-declare #:require-form (racket require) (name ...)
(defmodule #:multi (name ...)
#:no-declare
. content)]))
(define-syntax defmodule*
(syntax-rules ()
[(_ #:require-form req (name ...) #:use-sources (pname ...) . content)
(begin (declare-exporting name ... #:use-sources (pname ...))
(defmodule*/no-declare #:require-form req (name ...) . content))]
[(_ #:require-form req (name ...) . content)
(defmodule* #:require-form req (name ...) #:use-sources () . content)]
[(_ (name ...) #:use-sources (pname ...) . content)
(defmodule* #:require-form (racket require) (name ...) #:use-sources (pname ...)
. content)]
[(_ (name ...) . content)
(defmodule* (name ...) #:use-sources () . content)]))
(define-syntax defmodule
(syntax-rules ()
[(_ #:require-form req name . content)
(defmodule* #:require-form req (name) . content)]
[(_ name . content)
(defmodule* (name) . content)]))
[(_ #:require-form req (name ...) . options+content)
(defmodule #:require-form req #:multi (name ...)
. options+content)]
[(_ (name ...) . options+content)
(defmodule #:multi (name ...) . options+content)]))
(define-syntax defmodulelang*/no-declare
(syntax-rules ()
[(_ (lang ...) #:module-paths (modpath ...) . content)
(*defmodule (list lang ...)
(list (racketmodname modpath) ...)
#t (list . content) #f)]
[(_ (lang ...) . content)
(*defmodule (list (racketmodname lang) ...)
#f #t (list . content) #f)]))
[(_ (lang ...) . options+content)
(defmodule #:multi (lang ...)
#:lang
#:no-declare
. options+content)]))
(define-syntax defmodulelang*
(syntax-rules ()
[(_ (name ...) #:module-paths (modpath ...)
#:use-sources (pname ...)
. content)
(begin (declare-exporting modpath ... #:use-sources (pname ...))
(defmodulelang*/no-declare (name ...)
#:module-paths (modpath ...)
. content))]
[(_ (name ...) #:module-paths (modpath ...) . content)
(defmodulelang* (name ...)
#:module-paths (modpath ...)
#:use-sources () . content)]
[(_ (name ...) #:use-sources (pname ...) . content)
(defmodulelang* ((racketmodname name) ...)
#:module-paths (name ...)
#:use-sources (pname ...) . content)]
[(_ (name ...) . content)
(defmodulelang* (name ...) #:use-sources () . content)]))
[(_ (name ...) . options+content)
(defmodule #:multi (name ...)
#:lang
. options+content)]))
(define-syntax defmodulelang
(syntax-rules ()
[(_ lang #:module-path modpath . content)
(defmodulelang* (lang) #:module-paths (modpath) . content)]
[(_ lang . content)
(defmodulelang* (lang) . content)]))
[(_ lang #:module-path modpath . options+content)
(defmodule lang
#:module-paths (modpath)
#:lang
. options+content)]
[(_ lang . options+content)
(defmodule lang
#:lang
. options+content)]))
(define-syntax-rule (defmodulereader*/no-declare (lang ...) . content)
(*defmodule (list (racketmodname lang) ...)
#f 'reader (list . content) #f))
(define-syntax-rule (defmodulereader*/no-declare (lang ...) . options+content)
(defmodule #:multi (lang ...)
#:reader
#:no-declare
. options+content))
(define-syntax defmodulereader*
(syntax-rules ()
[(_ (name ...) #:use-sources (pname ...) . content)
(begin (declare-exporting name ... #:use-sources (pname ...))
(defmodulereader*/no-declare (name ...) . content))]
[(_ (name ...) . content)
(defmodulereader* (name ...) #:use-sources () . content)]))
[(_ (name ...) . options+content)
(defmodule #:multi (name ...)
#:reader
. options+content)]))
(define-syntax-rule (defmodulereader lang . content)
(defmodulereader* (lang) . content))
(define-syntax-rule (defmodulereader lang . options+content)
(defmodule lang
#:reader
. options+content))
(define (*defmodule names modpaths lang content req)
;; ----------------------------------------
(define (*defmodule names modpaths link-target? lang content req)
(let ([modpaths (or modpaths names)])
(make-splice
(cons
@ -131,6 +175,9 @@
"defmodule"
(map
(lambda (name modpath)
(define modname (if link-target?
(make-defracketmodname name modpath)
name))
(list
(make-flow
(list
@ -139,21 +186,24 @@
spacer
(case lang
[(#f)
(list (racket (#,req #,(make-defracketmodname name modpath))))]
(list (racket (#,req #,modname)))]
[(#t)
(list (hash-lang) spacer (make-defracketmodname name modpath))]
(list (hash-lang) spacer modname)]
[(reader)
(list (racketmetafont "#reader") spacer (make-defracketmodname name modpath))]
(list (racketmetafont "#reader") spacer modname)]
[(just-lang)
(list (hash-lang) spacer (make-defracketmodname name modpath))])))))))
(list (hash-lang) spacer modname)]
[else (error 'defmodule "unknown mode: ~e" lang)])))))))
names
modpaths))
(append (map (lambda (modpath)
(make-part-tag-decl
(intern-taglet
`(mod-path ,(datum-intern-literal
(element->string modpath))))))
modpaths)
(append (if link-target?
(map (lambda (modpath)
(make-part-tag-decl
(intern-taglet
`(mod-path ,(datum-intern-literal
(element->string modpath))))))
modpaths)
null)
(flow-paragraphs (decode-flow content)))))))
(define the-module-path-index-desc (make-module-path-index-desc))

View File

@ -518,87 +518,90 @@ corresponding @racketidfont{racket...} binding.}
@; ------------------------------------------------------------------------
@section[#:tag "doc-modules"]{Documenting Modules}
@defform/subs[(defmodule maybe-req id maybe-sources pre-flow ...)
@defform/subs[(defmodule maybe-req one-or-multi option ... pre-flow ...)
([maybe-req code:blank
(code:line #:require-form expr)]
[maybe-sources code:blank
(code:line #:use-sources (mod-path ...))])]{
(code:line #:require-form content-expr)]
[one-or-multi module-spec
(code:line #:multi (module-spec ...+))]
[module-spec module-path
content-expr]
[option (code:line #:module-paths (module-path ...))
#:no-declare
(code:line #:use-sources (src-module-path ...))
(code:line #:link-target? link-target?-expr)
#:lang
#:reader])]{
Produces a sequence of flow elements (encaptured in a @racket[splice])
to start the documentation for a module that can be @racket[require]d
using the path @racket[id]. The @tech{decode}d @racket[pre-flow]s
introduce the module, but need not include all of the module content.
Produces a sequence of flow elements (in a @racket[splice])
to start the documentation for a module---or for multiple modules, if
the @racket[#:multi] form is used.
Besides generating text, this form expands to a use of
@racket[declare-exporting] with @racket[id]; the
@racket[#:use-sources] clause, if provided, is propagated to
@racket[declare-exporting]. Consequently, @racket[defmodule] should be
used at most once in a section, though it can be shadowed with
@racket[defmodule]s in sub-sections.
Each documented module specified as either a @racket[module-path] (in
the sense of @racket[require]), in which case the module path is
typeset using @racket[racketmodname], or by a
@racket[content-expr]. The latter case is triggered by the presence of
a @racket[#:module-paths] clause, which provides a plain
@racket[module-path] for each @racket[module-spec], and the plain
@racket[module-path] is used for cross-referencing.
If a @racket[#:require-form] clause is provided, the given expression
produces an element to use instead of @racket[require] for
the declaration of the module. This is useful to suggest a different
way of accessing the module instead of through @racket[require].
If a @racket[#:require-form] clause is provided and if @racket[#:lang]
and @racket[#:reader] are not provided, the given expression produces
content to use instead of @racket[require] for the declaration of the
module. The @racket[#:require-form] clause is useful to suggest a
different way of accessing the module instead of through
@racket[require].
Hyperlinks created by @racket[racketmodname] are associated with the
enclosing section, rather than the local @racket[id] text.}
Besides generating text, unless @racket[#:no-declare] appears as an
option, this form expands to a use of @racket[declare-exporting] with
@racket[module-path]s; the @racket[#:use-sources] clause, if provided,
is propagated to @racket[declare-exporting]. Consequently,
@racket[defmodule] should be used at most once in a section without
@racket[#:no-declare], though it can be shadowed with
@racket[defmodule]s in sub-sections. Use @racket[#:no-declare] form
when you want to provide a more specific list of modules (e.g., to
name both a specific module and one that combines several modules) via
your own @racket[declare-exporting] declaration
Unless @racket[#:link-target?] is specified with an expression that
produces a true value, then the @racket[module-path]s are also
declared as link targets though a @racket[part-tag-decl] (which means
that the @racket[defmodule] form must appear before any
sub-parts). These link targets are referenced via
@racket[racketmodname], which thus points to the enclosing section,
rather than the individual @racket[module-path]s.
If @racket[#:lang] is provided as an option, then the module name is
shown after @hash-lang[] (instead of in a @racket[require] form) to
indicate that the @racket[module-path]s are suitable for use by either
@racket[require] or @hash-lang[]. If the module path for
@racket[require] is syntactically different from the @hash-lang[]
form, use @racket[#:module-paths] to provide the @racket[require]
variant (and make each @racket[module-spec] a @racket[content-expr]).
If @racket[#:reader] is provided, then the module name is shown after
@racketmetafont{#reader} to indicate that the module path is intended
for use as a reader module.
Each @racket[option] form can appear at most once, and @racket[#:lang]
and @racket[#:reader] are mutually exclusive.
The @tech{decode}d @racket[pre-flow]s introduce the module, but need
not include all of the module content.}
@defform*[[(defmodulelang id maybe-sources pre-flow ...)
(defmodulelang content-expr #:module-paths (mod-path ...)
maybe-sources pre-flow ...)]]{
Like @racket[defmodule], but documents @racket[id] as a module path
suitable for use by either @racket[require] or @hash-lang[]. If the
module path for @racket[require] is syntactically different from the
@hash-lang[] form, use the @racket[#:module-paths] to provide them
separately.}
@defform[(defmodulereader id maybe-sources pre-flow ...)]{
Like @racket[defmodule], but documents @racket[id] as a module path
suitable for use with @racketmetafont{#reader}.}
@deftogether[(
@defform[(defmodule* maybe-req (id ...+) maybe-sources pre-flow ...)]
@defform*[[(defmodulelang* (id ...+) maybe-sources pre-flow ...)
(defmodulelang* (content-expr ...+) #:module-paths (mod-path ...+)
maybe-sources pre-flow ...)]]
@defform[(defmodulereader* (id ...+) maybe-sources pre-flow ...)]
)]{
Like @racket[defmodule], etc., but introduces multiple module paths instead
of just one.}
@deftogether[(
@defform[(defmodule*/no-declare maybe-req (id ...) pre-flow ...)]
@defform*[[(defmodulelang*/no-declare (id ...) pre-flow ...)
(defmodulelang*/no-declare (content-expr ...)
#:module-paths (mod-path ...+) pre-flow ...)]]
@defform[(defmodulereader*/no-declare (id ...) pre-flow ...)]
)]{
Like @racket[defmodule*], etc., but without expanding to
@racket[declare-exporting]. Use this form when you want to provide a
more specific list of modules (e.g., to name both a specific module
and one that combines several modules) via your own
@racket[declare-exporting] declaration.}
@defform/subs[(declare-exporting mod-path ... maybe-sources)
@defform/subs[(declare-exporting module-path ... maybe-sources)
([maybe-sources code:blank
(code:line #:use-sources (mod-path ...))])]{
(code:line #:use-sources (module-path ...))])]{
Associates the @racket[mod-path]s to all bindings defined within the
Associates the @racket[module-path]s to all bindings defined within the
enclosing section, except as overridden by other
@racket[declare-exporting] declarations in nested sub-sections. The
list of @racket[mod-path]s before @racket[#:use-sources] is shown, for
list of @racket[module-path]s before @racket[#:use-sources] is shown, for
example, when the user hovers the mouse over one of the bindings
defined within the section.
More significantly, the first @racket[mod-path] before
@racket[#:use-sources] plus the @racket[mod-path]s after
More significantly, the first @racket[module-path] before
@racket[#:use-sources] plus the @racket[module-path]s after
@racket[#:use-sources] determine the binding that is documented by
each @racket[defform], @racket[defproc], or similar form within the
section that contains the @racket[declare-exporting] declaration:
@ -607,15 +610,15 @@ section that contains the @racket[declare-exporting] declaration:
@item{If no @racket[#:use-sources] clause is supplied, then the
documentation applies to the given name as exported by the first
@racket[mod-path].}
@racket[module-path].}
@item{If @racket[#:use-sources] @racket[mod-path]s are supplied, then
they are tried in order before the first @racket[mod-path]. The
@racket[mod-path] that provides an export with the same
@item{If @racket[#:use-sources] @racket[module-path]s are supplied, then
they are tried in order before the first @racket[module-path]. The
@racket[module-path] that provides an export with the same
symbolic name and @racket[free-label-identifier=?] to the given
name is used as the documented binding. This binding is assumed
to be the same as the identifier as exported by the first
@racket[mod-path] in the @racket[declare-exporting]
@racket[module-path] in the @racket[declare-exporting]
declaration.}
]
@ -651,11 +654,11 @@ documentation for @racketmodname[racket/base] (unless a re-export has
its own documentation, which would override the automatic connection
when searching for documentation).
The initial @racket[mod-path]s sequence can be empty if
@racket[mod-path]s are given with @racket[#:use-sources]. In that
The initial @racket[module-path]s sequence can be empty if
@racket[module-path]s are given with @racket[#:use-sources]. In that
case, the rendered documentation never reports an exporting module for
identifiers that are documented within the section, but the
@racket[mod-path]s in @racket[#:use-sources] provide a binding context
@racket[module-path]s in @racket[#:use-sources] provide a binding context
for connecting (via hyperlinks) definitions and uses of identifiers.
The @racket[declare-exporting] form should be used no more than once
@ -671,6 +674,31 @@ sub-sections.}
}
@defform*[[(defmodulelang one-or-multi maybe-sources option ... pre-flow ...)
(defmodulelang one-or-multi #:module-path module-path
option ... pre-flow ...)]]{
Equivalent to @racket[defmodule] with @racket[#:lang]. The
@racket[#:module-path module-path] is provided, it is converted to
@racket[#:module-paths (module-path)].}
@defform[(defmodulereader one-or-multi option ... pre-flow ...)]{
Equivalent to @racket[defmodule] with @racket[#:reader].}
@deftogether[(
@defform[(defmodule* maybe-req (module-spec ...+) option ... pre-flow ...)]
@defform[(defmodulelang* (module-spec ...+) option ... pre-flow ...)]
@defform[(defmodulereader* (module-spec ...+) option ... pre-flow ...)]
)]{
Equivalent to @racket[defmodule] variants with @racket[#:multi].}
@deftogether[(
@defform[(defmodule*/no-declare maybe-req (module-spec ...) option ... pre-flow ...)]
@defform[(defmodulelang*/no-declare (module-spec ...) option ... pre-flow ...)]
@defform[(defmodulereader*/no-declare (module-spec ...) option ... pre-flow ...)]
)]{
Equivalent to @racket[defmodule] variants @racket[#:no-declare].}
@; ------------------------------------------------------------------------
@section[#:tag "doc-forms"]{Documenting Forms, Functions, Structure Types, and Values}

View File

@ -65,4 +65,48 @@
@defstruct*[#:link-target? #f pn ([x real?] [y real?]) #:extra-constructor-name pt]{A structure type with extra name, again.}
@defstruct[#:link-target? #f pt ([x real?] [y real?]) #:mutable]{A mutable structure type with extra name, again.}
@defstruct[#:link-target? #f pt ([x real?] [y real?]) #:mutable]{A mutable structure type with extra name, again.}
@defmodule["manual-ex0.rkt" #:no-declare #:link-target? #f]
@defmodule["manual-ex0.rkt" #:lang #:no-declare #:link-target? #f]
@defmodule["manual-ex0.rkt" #:reader #:no-declare #:link-target? #f]
@section{Sub2}
@defmodule["manual-ex2.rkt" #:no-declare]
@section{Sub2a}
@defmodule*/no-declare[("manual-ex2a.rkt")]
@section{Sub3}
@defmodule["manual-ex3.rkt" #:lang #:no-declare]
@section{Sub3a}
@defmodulelang*/no-declare[("manual-ex3a.rkt")]
@section{Sub4-5}
@defmodule[#:multi ("manual-ex4.rkt" "manual-ex5.rkt")]
@section{Sub4a-5a}
@defmodule*[("manual-ex4a.rkt" "manual-ex5a.rkt")]
@section{Sub6}
@defmodule[#:require-form (racket load) "manual-ex6.rkt"]
@section{Sub6a}
@defmodule*[#:require-form (racket load) ("manual-ex6a.rkt")]
@section{Sub7}
@defmodule["manual-ex7.rkt" #:use-sources (racket/base)]
@section{Sub7a}
@defmodule*[("manual-ex7a.rkt") #:use-sources (racket/base)]
@section{Sub8}
@defmodule["manual-ex8.rkt" #:reader]
@section{Sub8a}
@defmodulereader["manual-ex8a.rkt"]
@section{Sub8b}
@defmodulereader*[("manual-ex8b.rkt")]

View File

@ -165,3 +165,63 @@ A structure type with extra name, again.
  y : real?
A mutable structure type with extra name, again.
 (require "manual-ex0.rkt")
 #lang "manual-ex0.rkt"
 #reader "manual-ex0.rkt"
1. Sub2
 (require "manual-ex2.rkt")
2. Sub2a
 (require "manual-ex2a.rkt")
3. Sub3
 #lang "manual-ex3.rkt"
4. Sub3a
 #lang "manual-ex3a.rkt"
5. Sub4-5
 (require "manual-ex4.rkt")
 (require "manual-ex5.rkt")
6. Sub4a-5a
 (require "manual-ex4a.rkt")
 (require "manual-ex5a.rkt")
7. Sub6
 (load "manual-ex6.rkt")
8. Sub6a
 (load "manual-ex6a.rkt")
9. Sub7
 (require "manual-ex7.rkt")
10. Sub7a
 (require "manual-ex7a.rkt")
11. Sub8
 #reader "manual-ex8.rkt"
12. Sub8a
 #reader "manual-ex8a.rkt"
13. Sub8b
 #reader "manual-ex8b.rkt"