Programming Language

Nocter

A self-contained systems language built around simplicity, encapsulation, and foolproof design.

/development/std/iter/index.nct

index.nct

//! Allocation-free readonly iteration over contiguous views.
//!
//! ViewIter retains the source view itself so element borrows preserve the
//! view's storage origin. Iterator names are ordinary standard-library APIs.

use ./core
use ./ops
use ./sources
use std/vec.Vec

/// Lazily transforms items from an owning source iterator.
pub struct MapIter<T, U, I, F> {
    pub(/) source: I
    pub(/) transform: F
}

/// Lazily retains items from an owning source iterator.
pub struct FilterIter<T, I, F> {
    pub(/) source: I
    pub(/) predicate: F
}

/// Yields at most `remaining` items from `source`.
pub struct TakeIter<T, I> {
    pub(/) source: I
    pub(/) remaining: usize
}

/// Discards at most `remaining_to_skip` source items before yielding.
pub struct SkipIter<T, I> {
    pub(/) source: I
    pub(/) remaining_to_skip: usize
}

/// Yields every left item before every right item.
pub struct ChainIter<T, L, R> {
    pub(/) left: L
    pub(/) right: R
    pub(/) in_left: bool
}

/// One indexed item yielded by `EnumerateIter`.
pub struct Indexed<T> {
    pub index: usize
    pub item: T
}

/// An owning iterator that pairs every item with its zero-based index.
pub struct EnumerateIter<T, I> {
    pub(/) source: I
    pub(/) next_index: usize
}

/// Owned state passed to a folding callback.
pub struct FoldStep<T, U> {
    pub accumulator: U
    pub item: T
}

/// Advances a mutable iterator and returns one item, or `none` at exhaustion.
pub interface Iterator<T> {
    pub method &+self.next(): T?

    /// Lazily transforms every yielded item.
    pub method self.map<U, F: &+func(T): U>(transform: F): MapIter<T, U, Self, F> from self | transform {
        return MapIter<T, U, Self, F> {
            source: move self,
            transform: move transform,
        }
    }

    /// Lazily retains items accepted by `predicate`.
    pub method self.filter<F: &+func(&T): bool>(predicate: F): FilterIter<T, Self, F> from self | predicate {
        return FilterIter<T, Self, F> {
            source: move self,
            predicate: move predicate,
        }
    }

    /// Lazily yields at most `limit` items.
    pub method self.take(limit: usize): TakeIter<T, Self> {
        return TakeIter<T, Self> {
            source: move self,
            remaining: limit,
        }
    }

    /// Lazily discards at most `count` items before yielding.
    pub method self.skip(amount: usize): SkipIter<T, Self> {
        return SkipIter<T, Self> {
            source: move self,
            remaining_to_skip: amount,
        }
    }

    /// Lazily yields this iterator followed by `right`.
    pub method self.chain<R: Iterator<T>>(right: R): ChainIter<T, Self, R> from self | right {
        return ChainIter<T, Self, R> {
            left: move self,
            right: move right,
            in_left: true,
        }
    }

    /// Lazily pairs every yielded item with its zero-based index.
    pub method self.enumerate(): EnumerateIter<T, Self> {
        return EnumerateIter<T, Self> {
            source: move self,
            next_index: 0,
        }
    }

    /// Consumes the iterator and returns the number of yielded items.
    pub method self.count(): usize {
        var source = move self
        var total: usize = 0
        while true {
            let item = source.next() otherwise { return total }
            total = total + 1
        }
        return 0
    }

    /// Consumes the iterator and returns its last item, or `none`.
    pub method self.last(): T? {
        var source = move self
        var result: T? = none
        while true {
            let item = source.next() otherwise { break }
            result = move item
        }
        return move result
    }

    /// Returns the first item accepted by `predicate`, or `none`.
    pub method self.find<F: &+func(&T): bool>(predicate: F): T? from self {
        var source = move self
        var predicate_fn = move predicate
        while true {
            let item = source.next()?
            if predicate_fn(&item) {
                return move item
            }
        }
        return none
    }

    /// Returns true when `predicate` accepts any yielded item.
    pub method self.any<F: &+func(&T): bool>(predicate: F): bool {
        var source = move self
        var predicate_fn = move predicate
        while true {
            let item = source.next() otherwise { return false }
            if predicate_fn(&item) {
                return true
            }
        }
        return false
    }

