#lang scheme/base
(require (for-syntax scheme/base
"base.ss")
scheme/dict
"base.ss"
"debug.ss"
(only-in "project.ss" partition/mask)
(only-in "yield.ss" yieldable))
(define generator-end (gensym 'generator-end))
(define-syntax gen->
(syntax-rules ()
[(_ expr)
(-> (or/c expr generator-end?))]))
(define (generator-end? item)
(eq? item generator-end))
(define (generate-all gens)
(map (lambda (item)
(item))
gens))
(define (generator-map fn . gens)
(let ([id (gensym)])
(lambda ()
(let ([args (generate-all gens)])
(if (ormap generator-end? args)
generator-end
(apply fn args))))))
(define (generator-fold-map proc accum . gens)
(lambda ()
(let ([args (generate-all gens)])
(if (ormap generator-end? args)
generator-end
(begin
(set! accum (apply proc (append args (list accum))))
accum)))))
(define (generator-filter test gen)
(letrec ([ans (lambda ()
(let ([arg (gen)])
(cond [(generator-end? arg) generator-end]
[(test arg) arg]
[else (ans)])))])
ans))
(define (generator-filter-map test gen)
(letrec ([ans (lambda ()
(let ([arg (gen)])
(if (generator-end? arg)
generator-end
(let ([answer (test arg)])
(if answer answer (ans))))))])
ans))
(define generator-remove-duplicates
(let ([empty (gensym)])
(lambda (gen [same? equal?])
(let ([last empty])
(lambda ()
(let loop ([curr (gen)])
(cond [(generator-end? curr) generator-end]
[(same? last curr) (set! last curr)
(loop (gen))]
[else (set! last curr)
curr])))))))
(define (generator-debug message generate)
(lambda ()
(let ([item (generate)])
(printf "~a ~s~n" message item)
item)))
(define (generator-for-each proc . gens)
(let ([args (generate-all gens)])
(if (ormap generator-end? args)
(void)
(begin (apply proc args)
(apply generator-for-each (cons proc gens))))))
(define (generator-fold proc accum0 . gens)
(let loop ([accum accum0])
(let ([args (generate-all gens)])
(if (ormap generator-end? args)
accum
(loop (apply proc (append args (list accum))))))))
(define (generator-append . gens)
(letrec ([ans (lambda ()
(if (null? gens)
generator-end
(let ([val ((car gens))])
(if (generator-end? val)
(begin (set! gens (cdr gens))
(ans))
val))))])
ans))
(define (list->generator data)
(lambda ()
(if (null? data)
generator-end
(begin0 (car data)
(set! data (cdr data))))))
(define (range->generator start [end #f] [step 1])
(define counter start)
(lambda ()
(cond [(not end) (begin0 counter (set! counter (+ counter step)))]
[(and (> step 0) (>= counter end)) generator-end]
[(and (< step 0) (<= counter end)) generator-end]
[else (begin0 counter (set! counter (+ counter step)))])))
(define (generator->list gen)
(reverse (generator-fold cons null gen)))
(define (generator->hash gen item->key [item->val (lambda (x) x)] [hash (make-hash)])
(generator-for-each (lambda (item)
(hash-set! hash
(item->key item)
(item->val item)))
gen)
hash)
(define (generator-project mask generate [same? eq?])
(define (projectable? x)
(or (pair? x) (null? x)))
(define last (generate))
(define-values (last-keys nonkeys-accum)
(if (list? last)
(let-values ([(last-keys last-nonkeys)
(partition/mask last mask)])
(values last-keys (list last-nonkeys)))
(values #f null)))
(define (make-answer keys nonkeys)
(append keys (list (reverse nonkeys))))
(define (loop)
(define next (generate))
(define-values (next-keys next-nonkeys)
(if (projectable? next)
(partition/mask next mask)
(values #f null)))
(if (projectable? last)
(if (projectable? next)
(if (andmap same? last-keys next-keys)
(begin (set! last next)
(set! last-keys next-keys)
(set! nonkeys-accum (cons next-nonkeys nonkeys-accum))
(loop))
(begin0 (make-answer last-keys nonkeys-accum)
(set! last next)
(set! last-keys next-keys)
(set! nonkeys-accum (list next-nonkeys))))
(begin0 (make-answer last-keys nonkeys-accum)
(set! last next)
(set! last-keys #f)
(set! nonkeys-accum null)))
(if (projectable? next)
(begin0 last
(set! last next)
(set! last-keys next-keys)
(set! nonkeys-accum (list next-nonkeys)))
(begin0 last
(set! last next)
(set! last-keys #f)
(set! nonkeys-accum null)))))
loop)
(define (in-generator g:items)
(make-do-sequence
(let ([current-position (g:items)])
(lambda ()
(values (lambda (pos)
current-position)
(lambda (pos)
(set! current-position (g:items))
#t)
#t (lambda (pos)
(not (generator-end? current-position)))
(lambda (val)
(not (generator-end? val)))
(lambda (pos val)
(or (not (generator-end? current-position))
(not (generator-end? val)))))))))
(provide gen->
generator-end
generator-end?)
(provide/contract
[generator-map (->* (procedure?) () #:rest (listof procedure?) procedure?)]
[generator-fold-map (->* (procedure? any/c) () #:rest (listof procedure?) procedure?)]
[generator-filter (-> procedure? procedure? procedure?)]
[generator-filter-map (-> procedure? procedure? procedure?)]
[generator-remove-duplicates (->* (procedure?) (procedure?) procedure?)]
[generator-debug (-> string? procedure? procedure?)]
[generator-for-each (->* (procedure?) () #:rest (listof procedure?) any)]
[generator-fold (->* (procedure? any/c) () #:rest (listof procedure?) any)]
[generator-append (->* () () #:rest (listof procedure?) procedure?)]
[generator->list (-> procedure? (or/c pair? null?))]
[generator->hash (->* (procedure? procedure?)
(procedure? (and/c hash? dict-mutable?))
(and/c hash? dict-mutable?))]
[list->generator (-> (or/c pair? null?) procedure?)]
[range->generator (->* (integer?) ((or/c integer? false/c) integer?) procedure?)]
[generator-project (->* ((listof boolean?) procedure?) (procedure?) procedure?)]
[in-generator (-> (gen-> any/c) sequence?)])