Writing code is fun, writing documentation is not. Since comments are obligatory in the code, it would be nice, if documentation could be generated automatically from these comments. This is exactly what this module does: It creates modules, documentation and tests from so called premodules, which differ from modules insofar, that there is
- no license - no export-clause - no documentation routine
Instead, those texts are inserted automatically into the final module (provided, a license file is supplied and referenced in a parameter) as well as into the final documentation, using special comment clauses #|[ and ]|# each on a line by itself
Note, that #| and |# delimit multiline comments in Chicken, so the spezial clauses above are consistent with chicken syntax.
A standardized routine comment looks like this
#|[ (name . args) ... --- type --- doc ... ]|#
where type is one of procedure, macro or parameter and is enclosed in at least three hyphens on each side
Note, that this comment is placed immediately before the routine's implemenatation, so if the implementation changes signature, the documentation will probably change too. Hence the procedurally created documenation will always be up to date.
Note also, that only exported routines will be commented like this, so the export clause can be generated automatically.
Other standardized comments begin with at least three semicolons and end with at least three semicolons. For example, the body of a premodule starts with
- ;;;;;;;;;;; module loops ;;;;;;;;;;;
and ends with the start of the internal and exported tests
- ;;;;;;;;;;;;;;;;;;; tests ;;;;;;;;;;;;;;;;;;;;
Everything before the module-line is a global Scheme comment and should be encoded as a comment, it will be extended with a copyright notice.
Everything after the test-line with the closing parenthesis is test code, some of it internal, some exported.
Exported tests are implemented with define-checks, internal tests with one of the ppp macros or check frome the simple-tests egg. The former go into the automatically generated run.scm and the documentation files, the latter are only invoked in the process of coding and ignored in the generated code.
Besides the documentation procedure, premodules, there are only three exported procedures, premodule->module, premodule->tests and premodule->docs.
- premodules symprocedure
the former returns the list of the two exported symbols, and the latter documentation of such a symbol.
- premodule->module pre-file module-fileprocedure
writes the module file from the premodule file.
- premodule->tests pre-file test-fileprocedure
writes the test file from the premodule file.
- premodule->docs pre-file doc-fileprocedure
writes the documentation file from the premodule file.
The following is a premodule version of my simple-loops egg. It's called preloops.scm
#|[ some simple loop macros ]|# ;;;;;;;;;;;; module loops ;;;;;;;;;;; (import scheme (chicken base)) #|[ (do-times i upto xpr ....) --- macro --- loops a fixed number of times execute xpr .... for i from 0 to upto ]|# (define-syntax do-times (syntax-rules () ((_ i upto xpr0 xpr1 ...) (let ((n upto)) (let loop ((i 0)) (unless (>= i n) xpr0 xpr1 ... (loop (+ i 1)))))))) #|[ (do-list i lst xpr ....) --- macro --- execute xpr .... for i in lst ]|# (define-syntax do-list (syntax-rules () ((_ i lst xpr xpr1 ...) (let loop ((sublst lst)) (if (not (null? sublst)) (let ((i (car sublst))) xpr xpr1 ... (loop (cdr sublst)))))))) #|[ (do-for var (start stop step) xpr ....) --- macro --- do xpr .... for var in [start stop[ with steps (default 1) ]|# (define-syntax do-for (syntax-rules () ((_ var (start stop step) xpr xpr1 ...) (let ((%stop stop)) (let loop ((var start)) (unless (>= var %stop) xpr xpr1 ... (loop (+ step var)))))) ((_ var (start stop) xpr . xprs) (do-for var (start stop 1) xpr . xprs)))) #|[ (do-while test? xpr ....) --- macro --- execute xpr .... while test? is true ]|# (define-syntax do-while (syntax-rules () ((_ test? xpr xpr1 ...) (let loop () (if test? (begin xpr xpr1 ... (loop))))))) #|[ (do-until test? xpr ....) --- macro --- execute xpr .... while test? is false ]|# (define-syntax do-until (syntax-rules () ((_ test? xpr xpr1 ...) (let loop () (if (not test?) (begin xpr xpr1 ... (loop))))))) #|[ (do-forever xpr ....) --- macro --- executes body xpr .... until exit is called. The macro is unhygienic on purpose, it exports the exit symbol behind the scene. So it can not be defined with syntax-rules ]|# (define-syntax do-forever (ir-macro-transformer (lambda (form inject compare?) (let ((xpr (cadr form)) (xprs (cddr form))) `(call-with-current-continuation (lambda (,(inject 'exit)) (let loop () ,xpr ,@xprs (loop)))))))) ;;;;;;;;;;;;;;;;;;;; tests ;;;;;;;;;;;;;;;;;;;; ;; to be exported (import simple-tests) ;; to be skipped (ppp (car '(0 1 2 3)) ) ;; to be skipped (check ((lst '(0 1 2 3))) lst '(0 1 2 3) (car lst) 0 (cadr lst) 1 (cddr lst) '(2 3) ) ;; to be exported (define-checks (dos verbose?) (let ((lst '())) (do-for i (1 65 i) (set! lst (cons i lst))) (reverse lst)) '(1 2 4 8 16 32 64) (let ((n 3) (lst '())) (do-forever (if (zero? n) (exit lst)) (set! lst (cons 'a lst)) (set! n (- n 1)))) '(a a a) (let ((lst '())) (do-list i '(1 2 3) (set! lst (cons i lst))) lst) '(3 2 1) (let ((lst '())) (do-times i (+ 2 3) (set! lst (cons i lst))) lst) '(4 3 2 1 0) (let ((n 4) (lst '())) (do-until (> 0 n) (set! lst (cons n lst)) (set! n (- n 1))) lst) '(0 1 2 3 4) (let ((n 4) (lst '())) (do-while (<= 0 n) (set! lst (cons n lst)) (set! n (- n 1))) lst) '(0 1 2 3 4) )
Note, that this is a normal Scheme file, you can run it and look at the result of the internal and exported tests.
The following three calls will write the desired files, loops.scm, run.scm and loops. The only thing you have to do is to copy the files to the appropriate positions. Of course, you can edit these files, if the need arises.
(premodule->module "preloops.scm" "loops.scm") (premodule->tests "preloops.scm" "run.scm") (premodule->docs "preloops.scm" "loops")
But it's much easyer to compile the three new files premodule2module.scm, premodule2tests.scm and premodule2docs.scm with csc, copy the results to a directory in the path and run them with
premoudle2module preloops.scm loops.scm premoudle2tests preloops.scm run.scm premoudle2docs preloops.scm loops
Sep 06, 2020
Copyright (c) 2020 , Juergen Lorenz, ju (at) jugilo (dot) de All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. Neither the name of the author nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- premoudle2module, premoudle2docs and premoudle2tests added, license file as parameter
- line in test-file added
- Initial check in