diff --git a/collects/mzlib/for.ss b/collects/mzlib/for.ss new file mode 100644 index 0000000000..de99c91659 --- /dev/null +++ b/collects/mzlib/for.ss @@ -0,0 +1,918 @@ +(module for mzscheme + + (provide fold-for fold-for-values fold-for* fold-for*-values + for for-values for* for*-values + list-for list-for-values list-for* list-for*-values + lists-for lists-for-values lists-for* lists-for*-values + and-for and-for-values and-for* and-for*-values + or-for or-for-values or-for* or-for*-values + first-for first-for-values first-for* first-for*-values + last-for last-for-values last-for* last-for*-values + + (rename *range range) + (rename *nat-gen nat-gen) + (rename *list->gen list->gen) + (rename *vector->gen vector->gen) + (rename *string->gen string->gen) + (rename *bytes->gen bytes->gen) + input-port->byte-gen + input-port->char-gen + + parallel-gen + stop-before-gen + stop-after-gen + (rename *index-gen index-gen) + + generator-run + + define-generator-syntax + make-do-generator + :do-gen) + + ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + ;; generator transformers: + + (begin-for-syntax + (define-values (struct:generator-transformer + make-generator-transformer + generator-transformer? + generator-transformer-ref + generator-transformer-set!) + (make-struct-type 'generator-transformer #f + 3 0 #f + null (current-inspector) + 0)) + + (define (create-generator-transformer proc1 proc2 cert) + (unless (if (identifier? proc1) + (and (procedure? proc1) + (procedure-arity-includes? proc1 1)) + (raise-type-error 'define-generator-syntax + "identifier of procedure (arity 1)" + 0 + proc1 proc2)) + (unless (and (procedure? proc2) + (procedure-arity-includes? proc2 2)) + (raise-type-error 'define-generator-syntax + "procedure (arity 1)" + 1 + proc1 proc2)) + (make-generator-transformer (if (identifier? proc1) + (lambda (stx) + (if (identifier? stx) + proc1 + (datum->syntax-object stx + #`(#,proc1 . #,(cdr (syntax-e stx))) + stx + stx))) + proc1) + proc2 + cert))) + + (define (certify-clause clause certifier introducer) + ;; This is slightly painful. The painsion into `:do-gen' involves a lot of pieces + ;; that are no treated as sub-expressions. We have to push the certificates + ;; down to all the relevant identifiers and expressions: + (define (cert s) (certifier s #f introducer)) + (define (map-cert s) (map (lambda (s) (certifier s #f #;introducer)) + (syntax->list s))) + + (syntax-case clause (:do-gen) + [[(id ...) (:do-gen ([(outer-id ...) outer-expr] ...) + outer-check + ([loop-id loop-expr] ...) + pos-guard + ([(inner-id ...) inner-expr] ...) + pre-guard + post-guard + (loop-arg ...))] + (with-syntax ([((outer-id ...) ...) + (map map-cert + (syntax->list #'((outer-id ...) ...)))] + [(outer-expr ...) (map-cert #'(outer-expr ...))] + [outer-check (cert #'outer-check)] + [(loop-expr ...) (map-cert #'(loop-expr ...))] + [pos-guard (cert #'pos-guard)] + [((inner-id ...) ...) + (map map-cert + (syntax->list #'((inner-id ...) ...)))] + [pre-guard (cert #'pre-guard)] + [post-guard (cert #'post-guard)] + [(loop-arg ...) (map-cert #'(loop-arg ...))]) + #`[(id ...) (:do-gen ([(outer-id ...) outer-expr] ...) + outer-check + ([loop-id loop-expr] ...) + pos-guard + ([(inner-id ...) inner-expr] ...) + pre-guard + post-guard + (loop-arg ...))])] + [[(id ...) rhs] + #`[(id ...) #,(cert #'rhs)]] + [_else + ;; ill-formed clause... + clause])) + + (define (expand-clause orig-stx clause) + (let eloop ([use-transformer? #t]) + (syntax-case clause (values parallel-gen stop-before-gen stop-after-gen :do-gen) + [[(id ...) rhs] + (let ([ids (syntax->list #'(id ...))]) + (for-each (lambda (id) + (unless (identifier? id) + (raise-syntax-error + #f + "expected an identifier to bind" + orig-stx + id))) + ids) + (let ([dup (check-duplicate-identifier (syntax->list #'(id ...)))]) + (when dup + (raise-syntax-error + #f + "duplicate identifier as generator binding" + orig-stx + dup))) + #f) + 'just-checking] + [[(id ...) (form . rest)] + (and use-transformer? + (identifier? #'form) + (generator-transformer? (syntax-local-value #'form (lambda () #f)))) + (let ([m (syntax-local-value #'form)]) + (let ([xformer (generator-transformer-ref m 1)] + [introducer (make-syntax-introducer)] + [certifier (generator-transformer-ref m 2)]) + (let ([xformed (xformer orig-stx (introducer (syntax-local-introduce clause)))]) + (if xformed + (expand-clause orig-stx (certify-clause (syntax-local-introduce (introducer xformed)) + certifier + introducer)) + (eloop #f)))))] + [[(id ...) (:do-gen . body)] + (syntax-case #'body () + [(([(outer-id ...) outer-rhs] ...) + outer-check + ([loop-id loop-expr] ...) + pos-guard + ([(inner-id ...) inner-rhs] ...) + pre-guard + post-guard + (loop-arg ...)) #'body] + [else (raise-syntax-error #f "bad :do-gen clause" orig-stx clause)])] + [[(id) (values rhs)] + (expand-clause orig-stx #'[(id) rhs])] + [[(id ...) (parallel-gen rhs ...)] + (and (= (length (syntax->list #'(id ...))) + (length (syntax->list #'(rhs ...))))) + ;; flatten parallel-gen iterations: + (with-syntax ([(((outer-binding ...) + outer-check + (loop-binding ...) + pos-guard + (inner-binding ...) + pre-guard + post-guard + (loop-arg ...)) ...) + (map (lambda (id rhs) + (expand-clause orig-stx #`[(#,id) #,rhs])) + (syntax->list #'(id ...)) + (syntax->list #'(rhs ...)))]) + #`((outer-binding ... ...) + (and outer-check ...) + (loop-binding ... ...) + (and pos-guard ...) + (inner-binding ... ...) + (and pre-guard ...) + (and post-guard ...) + (loop-arg ... ...)))] + [[(id ...) (stop-before-gen gen-expr pred)] + (with-syntax ([((outer-binding ...) + outer-check + (loop-binding ...) + pos-guard + (inner-binding ...) + pre-guard + post-guard + (loop-arg ...)) + (expand-clause orig-stx #`[(id ...) gen-expr])]) + #`((outer-binding ...) + outer-check + (loop-binding ...) + pos-guard + (inner-binding ...) + (and pre-guard (not (pred id ...))) + post-guard + (loop-arg ...)))] + [[(id ...) (stop-after-gen gen-expr pred)] + (with-syntax ([((outer-binding ...) + outer-check + (loop-binding ...) + pos-guard + (inner-binding ...) + pre-guard + post-guard + (loop-arg ...)) + (expand-clause orig-stx #`[(id ...) gen-expr])]) + #`((outer-binding ...) + outer-check + (loop-binding ...) + pos-guard + (inner-binding ...) + pre-guard + (and post-guard (not (pred id ...))) + (loop-arg ...)))] + [[(id ...) rhs] + (let ([introducer (make-syntax-introducer)]) + (with-syntax ([[(id ...) rhs] (introducer (syntax-local-introduce clause))]) + (syntax-local-introduce + (introducer + #`(([(pos->vals pos-next init pos-cont? val-cont? all-cont?) + (#,((syntax-local-certifier #f) #'make-generator) '(id ...) rhs)]) + (void) + ([pos init]) + (pos-cont? pos) + ([(id ...) (pos->vals pos)]) + (val-cont? id ...) + (all-cont? pos id ...) + ((pos-next pos)))))))] + [_else + (raise-syntax-error + #f + "bad generator binding clause" + orig-stx + clause)])))) + + (define-syntax (:do-gen stx) + (raise-syntax-error #f "illegal outside of a loop or comprehension binding" stx)) + + ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + ;; generators + + (define-values (prop:generator :generator? :generator-ref) + (make-struct-type-property 'generator + (lambda (v sinfo) + (unless (and (procedure? v) + (procedure-arity-includes? v 1)) + (raise-type-error + 'generator-property-guard + "procedure (arity 1)" + v)) + v))) + + (define-values (struct:do-generator + make-do-generator + do-generator? + do-generator-ref + do-generator-set!) + (make-struct-type 'generator #f + 1 0 #f + (list (cons prop:generator (lambda (v) + ((do-generator-ref v 0))))))) + + (define-syntax define-generator-syntax + (syntax-rules () + [(_ id expr-transformer-expr clause-transformer-expr) + (define-syntax id (create-generator-transformer + expr-transformer-expr + clause-transformer-expr + (syntax-local-certifier #f)))])) + + (define (generator? v) + (or (:generator? v) + (list? v) + (vector? v) + (string? v) + (bytes? v) + (input-port? v))) + + (define (make-generator who v) + (cond + [(:generator? v) ((:generator-ref v) v)] + [(list? v) (:list-gen v)] + [(vector? v) (:vector-gen v)] + [(string? v) (:string-gen v)] + [(bytes? v) (:bytes-gen v)] + [(input-port? v) (:input-port-gen v)] + [else (raise + (make-exn:fail:contract + (format "for: expected a generator for ~a, got something else: ~v" + (if (= 1 (length who)) + (car who) + who) + v) + (current-continuation-marks)))])) + + (define range + (case-lambda + [(b) (range 0 b 1)] + [(a b) (range a b 1)] + [(a b step) + (unless (real? a) (raise-type-error 'range "real-number" a)) + (unless (real? b) (raise-type-error 'range "real-number" b)) + (unless (real? step) (raise-type-error 'range "real-number" step)) + (make-do-generator (lambda () + (values + (lambda (x) x) + (lambda (x) (+ x step)) + a + (if (step . >= . 0) + (lambda (x) (< x b)) + (lambda (x) (> x b))) + (lambda (x) #t) + (lambda (x y) #t))))])) + + (define (nat-gen) + (make-do-generator (lambda () + (values values + add1 + 0 + (lambda (x) #t) + (lambda (x) #t) + (lambda (x y) #t))))) + + (define (list->gen l) + (unless (list? l) (raise-type-error 'list->gen "list" l)) + (make-do-generator (lambda () (:list-gen l)))) + + (define (:list-gen l) + (values car cdr l pair? (lambda (x) #t) (lambda (p x) #t))) + + (define (vector->gen l) + (unless (vector? l) (raise-type-error 'vector->gen "vector" l)) + (make-do-generator (lambda () (:vector-gen l)))) + + (define (:vector-gen v) + (let ([len (vector-length v)]) + (values (lambda (i) + (vector-ref v i)) + add1 + 0 + (lambda (i) (< i len)) + (lambda (x) #t) + (lambda (x y) #t)))) + + (define (string->gen l) + (unless (string? l) (raise-type-error 'string->gen "string" l)) + (make-do-generator (lambda () (:string-gen l)))) + + (define (:string-gen v) + (let ([len (string-length v)]) + (values (lambda (i) + (string-ref v i)) + add1 + 0 + (lambda (i) (< i len)) + (lambda (x) #t) + (lambda (x y) #t)))) + + (define (bytes->gen l) + (unless (bytes? l) (raise-type-error 'bytes->gen "bytes" l)) + (make-do-generator (lambda () (:bytes-gen l)))) + + (define (:bytes-gen v) + (let ([len (bytes-length v)]) + (values (lambda (i) + (bytes-ref v i)) + add1 + 0 + (lambda (i) (< i len)) + (lambda (x) #t) + (lambda (x y) #t)))) + + (define (input-port->byte-gen l) + (unless (input-port? l) (raise-type-error 'input-port->byte-gen "input-port" l)) + (make-do-generator (lambda () (:input-port-gen l)))) + + (define (:input-port-gen v) + (values (lambda (v) (read-byte v)) + (lambda (v) v) + v + (lambda (v) #t) + (lambda (x) (not (eof-object? x))) + (lambda (x v) #t))) + + (define (input-port->char-gen v) + (unless (input-port? v) (raise-type-error 'input-port->char-gen "input-port" v)) + (make-do-generator (lambda () + (values (lambda (v) (read-char v)) + (lambda (v) v) + v + (lambda (v) #t) + (lambda (x) (not (eof-object? x))) + (lambda (x v) #t))))) + + (define (stop-before-gen g pred) + (unless (generator? g) (raise-type-error 'stop-before-gen "generator" g)) + (unless (and (procedure? pred) + (procedure-arity-includes? pred 1)) + (raise-type-error 'stop-before-gen "procedure (arity 1)" pred)) + (make-do-generator (lambda () + (let-values ([(pos->val pos-next init pos-cont? pre-cont? post-cont?) + (make-generator #f g)]) + (values pos->val + pos-next + init + pos-cont? + (case-lambda + [(val) (and (pre-cont? val) + (not (pred val)))] + [vals (and (apply pre-cont? vals) + (not (apply pred vals)))]) + post-cont?))))) + + (define (stop-after-gen g pred) + (unless (generator? g) (raise-type-error 'stop-after-gen "generator" g)) + (unless (and (procedure? pred) + (procedure-arity-includes? pred 1)) + (raise-type-error 'stop-after-gen "procedure (arity 1)" pred)) + (make-do-generator (lambda () + (let-values ([(pos->val pos-next init pos-cont? pre-cont? post-cont?) + (make-generator #f g)]) + (values pos->val + pos-next + init + pos-cont? + pre-cont? + (case-lambda + [(pos val) (and (post-cont? pos val) + (not (pred val)))] + [(pos . vals) (and (apply pos-cont? pos vals) + (not (apply pred vals)))])))))) + + (define (index-gen g) + (unless (generator? g) (raise-type-error 'index-gen "generator" g)) + (make-do-generator (lambda () + (let-values ([(pos->val pos-next init pos-cont? pre-cont? post-cont?) + (make-generator #f g)]) + (values (lambda (pos) (values (pos->val (car pos)) (cdr pos))) + (lambda (pos) (cons (pos-next (car pos)) (add1 (cdr pos)))) + (cons init 0) + (lambda (pos) (pos-cont? (car pos))) + (lambda (val idx) (pre-cont? val)) + (lambda (pos val idx) (post-cont? pos val))))))) + + ;; ---------------------------------------- + + (define (parallel-gen . generators) + (for-each (lambda (g) + (unless (generator? g) + (raise-type-error 'parallel-gen "generator" g))) + generators) + (if (= 1 (length generators)) + (car generators) + (make-do-generator + (lambda () + (let-values ([(pos->vals pos-nexts inits pos-cont?s pre-cont?s post-cont?s) + (lists-for (p->v p-s i ps? pr? po?) ([g generators]) + (make-generator #f g))]) + (values + (lambda (poses) (apply values (map (lambda (pos->val pos) (pos->val pos)) + pos->vals + poses))) + (lambda (poses) (map (lambda (pos-next pos) (pos-next pos)) + pos-nexts + poses)) + inits + (lambda (poses) (andmap (lambda (pos-cont? pos) (pos-cont? pos)) + pos-cont?s + poses)) + (lambda vals (andmap (lambda (pre-cont? val) (pre-cont? val)) + pre-cont?s + vals)) + (lambda (poses . vals) (andmap (lambda (post-cont? pos val) (post-cont? pos val)) + post-cont?s + poses + vals)))))))) + + ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + ;; runnign generators outside of a loop: + + (define (generator-run g) + (unless (generator? g) + (raise-type-error 'generator-run "generator" g)) + (let-values ([(pos->val pos-next init pos-cont? pre-cont? post-cont?) + (make-generator #f g)]) + (let ([pos init]) + (letrec ([more? #f] + [prep-val! #f] + [next #f]) + (letrec ([no-more (lambda () + (error "generator has no more values"))] + [init-more? + (lambda () (prep-val!) (more?))] + [init-next + (lambda () (prep-val!) (next))] + [init-prep-val! + (lambda () + (if (pos-cont? pos) + (call-with-values + (lambda () (pos->val pos)) + (lambda vals + (if (apply pre-cont? vals) + (begin + (set! more? (lambda () #t)) + (set! next + (lambda () + (let ([v vals]) + (set! prep-val! + (lambda () + (if (apply post-cont? pos vals) + (begin + (set! pos (pos-next pos)) + (set! prep-val! init-prep-val!) + (prep-val!)) + (begin + (set! more? (lambda () #f)) + (set! next no-more))))) + (set! more? init-more?) + (set! next init-next) + (apply values v)))) + (set! prep-val! void) + (apply values vals)) + (begin + (set! more? (lambda () #f)) + (set! next no-more))))) + (begin + (set! more? (lambda () #f)) + (set! next no-more))))]) + (set! more? init-more?) + (set! prep-val! init-prep-val!) + (set! next init-next) + (let ([generator-more? (lambda () (more?))] + [generator-next (lambda () (next))]) + (values generator-more? + generator-next))))))) + + ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + ;; core `fold-for' syntax + + (define-syntax values* + (syntax-rules () + [(_ x) x] + [(_ x ...) (values x ...)])) + + (define-syntax (fold-forX/derived stx) + (syntax-case stx () + ;; Done case (no more clauses, and no generated clauses to emit): + [(_ [orig-stx multi? first-multi? nested? emit? ()] ([fold-var fold-init] ...) () expr1 expr ...) + #`(let ([fold-var fold-init] ...) (let () expr1 expr ...))] + ;; Switch-to-emit case (no more clauses to generate): + [(_ [orig-stx multi? first-multi? nested? #f binds] ([fold-var fold-init] ...) () . body) + #`(fold-forX/derived [orig-stx multi? first-multi? nested? #t binds] ([fold-var fold-init] ...) () . body)] + ;; Emit case: + [(_ [orig-stx multi? first-multi? nested? #t binds] ([fold-var fold-init] ...) rest . body) + (with-syntax ([(([outer-binding ...] + outer-check + [loop-binding ...] + pos-guard + [inner-binding ...] + pre-guard + post-guard + [loop-arg ...]) ...) (reverse (syntax->list #'binds))]) + #'(let-values (outer-binding ... ...) + outer-check ... + (let comp-loop ([fold-var fold-init] ... + loop-binding ... ...) + (if (and pos-guard ...) + (let-values (inner-binding ... ...) + (if (and pre-guard ...) + (let-values ([(fold-var ...) + (fold-forX/derived [orig-stx multi? first-multi? nested? #f ()] ([fold-var fold-var] ...) rest . body)]) + (if (and post-guard ...) + (comp-loop fold-var ... loop-arg ... ...) + (values* fold-var ...))) + (values* fold-var ...))) + (values* fold-var ...)))))] + ;; Bad body cases: + [(_ [orig-stx . _rest] fold-bind ()) + (raise-syntax-error + #f + "missing body expression after generator bindings" + #'orig-stx)] + [(_ [orig-stx . _rest] fold-bind () . rest) + (raise-syntax-error + #f + "bad syntax (illegal use of `.') after generator bindings" + #'orig-stx)] + ;; Guard case, no pending emits: + [(_ [orig-stx multi? first-multi? nested? #f ()] ([fold-var fold-init] ...) (#:when expr . rest) . body) + #'(if expr + (fold-forX/derived [orig-stx multi? first-multi? nested? #f ()] ([fold-var fold-init] ...) rest . body) + (values* fold-init ...))] + ;; Guard case, pending emits need to be flushed first + [(_ [orig-stx multi? first-multi? nested? #f binds] ([fold-var fold-init] ...) (#:when expr . rest) . body) + #'(_ [orig-stx multi? first-multi? nested? #t binds] ([fold-var fold-init] ...) (#:when expr . rest) . body)] + ;; Convert single-value form to multi-value form: + [(_ [orig-stx #f #f nested? #f binds] fold-bind ([id rhs] . rest) . body) + (identifier? #'id) + #'(fold-forX/derived [orig-stx #f #t nested? #f binds] fold-bind ([(id) rhs] . rest) . body)] + ;; If we get here in single-value mode, then it's a bad clause: + [(_ [orig-stx #f #f nested? #f binds] fold-bind (clause . rest) . body) + (raise-syntax-error + #f + "bad generator binding clause" + #'orig-stx + #'clause)] + ;; Expand one multi-value clause, and push it into the results to emit: + [(_ [orig-stx multi? #t nested? #f binds] ([fold-var fold-init] ...) (clause . rest) . body) + (with-syntax ([bind (expand-clause #'orig-stx #'clause)]) + #`(_ [orig-stx multi? multi? nested? nested? (bind . binds)] ([fold-var fold-init] ...) rest . body))] + [(_ [orig-stx . _rest] . _rest2) + (raise-syntax-error #f "bad syntax" #'orig-stx)])) + + (define-syntax fold-for/derived + (syntax-rules () + [(_ orig-stx . rest) + (fold-forX/derived [orig-stx #f #f #f #f ()] . rest)])) + + (define-syntax fold-for-values/derived + (syntax-rules () + [(_ orig-stx . rest) + (fold-forX/derived [orig-stx #t #t #f #f ()] . rest)])) + + (define-syntax fold-for*/derived + (syntax-rules () + [(_ orig-stx . rest) + (fold-forX/derived [orig-stx #f #f #t #f ()] . rest)])) + + (define-syntax fold-for*-values/derived + (syntax-rules () + [(_ orig-stx . rest) + (fold-forX/derived [orig-stx #t #t #t #f ()] . rest)])) + + ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + ;; derived `for' syntax + + (define-for-syntax (for-variant-stx stx derived-id-stx fold-bind-stx wrap rhs-wrap combine multi?) + (with-syntax ([derived-id derived-id-stx] + [fold-bind fold-bind-stx]) + (syntax-case stx () + ;; When there's a bindings clause... + [(_ (bind ...) expr1 expr ...) + (with-syntax ([(bind ...) (let loop ([bs (syntax->list #'(bind ...))]) + (if (null? bs) + null + (syntax-case (car bs) () + [[ids rhs] + (if multi? + (andmap identifier? (or (syntax->list #'ids) '(#f))) + (identifier? #'ids)) + (cons #`[ids #,(rhs-wrap #'rhs)] + (loop (cdr bs)))] + [#:when (cons (car bs) + (if (null? (cdr bs)) + null + (cons (cadr bs) (loop (cddr bs)))))] + [_else + ;; a syntax error; les the /derived form handle it, and + ;; no need to wrap any more: + bs])))]) + (quasisyntax/loc stx + #,(wrap (quasisyntax/loc stx + (derived-id #,stx fold-bind (bind ...) #,(combine #'(let () expr1 expr ...)))))))] + ;; Let `derived-id' complain about the missing bindings and body expression: + [(_ . rest) + #`(derived-id #,stx fold-bind . rest)]))) + + (define-syntax define-syntax-via-derived + (syntax-rules () + [(_ id derived-id fold-bind wrap rhs-wrap combine multi?) + (define-syntax (id stx) (for-variant-stx stx #'derived-id #'fold-bind wrap rhs-wrap combine multi?))])) + + (define-syntax define-for-variants + (syntax-rules () + [(_ (for for-values for* for*-values) fold-bind wrap rhs-wrap combine) + (begin + (define-syntax-via-derived for fold-for/derived fold-bind wrap rhs-wrap combine #f) + (define-syntax-via-derived for-values fold-for-values/derived fold-bind wrap rhs-wrap combine #t) + (define-syntax-via-derived for* fold-for*/derived fold-bind wrap rhs-wrap combine #f) + (define-syntax-via-derived for*-values fold-for*-values/derived fold-bind wrap rhs-wrap combine #t))])) + + (define-syntax (fold-for stx) + (syntax-case stx () + [(_ . rest) (quasisyntax/loc stx (fold-for/derived #,stx . rest))])) + (define-syntax (fold-for-values stx) + (syntax-case stx () + [(_ . rest) (quasisyntax/loc stx (fold-for-values/derived #,stx . rest))])) + (define-syntax (fold-for* stx) + (syntax-case stx () + [(_ . rest) (quasisyntax/loc stx (fold-for*/derived #,stx . rest))])) + (define-syntax (fold-for*-values stx) + (syntax-case stx () + [(_ . rest) (quasisyntax/loc stx (fold-for*-values/derived #,stx . rest))])) + + (define-for-variants (for for-values for* for*-values) + ([fold-var (void)]) + (lambda (x) x) + (lambda (x) x) + (lambda (x) `(,#'begin ,x ,#'(void)))) + + (define-for-variants (list-for list-for-values list-for* list-for*-values) + ([fold-var null]) + (lambda (x) `(,#'reverse ,x)) + (lambda (x) x) + (lambda (x) `(,#'cons ,x ,#'fold-var))) + + (define-for-syntax (make-lists-for-values fold-for-id) + (lambda (stx) + (syntax-case stx () + [(_ (id ...) bindings expr1 expr ...) + (let ([ids (syntax->list #'(id ...))]) + (for-each (lambda (id) + (unless (identifier? id) + (raise-syntax-error #f + "not an identifier" + stx + id))) + ids) + (with-syntax ([(id2 ...) (generate-temporaries ids)] + [fold-for fold-for-id] + [orig-stx stx]) + #'(let-values ([(id ...) + (fold-for orig-stx ([id null] ...) bindings + (let-values ([(id2 ...) (let () + expr1 + expr ...)]) + (values* (cons id2 id) ...)))]) + (values* (reverse id) ...))))]))) + + (define-syntax lists-for (make-lists-for-values #'fold-for/derived)) + (define-syntax lists-for-values (make-lists-for-values #'fold-for-values/derived)) + (define-syntax lists-for* (make-lists-for-values #'fold-for*/derived)) + (define-syntax lists-for*-values (make-lists-for-values #'fold-for*-values/derived)) + + (define-for-variants (and-for and-for-values and-for* and-for*-values) + ([result #t]) + (lambda (x) x) + (lambda (rhs) #`(stop-after-gen #,rhs (lambda x (not result)))) + (lambda (x) x)) + + (define-for-variants (or-for or-for-values or-for* or-for*-values) + ([result #f]) + (lambda (x) x) + (lambda (rhs) #`(stop-after-gen #,rhs (lambda x result))) + (lambda (x) x)) + + (define-for-variants (first-for first-for-values first-for* first-for*-values) + ([val #f][stop? #f]) + (lambda (x) #`(let-values ([(val _) #,x]) val)) + (lambda (rhs) #`(stop-after-gen #,rhs (lambda x stop?))) + (lambda (x) #`(values #,x #t))) + + (define-for-variants (last-for last-for-values last-for* last-for*-values) + ([result #f]) + (lambda (x) x) + (lambda (rhs) rhs) + (lambda (x) x)) + + ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + ;; specific generators + + (define-generator-syntax *range + #'range + (lambda (orig-stx stx) + (let loop ([stx stx]) + (syntax-case stx () + [[(id) (_ a b step)] #`[(id) + (:do-gen + ;; outer bindings: + ([(start) a] [(end) b] [(inc) step]) + ;; outer check: + (void) +#; + (unless (and (real? a) (real? b) (real? inc)) + ;; let `range' report the error: + (range start end inc)) + ;; loop bindings: + ([pos start]) + ;; pos check + #,(cond + [(not (number? (syntax-e #'step))) + #`(if (step . >= . 0) (< pos end) (> pos end))] + [((syntax-e #'step) . >= . 0) + #'(< pos end)] + [else + #'(> pos end)]) + ;; inner bindings + ([(id) pos]) + ;; pre guard + #t + ;; post guard + #t + ;; loop args + ((+ pos inc)))]] + [[(id) (_ a b)] (loop #'[(id) (_ a b 1)])] + [[(id) (_ b)] (loop #'[(id) (_ 0 b 1)])] + [_else #f])))) + + (define-generator-syntax *nat-gen + #'nat-gen + (lambda (orig-stx stx) + (syntax-case stx () + [[(id) (_)] #`[(id) + (:do-gen + ;; outer bindings: + () + ;; outer check: + (void) + ;; loop bindings: + ([pos 0]) + ;; pos check + #t + ;; inner bindings + ([(id) pos]) + ;; pre guard + #t + ;; post guard + #t + ;; loop args + ((+ pos 1)))]] + [_else #f]))) + + (define-generator-syntax *list->gen + #'list->gen + (lambda (orig-stx stx) + (syntax-case stx () + [((id) (_ lst-expr)) + #'[(id) + (:do-gen + ;;outer bindings + ([(lst) lst-expr]) + ;; outer check + (unless (list? lst) (list->gen lst)) + ;; loop bindings + ([lst lst]) + ;; pos check + (pair? lst) + ;; inner bindings + ([(id) (car lst)]) + ;; pre guard + #t + ;; post guard + #t + ;; loop args + ((cdr lst)))]] + [_else #f]))) + + (define-for-syntax (vector-like-gen vector?-id + vector-length-id + vector->gen-id + vector-ref-id) + (lambda (orig-stx stx) + (with-syntax ([vector? vector?-id] + [vector->gen vector->gen-id] + [vector-length vector-length-id] + [vector-ref vector-ref-id]) + (syntax-case stx () + [((id) (_ vec-expr)) + #'[(id) + (:do-gen + ;;outer bindings + ([(vec len) (let ([vec vec-expr]) + (unless (vector? vec) + (vector->gen vec)) + (values vec (vector-length vec)))]) + ;; outer check + #f + ;; loop bindings + ([pos 0]) + ;; pos check + (pos . < . len) + ;; inner bindings + ([(id) (vector-ref vec pos)]) + ;; pre guard + #t + ;; post guard + #t + ;; loop args + ((add1 pos)))]] + [_else #f])))) + + (define-generator-syntax *vector->gen + #'vector->gen + (vector-like-gen #'vector? + #'vector-length + #'vector->gen + #'vector-ref)) + + (define-generator-syntax *string->gen + #'string->gen + (vector-like-gen #'string? + #'string-length + #'string->gen + #'string-ref)) + + (define-generator-syntax *bytes->gen + #'bytes->gen + (vector-like-gen #'bytes? + #'bytes-length + #'bytes->gen + #'bytes-ref)) + + (define-generator-syntax *index-gen + #'index-gen + (lambda (orig-stx stx) + (syntax-case stx () + [((id1 id2) (_ gen-expr)) + #'[(id1 id2) (parallel-gen gen-expr (*nat-gen))]]))))