"""Signal pattern matching."""

import re
from typing import Union


class PatternError(Exception):
    """Pattern error."""


class Pattern:
    """Signal pattern representation."""

    PATTERN_REGEX = re.compile(r"[01xX]+")
    PATTERN_REGEX_BYTES = re.compile(b"[01xX]+")

    def __init__(self, pattern: Union[str, bytes]):
        """Initialize."""
        if not isinstance(pattern, (str, bytes)):
            raise TypeError("pattern must be a string or bytes")

        # tolerate some variations
        if isinstance(pattern, str):
            if pattern.endswith("h"):
                pattern = bin(int(pattern[:-1], 16))
            if pattern.startswith("0b"):
                pattern = pattern[2:]

            m = self.PATTERN_REGEX.match(pattern)
        else:
            m = self.PATTERN_REGEX_BYTES.match(pattern)
        if m is None:
            raise PatternError(f"pattern is invalid: {pattern}")

        self._pattern = pattern

    @property
    def pattern(self):
        """Get pattern."""
        return self._pattern

    def __repr__(self):
        """Get representation."""
        return self.pattern

    def __len__(self):
        """Get length."""
        return len(self._pattern)

    def match(self, value: Union[str, bytes]) -> bool:
        """Match against value."""
        if not isinstance(value, (str, bytes)):
            raise TypeError("value must be string or bytes")

        if type(value) != type(self._pattern):
            raise TypeError("incompatible types for value and pattern")

        pattern = self._pattern
        if len(value) < len(self._pattern):
            # zero-extend incomin value
            count = len(self._pattern) - len(value)
            value = "0" * count + value
        elif len(value) > len(self._pattern):
            # zero-extend pattern
            count = len(value) - len(self._pattern)
            pattern = "0" * count + self._pattern
        for value_bit, expected_bit in zip(value, pattern):
            if expected_bit in ("x", "X"):
                # don't care
                continue
            if expected_bit != value_bit:
                return False

        return True

@@ -604,33 +604,31 @@
 n = 
-bin(int(pattern%5B:-1%5D, 16)
+self.hex_to_bin(pattern
 )%0A  
@@ -2170,8 +2170,391 @@
 rn True%0A
+%0A    @staticmethod%0A    def hex_to_bin(hexstr):%0A        %22%22%22Convert hex to binary including don't cares.%22%22%22%0A        if hexstr.endswith(%22h%22):%0A            hexstr = hexstr%5B:-1%5D%0A%0A        hexstr = hexstr.replace(%22x%22, %22X%22)%0A        split = hexstr.split(%22X%22)%0A        ret = %22%22%0A        for fragment in split:%0A            ret += bin(int(fragment, 16)) if fragment else %22xxxx%22%0A%0A        return ret%0A

Convert from hex with don't cares properly