#!/usr/bin/env sh
# -*- wisp -*-
guile -L $(dirname $(dirname $(realpath "$0"))) -c '(import (wisp-scheme) (language wisp spec))'
exec guile -L $(dirname $(dirname $(realpath "$0"))) --language=wisp -e '(@@ (examples doctests) main)' -s "$0" "$@"
; !#
define-module : examples doctests
. #:export : doctests-testmod
import : ice-9 optargs
ice-9 rdelim
ice-9 match
oop goops
texinfo reflection
; define basic dir
define* (dir #:key (all? #f))
if all?
map (λ (x) (cons (module-name x)
(module-map (λ (sym var) sym) (resolve-interface (module-name x)))))
cons (current-module) : module-uses (current-module)
module-map (λ (sym var) sym) (current-module)
; add support for giving the module as argument
define-generic dir
define-method (dir (all? <boolean>)) (dir #:all? all?)
define-method (dir (m <list>)) (module-map (λ (sym var) sym) (resolve-interface m))
; add support for using modules directly (interfaces are also modules, so this catches both)
define-method (dir (m <module>)) (module-map (λ (sym var) sym) m)
define : string-index s fragment
. "return the index of the first character of the FRAGMENT in string S."
let loop : (s s) (i 0)
if : = 0 : string-length s
. #f
if : string-prefix? fragment s
. i
loop (string-drop s 1) (+ i 1)
define : doctests-extract-from-string s
. "Extract all test calls from a given string.
This procedure provides an example test:"
. #((tests
(test 'mytest
(define v (make-vector 5 99))
(test-assert (vector? v))
(test-eqv 99 (vector-ref v 2))
(vector-set! v 2 7)
(test-eqv 7 (vector-ref v 2)))
(test 'mytest2
(test-assert #t))))
;; thanks to Vítor De Araújo: https://lists.gnu.org/archive/html/guile-user/2017-08/msg00003.html
let lp
: str s
tests : list
if : string-null? str
reverse tests
let : : idx : string-index str "(test"
if : not idx
reverse tests
let : : sub : substring str idx
lp ; recurse with the rest of the string
with-input-from-string sub
λ () (read) (read-string)
cons
with-input-from-string sub
λ () : read
. tests
define : doctests-testmod mod
. "Execute all doctests in the current module"
let*
: names : module-map (λ (sym var) sym) mod
filename
if (module-filename mod) (string-join (string-split (module-filename mod) #\/ ) "-")
string-join (cons "._" (map symbol->string (module-name mod))) "-"
docstrings
map (λ (x) (if (procedure? x) (procedure-documentation x)))
map (λ (x) (module-ref mod x)) names
doctests
map (λ (x) (if (procedure? x) (procedure-property x 'tests)))
map (λ (x) (module-ref mod x)) names
let loop
: names names
docstrings docstrings
doctests doctests
when : not : null? docstrings
when doctests
let*
: name : car names
docstring : car docstrings
doctest : car doctests
let loop-tests
: doctests doctest
when : and (list? doctests) : not : null? doctests
let*
: doctest : car doctests
testid
if : not : list? doctest
. #f
string-join : list filename (symbol->string name) : symbol->string : primitive-eval : car : cdr doctest
. "--"
cleaned
if : not : list? doctest
. '#f
append
cons 'begin
cons '(import (srfi srfi-64))
cons
list 'test-begin testid
cdr : cdr doctest
list : list 'test-end testid
when cleaned
let :
eval cleaned mod
newline
loop-tests : cdr doctests
loop (cdr names) (cdr docstrings) (cdr doctests)
define %this-module : current-module
define : main args
doctests-testmod %this-module