commit 36a92072fd5ba41d5f069141effe9264cf799ab2
parent 763a1a1b7d654f956d668a420d6ded763e85e8e5
Author: Eli Barzilay <eli@racket-lang.org>
Date: Sun, 15 Mar 2009 22:05:52 +0000
Start a proper preprocessor documentation, with tests included.
(proper "literate testing".)
svn: r14112
original commit: 6d31100a8999d34fa595e119fccc029230848b45
Diffstat:
3 files changed, 122 insertions(+), 37 deletions(-)
diff --git a/collects/scribblings/scribble/preprocessor.scrbl b/collects/scribblings/scribble/preprocessor.scrbl
@@ -1,7 +1,7 @@
#lang scribble/doc
-@(require scribble/manual
- "utils.ss"
+@(require scribble/manual scribble/struct "utils.ss"
(for-label scheme/base))
+@initialize-tests
@title[#:tag "preprocessor"]{Text Preprocessor}
@@ -12,35 +12,106 @@ changes that make it suitable as a preprocessor language:
@itemize{
@item{It uses @scheme[read-syntax-inside] to read the body of the
- module, similar to @secref["docreader"].}
-
- @item{It has a custom printer (@scheme[current-print]) that displays
- all values. The printer is also installed as the
- @scheme[port-display-handler] so it can be used through
- @scheme[display] as well as @litchar{~a} in format strings.
- The printer displays most values (as is usual for
- @scheme[display]), except for
- @itemize{@item{@scheme[void] and @scheme[#f] are not
- displayed,}
- @item{pairs are displayed recursively (just their
- contents, no parentheses),}
- @item{promises are forced, thunks are invoked.}}}}
+ module, similar to @secref["docreader"]. This means that by
+ default, all text is read in as Scheme strings; and
+ @seclink["reader"]|{@-forms}| can be used to use Scheme
+ functions and expression escapes.}
-}
-
-This means that to write a text file that has scheme code, you simply
-write it as a module in the @scheme[scribble/text] language, and run
-it through @exec{mzscheme}. Here is a sample file:
-
-@verbatim[#:indent 2]|{
- #lang scribble/text
- @(define (angled . body) (list "<" body ">"))@;
- @(define (shout . body) @angled[(map string-upcase body)])@;
- blah @angled{blah @shout{blah} blah} blah
-}|
+ @item{Values of expressions are printed with a custom
+ @scheme[output] function. This function displays most values
+ in a similar way to @scheme[display], except that it is more
+ convenient for a preprocessor output.}}
-(Note how @litchar["@;"] is used to avoid empty lines in the output.)
+}
+@;--------------------------------------------------------------------
+@section{Writing Preprocessor Files}
+
+The combination of the two features makes text in files in the
+@scheme[scribble/text] language be read as strings, which get printed
+out when the module is @scheme[require]d, for example, when a file is
+given as an argument to @exec{mzscheme}. (In these example the left
+part shows the source input, and the right part the printed result.)
+
+@example|-{#lang scribble/text
+ Programming languages should
+ be designed not by piling
+ feature on top of feature, but
+ blah blah blah.
+ ---***---
+ Programming languages should
+ be designed not by piling
+ feature on top of feature, but
+ blah blah blah.}-|
+
+Using @seclink["reader"]|{@-forms}| we can define and use Scheme
+functions.
+
+@example|-{#lang scribble/text
+ @(require scheme/list)
+ @(define Foo "Preprocessing")
+ @(define (3x . x)
+ (add-between (list x x x) " "))
+ @Foo languages should
+ be designed not by piling
+ feature on top of feature, but
+ @3x{blah}.
+ ---***---
+ Preprocessing languages should
+ be designed not by piling
+ feature on top of feature, but
+ blah blah blah.}-|
+
+As demonstrated in this case, the @scheme[output] function simply
+scans nested list structures recursively, which makes them convenient
+for function results. In addition, @scheme[output] prints most values
+similarly to @scheme[display] \- a notable exception are void and
+false values which cause no output to appear. This can be used for
+convenient conditional output.
+
+@example|-{#lang scribble/text
+ @(define (errors n)
+ (list n
+ " error"
+ (and (not (= n 1)) "s")))
+ You have @errors[3] in your code,
+ I fixed @errors[1].
+ ---***---
+ You have 3 errors in your code,
+ I fixed 1 error.}-|
+
+Using the scribble @seclink["reader"]|{@-forms}| syntax, you can write
+functions more conveniently too.
+
+@example|-{#lang scribble/text
+ @(define (errors n)
+ @list{@n error@;
+ @and[(not (= n 1))]{s}})
+ You have @errors[3] in your code,
+ I fixed @errors[1].
+ ---***---
+ You have 3 errors in your code,
+ I fixed 1 error.}-|
+
+Following the details of the scribble reader, you may notice that in
+these examples there are newline strings after each definition, yet
+they do not show in the output. To make it easier to write
+definitions, newlines after definitions and indentation spaces before
+them are ignored.
+
+@example|-{#lang scribble/text
+
+ @(define (plural n)
+ (unless (= n 1) "s"))
+
+ @(define (errors n)
+ @list{@n error@plural[n]})
+
+ You have @errors[3] in your code,
+ I fixed @errors[1].
+ ---***---
+ You have 3 errors in your code,
+ I fixed 1 error.}-|
@;--------------------------------------------------------------------
@section{Using External Files}
diff --git a/collects/scribblings/scribble/utils.ss b/collects/scribblings/scribble/utils.ss
@@ -102,9 +102,9 @@
(require scheme/list (for-syntax scheme/base scheme/list))
-(define max-textsample-width 32)
+(define max-textsample-width 35)
-(define (textsample-verbatim-boxes 1st 2nd more)
+(define (textsample-verbatim-boxes line 1st 2nd more)
(define (split str) (regexp-split #rx"\n" str))
(define strs1 (split 1st))
(define strs2 (split 2nd))
@@ -128,7 +128,8 @@
[s (in-list s)])
(max m (string-length s))))])
(if (negative? d)
- (error 'textsample-verbatim-boxes "left box too wide")
+ (error 'textsample-verbatim-boxes
+ "left box too wide for sample at line ~s" line)
(hspace d))))
(values
(make-table '([alignment right left] [valignment top top])
@@ -141,8 +142,8 @@
filenames strsm)))
box2))
-(define (textsample 1st 2nd . more)
- (define-values (box1 box2) (textsample-verbatim-boxes 1st 2nd more))
+(define (textsample line 1st 2nd . more)
+ (define-values (box1 box2) (textsample-verbatim-boxes line 1st 2nd more))
(make-table '([alignment left left left] [valignment center center center])
(list (map as-flow (list box1 (make-paragraph '(nbsp rarr nbsp)) box2)))))
@@ -189,8 +190,8 @@
[((file text ...) ...) files]
[add-to-tests (cadr tests-ids)])
(syntax/loc stx
- (let ([t (list (string-append i/o ...) ...
+ (let ([t (list line (string-append i/o ...) ...
(cons file (string-append text ...)) ...)])
- (add-to-tests (cons line t))
+ (add-to-tests t)
(apply textsample t)))))]
[_ (raise-syntax-error #f "no separator found in example text")]))]))
diff --git a/collects/tests/scribble/main.ss b/collects/tests/scribble/main.ss
@@ -1,8 +1,10 @@
#lang scheme/base
-(require tests/eli-tester scribble/text/syntax-utils scheme/runtime-path)
+(require tests/eli-tester scribble/text/syntax-utils scheme/runtime-path
+ scheme/sandbox (lib "scribblings/scribble/preprocessor.scrbl"))
(define-runtime-path text-dir "text")
+(define-runtime-path this-dir ".")
(test
@@ -78,7 +80,7 @@
(f 3 #:> "]" #:< "["))
=> '(1 ("<" 1 ">") ("[" 2 ">") ("[" 3 "]"))
- ;; preprocessor functionality
+ ;; preprocessor tests
(parameterize ([current-directory text-dir])
(for ([ifile (map path->string (directory-list))]
#:when (and (file-exists? ifile)
@@ -90,5 +92,16 @@
(parameterize ([current-output-port o])
(dynamic-require (path->complete-path ifile) #f))
(test (get-output-bytes o) => expected)))
+ ;; preprocessor tests that are part of the documentation
+ (parameterize ([current-directory this-dir]
+ [sandbox-output 'string]
+ [sandbox-error-output current-output-port])
+ (define (text-test line in out . more)
+ (define e (make-module-evaluator in))
+ (test
+ #:failure-message (format "preprocessor test failure at line ~s" line)
+ (equal? (get-output e) out)))
+ (call-with-trusted-sandbox-configuration
+ (lambda () (for ([t (in-list (tests))]) (apply text-test t)))))
)