Skip to content

Commit

Permalink
Support for custom operators
Browse files Browse the repository at this point in the history
---

+ Added a note to refer to necessary installation dependencies
+ Changed version to 2.0.1
  • Loading branch information
MatrixEditor committed Jun 29, 2024
1 parent 816650e commit 048e5a8
Show file tree
Hide file tree
Showing 7 changed files with 97 additions and 5 deletions.
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,9 @@ Simply use pip to install the package:
pip install "caterpillar[all]@git+https://github.com/MatrixEditor/caterpillar.git"
```

> [!NOTE]
> You will need to install `make`, a C compiler and the Python3.12 development package.
## Starting Point

Please visit the [Documentation](https://matrixeditor.github.io/caterpillar/), it contains a complete tutorial on how to use this library.
Expand Down
20 changes: 20 additions & 0 deletions docs/sphinx/source/tutorial/advanced.rst
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,26 @@ structs. They have to be wrapped by a :class:`~caterpillar.fields.Field` first:
>>> field = F(Format) @ 0x1234 # ok
Custom Operators
^^^^^^^^^^^^^^^^
In addition to standard Python operators it is possible to define new operators
tailored to specific use-cases. For instance, it is possible to define a
new operator that automatically creates an Array with a fixed length:
.. code-block:: python
from caterpillar.fields import _infix_
M = _infix_(lambda s, count: s[count * 2])
@struct
class Format:
values: uint16 /M/ 3
As of now, the operator is used only during pre-processing. It won't affect the
overall parsing process on its own but can be used to return custom structs.
Pointers
^^^^^^^^
Expand Down
21 changes: 21 additions & 0 deletions examples/operator_example.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
from caterpillar.model import struct, sizeof
from caterpillar.fields import uint16, _infix_
from caterpillar.options import S_REPLACE_TYPES

# Here, we define a custom operator named 'M' that will multiply
# the second argument by 2.
M = _infix_(lambda a, b: a[b*2])

# or directly as function
# @_infix_
# def M(a, b):
# return a[b*2]

@struct(options={S_REPLACE_TYPES})
class Format:
# __annotations__ should contain typing.List[int] as we've specified the
# documentation option.
f1: uint16 /M/ 3

print(Format.__annotations__)

2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ wheel.py-api = "cp312"

[project]
name = "caterpillar"
version = "2.0.0-b0"
version = "2.0.1"

description="Library to pack and unpack structurized binary data."
authors = [
Expand Down
2 changes: 1 addition & 1 deletion src/caterpillar/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.

__version__ = "2.0.0-b2"
__version__ = "2.0.1"
__release__ = None
__author__ = "MatrixEditor"

Expand Down
2 changes: 1 addition & 1 deletion src/caterpillar/fields/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
from ._base import Field, INVALID_DEFAULT, DEFAULT_OPTION, singleton
from ._mixin import FieldMixin, FieldStruct, Chain
from ._mixin import FieldMixin, FieldStruct, Chain, _infix_
from .common import (
FormatField,
Transformer,
Expand Down
52 changes: 50 additions & 2 deletions src/caterpillar/fields/_mixin.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,12 @@
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
import inspect

from io import BytesIO
from typing import Any, List, Union, Optional, Iterable
from typing import Any, List, Union, Optional, Iterable, Callable
from abc import ABC, abstractmethod
from functools import partial

from caterpillar.abc import _ContextLike, _StructLike, _ContextLambda, _Switch
from caterpillar.abc import _ContainsStruct, getstruct
Expand Down Expand Up @@ -293,7 +296,52 @@ def pack_single(self, obj: Any, context: _ContextLike) -> None:
obj = stream.getvalue()


# utility method
class _infix_:
"""Defines a custom opearator (user-defined)
It operates _infix_ between two statements and takes them as
agruments. For instance, the following example will return
an array of structures:
.. code-block:: python
from caterpillar.fields import uint16, _infix_
from caterpillar.model import struct
M = _infix_(lambda a, b: a[b*2])
@struct
class Format:
f1: uint16 /M/ 3
This class reserves the `/` operator. It is also possible to
use this class as a decorator on callable objects:
.. code-block:: python
@_infix_
def M(a, b):
return a[b*2]
:param func: The function to be applied.
:type func: Callable[[Any, Any], _StructLike]
"""

def __init__(self, func: Callable[[Any, Any], _StructLike]) -> None:
self.func = func

def __truediv__(self, arg2) -> _StructLike:
return self.func(arg2)

def __rtruediv__(self, arg1) -> '_infix_':
return _infix_(partial(self.func, arg1))

def __call__(self, arg1, arg2) -> _StructLike:
return self.func(arg1, arg2)



# utility methods
def get_args(args: Any, context: _ContextLike) -> List[Any]:
"""
Get arguments for an instance.
Expand Down

0 comments on commit 048e5a8

Please sign in to comment.