Source code for segram.semantic.frames

from typing import Self, Any, Sequence, Callable
from abc import abstractmethod
import re
from ..grammar import Sent, Phrase, NounPhrase, VerbPhrase
from ..symbols import Dep
from ..utils.matching import Matcher
from ..datastruct import DataIterator


[docs] class Frame(Sequence): """Semantic frame class. Semantic frames groups phrases from a given :class:`segram.semantic.Story` instance according to some selection criteria. Attributes ---------- story A Story the frame belongs to. matcher Arbitrary callable implementing filtering function. phrases Phrases matching the criteria. """ def __init__(self, story: "Story") -> None: self.story = story self.matcher = Matcher(self.is_match) def __len__(self) -> int: return len(self.phrases) def __getitem__(self, idx: int | slice) -> Phrase | tuple[Phrase, ...]: return self.phrases[idx] def __and__(self, other: Self) -> Self: if isinstance(other, Frame | Callable): new = self.copy() func = other.matcher if isinstance(other, Frame) else other new.matcher &= func return new return NotImplemented def __rand__(self, other: Self) -> Self: return self & other def __or__(self, other: Self) -> Self: if isinstance(other, Frame | Callable): new = self.copy() func = other.matcher if isinstance(other, Frame) else other new.matcher |= func return new return NotImplemented def __ror__(self, other: Self) -> Self: return self | other def __call__(self, phrase: Phrase) -> bool: return self.match(phrase) # Properties -------------------------------------------------------------- @property def phrases(self) -> DataIterator[Phrase]: return self.story.phrases.filter(self.match) @property def sents(self) -> DataIterator[Sent]: return self.phrases.unique(lambda p: (p.doc.idx, p.idx)).get("sent") # Methods -----------------------------------------------------------------
[docs] @abstractmethod def is_match(self, phrase: Phrase) -> bool: """Does ``phrase`` match the criteria of the frame."""
[docs] def match(self, phrase: Phrase) -> bool: """Match phrase against the selection criteria.""" return self.matcher(phrase)
def copy(self, **kwds: Any) -> Self: return self.__class__(**{ "story": self.story, **kwds })
[docs] @classmethod def subclass(cls, is_match: Callable[[Phrase], bool]) -> type[Self]: """Make subclass from a callable. Callable is injected into a class as a staticmethod, so it should not use the ``self`` parameter. """ name = re.sub(r"\W", r"", is_match.__name__) return type(f"{name}{id(is_match)}", (cls,), { "__slots__": (), "is_match": staticmethod(is_match) })
[docs] class Actants(Frame): """Semantic frame of actants.""" __slots__ = ()
[docs] def is_match(self, phrase: Phrase) -> bool: match phrase: case NounPhrase(dep=dep): return not dep & (Dep.nmod | Dep.desc | Dep.appos) case _: return False
[docs] class Events(Frame): """Semantic frame of events.""" __slots__ = ()
[docs] def is_match(self, phrase: Phrase) -> bool: match phrase: case VerbPhrase(dep=dep): return not dep & Dep.xcomp case _: return False