/* * Copyright 2025 Ry Wiese * * Use of this source code is governed by the Apache 2.0 license * that can be found in the LICENSE.md file. */pubmod Peekable {import java.lang.String////// Extends `Readable` with the ability to `peek` at values that have not yet been `read`,/// `skip` values that don't need to be read, and perform other kinds of conditional reads.///pubtraitPeekable[t] withReadable[t] {////// Returns `Ok(Some(x))` where x is the next value to be read, or `Ok(None)` if the `reader` is empty./// Does not read `x` from the `reader`.////// Returns `Err(err)` if there is an IO error.///pubdefpeek(reader: t): Result[IoError, Option[Readable.Elm[t]]] \ Readable.Aef[t]////// Advances the reader by `n`, skipping over the elements without reading them.////// Returns `Ok(k)` where `k` is the number of items skipped.////// Guarantees that `0 <= k <= max(n, 0)`. If `k < n`, then EOF has been reached.////// Returns `Err(err)` if there is an IO error.///pubdefskip(n: Int32, reader: t): Result[IoError, Int32] \ Readable.Aef[t]////// Reads from `reader` until an element is reached that does not satisfy predicate `p`.////// Returns `Ok(vec)` where `vec` is a vector of the first `k` elements where `k` is the index of the first element that does not satisfy `p`./// This element is not included in the vector, and will be read on the next read.////// All elements of `vec` are guaranteed to satisfy `p`.////// Returns `Err(err)` if there is an IO error.///pubdefreadWhile(p: Readable.Elm[t] -> Bool, reader: t): Result[IoError, Vector[Readable.Elm[t]]] \ Readable.Aef[t]////// Reads from `reader` until an element is reached that satisfies predicate `p`.////// Returns `Ok(vec)` where `vec` is a vector of the first `k` elements where `k` is the index of the first element that satisfies `p`./// This element is not included in the vector, and will be read on the next read.////// All elements of `vec` are guaranteed to not satisfy `p`.////// Returns `Err(err)` if there is an IO error.///pubdefreadUntil(p: Readable.Elm[t] -> Bool, reader: t): Result[IoError, Vector[Readable.Elm[t]]] \ Readable.Aef[t] = Peekable.readWhile(x -> notp(x), reader)////// Reads a single line from the `reader`.////// Returns `Ok(l)` where `l` is the first line. Returns `Ok("")` if EOF has been reached, or if an empty line was read.////// Lines are delineated by `\n`, `\r`, or `\r\n`. `\n` and `\r` characters are not included in the returned String.////// Returns `Err(err)` if there is an IO error.///pubdefreadLine(reader: t): Result[IoError, String] \ IO + Readable.Aef[t] whereReadable.Elm[t] ~ Char =forM (line <- Peekable.readUntil(c -> c=='\n'orc=='\r', reader);_ <- Peekable.skipElem('\r', reader);_ <- Peekable.skipElem('\n', reader) ) yieldnew String(line)////// Skips over the next element if it equals `a`.////// Returns `Ok(true)` if the element was skipped, `Ok(false)` if the element did not match or EOF was reached.////// Returns `Err(err)` if there is an IO error.///pubdefskipElem(elem: a, reader: t): Result[IoError, Bool] \ Readable.Aef[t] withEq[a] whereReadable.Elm[t] ~ a = Peekable.skipIf(e -> e==elem, reader)////// Skips over the next element if it matches the predicate `p`.////// Returns `Ok(true)` if the element was skipped, `Ok(false)` if the predicate did not match or EOF was reached.////// Returns `Err(err)` if there is an IO error.///pubdefskipIf(p: (a) -> Bool \ ef, reader: t): Result[IoError, Bool] \ ef + Readable.Aef[t] whereReadable.Elm[t] ~ a =forM (peeked <- reader |> Peekable.peek;n <- matchpeeked {case Some(next) =>if (p(next)) {reader |> Peekable.skip(1) } else { Ok(0) }case None => Ok(0) } ) yieldn!=0 }}