Przejdź do treści

olski.document

Gdzie w tekście kończy się jedno zdanie, a zaczyna następne.

Gramatyka bierze zdanie, a wejściem jest plik, więc ktoś musi jedno pociąć na drugie, zanim analizator zobaczy pierwszy segment. Robi to ten moduł i nic poza nim, bo cięcie jest rozstrzygnięciem o polszczyźnie — który skrót kończy zdanie, a który je ciągnie — a nie krokiem czytania pliku.

Cięcie stoi przed analizą, a nie po niej, i dlaczego, mówi sentences w olski/segmentacja.py: po analizie nie ma już czym zobaczyć spacji, która odróżnia granicę zdania od nazwy pliku.

PARAGRAPH_BREAK = re.compile('\\n[ \\t]*\\n') module-attribute

CLOSING_MARK = '[.!?…]+[”»\\"\')\\]]*' module-attribute

SENTENCE_END = re.compile(CLOSING_MARK + '(?=\\s|\\Z)', re.UNICODE) module-attribute

SENTENCE_CLOSE = re.compile(CLOSING_MARK + '\\s*\\Z', re.UNICODE) module-attribute

ABBREVIATIONS = frozenset({'al.', 'ang.', 'art.', 'cf.', 'cz.', 'dot.', 'dr.', 'ds.', 'dz.', 'gen.', 'gm.', 'godz.', 'hab.', 'im.', 'in.', 'inż.', 'łac.', 'm.in.', 'mgr.', 'min.', 'mld.', 'mln.', 'np.', 'ok.', 'os.', 'par.', 'pkt.', 'pl.', 'płk.', 'por.', 'poz.', 'proc.', 'prof.', 'pt.', 'r.', 'red.', 'rozdz.', 'rys.', 'sek.', 'str.', 'św.', 'sygn.', 'tab.', 'tj.', 'tys.', 'tzn.', 'tzw.', 'ul.', 'ur.', 'ust.', 'wg.', 'woj.', 'ww.', 'wyd.', 'zał.', 'zm.', 'zob.'}) module-attribute

TOKEN_BEFORE = re.compile('[\\w.]*\\Z', re.UNICODE) module-attribute

TOKEN_REACH = 12 module-attribute

NEXT_WORD = re.compile('\\s*(\\w)', re.UNICODE) module-attribute

Span dataclass

Source code in olski/document.py
76
77
78
79
@dataclass(frozen=True)
class Span:
    start: int
    end: int

start instance-attribute

end instance-attribute

__init__(start, end)

Document dataclass

Tekst i podział, który się z niego wylicza.

Podział jest leniwy i pamiętany, bo pyta o niego dopiero ten, kto zdania liczy albo im się przygląda, a wylicza się przy pierwszym pytaniu, więc nie ma dokumentu półgotowego: dwa o tym samym tekście odpowiadają tak samo, obojętne, które miejsce w programie je zbudowało.

Source code in olski/document.py
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
@dataclass(frozen=True)
class Document:
    """Tekst i podział, który się z niego wylicza.

    Podział jest leniwy i pamiętany, bo pyta o niego dopiero ten, kto zdania
    liczy albo im się przygląda, a wylicza się przy pierwszym pytaniu, więc nie
    ma dokumentu półgotowego: dwa o tym samym tekście odpowiadają tak samo,
    obojętne, które miejsce w programie je zbudowało.
    """

    text: str
    #: Reszta po nazwie, bo tekst i ścieżka są oba napisami: zamienione miejscami
    #: dają dokument, który tnie na zdania własną nazwę, i nic tego nie zgłasza.
    _: KW_ONLY
    path: str = "<text>"

    @cached_property
    def paragraphs(self) -> tuple[Span, ...]:
        """Akapity rozdzielone pustym wierszem, wewnątrz których stoją zdania."""
        return tuple(_paragraphs(self.text))

    @cached_property
    def sentences(self) -> tuple[Span, ...]:
        """Zdania, których żadne nie przechodzi przez granicę akapitu."""
        return tuple(_sentences(self.text, self.paragraphs))

    @cached_property
    def wcześniejsze(self) -> tuple[tuple[int, ...], ...]:
        """Numery zdań stojących przed każdym zdaniem w jego akapicie, po jednej krotce na zdanie.

        Wstecz, a nie w obie strony, bo czytelnik idzie od początku do końca,
        i do granicy akapitu, bo akapit jest tym, w czym „obok” się kończy;
        oba wywody trzyma :class:`olski.rozstrzyganie.Sąsiedztwo`.
        Numerami, a nie napisami, bo pytają o to dwie warstwy i pytają o co
        innego: warstwa rozstrzygająca o formy, jakie w tych zdaniach stały,
        a niejasne odniesienie o ich rozbiór (``olski/odniesienia.py``).
        """
        wynik: list[tuple[int, ...]] = []
        akapity = iter(self.paragraphs)
        akapit = next(akapity, None)
        bieżące: list[int] = []
        #  Jedno przejście, bo zdania i akapity idą w tej samej kolejności: zdanie
        #  nie przechodzi przez granicę akapitu (:attr:`sentences`).
        for numer, span in enumerate(self.sentences):
            while akapit is not None and akapit.end < span.end:
                akapit = next(akapity, None)
                bieżące = []
            wynik.append(tuple(bieżące))
            bieżące.append(numer)
        return tuple(wynik)

    def slice(self, span: Span | None = None) -> str:
        return self.text if span is None else self.text[span.start : span.end]

text instance-attribute

_ instance-attribute

path = '<text>' class-attribute instance-attribute

paragraphs cached property

Akapity rozdzielone pustym wierszem, wewnątrz których stoją zdania.

sentences cached property

Zdania, których żadne nie przechodzi przez granicę akapitu.

wcześniejsze cached property

Numery zdań stojących przed każdym zdaniem w jego akapicie, po jednej krotce na zdanie.

Wstecz, a nie w obie strony, bo czytelnik idzie od początku do końca, i do granicy akapitu, bo akapit jest tym, w czym „obok” się kończy; oba wywody trzyma olski.rozstrzyganie.Sąsiedztwo. Numerami, a nie napisami, bo pytają o to dwie warstwy i pytają o co innego: warstwa rozstrzygająca o formy, jakie w tych zdaniach stały, a niejasne odniesienie o ich rozbiór (olski/odniesienia.py).

__init__(text, *, path='<text>')

slice(span=None)

Source code in olski/document.py
133
134
def slice(self, span: Span | None = None) -> str:
    return self.text if span is None else self.text[span.start : span.end]