Permalink
Fetching contributors…
Cannot retrieve contributors at this time
2248 lines (2093 sloc) 93.6 KB
(module for '#%kernel
(#%require "more-scheme.rkt"
"misc.rkt"
"define.rkt"
"letstx-scheme.rkt"
"member.rkt"
"reverse.rkt"
"sort.rkt"
"performance-hint.rkt"
'#%unsafe
'#%flfxnum
(for-syntax '#%kernel
"stx.rkt"
"qqstx.rkt"
"define.rkt"
"member.rkt"
"small-scheme.rkt"
"stxcase-scheme.rkt"))
(#%provide for/fold for*/fold
for for*
for/list for*/list
for/vector for*/vector
for/lists for*/lists
for/and for*/and
for/or for*/or
for/first for*/first
for/last for*/last
for/sum for*/sum
for/product for*/product
for/hash for*/hash
for/hasheq for*/hasheq
for/hasheqv for*/hasheqv
for/fold/derived for*/fold/derived
(for-syntax split-for-body)
(for-syntax (rename expand-clause expand-for-clause))
(rename *in-range in-range)
(rename *in-naturals in-naturals)
(rename *in-list in-list)
(rename *in-mlist in-mlist)
(rename *in-vector in-vector)
(rename *in-string in-string)
(rename *in-bytes in-bytes)
(rename *in-stream in-stream)
(rename *in-input-port-bytes in-input-port-bytes)
(rename *in-input-port-chars in-input-port-chars)
(rename *in-port in-port)
(rename *in-lines in-lines)
(rename *in-bytes-lines in-bytes-lines)
in-hash
in-hash-keys
in-hash-values
in-hash-pairs
in-mutable-hash
in-mutable-hash-keys
in-mutable-hash-values
in-mutable-hash-pairs
in-immutable-hash
in-immutable-hash-keys
in-immutable-hash-values
in-immutable-hash-pairs
in-weak-hash
in-weak-hash-keys
in-weak-hash-values
in-weak-hash-pairs
in-directory
in-sequences
in-cycle
in-parallel
in-values-sequence
in-values*-sequence
stop-before
stop-after
(rename *in-producer in-producer)
(rename *in-indexed in-indexed)
(rename *in-value in-value)
stream?
stream-empty?
stream-first
stream-rest
prop:stream
stream-ref stream-via-prop? ; only provided for racket/stream
sequence->stream
empty-stream make-do-stream
sequence?
sequence-generate
sequence-generate*
prop:sequence
define-sequence-syntax
make-do-sequence
:do-in
define-in-vector-like
define-:vector-like-gen
(for-syntax make-in-vector-like
for-clause-syntax-protect))
;; redefininition of functions not in #%kernel
(begin-for-syntax
(define (format-id ctx str . args)
(define datum
(string->symbol (apply format str (map syntax->datum args))))
(datum->syntax ctx datum))
(define (join-ids ids sep) ; joins ids with sep; ids = stx-pair
(syntax-case ids ()
[(id) #'id]
[(id . ids) (format-id #'id "~a~a~a" #'id sep (join-ids #'ids sep))])))
;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; sequence transformers:
(begin-for-syntax
(define-values (struct:sequence-transformer
make-sequence-transformer
sequence-transformer?
sequence-transformer-ref
sequence-transformer-set!)
(make-struct-type 'sequence-transformer #f
2 0 #f
null (current-inspector)
0))
(define (create-sequence-transformer proc1 proc2)
(unless (and (procedure? proc1)
(or (procedure-arity-includes? proc1 1)
(procedure-arity-includes? proc1 0)))
(raise-argument-error 'define-sequence-syntax
"(or/c (procedure-arity-includes/c 0) (procedure-arity-includes/c 1))"
0
proc1 proc2))
(unless (and (procedure? proc2)
(procedure-arity-includes? proc2 1))
(raise-argument-error 'define-sequence-syntax
"(procedure-arity-includes/c 1)"
1
proc1 proc2))
(make-sequence-transformer
(if (procedure-arity-includes? proc1 0)
(lambda (stx)
(if (identifier? stx)
(proc1)
(datum->syntax stx
;; Use cons, not #`(#,op #,@args), to avoid replacing implicit #%app binding
(cons (proc1) (cdr (syntax-e stx)))
stx
stx)))
proc1)
proc2))
(define (arm-for-clause clause cert)
(define (map-cert s) (map cert (syntax->list s)))
(syntax-case clause (:do-in)
[[(id ...) (:do-in ([(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-in ([(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)]]
[_
;; ill-formed clause...
clause]))
(define orig-insp (variable-reference->module-declaration-inspector
(#%variable-reference)))
(define (for-clause-syntax-protect clause)
;; This is slightly painful. The expansion into `:do-in' involves a lot
;; of pieces that are not treated as sub-expressions. We have to push the
;; taints down to all the relevant identifiers and expressions:
(arm-for-clause clause syntax-arm))
(define sequence-specialization-logger
(make-logger 'sequence-specialization (current-logger)))
(define (check-identifier-bindings orig-stx ids-stx kind result)
(let ([ids (syntax->list ids-stx)])
(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 ids)])
(when dup
(raise-syntax-error #f
(format "duplicate identifier as ~a binding" kind) orig-stx dup)))
result))
(define (expand-clause orig-stx clause)
(define (unpack stx)
(syntax-case stx ()
[[ids rhs] ; remove dye pack on `rhs' in case it's `(form . rest)'
#`[ids #,(syntax-disarm #'rhs orig-insp)]]
[_ stx]))
(define (make-rearm)
(syntax-case clause ()
[(_ rhs)
(lambda (stx)
(syntax-rearm stx #'rhs))]))
(let eloop ([use-transformer? #t])
(define unpacked-clause (unpack clause))
(syntax-case unpacked-clause (values in-parallel stop-before stop-after :do-in)
[[(id ...) rhs]
(check-identifier-bindings orig-stx #'(id ...) "sequence" #f)
'just-checking]
[[(id ...) (form . rest)]
(and use-transformer?
(identifier? #'form)
(sequence-transformer? (syntax-local-value #'form (lambda () #f))))
(let ([m (syntax-local-value #'form)])
(let ([xformer (sequence-transformer-ref m 1)]
[introducer (make-syntax-introducer)])
(let ([xformed (xformer (introducer (syntax-local-introduce unpacked-clause)))])
(if xformed
(let ([r (expand-clause orig-stx
(arm-for-clause
(syntax-local-introduce (introducer xformed))
(make-rearm)))])
(syntax-property r
'disappeared-use
(cons (syntax-local-introduce #'form)
(or (syntax-property r 'disappeared-use)
null))))
(eloop #f)))))]
[[(id ...) (:do-in . 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-in clause" orig-stx clause)])]
[[(id) (values rhs)]
(expand-clause orig-stx #'[(id) rhs])]
[[(id ...) (in-parallel rhs ...)]
(and (= (length (syntax->list #'(id ...)))
(length (syntax->list #'(rhs ...)))))
;; flatten in-parallel 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-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-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]
#t
(let ([introducer (make-syntax-introducer)])
;; log non-specialized clauses, for performance tuning
(when (log-level? sequence-specialization-logger 'debug)
(log-message sequence-specialization-logger
'debug
(format "non-specialized for clause: ~a:~a:~a"
(syntax-source #'rhs)
(syntax-line #'rhs)
(syntax-column #'rhs))
#'rhs))
(with-syntax ([[(id ...) rhs] (introducer (syntax-local-introduce clause))])
(with-syntax ([(post-id ...) (generate-temporaries #'(id ...))])
(arm-for-clause
(syntax-local-introduce
(introducer
#`(([(pos->vals pos-pre-inc pos-next init pos-cont? val-cont? all-cont?)
#,(syntax-property
(syntax/loc #'rhs (make-sequence '(id ...) rhs))
'feature-profile:generic-sequence #t)])
(void)
([pos init])
#,(syntax-property
(syntax/loc #'rhs (if pos-cont? (pos-cont? pos) #t))
'feature-profile:generic-sequence #t)
([(id ... all-cont?/pos)
(let-values ([(id ...) #,(syntax-property
(syntax/loc #'rhs (pos->vals pos))
'feature-profile:generic-sequence #t)])
(values id ...
;; If we need to call `all-cont?`, close over
;; `id`s here, so `id`s are not implicitly
;; retained while the body runs:
(and all-cont?
(lambda (pos)
(all-cont? pos id ...)))))]
[(pos) #,(syntax-property
(syntax/loc #'rhs (if pos-pre-inc (pos-pre-inc pos) pos))
'feature-profile:generic-sequence #t)])
#,(syntax-property
(syntax/loc #'rhs (if val-cont? (val-cont? id ...) #t))
'feature-profile:generic-sequence #t)
#,(syntax-property
(syntax/loc #'rhs (if all-cont?/pos (all-cont?/pos pos) #t))
'feature-profile:generic-sequence #t)
#,(syntax-property
(syntax/loc #'rhs ((pos-next pos)))
'feature-profile:generic-sequence #t))))
(make-rearm)))))]
[_
(raise-syntax-error #f
"bad sequence binding clause" orig-stx clause)]))))
(define-syntax (:do-in stx)
(raise-syntax-error #f
"illegal outside of a loop or comprehension binding" stx))
(define-syntax-rule (unless-unsafe e)
(unless (variable-reference-from-unsafe? (#%variable-reference)) e))
;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; streams & sequences
;; structure type for generic sequences:
(define-values (struct:do-sequence
make-do-sequence
do-sequence?
do-sequence-ref
do-sequence-set!)
(make-struct-type 'sequence #f 1 0 #f))
;; property for generic streams
(define-values (prop:stream stream-via-prop? stream-ref)
(make-struct-type-property
'stream
(lambda (v si)
(unless (and (vector? v)
(= 3 (vector-length v))
(procedure? (vector-ref v 0))
(procedure-arity-includes? (vector-ref v 0) 1)
(procedure? (vector-ref v 1))
(procedure-arity-includes? (vector-ref v 1) 1)
(procedure? (vector-ref v 2))
(procedure-arity-includes? (vector-ref v 2) 1))
(raise-argument-error 'guard-for-prop:stream
(string-append
"(vector/c (procedure-arity-includes/c 1)\n"
" (procedure-arity-includes/c 1)\n"
" (procedure-arity-includes/c 1))")
v))
(vector->immutable-vector v))))
;; new-style sequence property, where the property value is a procedure
;; to get the sequence-driving value and procedures;
;; this property is not currently exported
(define-values (prop:gen-sequence sequence-via-prop? sequence-ref)
(make-struct-type-property
'sequence
(lambda (v si)
(unless (and (procedure? v)
(procedure-arity-includes? v 1))
(raise-argument-error 'guard-for-prop:sequence
"(procedure-arity-includes/c 1)"
v))
v)))
;; exported sequence property, where the property value
;; is a procedure to get a sequence
(define-values (prop:sequence :sequence? :sequence-ref)
(make-struct-type-property
'sequence
(lambda (v sinfo)
(unless (and (procedure? v) (procedure-arity-includes? v 1))
(raise-argument-error 'sequence-property-guard "(procedure-arity-includes/c 1)" v))
(lambda (self)
(let ([s (v self)])
(unless (sequence? s)
(raise-mismatch-error
'sequence-generate
"procedure (value of prop:sequence) produced a non-sequence: "
s))
s)))))
(define-syntax define-sequence-syntax
(syntax-rules ()
[(_ id expr-transformer-expr clause-transformer-expr)
(define-syntax id
(create-sequence-transformer expr-transformer-expr
clause-transformer-expr))]))
(define (stream? v)
(or (list? v)
(stream-via-prop? v)))
(define (unsafe-stream-not-empty? v)
(if (null? v)
#f
(or (pair? v)
(not ((unsafe-vector-ref (stream-ref v) 0) v)))))
(define (stream-empty? v)
(or (null? v)
(if (stream? v)
(if (pair? v)
#f
((unsafe-vector-ref (stream-ref v) 0) v))
(raise-argument-error 'stream-empty?
"stream?"
v))))
(define (unsafe-stream-first v)
(cond [(pair? v) (car v)]
[else ((unsafe-vector-ref (stream-ref v) 1) v)]))
(define (stream-first v)
(if (and (stream? v)
(not (stream-empty? v)))
(unsafe-stream-first v)
(raise-argument-error 'stream-first
"(and/c stream? (not/c stream-empty?))"
v)))
(define (unsafe-stream-rest v)
(cond [(pair? v) (cdr v)]
[else (let ([r ((unsafe-vector-ref (stream-ref v) 2) v)])
(unless (stream? r)
(raise-mismatch-error 'stream-rest-guard
"result is not a stream: "
r))
r)]))
(define (stream-rest v)
(if (and (stream? v)
(not (stream-empty? v)))
(unsafe-stream-rest v)
(raise-argument-error 'stream-rest
"(and/c stream? (not/c stream-empty?))"
v)))
(define (sequence? v)
(or (exact-nonnegative-integer? v)
(do-sequence? v)
(sequence-via-prop? v)
(stream? v)
(mpair? v)
(vector? v)
(flvector? v)
(fxvector? v)
(string? v)
(bytes? v)
(input-port? v)
(hash? v)
(and (:sequence? v) (not (struct-type? v)))))
(define (make-sequence who v)
(cond
[(exact-nonnegative-integer? v) (:integer-gen v)]
[(do-sequence? v)
(call-with-values (lambda () ((do-sequence-ref v 0)))
(case-lambda
[(pos->vals pos-next init pos-cont? val-cont? all-cont?)
(values pos->vals #f pos-next init pos-cont? val-cont? all-cont?)]
[(pos->vals pre-pos-next pos-next init pos-cont? val-cont? all-cont?)
(values pos->vals pre-pos-next pos-next init pos-cont? val-cont? all-cont?)]))]
[(mpair? v) (:mlist-gen v)]
[(list? v) (:list-gen v)]
[(vector? v) (:vector-gen v 0 (vector-length v) 1)]
[(flvector? v) (:flvector-gen v 0 (flvector-length v) 1)]
[(fxvector? v) (:fxvector-gen v 0 (fxvector-length v) 1)]
[(string? v) (:string-gen v 0 (string-length v) 1)]
[(bytes? v) (:bytes-gen v 0 (bytes-length v) 1)]
[(input-port? v) (:input-port-gen v)]
[(hash? v) (:hash-gen v hash-iterate-key+value
hash-iterate-first
hash-iterate-next)]
[(sequence-via-prop? v) ((sequence-ref v) v)]
[(:sequence? v) (make-sequence who ((:sequence-ref v) v))]
[(stream? v) (:stream-gen v)]
[else (raise
(exn:fail:contract
(format "for: expected a sequence for ~a, got something else: ~v"
(if (= 1 (length who))
(car who)
who)
v)
(current-continuation-marks)))]))
(define-values (struct:range
make-range
range?
range-ref
range-set!)
(make-struct-type 'stream #f 3 0 #f
(list (cons prop:stream
(vector
(lambda (v)
(let ([cont? (range-ref v 2)])
(and cont?
(not (cont? (range-ref v 0))))))
(lambda (v) (range-ref v 0))
(lambda (v) (make-range
((range-ref v 1) (range-ref v 0))
(range-ref v 1)
(range-ref v 2)))))
(cons prop:gen-sequence
(lambda (v)
(values
values
#f
(range-ref v 1)
(range-ref v 0)
(range-ref v 2)
#f
#f))))))
(define (check-range a b step)
(unless (real? a) (raise-argument-error 'in-range "real?" a))
(unless (real? b) (raise-argument-error 'in-range "real?" b))
(unless (real? step) (raise-argument-error 'in-range "real?" step)))
(define in-range
(case-lambda
[(b) (in-range 0 b 1)]
[(a b) (in-range a b 1)]
[(a b step)
(check-range a b step)
(let* ([cont? (if (step . >= . 0)
(lambda (x) (< x b))
(lambda (x) (> x b)))]
[inc (lambda (x) (+ x step))])
(make-range a inc cont?))]))
(define (:integer-gen v)
(values values #f add1 0 (lambda (i) (i . < . v)) #f #f))
(begin-encourage-inline
(define (check-naturals n)
(unless (and (integer? n)
(exact? n)
(n . >= . 0))
(raise-argument-error 'in-naturals
"exact-nonnegative-integer?"
n))))
(define in-naturals
(case-lambda
[() (in-naturals 0)]
[(n)
(check-naturals n)
(make-range n add1 #f)]))
(define-values (struct:list-stream
make-list-stream
list-stream?
list-stream-ref
list-stream-set!)
(make-struct-type 'stream #f 1 0 #f
(list (cons prop:stream
(vector
(lambda (v) (not (pair? (list-stream-ref v 0))))
(lambda (v) (car (list-stream-ref v 0)))
(lambda (v) (make-list-stream (cdr (list-stream-ref v 0))))))
(cons prop:gen-sequence
(lambda (v)
(values
car
cdr
values
(list-stream-ref v 0)
pair?
#f
#f))))))
(define (check-list l)
(unless (list? l) (raise-argument-error 'in-list "list?" l)))
(define (in-list l)
(check-list l)
(make-list-stream l))
(define (:list-gen l)
(values car cdr values l pair? #f #f))
(define (in-mlist l)
(unless (mpair? l) (raise-argument-error 'in-mlist "mpair?" l))
(make-do-sequence (lambda () (:mlist-gen l))))
(define (:mlist-gen l)
(values mcar #f mcdr l mpair? #f #f))
(define (in-input-port-bytes p)
(unless (input-port? p)
(raise-argument-error 'in-input-port-bytes "input-port?" p))
(make-do-sequence (lambda () (:input-port-gen p))))
(define (:input-port-gen p)
(values read-byte #f values p #f
(lambda (x) (not (eof-object? x)))
#f))
(define (in-input-port-chars p)
(unless (input-port? p)
(raise-argument-error 'in-input-port-chars "input-port?" p))
(in-producer (lambda () (read-char p)) eof))
(define (check-in-port r p)
(unless (and (procedure? r) (procedure-arity-includes? r 1))
(raise-argument-error 'in-port "(procedure-arity-includes/c 1)" r))
(unless (input-port? p) (raise-argument-error 'in-port "input-port?" p)))
(define in-port
(case-lambda
[() (in-port read (current-input-port))]
[(r) (in-port r (current-input-port))]
[(r p)
(check-in-port r p)
(in-producer (lambda () (r p)) eof)]))
(define (check-in-lines p mode)
(unless (input-port? p) (raise-argument-error 'in-lines "input-port?" p))
(unless (memq mode '(linefeed return return-linefeed any any-one))
(raise-argument-error
'in-lines
"(or/c 'linefeed 'return 'return-linefeed 'any 'any-one)"
mode)))
(define in-lines
(case-lambda
[() (in-lines (current-input-port) 'any)]
[(p) (in-lines p 'any)]
[(p mode)
(check-in-lines p mode)
(in-producer (lambda () (read-line p mode)) eof)]))
(define (check-in-bytes-lines p mode)
(unless (input-port? p) (raise-argument-error 'in-bytes-lines "input-port" p))
(unless (memq mode '(linefeed return return-linefeed any any-one))
(raise-argument-error
'in-bytes-lines
"(or/c 'linefeed 'return 'return-linefeed 'any 'any-one)"
mode)))
(define in-bytes-lines
(case-lambda
[() (in-bytes-lines (current-input-port) 'any)]
[(p) (in-bytes-lines p 'any)]
[(p mode)
(check-in-bytes-lines p mode)
(in-producer (lambda () (read-bytes-line p mode)) eof)]))
(define (in-stream l)
(unless (stream? l) (raise-argument-error 'in-stream "stream?" l))
(make-do-sequence (lambda () (:stream-gen l))))
(define (:stream-gen l)
(values
unsafe-stream-first unsafe-stream-rest values l unsafe-stream-not-empty? #f #f))
;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; hash sequences
;; assembles hash iterator functions to give to make-do-sequence
(define :hash-gen
(case-lambda
[(ht -get -first -next)
(values (lambda (pos) (-get ht pos))
#f
(lambda (pos) (-next ht pos))
(-first ht)
(lambda (pos) pos) ; #f position means stop
#f
#f)]
[(ht -get -first -next bad-v)
(values (lambda (pos) (-get ht pos bad-v))
#f
(lambda (pos) (-next ht pos))
(-first ht)
(lambda (pos) pos) ; #f position means stop
#f
#f)]))
(define (mutable? ht) (not (immutable? ht)))
(define (not-weak? ht) (not (hash-weak? ht)))
;; Each call defines 4 in-HASHTYPE-VALs sequences,
;; where VAL = key, value, pair, key+value (key+value not used in seq name)
;; and HASHTYPE specifies the the set of hash-iterate- fns to use
;; eg, hash, immutable-hash, mutable-hash, weak-hash
(define-syntax (define-in-hash-sequences stx)
(syntax-case stx (element-types:)
[(_ element-types: V ...)
(with-syntax
([VAL (join-ids #'(V ...) #'+)])
(with-syntax
([IN-HASH-DEFINER (format-id #'VAL "define-in-hash-~as-seq" #'VAL)])
#'(begin
;; 1) define sequence syntax definer
;; where HASHTYPE = hash, immutable-hash, etc
;; and "checks" are predicates to apply to the input hash
;; (not including hash?)
(define-syntax (IN-HASH-DEFINER stx)
(syntax-case stx (hash-type: checks:)
[(def hash-type: HASHTYPE) #'(def hash-type: HASHTYPE checks:)]
[(def hash-type: HASHTYPE checks: p? (... ...))
(with-syntax
([IN-HASH-SEQ
(if (equal? (syntax->datum #'VAL) 'key+value)
(format-id #'def "in-~a" #'HASHTYPE)
(format-id #'def "in-~a-~as" #'HASHTYPE #'VAL))]
[PREFIX
(if (equal? (syntax->datum #'HASHTYPE) 'hash)
(format-id #'def "~a-iterate" #'HASHTYPE)
(format-id #'def "unsafe-~a-iterate" #'HASHTYPE))]
[HASHTYPE? #'(lambda (ht) (and (hash? ht) (p? ht) (... ...)))]
[ERR-STR
(datum->syntax #'HASHTYPE
(if (null? (syntax->list #'(p? (... ...))))
"hash?"
(string-append
"(and/c hash? "
(symbol->string
(syntax->datum (join-ids #'(p? (... ...)) #'" ")))
")")))])
(with-syntax
([-first (format-id #'PREFIX "~a-first" #'PREFIX)]
[-next (format-id #'PREFIX "~a-next" #'PREFIX)]
[-VAL (format-id #'PREFIX "~a-~a" #'PREFIX #'VAL)]
[CHECK-SEQ (format-id #'def "check-~a" #'IN-HASH-SEQ)]
[AS-EXPR-SEQ (format-id #'def "default-~a" #'IN-HASH-SEQ)])
#'(begin
(begin-encourage-inline
(define (CHECK-SEQ ht)
(unless (HASHTYPE? ht)
(raise-argument-error 'IN-HASH-SEQ ERR-STR ht))))
(define AS-EXPR-SEQ
(let ([IN-HASH-SEQ
(case-lambda
[(ht)
(CHECK-SEQ ht)
(make-do-sequence (lambda () (:hash-gen ht -VAL -first -next)))]
[(ht bad-v)
(CHECK-SEQ ht)
(make-do-sequence (lambda () (:hash-gen ht -VAL -first -next bad-v)))])])
IN-HASH-SEQ))
(define-sequence-syntax IN-HASH-SEQ
(lambda () #'AS-EXPR-SEQ)
(lambda (stx)
(define (transform stx)
(syntax-case stx ()
[[(V ...) (_ ht-expr . extra-args)]
(for-clause-syntax-protect
#'[(V ...)
(:do-in
;;outer bindings
([(ht) ht-expr])
;; outer check
(unless-unsafe (CHECK-SEQ ht))
;; loop bindings
([i (-first ht)])
;; pos check
i
;; inner bindings
([(V ...) (-VAL ht i . extra-args)])
;; pre guard
#t
;; post guard
#t
;; loop args
((-next ht i)))])]))
(syntax-case stx ()
[[(V ...) (_ ht-expr)]
(transform stx)]
[[(V ...) (_ ht-expr bad-index-expr)]
(transform stx)]
[_ #f]))))))]))
;; 2) define sequence syntaxes (using just-defined definer):
(IN-HASH-DEFINER hash-type: hash)
(IN-HASH-DEFINER hash-type: mutable-hash checks: mutable? not-weak?)
(IN-HASH-DEFINER hash-type: immutable-hash checks: immutable?)
(IN-HASH-DEFINER hash-type: weak-hash checks: hash-weak?))))]))
(define-in-hash-sequences element-types: key value)
(define-in-hash-sequences element-types: key)
(define-in-hash-sequences element-types: value)
(define-in-hash-sequences element-types: pair)
;; Vector-like sequences --------------------------------------------------
;; (: check-ranges (Symbol Natural Integer Integer Natural -> Void))
;;
;; As no object can have more slots than can be indexed by
;; the largest fixnum, after running these checks start,
;; stop, and step are guaranteed to be fixnums.
(define (check-ranges who vec start stop step len)
(unless (and (exact-nonnegative-integer? start)
(or (< start len) (= len start stop)))
(raise-range-error who "vector" "starting " start vec 0 (sub1 len)))
(unless (and (exact-integer? stop) (<= -1 stop) (<= stop len))
(raise-range-error who "vector" "stopping " stop vec -1 len))
(unless (and (exact-integer? step) (not (zero? step)))
(raise-argument-error who "(and/c exact-integer? (not/c zero?))" step))
(when (and (< start stop) (< step 0))
(raise-arguments-error who
"starting index less than stopping index, but given a negative step"
"starting index" start
"stopping index" stop
"step" step))
(when (and (< stop start) (> step 0))
(raise-arguments-error who
"starting index more than stopping index, but given a positive step"
"starting index" start
"stopping index" stop
"step" step)))
;; (: normalise-inputs (A) (Symbol String (Any -> Boolean) (A -> Natural) Any Any Any Any -> (values Fixnum Fixnum Fixnum)))
;;
;; Checks all inputs are valid for an in-vector sequence,
;; and if so returns the vector, start, stop, and
;; step. Start, stop, and step are guaranteed to be Fixnum
(define (normalise-inputs who type-name vector? unsafe-vector-length
vec start stop step)
(unless (vector? vec)
(raise-argument-error who type-name vec))
(let* ([len (unsafe-vector-length vec)]
[stop* (if stop stop len)])
(check-ranges who vec start stop* step len)
(values vec start stop* step)))
(define-syntax define-in-vector-like
(syntax-rules ()
[(define-in-vector-like (in-vector-name check-vector-name)
type-name-str vector?-id vector-length-id :vector-gen-id)
(begin
(define in-vector-name
(case-lambda
[(v) (in-vector-name v 0 #f 1)]
[(v start) (in-vector-name v start #f 1)]
[(v start stop) (in-vector-name v start stop 1)]
[(v start stop step)
(let-values (([v start stop step]
(normalise-inputs 'in-vector-name type-name-str vector?-id vector-length-id
v start stop step)))
(make-do-sequence (lambda () (:vector-gen-id v start stop step))))]))
(define (check-vector-name v)
(unless (vector?-id v)
(raise-argument-error 'in-vector-name type-name-str v))))]))
(define-syntax define-:vector-like-gen
(syntax-rules ()
[(define-:vector-like-gen :vector-like-name unsafe-vector-ref-id)
(define (:vector-like-name v start stop step)
(values
;; pos->element
(lambda (i) (unsafe-vector-ref-id v i))
;; pre-pos-inc
#f
;; next-pos
;; Minor optimisation. I assume add1 is faster than \x.x+1
(if (= step 1) add1 (lambda (i) (+ i step)))
;; initial pos
start
;; continue?
(if (> step 0)
(lambda (i) (< i stop))
(lambda (i) (> i stop)))
#f
#f))]))
(define-for-syntax (make-in-vector-like in-vector-name
type-name-str
vector?-id
unsafe-vector-length-id
in-vector-id
check-vector-id
unsafe-vector-ref-id)
(define (in-vector-like stx)
(with-syntax ([in-vector-name in-vector-name]
[type-name type-name-str]
[vector? vector?-id]
[in-vector in-vector-id]
[check-vector check-vector-id]
[unsafe-vector-length unsafe-vector-length-id]
[unsafe-vector-ref unsafe-vector-ref-id])
(syntax-case stx ()
;; Fast case
[[(id) (_ vec-expr)]
(for-clause-syntax-protect
#'[(id)
(:do-in
;;outer bindings
([(vec len) (let ([vec vec-expr])
(check-vector vec)
(values vec (unsafe-vector-length vec)))])
;; outer check
#f
;; loop bindings
([pos 0])
;; pos check
(pos . unsafe-fx< . len)
;; inner bindings
([(id) (unsafe-vector-ref vec pos)])
;; pre guard
#t
;; post guard
#t
;; loop args
((unsafe-fx+ 1 pos)))])]
;; General case
[((id) (_ vec-expr start))
(in-vector-like (syntax ((id) (_ vec-expr start #f 1))))]
[((id) (_ vec-expr start stop))
(in-vector-like (syntax ((id) (_ vec-expr start stop 1))))]
[((id) (_ vec-expr start stop step))
(let ([all-fx? (memq (syntax-e #'step) '(1 -1))])
(for-clause-syntax-protect
#`[(id)
(:do-in
;; Outer bindings
;; start*, stop*, and step* are guaranteed to be exact integers
([(v* start* stop* step*)
(normalise-inputs (quote in-vector-name) type-name
;; reverse-eta triggers JIT inlining of
;; primitives, which is good for futures:
(lambda (x) (vector? x))
(lambda (x) (unsafe-vector-length x))
vec-expr start stop step)])
;; Outer check is done by normalise-inputs
#t
;; Loop bindings
([idx start*])
;; Pos guard
#,(cond
[(not (number? (syntax-e #'step)))
#`(if (step* . >= . 0) (< idx stop*) (> idx stop*))]
[((syntax-e #'step) . >= . 0)
(if all-fx?
#'(unsafe-fx< idx stop*)
#'(< idx stop*))]
[else
(if all-fx?
#'(unsafe-fx> idx stop*)
#'(> idx stop*))])
;; Inner bindings
([(id) (unsafe-vector-ref v* idx)])
;; Pre guard
#t
;; Post guard
#t
;; Loop args
((#,(if all-fx? #'unsafe-fx+ #'+) idx step)))]))]
[_ #f])))
in-vector-like)
(define-:vector-like-gen :vector-gen unsafe-vector-ref)
(define-in-vector-like (in-vector check-vector)
"vector" vector? vector-length :vector-gen)
(define-sequence-syntax *in-vector
(lambda () #'in-vector)
(make-in-vector-like 'in-vector
"vector"
#'vector?
#'unsafe-vector-length
#'in-vector
#'check-vector
#'unsafe-vector-ref))
(define-:vector-like-gen :string-gen string-ref)
(define-in-vector-like (in-string check-string)
"string" string? string-length :string-gen)
(define-sequence-syntax *in-string
(lambda () #'in-string)
(make-in-vector-like 'in-string
"string"
#'string?
#'unsafe-string-length
#'in-string
#'check-string
#'string-ref))
(define-:vector-like-gen :bytes-gen unsafe-bytes-ref)
(define-in-vector-like (in-bytes check-bytes)
"bytes" bytes? bytes-length :bytes-gen)
(define-sequence-syntax *in-bytes
(lambda () #'in-bytes)
(make-in-vector-like 'in-bytes
"bytes"
#'bytes?
#'unsafe-bytes-length
#'in-bytes
#'check-bytes
#'unsafe-bytes-ref))
(define-:vector-like-gen :flvector-gen unsafe-flvector-ref)
;; in-flvector is defined in racket/flonum
(define-:vector-like-gen :fxvector-gen unsafe-fxvector-ref)
;; in-fxvector is defined in racket/fixnum
;; ------------------------------------------------------------------------
(define (stop-before g pred)
(unless (sequence? g) (raise-argument-error 'stop-before "sequence?" g))
(unless (and (procedure? pred)
(procedure-arity-includes? pred 1))
(raise-argument-error 'stop-before "(procedure-arity-includes/c 1)" pred))
(make-do-sequence (lambda ()
(let-values ([(pos->val pre-pos-next pos-next init pos-cont? pre-cont? post-cont?)
(make-sequence #f g)])
(values pos->val
pre-pos-next
pos-next
init
pos-cont?
(case-lambda
[(val) (and (if pre-cont? (pre-cont? val) #t)
(not (pred val)))]
[vals (and (if pre-cont? (apply pre-cont? vals) #t)
(not (apply pred vals)))])
post-cont?)))))
(define (stop-after g pred)
(unless (sequence? g) (raise-argument-error 'stop-after "sequence?" g))
(unless (and (procedure? pred)
(procedure-arity-includes? pred 1))
(raise-argument-error 'stop-after "(procedure-arity-includes/c 1)" pred))
(make-do-sequence (lambda ()
(let-values ([(pos->val pre-pos-next pos-next init pos-cont? pre-cont? post-cont?)
(make-sequence #f g)])
(values pos->val
pre-pos-next
pos-next
init
pos-cont?
pre-cont?
(case-lambda
[(pos val) (and (if post-cont? (post-cont? pos val) #t)
(not (pred val)))]
[(pos . vals) (and (if post-cont? (apply post-cont? pos vals) #t)
(not (apply pred vals)))]))))))
(define (in-indexed g)
(unless (sequence? g) (raise-argument-error 'in-indexed "sequence?" g))
(make-do-sequence (lambda ()
(let-values ([(pos->val pre-pos-next pos-next init pos-cont? pre-cont? post-cont?)
(make-sequence #f g)])
(values (lambda (pos) (values (pos->val (car pos)) (cdr pos)))
(and pre-pos-next
(lambda (pos) (cons (pre-pos-next (car pos)) (cdr pos))))
(lambda (pos) (cons (pos-next (car pos)) (add1 (cdr pos))))
(cons init 0)
(and pos-cont?
(lambda (pos) (pos-cont? (car pos))))
(and pre-cont?
(lambda (val idx) (pre-cont? val)))
(and post-cont?
(lambda (pos val idx) (post-cont? pos val))))))))
(define (in-value v)
(make-do-sequence (lambda ()
(values (lambda (pos) v)
(lambda (pos) #f)
#t
(lambda (pos) pos)
#f
#f))))
(define (in-values-sequence g)
(unless (sequence? g) (raise-argument-error 'in-values-sequence "sequence?" g))
(make-do-sequence (lambda ()
(let-values ([(pos->val pre-pos-next pos-next init pos-cont? pre-cont? post-cont?)
(make-sequence #f g)])
(values (lambda (pos) (call-with-values (lambda () (pos->val pos))
list))
pre-pos-next
pos-next
init
pos-cont?
(and pre-cont?
(lambda (vals) (apply pre-cont? vals)))
(and post-cont?
(lambda (pos vals) (apply post-cont? pos vals))))))))
(define (in-values*-sequence g)
(unless (sequence? g) (raise-argument-error 'in-values-sequence "sequence?" g))
(make-do-sequence (lambda ()
(let-values ([(pos->val pre-pos-next pos-next init pos-cont? pre-cont? post-cont?)
(make-sequence #f g)])
(values (lambda (pos) (call-with-values (lambda () (pos->val pos))
(case-lambda
[(v) (if (list? v) (list v) v)]
[vs vs])))
pre-pos-next
pos-next
init
pos-cont?
(and pre-cont?
(lambda (vals)
(if (list? vals)
(apply pre-cont? vals)
(pre-cont? vals))))
(and post-cont?
(lambda (pos vals)
(if (list? vals)
(apply post-cont? pos vals)
(post-cont? pos vals)))))))))
;; ----------------------------------------
(define (append-sequences sequences cyclic?)
(define (seqs->m+g+r seqs)
(if (pair? seqs)
(let-values ([(more? get) (sequence-generate (car seqs))]
[(seqs) (cdr seqs)])
(if (more?) (list* more? get seqs) (seqs->m+g+r seqs)))
(and cyclic? (seqs->m+g+r sequences))))
(make-do-sequence
(lambda ()
;; place is (cur-more? cur-get rest-seqs ...) or #f
(values (lambda (m+g+r) ((cadr m+g+r)))
(lambda (m+g+r)
(if (and (pair? m+g+r) (not ((car m+g+r))))
(seqs->m+g+r (cddr m+g+r))
m+g+r))
(seqs->m+g+r sequences)
values
#f
#f))))
(define (check-sequences who sequences)
(for-each (lambda (g)
(unless (sequence? g) (raise-argument-error who "sequence?" g)))
sequences))
(define (in-sequences . sequences)
(check-sequences 'in-sequences sequences)
(if (and (pair? sequences) (null? (cdr sequences)))
(car sequences)
(append-sequences sequences #f)))
(define (in-cycle . sequences)
(check-sequences 'in-cycle sequences)
(append-sequences sequences #t))
(define (in-parallel . sequences)
(check-sequences 'in-parallel sequences)
(if (= 1 (length sequences))
(car sequences)
(make-do-sequence
(lambda ()
(let-values ([(pos->vals pre-pos-nexts pos-nexts inits pos-cont?s pre-cont?s post-cont?s)
(for/lists (p->v p-p-n p-n i ps? pr? po?) ([g sequences])
(make-sequence #f g))])
(values
(lambda (poses) (apply values (map (lambda (pos->val pos) (pos->val pos))
pos->vals
poses)))
(and (ormap values pre-pos-nexts)
(lambda (poses) (map (lambda (pre-pos-next pos) (if pre-pos-next (pre-pos-next pos) pos))
pre-pos-nexts
poses)))
(lambda (poses) (map (lambda (pos-next pos) (pos-next pos))
pos-nexts
poses))
inits
(and (ormap values pos-cont?s)
(lambda (poses) (andmap (lambda (pos-cont? pos)
(if pos-cont? (pos-cont? pos) #t))
pos-cont?s
poses)))
(and (ormap values pre-cont?s)
(lambda vals (andmap (lambda (pre-cont? val)
(if pre-cont? (pre-cont? val) #t))
pre-cont?s
vals)))
(and (ormap values post-cont?s)
(lambda (poses . vals) (andmap (lambda (post-cont? pos val)
(if post-cont? (post-cont? pos val) #t))
post-cont?s
poses
vals)))))))))
(define in-producer
(case-lambda
[(producer)
;; simple stop-less version
(make-do-sequence (lambda () (values (λ _ (producer)) void (void) #f #f #f)))]
[(producer stop . more)
(define produce!
(if (null? more)
(lambda (_) (producer))
(lambda (_) (apply producer more))))
(define stop?
(cond [(not (procedure? stop))
(lambda (x) (not (eq? x stop)))]
[(equal? 1 (procedure-arity stop))
(lambda (x) (not (stop x)))]
[else
(lambda xs (not (apply stop xs)))]))
(make-do-sequence
(lambda ()
(values produce! void (void) #f stop? #f)))]))
;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; running sequences outside of a loop:
(define-values (struct:do-stream
make-do-stream
do-stream?
do-stream-ref
do-stream-set!)
(make-struct-type 'stream #f 3 0 #f
(list (cons prop:stream
(vector
(lambda (v) ((do-stream-ref v 0)))
(lambda (v) ((do-stream-ref v 1)))
(lambda (v) ((do-stream-ref v 2))))))))
(define empty-stream (make-do-stream (lambda () #t) void void))
(define (sequence->stream s)
(unless (sequence? s)
(raise-argument-error 'sequence-generate "sequence?" s))
(cond
[(stream? s) s]
[else
(let-values ([(pos->val pre-pos-next pos-next init pos-cont? pre-cont? post-cont?)
(make-sequence #f s)])
(define (gen-stream pos)
(let ([done? #f]
[vals #f]
[empty? #f]
[next #f])
(define (force!)
(unless done?
(if (if pos-cont? (pos-cont? pos) #t)
(begin
(set! vals (call-with-values (lambda () (pos->val pos)) list))
(when pre-pos-next (set! pos (pre-pos-next pos)))
(unless (if pre-cont? (apply pre-cont? vals) #t)
(set! vals #f)
(set! empty? #t)))
(set! empty? #t))
(set! done? #t)))
(make-do-stream (lambda () (force!) empty?)
(lambda () (force!) (apply values vals))
(lambda ()
(force!)
(if next
next
(begin
(if (if post-cont? (apply post-cont? pos vals) #t)
(set! next (gen-stream (pos-next pos)))
(set! next empty-stream))
next))))))
(gen-stream init))]))
(define (no-more)
(raise (exn:fail:contract "sequence has no more values"
(current-continuation-marks))))
(define (sequence-generate g)
(unless (sequence? g)
(raise-argument-error 'sequence-generate "sequence?" g))
(let-values ([(pos->val pre-pos-next pos-next init pos-cont? pre-cont? post-cont?)
(make-sequence #f g)])
(let ([pos init])
(letrec ([more? #f]
[prep-val! #f]
[next #f])
(letrec ([init-more?
(lambda () (prep-val!) (more?))]
[init-next
(lambda () (prep-val!) (next))]
[init-prep-val!
(lambda ()
(if (if pos-cont? (pos-cont? pos) #t)
(call-with-values
(lambda ()
(begin0
(pos->val pos)
(when pre-pos-next
(set! pos (pre-pos-next pos)))))
(lambda vals
(if (if pre-cont? (apply pre-cont? vals) #t)
(begin
(set! more? (lambda () #t))
(set! next
(lambda ()
(let ([v vals])
(set! prep-val!
(lambda ()
(if (if post-cont?
(apply post-cont? pos vals)
#t)
(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 ([sequence-more? (lambda () (more?))]
[sequence-next (lambda () (next))])
(values sequence-more?
sequence-next)))))))
(define (sequence-generate* g)
(unless (sequence? g)
(raise-argument-error 'sequence-generate* "sequence?" g))
(let-values ([(pos->val pre-pos-next pos-next init pos-cont? pre-cont? post-cont?)
(make-sequence #f g)])
(letrec ([next!
(lambda (pos)
(if (if pos-cont? (pos-cont? pos) #t)
(call-with-values
(lambda () (begin0
(pos->val pos)
(when pre-pos-next
(set! pos (pre-pos-next pos)))))
(lambda vals
(if (if pre-cont? (apply pre-cont? vals) #t)
(values vals
(lambda ()
(if (if post-cont?
(apply post-cont? pos vals)
#t)
(next! (pos-next pos))
(values #f no-more))))
(values #f no-more))))
(values #f no-more)))])
(next! init))))
;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; core `for/fold' syntax
(define-syntax values*
(syntax-rules ()
[(_ x) x]
[(_ x ...) (values x ...)]))
(define-syntax-rule (inner-recur/fold (fold-var ...) (let () expr ...) next-k)
(let-values ([(fold-var ...) (let () expr ...)])
next-k))
(define-syntax (push-under-break stx)
(syntax-case stx ()
[(_ inner-recur fold-vars [expr ...] next-k break-k final?-id)
(let loop ([l (syntax->list #'(expr ...))] [pre-accum null])
(cond
[(null? l)
;; No #:break form
#'(inner-recur fold-vars (let-values () expr ...) (if final?-id break-k next-k))]
[(eq? '#:break (syntax-e (car l)))
;; Found a #:break form
#`(let-values ()
#,@(reverse pre-accum)
(if #,(cadr l)
break-k
(push-under-break inner-recur fold-vars #,(cddr l) next-k break-k final?-id)))]
[(eq? '#:final (syntax-e (car l)))
;; Found a #:final form
#`(let-values ()
#,@(reverse pre-accum)
(let ([final? (or #,(cadr l) final?-id)])
(push-under-break inner-recur fold-vars #,(cddr l) next-k break-k final?)))]
[else (loop (cdr l) (cons (car l) pre-accum))]))]))
(define-syntax (for/foldX/derived stx)
(syntax-case stx ()
;; Done case (no more clauses, and no generated clauses to emit):
[(_ [orig-stx inner-recur nested? emit? ()] ([fold-var fold-init] ...) next-k break-k final?-id ()
expr1 expr ...)
(if (syntax-e #'inner-recur)
;; General, non-nested-loop approach:
#`(let ([fold-var fold-init] ...)
(push-under-break inner-recur (fold-var ...) [expr1 expr ...] next-k break-k final?-id))
;; Nested-loop approach (which is slightly faster when it works):
#`(let ([fold-var fold-init] ...)
(let-values ([(fold-var ...) (let () expr1 expr ...)])
(values fold-var ...))))]
;; Switch-to-emit case (no more clauses to generate):
[(_ [orig-stx inner-recur nested? #f binds] fold-bind next-k break-k final?-id () . body)
#`(for/foldX/derived [orig-stx inner-recur nested? #t binds] fold-bind next-k break-k final?-id () . body)]
;; Emit case:
[(_ [orig-stx inner-recur nested? #t binds] ([fold-var fold-init] ...) next-k break-k final?-id rest expr1 . body)
(with-syntax ([(([outer-binding ...]
outer-check
[loop-binding ...]
pos-guard
[inner-binding ...]
pre-guard
post-guard
[loop-arg ...]) ...) (reverse (syntax->list #'binds))])
(quasisyntax/loc #'orig-stx
(let-values (outer-binding ... ...)
outer-check ...
#,(quasisyntax/loc #'orig-stx
(let for-loop ([fold-var fold-init] ...
loop-binding ... ...)
(if (and pos-guard ...)
(let-values (inner-binding ... ...)
(if (and pre-guard ...)
#,(if (syntax-e #'inner-recur)
;; The general non-nested-loop approach:
#'(let ([post-guard-var (lambda (fold-var ...) (and post-guard ...))])
(for/foldX/derived [orig-stx inner-recur nested? #f ()]
([fold-var fold-var] ...)
(if (post-guard-var fold-var ...)
(for-loop fold-var ... loop-arg ... ...)
next-k)
break-k final?-id
rest expr1 . body))
;; The specialized nested-loop approach, which is
;; slightly faster when it works:
#'(let-values ([(fold-var ...)
(for/foldX/derived [orig-stx inner-recur nested? #f ()]
([fold-var fold-var] ...)
next-k break-k final?-id
rest expr1 . body)])
(if (and post-guard ... (not final?-id))
(for-loop fold-var ... loop-arg ... ...)
next-k)))
next-k))
next-k))))))]
;; Bad body cases:
[(_ [orig-stx . _] fold-bind next-k break-k final?-id ())
(raise-syntax-error
#f "missing body expression after sequence bindings" #'orig-stx)]
[(_ [orig-stx . _] fold-bind next-k break-k final?-id () . rest)
(raise-syntax-error
#f "bad syntax (illegal use of `.') after sequence bindings" #'orig-stx)]
;; Guard case, no pending emits:
[(_ [orig-stx inner-recur nested? #f ()] ([fold-var fold-init] ...) next-k break-k final?-id (#:when expr . rest) . body)
#'(let ([fold-var fold-init] ...)
(if expr
(for/foldX/derived [orig-stx inner-recur nested? #f ()]
([fold-var fold-var] ...) next-k break-k final?-id rest . body)
next-k))]
;; Negative guard case, no pending emits:
[(_ [orig-stx inner-recur nested? #f ()] ([fold-var fold-init] ...) next-k break-k final?-id (#:unless expr . rest) . body)
#'(let ([fold-var fold-init] ...)
(if expr
(if final?-id break-k next-k)
(for/foldX/derived [orig-stx inner-recur nested? #f ()]
([fold-var fold-var] ...) next-k break-k final?-id rest . body)))]
;; Break case, no pending emits:
[(_ [orig-stx inner-recur nested? #f ()] ([fold-var fold-init] ...) next-k break-k final?-id (#:break expr . rest) . body)
#'(let ([fold-var fold-init] ...)
(if expr
break-k
(for/foldX/derived [orig-stx inner-recur nested? #f ()]
([fold-var fold-var] ...) next-k break-k final?-id rest . body)))]
;; Final case, no pending emits:
[(_ [orig-stx inner-recur nested? #f ()] ([fold-var fold-init] ...) next-k break-k final?-id (#:final expr . rest) . body)
#'(let ([fold-var fold-init] ...)
(let ([final? (or expr final?-id)])
(for/foldX/derived [orig-stx inner-recur nested? #f ()]
([fold-var fold-var] ...) next-k break-k final? rest . body)))]
;; Keyword case, pending emits need to be flushed first
[(frm [orig-stx inner-recur nested? #f binds] ([fold-var fold-init] ...) next-k break-k final?-id (kw expr . rest) . body)
(or (eq? (syntax-e #'kw) '#:when)
(eq? (syntax-e #'kw) '#:unless)
(eq? (syntax-e #'kw) '#:break)
(eq? (syntax-e #'kw) '#:final))
#'(frm [orig-stx inner-recur nested? #t binds] ([fold-var fold-init] ...) next-k break-k final?-id (kw expr . rest) . body)]
;; Convert single-value form to multi-value form:
[(_ [orig-stx inner-recur nested? #f binds] fold-bind next-k break-k final?-id ([id rhs] . rest) . body)
(identifier? #'id)
#'(for/foldX/derived [orig-stx inner-recur nested? #f binds] fold-bind next-k break-k final?-id
([(id) rhs] . rest) . body)]
;; If we get here in single-value mode, then it's a bad clause:
[(_ [orig-stx inner-recur #f #f nested? #f binds] fold-bind next-k break-k final?-id (clause . rest) . body)
(raise-syntax-error
#f "bad sequence binding clause" #'orig-stx #'clause)]
;; Expand one multi-value clause, and push it into the results to emit:
[(frm [orig-stx inner-recur nested? #f binds] ([fold-var fold-init] ...) next-k break-k final?-id (clause . rest) . body)
(with-syntax ([bind (expand-clause #'orig-stx #'clause)])
(let ([r #`(frm [orig-stx inner-recur nested? nested? (bind . binds)]
([fold-var fold-init] ...) next-k break-k final?-id rest . body)]
[d (syntax-property #'bind 'disappeared-use)])
(if d
(syntax-property r 'disappeared-use d)
r)))]
[(_ [orig-stx . _] for-bind next-k break-k final?-id clauses . _)
(not (syntax->list #'clauses))
(raise-syntax-error #f "bad sequence binding clauses" #'orig-stx #'clauses)]
[(_ [orig-stx . _] . _)
(raise-syntax-error #f "bad syntax" #'orig-stx)]))
(define-syntax (for/foldX/derived/final stx)
(syntax-case stx ()
[(_ [orig-stx nested?] fold-bind done-k (clause ...) expr ...)
;; If there's a `#:break` or `#:final`, then we need to use the
;; non-nested loop approach to implement them:
(ormap (lambda (s) (or (eq? '#:break (syntax-e s)) (eq? '#:final (syntax-e s))))
(syntax->list #'(clause ... expr ...)))
#'(for/foldX/derived [orig-stx inner-recur/fold nested? #f ()] fold-bind done-k done-k #f (clause ...) expr ...)]
[(_ [orig-stx nested?] fold-bind done-k . rest)
;; Otherwise, allow compilation as nested loops, which can be slightly faster:
#'(for/foldX/derived [orig-stx #f nested? #f ()] fold-bind done-k done-k #f . rest)]))
(define-syntax (for/fold/derived stx)
(syntax-case stx ()
[(_ orig-stx ([fold-var finid-init] ... #:result result-expr) . rest)
(check-identifier-bindings #'orig-stx #'(fold-var ...) "accumulator" #t)
(syntax/loc #'orig-stx
(let-values ([(fold-var ...) (for/foldX/derived/final [orig-stx #f]
([fold-var finid-init] ...)
(values* fold-var ...)
. rest)])
result-expr))]
[(_ orig-stx ([fold-var finid-init] ...) . rest)
(check-identifier-bindings #'orig-stx #'(fold-var ...) "accumulator" #t)
(syntax/loc #'orig-stx
(for/foldX/derived/final [orig-stx #f]
([fold-var finid-init] ...)
(values* fold-var ...)
. rest))]
[(_ orig-stx (bindings ...) . rst)
(raise-syntax-error #f "invalid accumulator binding clause(s)" #'orig-stx #'(bindings ...))]
[(_ orig-stx . rst)
(raise-syntax-error #f "bad syntax" #'orig-stx)]))
(define-syntax (for*/fold/derived stx)
(syntax-case stx ()
[(_ orig-stx ([fold-var finid-init] ... #:result result-expr) . rest)
(check-identifier-bindings #'orig-stx #'(fold-var ...) "accumulator" #t)
(syntax/loc #'orig-stx
(let-values ([(fold-var ...) (for/foldX/derived/final [orig-stx #t]
([fold-var finid-init] ...)
(values* fold-var ...)
. rest)])
result-expr))]
[(_ orig-stx ([fold-var finid-init] ...) . rest)
(check-identifier-bindings #'orig-stx #'(fold-var ...) "accumulator" #t)
(syntax/loc #'orig-stx
(for/foldX/derived/final [orig-stx #t]
([fold-var finid-init] ...)
(values* fold-var ...)
. rest))]
[(_ orig-stx (bindings ...) . rst)
(raise-syntax-error #f "invalid accumulator binding clause(s)" #'orig-stx #'(bindings ...))]
[(_ orig-stx . rst)
(raise-syntax-error #f "bad syntax" #'orig-stx)]))
;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; derived `for' syntax
(define-for-syntax (split-for-body stx body-stx)
(let ([lst (syntax->list body-stx)])
(if lst
(let loop ([exprs lst] [pre-kw null] [post-kw null])
(cond
[(null? exprs)
(if (null? post-kw)
(if (null? pre-kw)
(raise-syntax-error #f
"missing body"
stx)
(raise-syntax-error #f
(format "missing body form after ~a clause" (syntax-e (cadr pre-kw)))
stx
(cadr pre-kw)))
(list (reverse pre-kw) (reverse post-kw)))]
[(memq (syntax-e (car exprs)) '(#:break #:final))
(if (pair? (cdr exprs))
(loop (cddr exprs)
(append (list* (cadr exprs) (car exprs) post-kw)
pre-kw)
null)
(raise-syntax-error #f
(format "missing expression after ~a" (syntax-e (car exprs)))
stx
(car exprs)))]
[else
(loop (cdr exprs) pre-kw (cons (car exprs) post-kw))]))
(raise-syntax-error #f "bad syntax" stx))))
(define-for-syntax (for-variant-stx stx derived-id-stx fold-bind-stx wrap rhs-wrap combine)
(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]
(or (identifier? #'ids)
(andmap identifier? (or (syntax->list #'ids) '(#f))))
(cons #`[ids #,(rhs-wrap #'rhs)]
(loop (cdr bs)))]
[kw
(memq (syntax-e #'kw) '(#:when #:unless #:break #:final))
(cons (car bs)
(if (null? (cdr bs))
null
(cons (cadr bs) (loop (cddr bs)))))]
[_
;; a syntax error; let the /derived form
;; handle it, and no need to wrap any more:
bs])))]
[((middle-expr ...) (end-expr ...))
(split-for-body stx #'(expr1 expr ...))])
(quasisyntax/loc stx
#,(wrap (quasisyntax/loc stx
(derived-id #,stx fold-bind (bind ...)
middle-expr ...
#,(combine (syntax/loc stx (let () end-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)
(define-syntax (id stx)
(for-variant-stx stx #'derived-id #'fold-bind wrap rhs-wrap combine))]))
(define-syntax define-for-variants
(syntax-rules ()
[(_ (for for*) fold-bind wrap rhs-wrap combine)
(begin
(define-syntax-via-derived for for/fold/derived fold-bind wrap rhs-wrap combine)
(define-syntax-via-derived for* for*/fold/derived fold-bind wrap rhs-wrap combine))]))
(define-syntax (for/fold stx)
(syntax-case stx ()
[(_ . rest) (quasisyntax/loc stx (for/fold/derived #,stx . rest))]))
(define-syntax (for*/fold stx)
(syntax-case stx ()
[(_ . rest) (quasisyntax/loc stx (for*/fold/derived #,stx . rest))]))
(define-for-variants (for for*)
()
(lambda (x) `(,#'begin ,x ,#'(void)))
(lambda (x) x)
(lambda (x) `(,#'begin ,x ,#'(values))))
(define-for-variants (for/list for*/list)
([fold-var null])
(lambda (x) `(,#'alt-reverse ,x))
(lambda (x) x)
(lambda (x) `(,#'cons ,x ,#'fold-var)))
(define (grow-vector vec)
(define n (vector-length vec))
(define new-vec (make-vector (* 2 n)))
(vector-copy! new-vec 0 vec 0 n)
new-vec)
(define (shrink-vector vec i)
(define new-vec (make-vector i))
(vector-copy! new-vec 0 vec 0 i)
new-vec)
(define-for-syntax (for_/vector stx orig-stx for_/vector-stx for_/fold/derived-stx wrap-all?)
(syntax-case stx ()
[(_ (for-clause ...) body ...)
(with-syntax ([orig-stx orig-stx]
[for_/fold/derived for_/fold/derived-stx]
[((middle-body ...) (last-body ...)) (split-for-body stx #'(body ...))])
(syntax/loc stx
(let-values ([(vec i)
(for_/fold/derived
orig-stx
([vec (make-vector 16)]
[i 0])
(for-clause ...)
middle-body ...
(let ([new-vec (if (eq? i (unsafe-vector*-length vec))
(grow-vector vec)
vec)])
(unsafe-vector*-set! new-vec i (let () last-body ...))
(values new-vec (unsafe-fx+ i 1))))])
(shrink-vector vec i))))]
[(_ #:length length-expr #:fill fill-expr (for-clause ...) body ...)
(with-syntax ([orig-stx orig-stx]
[(limited-for-clause ...)
;; If `wrap-all?', wrap all binding clauses. Otherwise, wrap
;; only the first and the first after each keyword clause:
(let loop ([fcs (syntax->list #'(for-clause ...))] [wrap? #t])
(cond
[(null? fcs) null]
[(keyword? (syntax-e (car fcs)))
(if (null? (cdr fcs))
fcs
(list* (car fcs) (cadr fcs) (loop (cddr fcs) #t)))]
[(not wrap?)
(cons (car fcs) (loop (cdr fcs) #f))]
[else
(define fc (car fcs))
(define wrapped-fc
(syntax-case fc ()
[[ids rhs]
(or (identifier? #'ids)
(let ([l (syntax->list #'ids)])
(and l (andmap identifier? l))))
(syntax/loc fc [ids (stop-after
rhs
(lambda x
(unsafe-fx= i len)))])]
[_ fc]))
(cons wrapped-fc
(loop (cdr fcs) wrap-all?))]))]
[((middle-body ...) (last-body ...)) (split-for-body stx #'(body ...))]
[for_/vector for_/vector-stx]
[for_/fold/derived for_/fold/derived-stx])
(syntax/loc stx
(let ([len length-expr])
(unless (exact-nonnegative-integer? len)
(raise-argument-error 'for_/vector "exact-nonnegative-integer?" len))
(let ([v (make-vector len fill-expr)])
(unless (zero? len)
(for_/fold/derived
orig-stx
([i 0])
(limited-for-clause ...)
middle-body ...
(unsafe-vector*-set! v i (let () last-body ...))
(unsafe-fx+ 1 i)))
v))))]
[(_ #:length length-expr (for-clause ...) body ...)
(for_/vector #'(fv #:length length-expr #:fill 0 (for-clause ...) body ...)
orig-stx for_/vector-stx for_/fold/derived-stx wrap-all?)]))
(define-syntax (for/vector stx)
(for_/vector stx stx #'for/vector #'for/fold/derived #f))
(define-syntax (for*/vector stx)
(for_/vector stx stx #'for*/vector #'for*/fold/derived #t))
(define-for-syntax (do-for/lists for/fold-id 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)]
[for/fold for/fold-id]
[orig-stx stx])
#'(let-values ([(id ...)
(for/fold orig-stx ([id null] ...) bindings
(let-values ([(id2 ...) (let ()
expr1
expr ...)])
(values* (cons id2 id) ...)))])
(values* (alt-reverse id) ...))))]))
(define-syntax (for/lists stx) (do-for/lists #'for/fold/derived stx))
(define-syntax (for*/lists stx) (do-for/lists #'for*/fold/derived stx))
(define-for-variants (for/and for*/and)
([result #t])
(lambda (x) x)
(lambda (rhs) #`(stop-after #,rhs (lambda x (not result))))
(lambda (x) x))
(define-for-variants (for/or for*/or)
([result #f])
(lambda (x) x)
(lambda (rhs) #`(stop-after #,rhs (lambda x result)))
(lambda (x) x))
(define-for-variants (for/first for*/first)
([val #f] [stop? #f])
(lambda (x) #`(let-values ([(val _) #,x]) val))
(lambda (rhs) #`(stop-after #,rhs (lambda x stop?)))
(lambda (x) #`(values #,x #t)))
(define-for-variants (for/last for*/last)
([result #f])
(lambda (x) x)
(lambda (rhs) rhs)
(lambda (x) x))
(define-for-variants (for/sum for*/sum)
([result 0])
(lambda (x) x)
(lambda (rhs) rhs)
(lambda (x) #`(+ result #,x)))
(define-for-variants (for/product for*/product)
([result 1])
(lambda (x) x)
(lambda (rhs) rhs)
(lambda (x) #`(* result #,x)))
(define-for-variants (for/hash for*/hash)
([table #hash()])
(lambda (x) x)
(lambda (rhs) rhs)
(lambda (x)
#`(let-values ([(key val) #,x])
(hash-set table key val))))
(define-for-variants (for/hasheq for*/hasheq)
([table #hasheq()])
(lambda (x) x)
(lambda (rhs) rhs)
(lambda (x)
#`(let-values ([(key val) #,x])
(hash-set table key val))))
(define-for-variants (for/hasheqv for*/hasheqv)
([table #hasheqv()])
(lambda (x) x)
(lambda (rhs) rhs)
(lambda (x)
#`(let-values ([(key val) #,x])
(hash-set table key val))))
;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; specific sequences
(define-sequence-syntax *in-range
(lambda () #'in-range)
(lambda (stx)
(let loop ([stx stx])
(syntax-case stx ()
[[(id) (_ a b step)]
(let ([all-fx? (and (fixnum? (syntax-e #'a))
(fixnum? (syntax-e #'b))
(memq (syntax-e #'step) '(1 -1)))])
(for-clause-syntax-protect
#`[(id)
(:do-in
;; outer bindings:
([(start) a] [(end) b] [(inc) step])
;; outer check:
;; let `check-range' report the error:
(unless-unsafe (check-range start end inc))
;; loop bindings:
([pos start])
;; pos check
#,(cond [all-fx?
;; Special case, can use unsafe ops:
(if ((syntax-e #'step) . >= . 0)
#'(unsafe-fx< pos end)
#'(unsafe-fx> pos end))]
;; General cases:
[(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
((#,(if all-fx? #'unsafe-fx+ #'+) pos inc)))]))]
[[(id) (_ a b)] (loop #'[(id) (_ a b 1)])]
[[(id) (_ b)] (loop #'[(id) (_ 0 b 1)])]
[_ #f]))))
(define-sequence-syntax *in-naturals
(lambda () #'in-naturals)
(lambda (stx)
(let loop ([stx stx])
(syntax-case stx ()
[[(id) (_ start-expr)]
(for-clause-syntax-protect
#`[(id)
(:do-in
;; outer bindings:
([(start) start-expr])
;; outer check:
;; let `check-naturals' report the error:
(unless-unsafe (check-naturals start))
;; loop bindings:
([pos start])
;; pos check
#t
;; inner bindings
([(id) pos])
;; pre guard
#t
;; post guard
#t
;; loop args
((+ pos 1)))])]
[[(id) (_)]
(loop #'[(id) (_ 0)])]
[_ #f]))))
(define-sequence-syntax *in-list
(lambda () #'in-list)
(lambda (stx)
(syntax-case stx (list)
[[(id) (_ (list expr))] #'[(id) (:do-in ([(id) expr]) #t () #t () #t #f ())]]
[[(id) (_ lst-expr)]
(for-clause-syntax-protect
#'[(id)
(:do-in
;;outer bindings
([(lst) lst-expr])
;; outer check
(unless-unsafe (check-list lst))
;; loop bindings
([lst lst])
;; pos check
(pair? lst)
;; inner bindings
([(id) (unsafe-car lst)]
[(rest) (unsafe-cdr lst)]) ; so `lst` is not necessarily retained during body
;; pre guard
#t
;; post guard
#t
;; loop args
(rest))])]
[_ #f])))
(define-sequence-syntax *in-mlist
(lambda () #'in-mlist)
(lambda (stx)
(syntax-case stx (mlist)
[[(id) (_ (mlist expr))] #'[(id) (:do-in ([(id) expr]) #t () #t () #t #f ())]]
[[(id) (_ lst-expr)]
(for-clause-syntax-protect
#'[(id)
(:do-in
;;outer bindings
([(lst) lst-expr])
;; outer check
(void) ; (unless (list? lst) (in-list lst))
;; loop bindings
([lst lst])
;; pos check
(not (null? lst))
;; inner bindings
([(id) (mcar lst)])
;; pre guard
#t
;; post guard
#t
;; loop args
((mcdr lst)))])]
[_ #f])))
(define-sequence-syntax *in-stream
(lambda () #'in-stream)
(lambda (stx)
(syntax-case stx ()
[[(id) (_ lst-expr)]
(for-clause-syntax-protect
#'[(id)
(:do-in
;;outer bindings
([(lst) lst-expr])
;; outer check
(unless (unless-unsafe (stream? lst)) (in-stream lst))
;; loop bindings
([lst lst])
;; pos check
(unsafe-stream-not-empty? lst)
;; inner bindings
([(id) (unsafe-stream-first lst)]
[(rest) (unsafe-stream-rest lst)]) ; so `lst` is not necessarily retained during body
;; pre guard
#t
;; post guard
#t
;; loop args
(rest))])]
[_ #f])))
(define-sequence-syntax *in-indexed
(lambda () #'in-indexed)
(lambda (stx)
(syntax-case stx ()
[[(id1 id2) (_ gen-expr)]
#'[(id1 id2) (in-parallel gen-expr (*in-naturals))]])))
(define-sequence-syntax *in-value
(lambda () #'in-value)
(lambda (stx)
(syntax-case stx ()
[[(id) (_ expr)]
#'[(id) (:do-in ([(id) expr]) #t () #t () #t #f ())]])))
(define-sequence-syntax *in-producer
(lambda () #'in-producer)
(lambda (stx)
(syntax-case stx ()
;; cheap & simple stop-less and arg-less version
[[(id ...) (_ producer)]
#'[(id ...)
(:do-in ([(producer*) producer]) #t () #t ([(id ...) (producer*)])
#t #t ())]]
;; full version
[[(id ...) (_ producer stop more ...)]
(with-syntax ([(more* ...) (generate-temporaries #'(more ...))]
[single? (= 1 (length (syntax->list #'(id ...))))])
#'[(id ...)
(:do-in
;; outer bindings
([(producer*) producer]
[(more*) more] ...
[(stop?)
(let ([s stop])
(cond [(procedure? s) s]
['single? (lambda (x) (eq? x s))]
[else (error 'in-producer
"stop condition for ~a, got: ~e"
"multiple values must be a predicate" s)]))])
;; outer check
#t
;; loop bindings
()
;; pos check
#t
;; inner bindings
([(id ...) (producer* more* ...)])
;; pre guard
(not (stop? id ...))
;; post guard
#t
;; loop args
())])])))
;; Some iterators that are implemented using `*in-producer' (note: do not use
;; `in-producer', since in this module it is the procedure version).
(define-sequence-syntax *in-port
(lambda () #'in-port)
(lambda (stx)
(syntax-case stx ()
[[(id) (_)] #'[(id) (*in-port read (current-input-port))]]
[[(id) (_ r)] #'[(id) (*in-port r (current-input-port))]]
[[(id) (_ r p)]
#'[(id) (*in-producer
(let ([r* r] [p* p])
(check-in-port r* p*)
(lambda () (r* p*)))
eof)]])))
(define-sequence-syntax *in-lines
(lambda () #'in-lines)
(lambda (stx)
(syntax-case stx ()
[[(id) (_)] #'[(id) (*in-lines (current-input-port) 'any)]]
[[(id) (_ p)] #'[(id) (*in-lines p 'any)]]
[[(id) (_ p mode)]
#'[(id) (*in-producer
(let ([p* p] [mode* mode])
(check-in-lines p* mode*)
(lambda () (read-line p* mode*)))
eof)]])))
(define-sequence-syntax *in-bytes-lines
(lambda () #'in-bytes-lines)
(lambda (stx)
(syntax-case stx ()
[[(id) (_)] #'[(id) (*in-bytes-lines (current-input-port) 'any)]]
[[(id) (_ p)] #'[(id) (*in-bytes-lines p 'any)]]
[[(id) (_ p mode)]
#'[(id) (*in-producer
(let ([p* p] [mode* mode])
(check-in-bytes-lines p* mode*)
(lambda () (read-bytes-line p* mode*)))
eof)]])))
(define-sequence-syntax *in-input-port-bytes
(lambda () #'in-input-port-bytes)
(lambda (stx)
(syntax-case stx ()
[[(id) (_ p)]
#'[(id) (*in-producer
(let ([p* p])
(unless (input-port? p*) (in-input-port-bytes p*))
(lambda () (read-byte p*)))
eof)]])))
(define-sequence-syntax *in-input-port-chars
(lambda () #'in-input-port-chars)
(lambda (stx)
(syntax-case stx ()
[[(id) (_ p)]
#'[(id) (*in-producer
(let ([p* p])
(unless (input-port? p*) (in-input-port-chars p*))
(lambda () (read-char p*)))
eof)]])))
(define (dir-list full-d d acc)
(for/fold ([acc acc]) ([f (in-list (reverse (sort (directory-list full-d) path<?)))])
(cons (build-path d f) acc)))
(define (next-body l d init-dir use-dir?)
(let ([full-d (path->complete-path d init-dir)])
(if (and (directory-exists? full-d)
(use-dir? full-d))
(dir-list full-d d (cdr l))
(cdr l))))
(define (initial-state orig-dir init-dir)
(if orig-dir
(dir-list (path->complete-path orig-dir init-dir)
orig-dir null)
(directory-list init-dir)))
(define *in-directory
(case-lambda
[() (*in-directory #f (lambda (d) #t))]
[(orig-dir) (*in-directory orig-dir (lambda (d) #t))]
[(orig-dir use-dir?)
(define init-dir (current-directory))
;; current state of the sequence is a list of paths to produce; when
;; incrementing past a directory, add the directory's immediate
;; content to the front of the list:
(define (next l)
(define d (car l))
(next-body l d init-dir use-dir?))
(make-do-sequence
(lambda ()
(values
car
next
(initial-state orig-dir init-dir)
pair?
#f
#f)))]))
(define-sequence-syntax in-directory
(λ () #'*in-directory)
(λ (stx)
(syntax-case stx ()
[((d) (_)) #'[(d) (*in-directory #f)]]
[((d) (_ dir)) #'[(d) (*in-directory dir (lambda (d) #t))]]
[((d) (_ dir use-dir?-expr))
#'[(d)
(:do-in
([(orig-dir) (or dir #f)]
[(init-dir) (current-directory)]
[(use-dir?) use-dir?-expr])
#true
([l (initial-state orig-dir init-dir)])
(pair? l)
([(d) (car l)])
#true
#true
[(next-body l d init-dir use-dir?)])]])))
)