"""Utilities for iteration over collections of values."""# mypy: disable-error-code="empty-body, misc, override, valid-type, no-untyped-def"from__future__importannotationsfromtypingimportTYPE_CHECKING,Any,Self,no_type_checkfromguppylang_internals.decoratorimportcustom_function,extend_typefromguppylang_internals.definition.customimportNoopCompilerfromguppylang_internals.tys.builtinimportsized_iter_type_deffromguppylangimportguppyfromguppylang.std.optionimportOption,nothing,someifTYPE_CHECKING:fromguppylang.std.langimportcomptime,ownedfromguppylang.std.numimportnatL=guppy.type_var("L",copyable=False,droppable=False)n=guppy.nat_var("n")
[docs]@extend_type(sized_iter_type_def)classSizedIter:"""A wrapper around an iterator type `L` promising that the iterator will yield exactly `n` values. Annotating an iterator with an incorrect size is undefined behaviour. """
[docs]def__class_getitem__(cls,item:Any)->type:# Dummy implementation to allow subscripting of the `SizedIter` type in# positions that are evaluated by the Python interpreterreturncls
[docs]@custom_function(NoopCompiler())def__new__(iterator:L@owned)->SizedIter[L,n]:# type: ignore[type-arg]"""Casts an iterator into a `SizedIter`."""
[docs]@custom_function(NoopCompiler())defunwrap_iter(self:SizedIter[L,n]@owned)->L:"""Extracts the actual iterator."""
[docs]@custom_function(NoopCompiler())def__iter__(self:SizedIter[L,n]@owned)->SizedIter[L,n]:# type: ignore[type-arg]"""Dummy implementation making sized iterators iterable themselves."""
@guppy.structclassRange:_next:int_stop:int_step:int@guppy@no_type_checkdef__iter__(self:Self@owned)->Self:returnself@guppy@no_type_checkdef__next__(self:Self@owned)->Option[tuple[int,Self]]:end=((self._next>=self._stop)ifself._step>=0else(self._next<=self._stop))ifend:returnnothing()actual_next=self._nextself._next+=self._stepreturnsome((actual_next,self))@guppy@no_type_checkdef__reversed__(self:Self)->None:ifself._step==0:panic("Range.__reversed__: step is zero")diff=self._stop-self._next# The range is empty when diff and step have different signs,# or when the difference is zero.ifdiff==0or(diff<0)!=(self._step<0):last=self._nextself._stop=self._nextelse:distance=diffifdiff>=0else-diffstep=self._stepifself._step>=0else-self._stepcount=(distance+step-1)//steplast=self._next+(count-1)*self._stepself._stop=self._next-self._stepself._next=lastself._step=-self._step@guppy@no_type_checkdef_range1(stop:int)->Range:returnRange(0,stop,1)@guppy@no_type_checkdef_range2(start:int,stop:int)->Range:returnRange(start,stop,1)@guppy@no_type_checkdef_range3(start:int,stop:int,step:int)->Range:ifstep==0:panic("range() arg 3 must not be zero")returnRange(start,stop,step)@guppy@no_type_checkdef_range_comptime(stop:nat@comptime)->"SizedIter[Range, stop]":# noqa: F821 UP037returnSizedIter(Range(0,stop,1))
[docs]@guppy.overload(_range_comptime,_range1,_range2,_range3)defrange(start:int,stop:int=0,step:int=1)->Range:"""An iterator that yields a sequence of integers. Behaves like the builtin Python `range` function. Concretely, the ``i``th yielded number is ``start + i * step``. Numbers are yielded as long as they are * ``< stop`` in the case where ``step >= 0``, or * ``> stop`` otherwise. ``start`` defaults to ``0`` and ``step`` defaults to ``1``. If the provided ``stop`` value is comptime known, then the returned iterator will have a static size annotation and may for example be used inside array comprehensions. Iterating with a ``step`` of ``0`` raises a runtime panic. """
# Delayed import to avoid cyclic import since `iter` is loaded very early via# `builtins`/`array` (which import `SizedIter` from this module).fromguppylang.std.platformimportpanic# noqa: E402