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
|
#lang scribble/doc
|
||||||
@(require scribble/manual
|
@(require scribble/manual scribble/struct "utils.ss"
|
||||||
"utils.ss"
|
|
||||||
(for-label scheme/base))
|
(for-label scheme/base))
|
||||||
|
@initialize-tests
|
||||||
|
|
||||||
@title[#:tag "preprocessor"]{Text Preprocessor}
|
@title[#:tag "preprocessor"]{Text Preprocessor}
|
||||||
|
|
||||||
|
@ -12,35 +12,106 @@ changes that make it suitable as a preprocessor language:
|
||||||
@itemize{
|
@itemize{
|
||||||
|
|
||||||
@item{It uses @scheme[read-syntax-inside] to read the body of the
|
@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
|
@item{Values of expressions are printed with a custom
|
||||||
all values. The printer is also installed as the
|
@scheme[output] function. This function displays most values
|
||||||
@scheme[port-display-handler] so it can be used through
|
in a similar way to @scheme[display], except that it is more
|
||||||
@scheme[display] as well as @litchar{~a} in format strings.
|
convenient for a preprocessor output.}}
|
||||||
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.}}}}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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
|
@section{Writing Preprocessor Files}
|
||||||
it through @exec{mzscheme}. Here is a sample file:
|
|
||||||
|
|
||||||
@verbatim[#:indent 2]|{
|
The combination of the two features makes text in files in the
|
||||||
#lang scribble/text
|
@scheme[scribble/text] language be read as strings, which get printed
|
||||||
@(define (angled . body) (list "<" body ">"))@;
|
out when the module is @scheme[require]d, for example, when a file is
|
||||||
@(define (shout . body) @angled[(map string-upcase body)])@;
|
given as an argument to @exec{mzscheme}. (In these example the left
|
||||||
blah @angled{blah @shout{blah} blah} blah
|
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}
|
@section{Using External Files}
|
||||||
|
|
|
@ -102,9 +102,9 @@
|
||||||
|
|
||||||
(require scheme/list (for-syntax scheme/base scheme/list))
|
(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 (split str) (regexp-split #rx"\n" str))
|
||||||
(define strs1 (split 1st))
|
(define strs1 (split 1st))
|
||||||
(define strs2 (split 2nd))
|
(define strs2 (split 2nd))
|
||||||
|
@ -128,7 +128,8 @@
|
||||||
[s (in-list s)])
|
[s (in-list s)])
|
||||||
(max m (string-length s))))])
|
(max m (string-length s))))])
|
||||||
(if (negative? d)
|
(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))))
|
(hspace d))))
|
||||||
(values
|
(values
|
||||||
(make-table '([alignment right left] [valignment top top])
|
(make-table '([alignment right left] [valignment top top])
|
||||||
|
@ -141,8 +142,8 @@
|
||||||
filenames strsm)))
|
filenames strsm)))
|
||||||
box2))
|
box2))
|
||||||
|
|
||||||
(define (textsample 1st 2nd . more)
|
(define (textsample line 1st 2nd . more)
|
||||||
(define-values (box1 box2) (textsample-verbatim-boxes 1st 2nd more))
|
(define-values (box1 box2) (textsample-verbatim-boxes line 1st 2nd more))
|
||||||
(make-table '([alignment left left left] [valignment center center center])
|
(make-table '([alignment left left left] [valignment center center center])
|
||||||
(list (map as-flow (list box1 (make-paragraph '(nbsp rarr nbsp)) box2)))))
|
(list (map as-flow (list box1 (make-paragraph '(nbsp rarr nbsp)) box2)))))
|
||||||
|
|
||||||
|
@ -189,8 +190,8 @@
|
||||||
[((file text ...) ...) files]
|
[((file text ...) ...) files]
|
||||||
[add-to-tests (cadr tests-ids)])
|
[add-to-tests (cadr tests-ids)])
|
||||||
(syntax/loc stx
|
(syntax/loc stx
|
||||||
(let ([t (list (string-append i/o ...) ...
|
(let ([t (list line (string-append i/o ...) ...
|
||||||
(cons file (string-append text ...)) ...)])
|
(cons file (string-append text ...)) ...)])
|
||||||
(add-to-tests (cons line t))
|
(add-to-tests t)
|
||||||
(apply textsample t)))))]
|
(apply textsample t)))))]
|
||||||
[_ (raise-syntax-error #f "no separator found in example text")]))]))
|
[_ (raise-syntax-error #f "no separator found in example text")]))]))
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
#lang scheme/base
|
#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 text-dir "text")
|
||||||
|
(define-runtime-path this-dir ".")
|
||||||
|
|
||||||
(test
|
(test
|
||||||
|
|
||||||
|
@ -78,7 +80,7 @@
|
||||||
(f 3 #:> "]" #:< "["))
|
(f 3 #:> "]" #:< "["))
|
||||||
=> '(1 ("<" 1 ">") ("[" 2 ">") ("[" 3 "]"))
|
=> '(1 ("<" 1 ">") ("[" 2 ">") ("[" 3 "]"))
|
||||||
|
|
||||||
;; preprocessor functionality
|
;; preprocessor tests
|
||||||
(parameterize ([current-directory text-dir])
|
(parameterize ([current-directory text-dir])
|
||||||
(for ([ifile (map path->string (directory-list))]
|
(for ([ifile (map path->string (directory-list))]
|
||||||
#:when (and (file-exists? ifile)
|
#:when (and (file-exists? ifile)
|
||||||
|
@ -90,5 +92,16 @@
|
||||||
(parameterize ([current-output-port o])
|
(parameterize ([current-output-port o])
|
||||||
(dynamic-require (path->complete-path ifile) #f))
|
(dynamic-require (path->complete-path ifile) #f))
|
||||||
(test (get-output-bytes o) => expected)))
|
(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