From 50f835c4f1473eba1e8e342c27711ba51d0eaef7 Mon Sep 17 00:00:00 2001 From: Matthew Flatt Date: Fri, 11 Dec 2015 10:55:00 -0700 Subject: [PATCH] add `scribble/examples` The `scribble/examples` library provides a cleaner and more modern interface for evaluation compared to `scribble/eval`. --- scribble-doc/scribblings/scribble/eval.scrbl | 302 +++++------------ .../scribblings/scribble/examples.scrbl | 303 ++++++++++++++++++ scribble-doc/scribblings/scribble/plt.scrbl | 2 +- scribble-doc/scribblings/scribble/utils.rkt | 1 - scribble-lib/scribble/eval.rkt | 3 +- scribble-lib/scribble/examples.rkt | 113 +++++++ 6 files changed, 494 insertions(+), 230 deletions(-) create mode 100644 scribble-doc/scribblings/scribble/examples.scrbl create mode 100644 scribble-lib/scribble/examples.rkt diff --git a/scribble-doc/scribblings/scribble/eval.scrbl b/scribble-doc/scribblings/scribble/eval.scrbl index f2832e61..c9fa7249 100644 --- a/scribble-doc/scribblings/scribble/eval.scrbl +++ b/scribble-doc/scribblings/scribble/eval.scrbl @@ -1,172 +1,112 @@ #lang scribble/doc @(require scribble/manual "utils.rkt" - (for-label racket/sandbox + (for-label scribble/eval + racket/sandbox racket/pretty file/convertible racket/serialize)) -@title[#:tag "eval"]{Evaluation and Examples} +@(define-syntax-rule (define-new-examples new-examples) + (begin + (require (for-label scribble/examples)) + (define new-examples @racket[examples]))) +@(define-new-examples new-examples) + + +@title[#:tag "old-eval"]{Legacy Evaluation} + +@defmodule[scribble/eval]{The @racketmodname[scribble/eval] library provides +an older interface to the functionality of @racketmodname[scribble/examples]. +The @racketmodname[scribble/examples] library should be used, instead.} + +In addition to the forms listed below, @racket[scribble/eval] +re-exports several functions from @racket[scribble/examples]: +@racket[make-base-eval] @racket[make-base-eval-factory], +@racket[make-eval-factory], @racket[make-log-based-eval], +@racket[close-eval], and @racket[scribble-eval-handler]. -@defmodule[scribble/eval]{The @racket[scribble/eval] library provides -utilities for evaluating code at document-build time and incorporating -the results in the document, especially to show example uses of -defined procedures and syntax.} @defform/subs[(interaction maybe-options datum ...) - ([maybe-options maybe-eval maybe-escape maybe-disallow-errors] + ([maybe-options maybe-eval maybe-escape maybe-no-errors] [maybe-eval code:blank (code:line #:eval eval-expr)] [maybe-escape code:blank (code:line #:escape escape-id)] - [maybe-disallow-errors code:blank - (code:line #:no-errors? no-errors?-expr)])]{ + [maybe-no-errors code:blank + (code:line #:no-errors? no-errors?-expr)])]{ -Like @racket[racketinput], except that the result for each input -@racket[datum] is shown on the next line. The result is determined by -evaluating the @racket[quote]d form of the @racket[datum] using the -evaluator produced by @racket[eval-expr], if provided. - -The @racket[eval-expr] must produce a sandbox evaluator via -@racket[make-evaluator] or @racket[make-module-evaluator] with the -@racket[sandbox-output] and @racket[sandbox-error-output] parameters -set to @racket['string]. If @racket[eval-expr] is not provided or is -@racket[#f], an evaluator is created using @racket[make-base-eval]. See also -@racket[make-eval-factory]. - -If @racket[no-errors?-expr] is provided and produces a true -value, then when evaluating a @racket[datum] produces an error (and -@racket[datum] does not have an @racket[eval:error] wrapper), an -exception is raised by @racket[interaction]. Otherwise, any exception -produced by a @racket[datum] evaluation is typeset as an error result. - -If the value of @racket[current-print] in the sandbox is changed from -its default value, or if @racket[print-as-expression] in the sandbox -is set to @racket[#f], then each evaluation result is formatted to a -port by applying @racket[(current-print)] to the value; the output -port is set to a pipe that supports specials in the sense of -@racket[write-special], and non-character values written to the port -are used as @tech{content}. Otherwise, when the default -@racket[current-print] is in place, result values are typeset using -@racket[to-element/no-color]. - -Certain patterns in @racket[datum] are treated specially: +Like @|new-examples| from @racketmodname[scribble/examples], except that @itemlist[ - @item{A @racket[datum] of the form - @racket[(@#,indexed-racket[code:line] _code-datum (@#,racketidfont{code:comment} _comment-datum ...))] - is treated as @racket[_code-datum] for evaluation.} + @item{the ``Examples:'' label is always supressed,} -@item{A @racket[datum] of the form - @racket[(@#,indexed-racket[code:line] _code-datum ...)] - evaluates each @racket[_code-datum], but only the last result is used.} + @item{exceptions raised during the evaluation of a @racket[datum] are + always rendered as errors, unless @racket[#:no-errors?] is + specified with a true value; and} - @item{Other uses of @racketidfont{code:comment}, @racketidfont{code:contract}, and - @racketidfont{code:blank} are stripped from each @racket[datum] - before evaluation.} - - @item{A @racket[datum] of the form - @racket[(@#,indexed-racket[eval:error] #,(svar eval-datum))] is - treated like @racket[_eval-datum], but @racket[_eval-datum] is - expected to raise an exception, and an error is shown as the - evaluation's result---even if @racket[#:no-errors? #t] is - specified for the @racket[interactions] form.} - - @item{A @racket[datum] of the form - @racket[(@#,indexed-racket[eval:alts] #,(svar show-datum) #,(svar eval-datum))] - is treated as @svar[show-datum] for typesetting and @svar[eval-datum] for evaluation.} - - @item{A @racket[datum] of the form - @racket[(@#,indexed-racket[eval:check] #,(svar eval-datum) #,(svar expect-datum))] - is treated like @racket[_eval-datum], but @svar[check-datum] is also - evaluated, and an error is raised if they are not @racket[equal?].} - - @item{A @racket[datum] of the form - @racket[(@#,indexed-racket[eval:result] _content-expr _out-expr _err-expr)] - involves no sandboxed evaluation; instead, the @tech{content} result of @racket[_content-expr] is used as the - typeset form of the result, @racket[_out-expr] is treated as output printed - by the expression, and @racket[_err-expr] is error output printed by the - expression. The @racket[_out-expr] and/or @racket[_err-expr] can be omitted, - in which case they default to empty strings. - - Normally, @racketidfont{eval:result} - is used in the second part of an @racketidfont{eval:alts} combination.} - - @item{A @racket[datum] of the form - @racket[(@#,indexed-racket[eval:results] _content-list-expr _out-expr _err-expr)] - is treated like an @racketidfont{eval:result} form, except that @racket[_content-list-expr] - should produce a list of @tech{content} for multiple results of evaluation. As - with @racketidfont{eval:result}, @racket[_out-expr] and @racket[_err-expr] are optional.} - - @item{A @racket[datum] of the form - @racket[(@#,indexed-racket[eval:no-prompt] _eval-datum ...)] - is treated like @racket[(@#,racket[code:line] _eval-datum ...)], but no prompt is shown before - the group, and a blank line is added before and after - @(svar eval-datum) and its result.} + @item{the @racket[#:once] option is never implicitly used.} ] -As an example, - -@codeblock|{ -#lang scribble/manual -@(require racket/sandbox - scribble/eval) -@(define my-evaluator - (parameterize ([sandbox-output 'string] - [sandbox-error-output 'string]) - (make-evaluator 'typed/racket/base))) -@interaction[#:eval my-evaluator - - (: my-sqr (Real -> Real)) - (define (my-sqr x) - (* x x)) - (my-sqr 42)] -}| - -uses an evaluator whose language is @racketmodname[typed/racket/base]. - @history[#:changed "1.14" @elem{Added @racket[#:no-errors?], @racket[eval:no-prompt], and @racket[eval:error], and changed @racket[code:line] to support multiple @racket[_datum]s.}]} + @defform[(interaction0 maybe-options datum ...)]{ Like @racket[interaction], but without insetting the code via -@racket[nested].} +@racket[nested]. + +Use @|new-examples| with @racket[#:no-indent], instead.} + @defform[(interaction/no-prompt maybe-eval maybe-escape datum)]{ Like @racket[interaction], but does not render each @racket[datum] with a prompt. -} + + Use @|new-examples| with @racket[#:no-prompt], instead.} + @defform[(interaction-eval maybe-eval datum)]{ Like @racket[interaction], evaluates the @racket[quote]d form of @racket[datum], but returns the empty string and does not catch -exceptions (so @racket[eval:error] has no effect).} +exceptions (so @racket[eval:error] has no effect). + +Use @|new-examples| with @racket[#:hidden], instead.} @defform[(interaction-eval-show maybe-eval datum)]{ Like @racket[interaction-eval], but produces an element representing -the printed form of the evaluation result.} +the printed form of the evaluation result. + +Use @|new-examples| with @racket[#:result-only], instead.} @defform[(racketblock+eval maybe-eval maybe-escape datum ...)]{ -Combines @racket[racketblock] and @racket[interaction-eval].} +Combines @racket[racketblock] and @racket[interaction-eval]. + +Use @|new-examples| with @racket[#:no-result], instead.} @defform[(racketblock0+eval maybe-eval maybe-escape datum ...)]{ -Combines @racket[racketblock0] and @racket[interaction-eval].} +Combines @racket[racketblock0] and @racket[interaction-eval]. + +Use @|new-examples| with @racket[#:no-result] and +@racket[#:no-indent], instead.} @defform[(racketmod+eval maybe-eval maybe-escape name datum ...)]{ -Combines @racket[racketmod] and @racket[interaction-eval].} +Combines @racket[racketmod] and @racket[interaction-eval]. + +Use @|new-examples| with @racket[#:lang], instead.} @defform[(def+int maybe-options defn-datum expr-datum ...)]{ @@ -178,31 +118,45 @@ space is inserted before the @racket[expr-datum]s.} @defform[(defs+int maybe-options (defn-datum ...) expr-datum ...)]{ -Like @racket[def+int], but for multiple leading definitions.} +Like @racket[def+int], but for multiple leading definitions. + +Use @|new-examples| with @racket[eval:no-prompt] wrappers on +definitions, instead.} @defform[(examples maybe-options datum ...)]{ -Like @racket[interaction], but with an ``Examples:'' label prefixed.} +Like @racket[interaction], but with an ``Examples:'' label prefixed. + +Use @|new-examples| from @racketmodname[scribble/examples], instead.} @defform[(examples* label-expr maybe-options datum ...)]{ Like @racket[examples], but using the result of @racket[label-expr] in -place of the default ``Examples:'' label.} +place of the default ``Examples:'' label. + +Use @|new-examples| from @racketmodname[scribble/examples] with the +@racket[#:label] option, instead.} @defform[(defexamples maybe-options datum ...)]{ Like @racket[examples], but each definition using @racket[define] or @racket[define-struct] among the @racket[datum]s is typeset without a -prompt, and with line of space after it.} +prompt, and with line of space after it. + +Use @|new-examples| with @racket[eval:no-prompt] wrappers on +definitions, instead.} @defform[(defexamples* label-expr maybe-options datum ...)]{ Like @racket[defexamples], but using the result of @racket[label-expr] in -place of the default ``Examples:'' label.} +place of the default ``Examples:'' label. + +Use @|new-examples| with the @racket[#:label] option and +@racket[eval:no-prompt] wrappers on definitions, instead.} @defproc*[([(as-examples [b block?]) block?] @@ -213,114 +167,6 @@ place of the default ``Examples:'' label.} Adds an ``examples'' label to @racket[b], using either a default label or the given @racket[label].} - -@defproc[(make-base-eval [#:pretty-print? pretty-print? any/c #t] - [#:lang lang - (or/c module-path? - (list/c 'special symbol?) - (cons/c 'begin list?)) - '(begin)] - [input-program any/c] ...) - (any/c . -> . any)]{ - -Creates an evaluator using @racket[(make-evaluator 'racket/base #:lang lang input-program ...)], -setting sandbox parameters to disable limits, setting the outputs to -@racket['string], and not adding extra security guards. - -If @racket[pretty-print?] is true, the sandbox's printer is set to -@racket[pretty-print-handler]. In that case, values that are convertible -in the sense of @racket[convertible?] are printed using @racket[write-special], -except that values that are serializable in the sense of @racket[serializable?] -are serialized for tranfers from inside the sandbox to outside (which can avoid -pulling code and support from the sandboxed environment into the document-rendering -environment). - -@history[#:changed "1.6" @elem{Changed treatment of convertible values that are - serializable.}]} - - -@defproc[(make-base-eval-factory [mod-paths (listof module-path?)] - [#:pretty-print? pretty-print? any/c #t] - [#:lang lang - (or/c module-path? - (list/c 'special symbol?) - (cons/c 'begin list?)) - '(begin)]) - (-> (any/c . -> . any))]{ - -Produces a function that is like @racket[make-base-eval], except that -each module in @racket[mod-paths] is attached to the evaluator's -namespace. The modules are loaded and instantiated once (when the -returned @racket[make-base-eval]-like function is called the first -time) and then attached to each evaluator that is created.} - - -@defproc[(make-eval-factory [mod-paths (listof module-path?)] - [#:pretty-print? pretty-print? any/c #t] - [#:lang lang - (or/c module-path? - (list/c 'special symbol?) - (cons/c 'begin list?)) - '(begin)]) - (-> (any/c . -> . any))]{ - -Like @racket[make-base-eval-factory], but each module in @racket[mod-paths] is -also required into the top-level environment for each generated evaluator.} - - -@defproc[(make-log-based-eval [log-file path-string?] - [mode (or/c 'record 'replay)]) - (-> any/c any)]{ - -Creates an evaluator (like @racket[make-base-eval]) that uses a log -file to either record or replay evaluations. - -If @racket[mode] is @racket['record], the evaluator records every -interaction to @racket[log-file], replacing @racket[log-file] if it -already exists. The result of each interaction must be -@seclink["serialization" #:doc '(lib -"scribblings/reference/reference.scrbl")]{serializable}. - -If @racket[mode] is @racket['replay], the evaluator uses the contents -of @racket[log-file] instead of actually performing evaluatings. For -each interaction, it compares the term to evaluate against the next -interaction recorded in @racket[log-file]. If the term matches, the -stored result is returned; if not, the evaluator raises an error -indicating that it is out of sync with @racket[log-file]. - -Use @racket[make-log-based-eval] to document libraries when the -embedded examples rely on external features that may not be present or -appropriately configured on all machines. - -@history[#:added "1.12"]{} -} - - -@defproc[(close-eval [eval (any/c . -> . any)]) (one-of/c "")]{ - -Shuts down an evaluator produced by @racket[make-base-eval]. Use -@racket[close-eval] when garbage collection cannot otherwise reclaim -an evaluator (e.g., because it is defined in a module body).} - - -@defparam[scribble-eval-handler handler - ((any/c . -> . any) any/c boolean? . -> . any)]{ - -A parameter that serves as a hook for evaluation. The evaluator to use -is supplied as the first argument to the parameter's value, and the -second argument is the form to evaluate. The last argument is -@racket[#t] if exceptions are being captured (to display exception -results), @racket[#f] otherwise.} - -@defparam[scribble-exn->string handler (-> (or/c exn? any/c) string?)]{ - A parameter that controls how exceptions are rendered by - @racket[interaction]. Defaults to - @racketblock[(λ (e) - (if (exn? e) - (exn-message e) - (format "uncaught exception: ~s" e)))] -} - @defform[(with-eval-preserve-source-locations expr ...)]{ By default, the evaluation forms provided by this module, such as @@ -329,4 +175,6 @@ locations from the expressions they evaluate. Within a @racket[with-eval-preserve-source-locations] form, the source locations are preserved. This can be useful for documenting forms that depend on source locations, such as Redex's typesetting macros. -} + +Use @|new-examples| with the @racket[#:preserve-source-locations] +option, instead.} diff --git a/scribble-doc/scribblings/scribble/examples.scrbl b/scribble-doc/scribblings/scribble/examples.scrbl new file mode 100644 index 00000000..76362a33 --- /dev/null +++ b/scribble-doc/scribblings/scribble/examples.scrbl @@ -0,0 +1,303 @@ +#lang scribble/doc +@(require scribble/manual + "utils.rkt" + (for-label scribble/examples + racket/sandbox + racket/pretty + file/convertible + racket/serialize)) + +@title[#:tag "eval"]{Evaluation and Examples} + +@defmodule[scribble/examples #:use-sources (scribble/eval scribble/examples)]{The +@racket[scribble/examples] library provides +utilities for evaluating code at document-build time and incorporating +the results in the document, especially to show example uses of +defined procedures and syntax.} + +@history[#:added "1.14"] + +@defform/subs[(examples option ... datum ...) + ([option (code:line #:eval eval-expr) + #:once + (code:line #:escape escape-id) + (code:line #:label label-expr) + #:hidden + #:result-only + #:no-inset + #:no-prompt + #:preserve-source-locations + #:no-result + (code:line #:lang language-name)])]{ + +Similar to @racket[racketinput], except that the result for each input +@racket[datum] is shown on the next line. The result is determined by +evaluating the @racket[quote]d form of the @racket[datum] using the +evaluator produced by @racket[eval-expr]. + +Each keyword option can be provided at most once: + +@itemlist[ + + @item{@racket[#:eval eval-expr] --- Specifies an evaluator, where + @racket[eval-expr] must produce either @racket[#f] or a sandbox + evaluator via @racket[make-evaluator] or + @racket[make-module-evaluator] with the @racket[sandbox-output] + and @racket[sandbox-error-output] parameters set to + @racket['string]. If @racket[eval-expr] is not provided or is + @racket[#f], an evaluator is created using + @racket[make-base-eval]. See also @racket[make-eval-factory].} + + @item{@racket[#:once] --- Specifies that the evaluator should be + closed with @racket[close-eval] after the all @racket[datum]s + are evaluated. The @racket[#:once] option is assumed if + @racket[#:eval] is not specified.} + + @item{@racket[@#,racket[#:escape] escape-id] --- Specifies an escape + identifier, as in @racket[racketblock].} + + @item{@racket[#:label label-expr] --- Specifies a label for the + examples, which defaults to ``Example:'' or ``Examples:'' + (depending on the number of @racket[datum]s). A @racket[#f] + value for @racket[label-expr] suppresses the label.} + + @item{@racket[#:hidden] --- Specifies that the @racket[datum]s and + results should not be typeset, but instead evaluated for a + side-effect, and disables @racket[eval:error]. Typically, this + option is combined with @racket[#:eval] to configure an + evaluator.} + + @item{@racket[#:result-only] --- Specifies that the @racket[datum] + results should be typeset, but not the @racket[datum]s + themselves, and implies @racket[#:label #f].} + + @item{@racket[#:no-result] --- Implies @racket[#:no-prompt] and + @racket[#:label #f], specifies that no results should be + typeset, and disables @racket[eval:error].} + + @item{@racket[#:no-inset] --- Specifies that the examples should be + typeset without indentation, i.e., like @racket[racketinput0] + instead of @racket[racketinput].} + + @item{@racket[#:no-prompt] --- Specifies that each examples should + be typeset without a leading prompt, i.e., like + @racket[racketblock] instead of @racket[racketinput]. A prompt + can be omitted from a specific @racket[_datum] by wrapping it + with @racket[eval:no-prompt].} + + @item{@racket[#:preserve-source-locations] --- Specifies that the + original source locations for each @racket[datum] should be + preserved for evaluation. Preserving source locations can be + useful for documenting forms that depend on source locations, + such as Redex's typesetting macros.} + + @item{@racket[#:lang] --- Implies @racket[#:no-result] prefixes the + typeset @racket[datum] sequence with a @hash-lang[] line using + @racket[language-name] as the module's language.} + +] + +Certain patterns in @racket[datum] are treated specially: + +@itemlist[ + + @item{A @racket[datum] of the form + @racket[(@#,indexed-racket[code:line] _code-datum (@#,racketidfont{code:comment} _comment-datum ...))] + is treated as @racket[_code-datum] for evaluation.} + +@item{A @racket[datum] of the form + @racket[(@#,indexed-racket[code:line] _code-datum ...)] + evaluates each @racket[_code-datum], but only the last result is used.} + + @item{Other uses of @racketidfont{code:comment}, @racketidfont{code:contract}, and + @racketidfont{code:blank} are stripped from each @racket[datum] + before evaluation.} + + @item{A @racket[datum] of the form + @racket[(@#,indexed-racket[eval:error] #,(svar eval-datum))] is + treated like @racket[_eval-datum], but @racket[_eval-datum] is + expected to raise an exception, and an error is shown as the + evaluation's result---even if @racket[#:no-errors? #t] is + specified for the @racket[interactions] form.} + + @item{A @racket[datum] of the form + @racket[(@#,indexed-racket[eval:alts] #,(svar show-datum) #,(svar eval-datum))] + is treated as @svar[show-datum] for typesetting and @svar[eval-datum] for evaluation.} + + @item{A @racket[datum] of the form + @racket[(@#,indexed-racket[eval:check] #,(svar eval-datum) #,(svar expect-datum))] + is treated like @racket[_eval-datum], but @svar[check-datum] is also + evaluated, and an error is raised if they are not @racket[equal?].} + + @item{A @racket[datum] of the form + @racket[(@#,indexed-racket[eval:result] _content-expr _out-expr _err-expr)] + involves no sandboxed evaluation; instead, the @tech{content} result of @racket[_content-expr] is used as the + typeset form of the result, @racket[_out-expr] is treated as output printed + by the expression, and @racket[_err-expr] is error output printed by the + expression. The @racket[_out-expr] and/or @racket[_err-expr] can be omitted, + in which case they default to empty strings. + + Normally, @racketidfont{eval:result} + is used in the second part of an @racketidfont{eval:alts} combination.} + + @item{A @racket[datum] of the form + @racket[(@#,indexed-racket[eval:results] _content-list-expr _out-expr _err-expr)] + is treated like an @racketidfont{eval:result} form, except that @racket[_content-list-expr] + should produce a list of @tech{content} for multiple results of evaluation. As + with @racketidfont{eval:result}, @racket[_out-expr] and @racket[_err-expr] are optional.} + + @item{A @racket[datum] of the form + @racket[(@#,indexed-racket[eval:no-prompt] _eval-datum ...)] + is treated like @racket[(@#,racket[code:line] _eval-datum ...)], but no prompt is shown before + the group, and a blank line is added before and after + @(svar eval-datum) and its result.} + +] + +A @racket[datum] cannot be a keyword. To specify a @racket[datum] that +is a keyword, wrap it with @racket[code:line]. + +When evaluating a @racket[datum] produces an error (and @racket[datum] +does not have an @racket[eval:error] wrapper), an exception is raised +by @racket[examples]. + +If the value of @racket[current-print] in the sandbox is changed from +its default value, or if @racket[print-as-expression] in the sandbox +is set to @racket[#f], then each evaluation result is formatted to a +port by applying @racket[(current-print)] to the value; the output +port is set to a pipe that supports specials in the sense of +@racket[write-special], and non-character values written to the port +are used as @tech{content}. Otherwise, when the default +@racket[current-print] is in place, result values are typeset using +@racket[to-element/no-color]. + +As an example, + +@codeblock|{ +#lang scribble/manual +@(require racket/sandbox + scribble/eval) +@(define my-evaluator + (parameterize ([sandbox-output 'string] + [sandbox-error-output 'string]) + (make-evaluator 'typed/racket/base))) + +@examples[#:eval my-evaluator + (: my-sqr (Real -> Real)) + (define (my-sqr x) + (* x x)) + (my-sqr 42)] +}| + +uses an evaluator whose language is @racketmodname[typed/racket/base].} + + +@defproc[(make-base-eval [#:pretty-print? pretty-print? any/c #t] + [#:lang lang + (or/c module-path? + (list/c 'special symbol?) + (cons/c 'begin list?)) + '(begin)] + [input-program any/c] ...) + (any/c . -> . any)]{ + +Creates an evaluator using @racket[(make-evaluator 'racket/base #:lang lang input-program ...)], +setting sandbox parameters to disable limits, setting the outputs to +@racket['string], and not adding extra security guards. + +If @racket[pretty-print?] is true, the sandbox's printer is set to +@racket[pretty-print-handler]. In that case, values that are convertible +in the sense of @racket[convertible?] are printed using @racket[write-special], +except that values that are serializable in the sense of @racket[serializable?] +are serialized for tranfers from inside the sandbox to outside (which can avoid +pulling code and support from the sandboxed environment into the document-rendering +environment). + +@history[#:changed "1.6" @elem{Changed treatment of convertible values that are + serializable.}]} + + +@defproc[(make-base-eval-factory [mod-paths (listof module-path?)] + [#:pretty-print? pretty-print? any/c #t] + [#:lang lang + (or/c module-path? + (list/c 'special symbol?) + (cons/c 'begin list?)) + '(begin)]) + (-> (any/c . -> . any))]{ + +Produces a function that is like @racket[make-base-eval], except that +each module in @racket[mod-paths] is attached to the evaluator's +namespace. The modules are loaded and instantiated once (when the +returned @racket[make-base-eval]-like function is called the first +time) and then attached to each evaluator that is created.} + + +@defproc[(make-eval-factory [mod-paths (listof module-path?)] + [#:pretty-print? pretty-print? any/c #t] + [#:lang lang + (or/c module-path? + (list/c 'special symbol?) + (cons/c 'begin list?)) + '(begin)]) + (-> (any/c . -> . any))]{ + +Like @racket[make-base-eval-factory], but each module in @racket[mod-paths] is +also required into the top-level environment for each generated evaluator.} + + +@defproc[(make-log-based-eval [log-file path-string?] + [mode (or/c 'record 'replay)]) + (-> any/c any)]{ + +Creates an evaluator (like @racket[make-base-eval]) that uses a log +file to either record or replay evaluations. + +If @racket[mode] is @racket['record], the evaluator records every +interaction to @racket[log-file], replacing @racket[log-file] if it +already exists. The result of each interaction must be +@seclink["serialization" #:doc '(lib +"scribblings/reference/reference.scrbl")]{serializable}. + +If @racket[mode] is @racket['replay], the evaluator uses the contents +of @racket[log-file] instead of actually performing evaluatings. For +each interaction, it compares the term to evaluate against the next +interaction recorded in @racket[log-file]. If the term matches, the +stored result is returned; if not, the evaluator raises an error +indicating that it is out of sync with @racket[log-file]. + +Use @racket[make-log-based-eval] to document libraries when the +embedded examples rely on external features that may not be present or +appropriately configured on all machines. + +@history[#:added "1.12"]} + + +@defproc[(close-eval [eval (any/c . -> . any)]) (one-of/c "")]{ + +Shuts down an evaluator produced by @racket[make-base-eval]. Use +@racket[close-eval] when garbage collection cannot otherwise reclaim +an evaluator (e.g., because it is defined in a module body).} + + +@defparam[scribble-eval-handler handler + ((any/c . -> . any) any/c boolean? . -> . any)]{ + +A parameter that serves as a hook for evaluation. The evaluator to use +is supplied as the first argument to the parameter's value, and the +second argument is the form to evaluate. The last argument is +@racket[#t] if exceptions are being captured (to display exception +results), @racket[#f] otherwise.} + +@defparam[scribble-exn->string handler (-> (or/c exn? any/c) string?)]{ + A parameter that controls how exceptions are rendered by + @racket[interaction]. Defaults to + @racketblock[(λ (e) + (if (exn? e) + (exn-message e) + (format "uncaught exception: ~s" e)))] +} + +@; ------------------------------------------------------------ + +@include-section["eval.scrbl"] diff --git a/scribble-doc/scribblings/scribble/plt.scrbl b/scribble-doc/scribblings/scribble/plt.scrbl index 403b94ae..1aa71735 100644 --- a/scribble-doc/scribblings/scribble/plt.scrbl +++ b/scribble-doc/scribblings/scribble/plt.scrbl @@ -19,7 +19,7 @@ relevant libraries and APIs in detail. @include-section["style.scrbl"] @include-section["manual.scrbl"] @include-section["scheme.scrbl"] -@include-section["eval.scrbl"] +@include-section["examples.scrbl"] @include-section["srcdoc.scrbl"] @include-section["bnf.scrbl"] @include-section["compat.scrbl"] diff --git a/scribble-doc/scribblings/scribble/utils.rkt b/scribble-doc/scribblings/scribble/utils.rkt index f1acb71d..ba37ecf6 100644 --- a/scribble-doc/scribblings/scribble/utils.rkt +++ b/scribble-doc/scribblings/scribble/utils.rkt @@ -23,7 +23,6 @@ scribble/racket scribble/html-properties scribble/latex-properties - scribble/eval scribble/bnf) (provide scribble-examples litchar/lines doc-render-examples) diff --git a/scribble-lib/scribble/eval.rkt b/scribble-lib/scribble/eval.rkt index d320d24b..dc70f15f 100644 --- a/scribble-lib/scribble/eval.rkt +++ b/scribble-lib/scribble/eval.rkt @@ -315,7 +315,8 @@ render+output) (lambda (str) (if (eval-results? str) - (list (map formatted-result (eval-results-contents str)) + (list #f + (map formatted-result (eval-results-contents str)) (eval-results-out str) (eval-results-err str)) (extract-to-evaluate diff --git a/scribble-lib/scribble/examples.rkt b/scribble-lib/scribble/examples.rkt new file mode 100644 index 00000000..986d112b --- /dev/null +++ b/scribble-lib/scribble/examples.rkt @@ -0,0 +1,113 @@ +#lang racket/base + +(require "eval.rkt" + (only-in "struct.rkt" make-paragraph) + (for-syntax racket/base + syntax/parse)) + +(provide examples + + ;; Re-exports: + + make-base-eval + make-base-eval-factory + make-eval-factory + close-eval + + scribble-exn->string + scribble-eval-handler + make-log-based-eval) + +(define example-title + (make-paragraph (list "Example:"))) +(define examples-title + (make-paragraph (list "Examples:"))) + +(define-syntax (examples stx) + (syntax-parse stx + [(_ (~or (~optional (~seq #:eval eval:expr)) + (~optional (~and #:once once-kw)) + (~optional (~seq #:escape escape:id)) + (~optional (~seq #:label title:expr)) + (~optional (~and #:no-inset no-inset-kw)) + (~optional (~and #:no-prompt no-prompt-kw)) + (~optional (~and #:result-only no-form-kw)) + (~optional (~and #:no-result block-kw)) + (~optional (~and #:hidden no-result-kw)) + (~optional (~and #:preserve-source-locations preserve-srclocs-kw)) + (~optional (~seq #:lang module-name))) + ... + form:expr ...) + (define once? (or (attribute once-kw) + (not (attribute eval)))) + (define eval-stx (or (attribute eval) #'(make-base-eval))) + (define base-form + (with-syntax ([eval (if once? #'once-eval eval-stx)] + [escape (or (attribute escape) #'unsyntax)]) + (cond + [(attribute block-kw) + (when (attribute module-name) + (raise-syntax-error #f "#:block and #:module are mutually exclusive" stx)) + (cond + [(attribute no-inset-kw) + (syntax/loc stx + (racketblock0+eval #:eval eval #:escape escape + form ...))] + [else + (syntax/loc stx + (racketblock+eval #:eval eval #:escape escape + form ...))])] + [(attribute module-name) + (syntax/loc stx + (racketmod+eval #:eval eval #:escape escape module-name + form ...))] + [(attribute no-result-kw) + (syntax/loc stx + (interaction-eval #:eval eval form ...))] + [(attribute no-form-kw) + (syntax/loc stx + (interaction-eval-show #:eval eval form ...))] + [(attribute no-prompt-kw) + (syntax/loc stx + (interaction/no-prompt #:eval eval #:escape escape #:no-errors? #t + form ...))] + [(attribute no-inset-kw) + (syntax/loc stx + (interaction0 #:eval eval #:escape escape #:no-errors? #t + form ...))] + [else + (syntax/loc stx + (interaction #:eval eval #:escape escape #:no-errors? #t + form ...))]))) + (define srcloced-form + (cond + [(attribute preserve-srclocs-kw) + (with-syntax ([base-form base-form]) + (syntax/loc stx + (with-eval-preserve-source-locations base-form)))] + [else base-form])) + (define examples-form + (cond + [(or (attribute title) + (not (or (attribute block-kw) + (attribute module-name) + (attribute no-result-kw) + (attribute no-form-kw)))) + (with-syntax ([srcloced-form srcloced-form] + [title (or (attribute title) + (cond + [(= 1 (length (syntax->list #'(form ...)))) + #'example-title] + [else #'examples-title]))]) + (syntax/loc stx (as-examples title srcloced-form)))] + [else + srcloced-form])) + (if once? + (with-syntax ([eval eval-stx] + [examples-form examples-form]) + (syntax/loc stx + (let ([once-eval eval]) + (begin0 + examples-form + (close-eval eval))))) + examples-form)]))