TakeSkip
A Python library for declarative bit manipulation using an intuitive command syntax.
Overview
TakeSkip provides a domain-specific language for selecting, rearranging, and manipulating bits in binary arrays. Instead of writing complex indexing logic, you can express operations using simple commands like t8s4r8 (take 8 bits, skip 4, reverse 8).
Installation
pip install takeskip
Basic Usage
import numpy as np
from takeskip import takeskip
# Create a binary array (8 bits)
bits = np.array([1, 0, 1, 1, 0, 0, 1, 0], dtype=np.uint8)
# Skip first 2 bits, take next 4
result = takeskip("s2t4", bits)
print(result) # [1, 1, 0, 0]
# Take 4 bits, reverse next 4
result = takeskip("t4r4", bits)
print(result) # [1, 0, 1, 1, 0, 1, 0, 0]
Command Reference
Basic Operations
| Command | Description | Example |
|---|---|---|
t<n> |
Take n bits | t8 - take 8 bits |
s<n> |
Skip n bits | s4 - skip 4 bits |
r<n> |
Reverse n bits | r8 - reverse 8 bits |
i<n> |
Invert n bits (0↔1) | i4 - invert 4 bits |
ri<n> |
Reverse and invert n bits | ri8 - reverse then invert 8 bits |
b<n> |
Backup pointer n positions | b4 - move back 4 positions |
Padding Operations
| Command | Description | Example |
|---|---|---|
z<n> |
Insert n zeros | z4 - add 4 zeros |
n<n> |
Insert n ones | n4 - add 4 ones |
d<binary> |
Insert literal binary data | d101 - add bits 1,0,1 |
Permutation
| Command | Description | Example |
|---|---|---|
p<indices> |
Permute using 1-based indices | p1,3,5 - select bits 1, 3, 5 |
p<range> |
Permute using ranges | p1-4 - select bits 1 through 4 |
p<mixed> |
Mix indices and ranges | p1-4,8,6-5 - complex permutation |
Note: Permutation indices are 1-based by default (pass permute_base=0 for 0-based). Index 0 is invalid in 1-based mode and raises ValueError. Indices are relative to the current pointer position, and the pointer advances past the last referenced bit. Ranges are inclusive.
Grouping and Repetition
# Repeat a sequence
bits = np.array([1, 0] * 8, dtype=np.uint8)
result = takeskip("(t4s4)3", bits) # Repeat "take 4, skip 4" three times
# Group complex operations
result = takeskip("(t2r2s1)4", bits) # Repeat grouped operations
Advanced Examples
Nibble Swap (swap 4-bit chunks)
bits = np.array([1, 1, 1, 1, 0, 0, 0, 0], dtype=np.uint8)
result = takeskip("s4t4b8t4", bits) # [0, 0, 0, 0, 1, 1, 1, 1]
Extract Every Other Bit
bits = np.array([1, 0, 1, 0, 1, 0, 1, 0], dtype=np.uint8)
result = takeskip("(t1s1)4", bits) # [1, 1, 1, 1]
Deinterleave Nibbles
bits = np.array([1, 0, 1, 0, 1, 0, 1, 0], dtype=np.uint8)
result = takeskip("(t1s1)4 b8 (s1t1)4", bits) # [1, 1, 1, 1, 0, 0, 0, 0]
Complex Permutation
bits = np.array([1, 0, 1, 1, 0, 0, 1, 0], dtype=np.uint8)
# Take bits at positions 1,2,3,4 then 8,7,6,5 (1-based)
result = takeskip("p1-4,8-5", bits) # [1, 0, 1, 1, 0, 1, 0, 0]
Interleave Pattern with Padding
bits = np.array([1, 1, 1, 1, 0, 0, 0, 0], dtype=np.uint8)
result = takeskip("(t1z1)4", bits)
# [1, 0, 1, 0, 1, 0, 1, 0] - interleaved with zeros
Remnant Handling
Control what happens to remaining bits after commands execute:
bits = np.array([1, 0, 1, 1, 0, 0, 1, 0], dtype=np.uint8)
# Default: discard remaining bits
result = takeskip("t4", bits, remnant="remove") # [1, 0, 1, 1]
# Keep remaining bits
result = takeskip("t4", bits, remnant="keep") # [1, 0, 1, 1, 0, 0, 1, 0]
# Pad with zeros to original length
result = takeskip("t4", bits, remnant="pad") # [1, 0, 1, 1, 0, 0, 0, 0]
Multi-dimensional Arrays
TakeSkip operates on the last axis of multi-dimensional arrays:
# 2D array: 3 rows of 8 bits each
bits = np.array([
[1, 0, 1, 1, 0, 0, 1, 0],
[0, 1, 0, 1, 1, 1, 0, 1],
[1, 1, 0, 0, 1, 0, 1, 1],
], dtype=np.uint8)
result = takeskip("s2t4", bits)
# [[1, 1, 0, 0],
# [0, 1, 1, 1],
# [0, 0, 1, 0]]
Use Cases
- Data packing/unpacking: Extract fields from packed binary formats
- Protocol parsing: Parse binary protocols with field alignment
- Bit manipulation: Rearrange bits in encryption/encoding operations
Command Chaining
Commands are executed left-to-right with a maintained pointer. The pointer must stay within [0, array_length] after each command; a ValueError is raised if Backup moves before the start or Skip/Take/Permute moves past the end.
bits = np.array([1, 0, 1, 1, 0, 0, 1, 0], dtype=np.uint8)
# Pointer starts at 0
# t2: take bits 0-1 -> [1,0], pointer now at 2
# s2: skip bits 2-3, pointer now at 4
# t4: take bits 4-7 -> [0,0,1,0], pointer now at 8
result = takeskip("t2s2t4", bits) # [1, 0, 0, 0, 1, 0]
Syntax Notes
- Commands are case-insensitive:
t4andT4are equivalent - Whitespace is ignored:
t4 s2 r8=t4s2r8 - Parentheses create groups:
(t8s8)4repeats the sequence 4 times - Permutation uses 1-based indexing by default (bit 1 is the first bit); pass
permute_base=0for 0-based. Index0in 1-based mode raisesValueError - Permutation indices are relative to the current pointer position
- Ranges in permutation are inclusive:
1-4includes bits 1, 2, 3, and 4
API Documentation
Main Function
def takeskip(
command: str,
array: npt.NDArray[np.uint8],
*,
remnant: Literal["remove", "keep", "pad"] = "remove",
permute_base: int = 1,
) -> np.ndarray
Parameters:
command: Command string expressing the operationarray: Target numpy array (dtype=uint8, values 0 or 1)remnant: How to handle remaining bits ("remove", "keep", or "pad")permute_base: Numbering base for permute indices, 0 or 1 (default: 1)
Returns:
- Numpy array with same dtype, modified according to commands
Raises:
TypeError:arrayis not a numpy arrayValueError: Invalidremnantorpermute_baseargument, invalid command syntax, or a command that moves the pointer out of bounds
Note: Array values are not validated. Values other than 0 and 1 pass through t/s/r/p unchanged, but produce meaningless results with i/ri (which XOR each value with 1).
Contributing
When adding new commands:
- Define command class in
commands.pyinheriting fromCommand - Add grammar rule in
takeskip.lark - Add transformer method in
parser.py - Update documentation
License
takeskip is licensed under the MIT License - see the LICENSE file for details
Metadata
Release files for takeskip 0.3.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| takeskip-0.3.0.tar.gz | 45.9 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| takeskip-0.3.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 58.2 kB
Release files / takeskip-0.3.0.tar.gz
| Download URL | takeskip-0.3.0.tar.gz |
|---|---|
| Size | 45.9 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
1d425156688e7dd028746a8f36c6642af76d15bdfa519d90d0cde288afd9b579
|
|
BLAKE2b-256 checksum How to use checksums |
eecada2dd05f1bb10c45aea3c802934fc40962e8b82f64e04ce099ae65994b7e
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.12.18 {"installer":{"name":"uv","version":"0.12.18","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Release files / takeskip-0.3.0-py3-none-any.whl
| Download URL | takeskip-0.3.0-py3-none-any.whl |
|---|---|
| Size | 12.3 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
d98a285d8029737cb60859faefe033e245f30c8855bb98c242f8d89a47830e8f
|
|
BLAKE2b-256 checksum How to use checksums |
22fe3dfcb1525d1aa28ef372eb8cc0e0b9305c5cd7bac12fd708feebd4c3d5ba
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.12.18 {"installer":{"name":"uv","version":"0.12.18","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|