silo:std.iter

Source src/silo:std.iter

1##! Lazy iteration protocol and adapter types.
2##!
3##! Defines the `Iterator` trait (the `@lang(iterator)` item) and two
4##! concrete wrapper records: `CharsIter` for `Str` codepoint
5##! iteration and `Peekable` for one-slot lookahead over any underlying
6##! `Iterator`.
7
8:use
9  :open core Option None Some Char AnyInt Str
10  :open traits Ord
11  :open collections Foldable
12:end
13
14# si[impl coll.iterator]
15# si[impl lang.iterator]
16## Lazy, stateful element-at-a-time iteration protocol.
17## Returns `Some item` on advance, `None` on exhaustion.
18@lang(iterator)
19:trait(pub) Iterator | item
20  ## Advance one element; returns the iterator and an optional value.
21  ## ( Self -> Self (Option item) )
22  .next ( Self -> Self (Option item) )
23:end
24
25## Lazy character iterator over a Str.
26## Tracks the UTF-8 byte position of the next codepoint to yield.
27:record(pub) CharsIter .str Str .pos AnyInt :end
28
29:impl Iterator CharsIter | Char
30  .next ( CharsIter -> CharsIter (Option Char) )
31    pop-> it
32    it .pos it .str str-len:if it None :ret :end
33    it .pos it .str str-next-char pop-> ch pop-> new-pos
34    new-pos it .pos<-
35    ch Some ;
36:end
37
38## Construct a `CharsIter` over the given `Str`, starting at position 0.
39## The lazy iterator form is named `chars-iter` to avoid colliding with
40## `StrExt`'s eager `.chars` method (which returns `(Vec Codepoint)`).
41## ( Str -> CharsIter )
42:fn(pub) chars-iter ( Str -> CharsIter ) 0 CharsIter :end
43
44# Foldable for CharsIter (via Iterator .next)
45:impl Foldable CharsIter
46  .fold ( CharsIter acc [acc Char -> acc] -> acc )
47    pop-> f pop-> acc pop-> it
48    :loop
49      it .next pop-> opt pop-> it
50      opt :match
51        | None => :break
52        | Some e => acc e f .call
53      :end
54      pop-> acc
55    :end acc ;
56  .for-each ( CharsIter [Char ->] -> )
57    pop-> f pop-> it
58    :loop
59      it .next pop-> opt pop-> it
60      opt :match
61        | None => :ret
62        | Some e => e f .call
63      :end
64    :end ;
65:end
66
67# si[impl stdlib.iter.peekable]
68## Lookahead protocol for iterators that support inspecting the next
69## element without consuming it. Every `Peekable` MUST also be an
70## `Iterator` — the associated `item` type is shared with the iterator's
71## `item` via the `(Iterator Self | item)` constraint.
72##
73## Types with a natural one-slot lookahead (token streams, buffered
74## readers, lookahead parsers) implement this trait directly. There is
75## no auto-wrapper for "any Iterator"; callers that need lookahead over
76## a plain Iterator MUST provide their own `:impl`.
77:trait(pub) Peekable | item { (Iterator Self | item) }
78  ## Return the next element without consuming it.
79  ## Successive `.peek` calls without an intervening `.next` MUST return
80  ## the same value. The returned `Self` MAY be the same handle with an
81  ## internally populated cache.
82  ## ( Self -> Self (Option item) )
83  .peek ( Self -> Self (Option item) )
84:end