ハロウィンなのでPythonを八つ裂きにしてみる

前置き
---
Pythonにはスライスと呼ばれる非常に便利な構文がある。

```Python
>>> src = 'spamhamegg'
>>>
>>> src[2:]
'amhamegg'
>>> src[:-3]
'spamham'
>>> src[::-1]
'ggemahmaps'
```

それを悪用して、変なコードを書いてみた話。
去年も[全く生産的でない記事](https://qiita.com/LouiS0616/items/b26883cdb9c51e1f986f)を書いたので、ハロウィンネタ第二段とも言える。

成果物
---
こんなコードを書いてみた。
Pythonのコードとして実行可能であることはもちろん、**PEP8にもある程度準拠している。**[^1]

[^1]: 少なくともpycodestyle先生は文句を言わない。

```Python
from brainbite import Biter

_ = Biter()[::...][::...][::...][::...][::...][::...][::...][::...][:...]
_ = _[:...:...][::...][::...][::...][::...][:...][:...:...][::...][::...]
_ = _[:...:...][::...][::...][::...][:...:...][::...][::...][::...]
_ = _[:...:...][::...][...:...][...:...][...:...][...:...][...:][...::...]
_ = _[:...:...][::...][:...:...][::...][:...:...][...:][:...:...][:...:...]
_ = _[::...][:...][...:...][...::...][...:...][...:][...::...][:...:...]
_ = _[:...:...][:][:...:...][...:][...:][...:][:][::...][::...][::...]
_ = _[::...][::...][::...][::...][:][:][::...][::...][::...][:][:...:...]
_ = _[:...:...][:][...:...][...:][:][...:...][:][::...][::...][::...][:]
_ = _[...:][...:][...:][...:][...:][...:][:][...:][...:][...:][...:][...:]
_ = _[...:][...:][...:][:][:...:...][:...:...][::...][:][:...:...][::...]
_ = _[::...][:]()

```

**実行結果**

```text
Hello World!
```

ここでインポートしているbrainbiteパッケージは、ちゃっかりPyPIに登録済みだ。
試してみたかったら`pip install brainbite`で入る。[^2]

[^2]: Python3.6以降で動作する。

brainbiteの実装はこちら： [GitHub - LouiS0616/brainbite](https://github.com/LouiS0616/brainbite)
<details><summary>コアな部分だけここにも貼っておく。</summary><div>

```python:data_model.py
import sys


class BFDataModel:
    """
    This holds cells what behaves as the Turing Machine's tape.
    """

    def __init__(self, size=30000):
        self._data = [0 for _ in range(size)]
        self._index = 0

    @property
    def pointer(self):
        return self._index

    @pointer.setter
    def pointer(self, value):
        self._index = value % len(self._data)

    @property
    def pointee(self):
        return self._data[self._index]

    @pointee.setter
    def pointee(self, value):
        self._data[self._index] = value

    def increment_pointer(self):
        """This corresponds to operation '>'."""
        self.pointer += 1

    def decrement_pointer(self):
        """This corresponds to operation '<'."""
        self.pointer -= 1

    def increment_pointee(self):
        """This corresponds to operation '+'."""
        self.pointee += 1

    def decrement_pointee(self):
        """This corresponds to operation '-'."""
        self.pointee -= 1

    def conditional_skip(self) -> bool:
        """This corresponds to operation '['."""
        return self.pointee == 0

    def unconditional_jump(self):
        """This corresponds to operation ']'."""
        pass

    def print_pointee(self):
        """This corresponds to operation '.'."""
        sys.stdout.write(
            chr(self.pointee)
        )

    def input_to_pointee(self):
        """This corresponds to operation ','."""
        self.pointee = ord(sys.stdin.read(1))

```

```python:biter.py
from .data_model import BFDataModel as Model


class _BFLikeInterpreter:
    """
    This class should be considered as abstract one.
    """

    def __init__(self, parent):
        self._parent = parent
        self._op_list = []

        self._op_dict = {
            (None, None, None): Model.print_pointee,
            (None, None,  ...): Model.increment_pointee,
            (None,  ..., None): 'begin of brace',
            (None,  ...,  ...): Model.increment_pointer,
            (...,  None, None): Model.decrement_pointee,
            (...,  None,  ...): 'end of brace',
            (...,   ..., None): Model.decrement_pointer,
            (...,   ...,  ...): Model.input_to_pointee,
        }

    @property
    def op_list(self):
        return self._op_list

    def __getitem__(self, arg):
        if not isinstance(arg, slice):
            raise TypeError(
                f'BFLikeInterpreter indices must be slices, not {type(arg).__name__}'
            )

        operation = self._op_dict[
            arg.start, arg.stop, arg.step
        ]

        # begin of brace
        if operation == 'begin of brace':
            child = _BFLikeInnerInterpreter(parent=self)
            child.op_list.append(
                Model.conditional_skip
            )
            self._op_list.append(child)

            return child

        # end of brace
        if operation == 'end of brace':
            self._op_list.append(
                Model.unconditional_jump
            )
            return self._parent

        # other
        self._op_list.append(operation)
        return self


class _BFLikeRootInterpreter(_BFLikeInterpreter):
    """
    This class handles whole of program.
    So this must be instantiated just one time, no more no less.
    """

    def __init__(self):
        super().__init__(parent=None)
        self._model = Model()

    def __call__(self):
        for operation in self._op_list:
            operation(self._model)

        self._op_list = []
        return self


class _BFLikeInnerInterpreter(_BFLikeInterpreter):
    """
    This class handles part of program what is located between '[' and ']'.
    """

    def __call__(self, model):
        conditional_skip, *op_list, unconditional_jump = self._op_list

        assert conditional_skip is Model.conditional_skip
        assert unconditional_jump is Model.unconditional_jump

        while True:
            if conditional_skip(model):
                return self._parent

            for operation in op_list:
                operation(model)


Biter = _BFLikeRootInterpreter

```
</div></details>

仕組み
---
パッケージ名で察している読者もいると思うが、結局Brainf*ckの焼き直しである。
[Wikipedia - Brainfuck](https://ja.wikipedia.org/wiki/Brainfuck)

Brainf*ckの各命令が、次のようにスライスに対応している。

||| 機能 (Wikipediaより引用) |
|:-:|:---------------:|:-----------------|
|`>`|`[:...:...]`|ポインタをインクリメントする。|
|`<`|`[...:...]`|ポインタをデクリメントする。|
|`+`|`[::...]`|ポインタが指す値をインクリメントする。|
|`-`|`[...:]`|ポインタが指す値をデクリメントする。|
|`.`|`[:]`|ポインタが指す値を出力に書き出す。|
|`,`|`[...:...:...]`|入力から1バイト読み込んで、ポインタが指す先に代入する。|
|`[`|`[:...]`|ポインタが指す値が0なら、対応する ] の直後にジャンプする。|
|`]`|`[...::...]`|ポインタが指す値が0でないなら、対応する [ （の直後）にジャンプする。|

`...`はあまり見慣れないかもしれないが、[Ellipsis](https://docs.python.jp/3/library/constants.html#Ellipsis)と呼ばれる立派な組み込み変数である。

```Python
>>> ...
Ellipsis
```

もう少し詳しい仕組み [スライス編]
---
メソッド`__getitem__`を適切に用意すれば、スライス操作を受け付けるクラスを作ることができる。

```Python
class SliceableClass:
    def __getitem__(self, key):
        print(key)
        
sliceable = SliceableClass()
sliceable[1:]     # slice(1, None, None)
sliceable[::2]    # slice(None, None, 2)
sliceable[...:]   # slice(Ellipsis, None, None)
```

スライスオブジェクトには三つの値を指定できるが、省略された場合Noneが渡される。
もちろん明示的に渡しても良い。

```Python
>>> src
'spamhamegg'
>>>
>>> src[1:]
'pamhamegg'
>>> src[1:None:None]
'pamhamegg'
```

もう少し詳しい仕組み [モデル編]
---
brainbiteの実行モデルの核となるクラスは次の二つである。

**Biter**
スライスを命令に変換し、キューに押し込んでいく。
命令はcallされたときにまとめて実行される。

実際にはもうちょっと抽象的な作りにしているが、ここでは割愛。[^3]

[^3]: 単に私の説明能力が低いだけである。

**BFDataModel**
実際のデータを保持しているモデルで、Brainf*ckの各命令に対応するメソッドを持つ。

コーディング例
---
100までの素数を出力するコード。
このコードの大元となるbfコードは、[白月のIT入門 - Brainfuck -] (http://hakugetu.so.land.to/program/brainfuck/1-4.php) からお借りした。

```Python
from brainbite import Biter

_ = Biter()[:...:...][::...][::...][::...][::...][:...][...:...][::...]
_ = _[::...][::...][::...][::...][::...][::...][::...][:...:...][...:]
_ = _[...::...][:...:...][::...][::...][::...][::...][::...][::...][::...]
_ = _[::...][:...][...:...][::...][::...][::...][::...][::...][::...]
_ = _[:...:...][...:][...::...][...:...][::...][::...][:][...:...][:]
_ = _[:...:...][::...][:][...:...][:][:...:...][::...][::...][:][...:...][:]
_ = _[:...:...][::...][::...][:][...:...][:][:...:...][...:][...:][...:]
_ = _[...:][...:][...:][:][:][...:...][:][:...:...][:][::...][::...][:]
_ = _[...:...][:][:...:...][...:][...:][:][::...][::...][::...][::...]
_ = _[::...][::...][:][...:...][:][:...:...][...:][...:][...:][...:][...:]
_ = _[...:][:][:...:...][::...][::...][::...][:...][...:...][::...][::...]
_ = _[::...][:...:...][...:][...::...][...:...][...:][:][...:...][:]
_ = _[:...:...][...:][...:][...:][...:][...:][...:][...:][:][::...][:]
_ = _[...:...][:][:...:...][...:][:][::...][::...][::...][::...][::...]
_ = _[::...][::...][:][...:...][:][:...:...][...:][...:][...:][...:][...:]
_ = _[...:][:][...:][...:][:][...:...][:][:...:...][::...][::...][:][::...]
_ = _[::...][::...][::...][:][...:...][:][:...:...][...:][...:][...:][:]
_ = _[...:][...:][...:][:][...:...][:][:...:...][::...][::...][::...][:]
_ = _[...:][:][...:...][:][:...:...][::...][:][::...][::...][::...][:]
_ = _[...:...][:][:...:...][...:][...:][:][...:][...:][:][...:...][:]
_ = _[:...:...][::...][::...][:][::...][::...][::...][::...][:][...:...][:]
_ = _[:...:...][...:][...:][...:][:][...:][...:][...:][...:][...:][:]
_ = _[...:...][:][:...:...][::...][::...][::...][::...][::...][:][::...][:]
_ = _[...:...][:][:...:...][:][...:][...:][...:][...:][...:][...:][:]
_ = _[...:...][:][:...:...][::...][::...][::...][::...][::...][::...][:]
_ = _[...:][...:][...:][...:][:][...:...][:][:...:...][::...][::...][::...]
_ = _[::...][:][::...][::...][:][...:...][:][:...:...][...:][:][...:][...:]
_ = _[...:][...:][...:][:][...:...][:][:...:...][::...][::...][::...][::...]
_ = _[::...][:][::...][:][...:...][:][:...:...][:][...:][...:][:]()

```

**実行結果**

```text
2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97
```

後書き
---
相も変わらず、誰の役にも立たないコードを書いてしまった。
しかし書いていてなかなか楽しかったし、パッケージ配布の練習にもなったので良かったこととする。

~~パッケージのmainモジュールやドキュメントが中途半端なのは、飽きたからである。~~