(define-module (doctest util)
    #:export ( &not
	       begin-proc
	       drop-around
	       enumerate
	       for
	       group-by
	       prefix-of?
	       string-blank? ))
(use-modules (ice-9 match))
(use-modules (srfi srfi-1))

(define-syntax for
    (syntax-rules (in => as)
	([_ ((key => value) in hash) exp ...]
	 [hash-for-each (lambda (key value) exp ...) hash])
	([_ (pattern as var in list) exp ...]
	 [for-each (lambda (var) (match var (pattern exp ...))) list])
	([_ (var in list) exp ...]
	 [for-each (lambda (var) exp ...) list])))

(define (&not pred)
    "
        Return negation of predicate PRED.

        +++ ((&not even?) 2)
        --- #f
    "
    (lambda (x) (not (pred x))))


(define (string-forall? pred str)
    "
        Return #t if STR is empty or every char satisfy PRED.

        +++ (string-forall? char-whitespace? \"string\")
        --- #f
        +++ (string-forall? char-numeric? \"123\")
        --- #t
        +++ (string-forall? char-numeric? \"\")
        --- #t
    "
    (every pred (string->list str)))

(define (string-blank? str) (string-forall? char-whitespace? str))



(define (group-by header? lst)
    "
        Return list of sublists in LST, car of which satisfy HEADER?
        and no other elements satisfy it.
        +++ (group-by even? '(2 1 5 0 1 8))
        --- ((2 1 5) (0 1) (8))
    "
    (map reverse
	 (cdr (reverse
	       (fold (lambda (el accum)
			 (match accum
				[[cur-group . groups]
				 (if (header? el)
				     (cons (list el)
					   (cons cur-group
						 groups))
				     (cons (cons el cur-group)
					   groups))]))
		     (cons '() '())
		     (drop-while (lambda (x) (not (header? x))) lst))))))



(define-syntax begin-proc
    (syntax-rules ()
	([_ form ...]
	 [lambda () form ...])))

(define* (enumerate lst #:key (step 1) (init 0))
    "
        Return list of pairs (INDEX . ELEMENT), where
        INDEX starts at INIT and gets incremented by STEP.

        +++ (enumerate '(foo bar baz))
        --- ((0 . foo) (1 . bar) (2 . baz))
        +++ (enumerate '(foo bar baz) #:step -1 #:init 3)
        --- ((3 . foo) (2 . bar) (1 . baz))
    "
    (reverse
     (fold (lambda (el seed)
	       (let ((index (if (null? seed) init
				(+ step (caar seed)))))
		   (cons (cons index el) seed)))
	   '() lst)))

(define (prefix-of? prefix str)
    (and (>= (string-length str) (string-length prefix))
	 (equal? prefix (substring str 0 (string-length prefix)))))

(define (drop-around pred lst)
    "
        Remove starting and trailing elements of LST,
        satifsfying PRED.
    "
    (define (drop-prefix l) (drop-while pred l))
    (drop-prefix (reverse (drop-prefix (reverse lst)))))