    /// Returns true when `predicate` accepts every yielded item.
    pub method self.all<F: &+func(&T): bool>(predicate: F): bool {
        var source = move self
        var predicate_fn = move predicate
        while true {
            let item = source.next() otherwise { return true }
            if !predicate_fn(&item) {
                return false
            }
        }
        return true
    }

    /// Reduces every item into one accumulator value.
    pub method self.fold<U, F: &+func(FoldStep<T, U>): U>(
        initial: U,
        combine: F,
    ): U from self | initial | combine {
        var source = move self
        var callback = move combine
        var accumulator = move initial
        while true {
            let item = source.next() otherwise { break }
            accumulator = callback(FoldStep<T, U> {
                accumulator: move accumulator,
                item: move item,
            })
        }
        return move accumulator
    }

    /// Consumes the iterator into a newly allocated Vec.
    pub method self.to_vec(): Vec<T> {
        var source = move self
        var result: Vec<T> = Vec.empty()
        while true {
            let item = source.next() otherwise { break }
            result.push(move item)
        }
        return move result
    }
}

/// Reports the exact number of values that the iterator has not yielded yet.
pub interface ExactSizeIterator<T> {
    pub method &self.remaining_len(): usize
}

/// Creates a readonly iterator whose yielded values retain the collection origin.
pub interface Iterable<T, I> {
    pub method &self.iter(): I
}

/// Transfers a collection into an owning iterator.
pub interface IntoIterator<T, I> {
    pub method self.into_iter(): I
}

/// Constructs a lazy mapping adapter without allocating.
pub func map<T, U, I: Iterator<T>, F: &+func(T): U>(
    source: I,
    transform: F,
): MapIter<T, U, I, F> from source | transform

/// Constructs a lazy filtering adapter without allocating.
pub func filter<T, I: Iterator<T>, F: &+func(&T): bool>(
    source: I,
    predicate: F,
): FilterIter<T, I, F> from source | predicate

pub func take<T, I: Iterator<T>>(source: I, limit: usize): TakeIter<T, I>

pub func skip<T, I: Iterator<T>>(source: I, amount: usize): SkipIter<T, I>

pub func chain<T, L: Iterator<T>, R: Iterator<T>>(
    left: L,
    right: R,
): ChainIter<T, L, R> from left | right

pub func enumerate<T, I: Iterator<T>>(source: I): EnumerateIter<T, I>

/// An iterator that never yields an item.
pub struct EmptyIter<T> {
    exhausted: bool
}

/// An iterator that owns at most one unconsumed item.
pub struct OnceIter<T> {
    item: T?
    remaining: usize
}

pub func empty<T>(): EmptyIter<T>
pub func once<T>(item: T): OnceIter<T>
pub func count<T, I: Iterator<T>>(source: I): usize

pub func last<T, I: Iterator<T>>(source: I): T?

/// A forward cursor over readonly borrows into contiguous storage.
pub struct ViewIter<T> {
    view: &[T]
    next_index: usize
}

construct ViewIter<T> {
    /// Creates an allocation-free readonly iterator over `view`.
    pub default func from_view(view: &[T]): Self {
        return ViewIter<T> {
            view: view,
            next_index: 0,
        }
    }
}

pub func from_view<T>(view: &[T]): ViewIter<T> {
    return ViewIter.from_view(view)
}

pub func remaining<T>(iterator: &ViewIter<T>): usize {
    return iterator.view.len() - iterator.next_index
}

pub func next<T>(iterator: &+ViewIter<T>): &T? {
    if iterator.next_index >= iterator.view.len() {
        return none
    }
    let index: usize = iterator.next_index
    iterator.next_index = index + 1
    return &iterator.view[index]
}

impl<T> ViewIter<T> {
    /// Returns the number of elements not yet yielded.
    pub method &self.remaining(): usize {
        return remaining(self)
    }

}

impl<T> ExactSizeIterator<&T> for ViewIter<T> {
    /// Implements exact-size iteration for compiler-owned element packs.
    method &self.remaining_len(): usize {
        return remaining(self)
    }
}

impl<T> Iterator<&T> for ViewIter<T> {
    /// Advances and returns a readonly borrow, or `none` at exhaustion.
    method &+self.next(): &T? {
        if self.next_index >= self.view.len() {
            return none
        }
        let index: usize = self.next_index
        self.next_index = index + 1
        return &self.view[index]
    }
}