Start a proper preprocessor documentation, with tests included.
(proper "literate testing".) svn: r14112 original commit: 6d31100a8999d34fa595e119fccc029230848b45
This commit is contained in:
parent
763a1a1b7d
commit
36a92072fd
|
@ -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"].}
|
||||
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.}
|
||||
|
||||
@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.}}}}
|
||||
@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.}}
|
||||
|
||||
}
|
||||
|
||||
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:
|
||||
@;--------------------------------------------------------------------
|
||||
@section{Writing Preprocessor Files}
|
||||
|
||||
@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
|
||||
}|
|
||||
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.)
|
||||
|
||||
(Note how @litchar["@;"] is used to avoid empty lines in the output.)
|
||||
@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}
|
||||
|
|
|
@ -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")]))]))
|
||||
|
|
|
@ -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)))))
|
||||
|
||||
)
|
||||
|
|
Loading…
Reference in New Issue
Block a user