"""Define the base class from which all commands will inherit."""
from abc import abstractmethod
from collections.abc import Iterable, Sequence
from typing import override
from lightwin.core.instruction import Instruction
from lightwin.tracewin_utils.line import DatLine
[docs]
class Command(Instruction):
"""A generic Command class.
Parameters
----------
idx : dict[str, int]
Dictionary holding useful indexes. Keys are ``'dat_idx'`` (position in
the ``.dat`` file) and ``'influenced_elements'`` (position in the
``.dat`` file of the elements concerned by current command).
line : list[str]
Line in the ``.dat`` file corresponding to current command.
See Also
--------
:meth:`.ListOfElementsFactory.subset_list_run`
:func:`.dat_filecontent_from_smaller_list_of_elements`
"""
[docs]
def __init__(
self, line: DatLine, dat_idx: int | None = None, **kwargs
) -> None:
"""Instantiate mandatory attributes."""
super().__init__(line, dat_idx, **kwargs)
self.influenced = slice(0, 1)
[docs]
def set_influenced_elements(
self, instructions: list[Instruction], **kwargs: float
) -> None:
"""Determine the index of the elements concerned by :func:`apply`."""
start = self.idx["dat_idx"] + 1
influenced = self._indexes_between_this_command_and(
instructions[start:], type(self)
)
self.influenced = influenced
return
[docs]
@abstractmethod
def apply(
self, instructions: list[Instruction], **kwargs: float
) -> list[Instruction]:
"""Apply the command."""
return instructions
[docs]
def concerns_one_of(self, dat_indexes: Iterable[int]) -> bool:
"""Tell if ``self`` concerns an element, which ``dat_idx`` is given.
Internally, we convert the ``self.influenced`` from a :class:`set` to
a :class:`list` object and check intersections with ``dat_indexes``.
Parameters
----------
dat_indexes : list[int]
Indexes in the ``.dat`` file of the sub-list of elements under
creation.
"""
idx_influenced = range(self.influenced.start, self.influenced.stop)
idx_influenced = [i for i in idx_influenced]
intersect = list(set(idx_influenced).intersection(dat_indexes))
return len(intersect) > 0
[docs]
def _indexes_between_this_command_and(
self,
instructions_after_self: Sequence[Instruction],
*stop_types: type,
) -> slice:
"""
Determine the indexes of the instructions affected by an instruction.
We return the indexes of instructions between the first of
``instructions`` and the first instruction which type is in
``stop_types``.
Parameters
----------
instructions_after_self : Sequence[Instruction]
All instructions after ``self`` (``self`` not included).
stop_types : type
Type(s) of commands after which ``self`` has no influence. If not
provided, we set it to the type of ``self``. In other words, a
``FREQ`` influences every element up to the following ``FREQ``.
Returns
-------
slice
All the indexes of the instrutions that will be affected by self.
"""
if len(stop_types) == 0:
stop_types = (type(self),)
start = self.idx["dat_idx"] + 1
i = 0
for i, instruction in enumerate(instructions_after_self):
if isinstance(instruction, stop_types):
break
influenced = slice(start, start + i)
return influenced
[docs]
@override
def increment_dat_position(self, increment: int = 1) -> None:
"""Increment dat_index and indexes of elements concerned by command."""
self.influenced = slice(
self.influenced.start + increment, self.influenced.stop + increment
)
return super().increment_dat_position(increment)