From bd4aa68980f2dd2cbbb7feb86ce4dd9bb73ce697 Mon Sep 17 00:00:00 2001 From: IxMxAMAR Date: Wed, 6 May 2026 14:54:20 +0530 Subject: [PATCH] feat(programming): add 8 logic + bitwise ops --- nodes/programming/__init__.py | 1 + nodes/programming/operations/logic_bits.py | 115 +++++++++++++++++++++ tests/unit/test_programming_logic_bits.py | 53 ++++++++++ 3 files changed, 169 insertions(+) create mode 100644 nodes/programming/operations/logic_bits.py create mode 100644 tests/unit/test_programming_logic_bits.py diff --git a/nodes/programming/__init__.py b/nodes/programming/__init__.py index 40cd628..89324ed 100644 --- a/nodes/programming/__init__.py +++ b/nodes/programming/__init__.py @@ -8,6 +8,7 @@ op = REGISTRY.register from nodes.programming.operations import ( # noqa: E402, F401 _smoke, encoding, + logic_bits, ) diff --git a/nodes/programming/operations/logic_bits.py b/nodes/programming/operations/logic_bits.py new file mode 100644 index 0000000..756e97c --- /dev/null +++ b/nodes/programming/operations/logic_bits.py @@ -0,0 +1,115 @@ +"""Boolean and bitwise operations.""" + +from .. import op + + +@op( + op_id="bool_and", + display_name="Boolean AND", + category="Logic & Bits", + input_schema={"required": { + "a": ("BOOLEAN", {"default": False}), + "b": ("BOOLEAN", {"default": False}), + }}, + output_indices=(3,), +) +def bool_and(self, a, b): + return (bool(a) and bool(b),) + + +@op( + op_id="bool_or", + display_name="Boolean OR", + category="Logic & Bits", + input_schema={"required": { + "a": ("BOOLEAN", {"default": False}), + "b": ("BOOLEAN", {"default": False}), + }}, + output_indices=(3,), +) +def bool_or(self, a, b): + return (bool(a) or bool(b),) + + +@op( + op_id="bool_not", + display_name="Boolean NOT", + category="Logic & Bits", + input_schema={"required": {"a": ("BOOLEAN", {"default": False})}}, + output_indices=(3,), +) +def bool_not(self, a): + return (not bool(a),) + + +@op( + op_id="bool_xor", + display_name="Boolean XOR", + category="Logic & Bits", + input_schema={"required": { + "a": ("BOOLEAN", {"default": False}), + "b": ("BOOLEAN", {"default": False}), + }}, + output_indices=(3,), +) +def bool_xor(self, a, b): + return (bool(a) != bool(b),) + + +@op( + op_id="bitwise_and", + display_name="Bitwise AND", + category="Logic & Bits", + input_schema={"required": { + "a": ("INT", {"default": 0}), + "b": ("INT", {"default": 0}), + }}, + output_indices=(1,), +) +def bitwise_and(self, a, b): + return (int(a) & int(b),) + + +@op( + op_id="bitwise_or", + display_name="Bitwise OR", + category="Logic & Bits", + input_schema={"required": { + "a": ("INT", {"default": 0}), + "b": ("INT", {"default": 0}), + }}, + output_indices=(1,), +) +def bitwise_or(self, a, b): + return (int(a) | int(b),) + + +@op( + op_id="bitwise_xor", + display_name="Bitwise XOR", + category="Logic & Bits", + input_schema={"required": { + "a": ("INT", {"default": 0}), + "b": ("INT", {"default": 0}), + }}, + output_indices=(1,), +) +def bitwise_xor(self, a, b): + return (int(a) ^ int(b),) + + +@op( + op_id="bitwise_shift", + display_name="Bitwise Shift", + category="Logic & Bits", + input_schema={"required": { + "a": ("INT", {"default": 0}), + "places": ("INT", {"default": 0, "min": -64, "max": 64}), + }}, + output_indices=(1,), + description="Positive places shifts left, negative shifts right.", +) +def bitwise_shift(self, a, places=0): + if places >= 0: + return (int(a) << places,) + return (int(a) >> -places,) diff --git a/tests/unit/test_programming_logic_bits.py b/tests/unit/test_programming_logic_bits.py new file mode 100644 index 0000000..5d04379 --- /dev/null +++ b/tests/unit/test_programming_logic_bits.py @@ -0,0 +1,53 @@ +"""Tests for the Logic & Bits category.""" + +from nodes.programming import ProgrammingNode + + +def run(mode, **kwargs): + return ProgrammingNode().process(mode=mode, theme="(use pack default)", **kwargs) + + +def test_bool_and_truth_table(): + assert run("bool_and", a=True, b=True)[3] is True + assert run("bool_and", a=True, b=False)[3] is False + assert run("bool_and", a=False, b=False)[3] is False + + +def test_bool_or_truth_table(): + assert run("bool_or", a=False, b=False)[3] is False + assert run("bool_or", a=True, b=False)[3] is True + + +def test_bool_not_inverts(): + assert run("bool_not", a=True)[3] is False + assert run("bool_not", a=False)[3] is True + + +def test_bool_xor_truth_table(): + assert run("bool_xor", a=True, b=True)[3] is False + assert run("bool_xor", a=True, b=False)[3] is True + assert run("bool_xor", a=False, b=False)[3] is False + + +def test_bitwise_and(): + assert run("bitwise_and", a=0b1100, b=0b1010)[1] == 0b1000 + + +def test_bitwise_or(): + assert run("bitwise_or", a=0b1100, b=0b1010)[1] == 0b1110 + + +def test_bitwise_xor(): + assert run("bitwise_xor", a=0b1100, b=0b1010)[1] == 0b0110 + + +def test_bitwise_shift_left(): + assert run("bitwise_shift", a=1, places=4)[1] == 16 + + +def test_bitwise_shift_right(): + assert run("bitwise_shift", a=16, places=-2)[1] == 4 + + +def test_bitwise_shift_zero_is_identity(): + assert run("bitwise_shift", a=42, places=0)[1] == 42