Source code for conllu_tools.matching.token

"""TokenPattern class definition."""

from __future__ import annotations

from typing import Any

import conllu

from .condition import Condition


[docs] class TokenPattern: """Represents a series of conditions to match in a token. Attributes: conditions (list[Condition]): The list of conditions to match. negate (bool): Whether to negate the match result. count (int): Exact number of times the pattern should match. min_count (int): Minimum number of times the pattern should match. max_count (int): Maximum number of times the pattern should match. match_multiple (bool): Whether the pattern can match multiple times. """
[docs] def __init__( self, conditions: list[Condition] | None = None, negate: bool = False, # noqa: FBT001 count: int | None = None, min_count: int | None = None, max_count: int | None = None, ) -> None: """Initialize the TokenPattern.""" if isinstance(conditions, list) and not all(isinstance(p, Condition) for p in conditions): msg = "Parameter 'conditions' must be a list of Condition instances." raise ValueError(msg) self.conditions: list[Condition] = conditions if conditions is not None else [] self.negate: bool = negate self.count: int = count if count is not None else 0 self.min_count: int = min_count if min_count is not None else 0 self.max_count: int = max_count if max_count is not None else 1 self.match_multiple: bool = self.max_count != 1 self.counter: int = 0 self.matches_any: bool = not conditions or len(conditions) == 0 self.error_msg: str = ''
[docs] def test(self, target: conllu.Token | dict[str, Any]) -> bool: """Test if a token meets the conditions in the pattern.""" if not self.is_valid: raise ValueError(self.error_msg) result = all(cond.test(target) for cond in self.conditions) result = not result if self.negate else result self.counter += 1 if result else 0 return result
@property def is_satisfied(self) -> bool: """Check if the minimum count is satisfied.""" return self.counter >= self.min_count @property def is_exceeded(self) -> bool: """Check if the maximum count is exceeded.""" return self.counter >= self.max_count @property def is_valid(self) -> bool: """Check if the TokenPattern is properly configured.""" if not self.matches_any: condition_errors = [i for i, cond in enumerate(self.conditions) if not cond.is_valid] if condition_errors: error_indices_str = ', '.join(str(idx) for idx in condition_errors) self.error_msg = f'TokenPattern has invalid conditions at indices: {error_indices_str}.' return False return True
[docs] def explain(self) -> str: """Provide a string explanation of the TokenPattern.""" if not self.is_valid: return self.error_msg range_phrase = '' if self.count != 0: range_phrase = f'exactly {self.count} times.' elif self.min_count != 0 or self.max_count != 1: range_phrase = f'between {self.min_count} and {self.max_count} times.' if self.matches_any: return f'Matches any token {range_phrase}' conds_str = '' if len(self.conditions) > 2: # noqa: PLR2004 conds_str += ', '.join(cond.explain() for cond in self.conditions[:-1]) conds_str += f', and {self.conditions[-1].explain()}' elif len(self.conditions) == 2: # noqa: PLR2004 conds_str += f'{self.conditions[0].explain()} and {self.conditions[1].explain()}' else: conds_str += self.conditions[0].explain() sent_start = 'Does not match' if self.negate else 'Matches' return f'{sent_start} a token when {conds_str} {range_phrase}'
[docs] def __repr__(self) -> str: """Return a string representation of the TokenPattern.""" if not self.is_valid: return self.error_msg attributes = [] if self.count != 0: attributes.append(f'count={self.count}') elif self.min_count != 0 or self.max_count != 1: attributes.append(f'min_count={self.min_count}, max_count={self.max_count}') if self.conditions: conds_str = ', '.join(repr(c) for c in self.conditions) attributes.append(f'conditions=[{conds_str}]') if self.negate: attributes.append('negate=True') return f'TokenPattern({", ".join(attributes)})'
[docs] def __str__(self) -> str: """Return a string description of the TokenPattern.""" return self.__repr__()