Przejdź do treści

olski.segmentacja.projekt

Leksykon projektu: polskie słowo odmienione, którego słownik nie ma.

Morfeusza prosi się wprost, żeby formy nieznanej nie zgadywał (olski/morph.py), więc commitów wraca jako ign i nie bierze go ani jedna produkcja. Odmianę takiego słowa deklaruje sekcja leksykon konfiguracji projektu wpisem o trzech polach (olski/konfiguracja.py), a docs/warstwa-leksykalna.md wywodzi, czemu deklaracja, czemu wskazanie leksemu zamiast listy form i co to kosztuje.

Odmianę wydaje z takiego wpisu sam słownik. Temat wzorca podmienia się na temat naszego słowa, a granicę między tematem i końcówką wycina to, na czym formy wzorca przestają się zgadzać (_granica), więc alternację niesie wzorzec: bat ma bacie, a commit bierze stamtąd i commitach, i commicie. Wzorzec alternujący inaczej niż nasze słowo spełnia przy tym warunek na końcówkę i wydaje formę fałszywą — pies dałby dla bies dopełniacz bsa — i po to jest trzecie pole, czyli forma, którą wzorzec ma wydać: odmiana sprawdza ją, zamiast zostawić pomyłkę w ciszy.

Deklaracje te czyta się przy imporcie, a formy liczy się przy pierwszym pytaniu, i różni je cena: wpisy stoją w pliku, a formy żądają Morfeusza w trybie syntezy, którego sama analiza tekstu nie potrzebuje do niczego.

Czytania leksykonu dochodzą do czytań słownika w z_leksykonu i woła ją cała analiza. Funkcja jest jedna, bo dołożona osobno w każdym z wołających byłaby tą samą regułą napisaną dwa razy. Skład go nie czyta i o tym, co mu z tego zostaje, mówi todo/.

WPISY_KLUCZ = 'wpisy' module-attribute

SKRÓT = 'brev' module-attribute

WPISY = czytaj(sekcja(LEKSYKON, (WPISY_KLUCZ,))) module-attribute

ZłyWpis

Bases: Exception

Wpis leksykonu, z którego nie wychodzi odmiana, którą on obiecuje.

Wyjątek, a nie forma pominięta, bo każda przyczyna jest usterką w pliku pisanym ręką: leksem, którego słownik nie ma, paradygmat o dwóch tematach, końcówka, której nasz lemat nie ma, i świadek, którego wzorzec nie wydaje. Ruch po zgłoszeniu jest za każdym razem ten sam, czyli poprawiony wpis, i dlatego klasa jest jedna.

Source code in olski/projekt.py
52
53
54
55
56
57
58
59
60
class ZłyWpis(Exception):
    """Wpis leksykonu, z którego nie wychodzi odmiana, którą on obiecuje.

    Wyjątek, a nie forma pominięta, bo każda przyczyna jest usterką w pliku
    pisanym ręką: leksem, którego słownik nie ma, paradygmat o dwóch tematach,
    końcówka, której nasz lemat nie ma, i świadek, którego wzorzec nie wydaje.
    Ruch po zgłoszeniu jest za każdym razem ten sam, czyli poprawiony wpis,
    i dlatego klasa jest jedna.
    """

Wpis

Bases: NamedTuple

Co leksykon mówi o jednym słowie.

Source code in olski/projekt.py
63
64
65
66
67
68
69
70
71
72
73
74
class Wpis(NamedTuple):
    """Co leksykon mówi o jednym słowie."""

    #: Lemat słowa, którego słownik nie ma.
    lemat: str
    #: Identyfikator leksemu SGJP, wedle którego to słowo się odmienia.
    #: Leksem, a nie lemat, bo pod jednym napisem stoi ich kilka i różnią się
    #: odmianą: ``bat:Sm3~a`` ma dopełniacz ``bata``, a ``bat:Sm3~u`` ``batu``.
    wzorzec: str
    #: Forma, którą wzorzec ma wydać. Inna niż lemat, bo lemat wychodzi z każdego
    #: wzorca, który przeszedł warunek na końcówkę.
    świadek: str

lemat instance-attribute

wzorzec instance-attribute

świadek instance-attribute

czytaj(dane)

Leksykon jako wpisy, w kolejności, w jakiej konfiguracja je wypisuje.

Kluczem nic tu nie jest, bo pytanie idzie o formę, a nie o lemat, a jeden lemat ma tyle wpisów, ile leksemów mu się należy: olski jest i przymiotnikiem, i rzeczownikiem, tak jak polski.

Source code in olski/projekt.py
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
def czytaj(dane: Mapping[str, Any]) -> tuple[Wpis, ...]:
    """Leksykon jako wpisy, w kolejności, w jakiej konfiguracja je wypisuje.

    Kluczem nic tu nie jest, bo pytanie idzie o formę, a nie o lemat,
    a jeden lemat ma tyle wpisów, ile leksemów mu się należy:
    ``olski`` jest i przymiotnikiem, i rzeczownikiem, tak jak ``polski``.
    """
    wpisy = []
    for numer, wpis in enumerate(dane.get(WPISY_KLUCZ, []), start=1):
        # Liczba pól wychodzi z samej klasy, więc pole dopisane do niej nie
        # zostawia tu warunku, który przepuszcza wpis bez tego pola.
        if not isinstance(wpis, list) or len(wpis) != len(Wpis._fields):
            raise ZłaKonfiguracja(
                f"sekcja {LEKSYKON}, wpis {numer}: "
                f"wpisem jest lemat, leksem i świadek, a nie {wpis!r}"
            )
        if not all(isinstance(pole, str) for pole in wpis):
            raise ZłaKonfiguracja(f"sekcja {LEKSYKON}, wpis {numer}: pola są napisami")
        wpisy.append(Wpis(*wpis))
    return tuple(wpisy)

odmiana(wpis) cached

Czytania, jakie ten wpis daje słowu, po jednym na formę i tag wzorca.

Temat wzorca podmienia się na temat naszego słowa wszędzie tam, gdzie stoi, a nie na samym początku formy, bo formę wolno poprzedzić przedrostkiem: słownik trzyma niemalowanie w paradygmacie malować, więc lintować bierze stamtąd nielintowanie.

Kolejność jest ustalona sortowaniem, a nie kolejnością słownika, bo czytania wychodzą stąd do wydruku werdyktu.

Source code in olski/projekt.py
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
135
136
@functools.cache
def odmiana(wpis: Wpis) -> tuple[Reading, ...]:
    """Czytania, jakie ten wpis daje słowu, po jednym na formę i tag wzorca.

    Temat wzorca podmienia się na temat naszego słowa wszędzie tam, gdzie stoi,
    a nie na samym początku formy, bo formę wolno poprzedzić przedrostkiem:
    słownik trzyma ``niemalowanie`` w paradygmacie ``malować``,
    więc ``lintować`` bierze stamtąd ``nielintowanie``.

    Kolejność jest ustalona sortowaniem, a nie kolejnością słownika,
    bo czytania wychodzą stąd do wydruku werdyktu.
    """
    formy = [
        (forma, surowy)
        for forma, leksem, surowy, _nazwy, _kwalifikatory in generuj(_lemat(wpis.wzorzec))
        if leksem == wpis.wzorzec and surowy.split(":", 1)[0] != SKRÓT
    ]
    if not formy:
        raise ZłyWpis(f"{wpis.lemat}: słownik nie ma leksemu {wpis.wzorzec}")
    temat, końcówka = _granica(wpis.wzorzec, [forma for forma, _ in formy])
    if not wpis.lemat.endswith(końcówka):
        raise ZłyWpis(
            f"{wpis.lemat}: leksem {wpis.wzorzec} odmienia się od końcówki "
            f"„{końcówka}”, której ten lemat nie ma"
        )
    nasz = wpis.lemat[: len(wpis.lemat) - len(końcówka)]
    nasze = {
        Reading(form=forma.replace(temat, nasz, 1), lemma=wpis.lemat, tag=tag(surowy))
        for forma, surowy in formy
    }
    czytania = tuple(sorted(nasze, key=lambda czytanie: (czytanie.form, czytanie.tag.raw)))
    _sprawdź_świadka(wpis, czytania)
    return czytania

czytania(forma)

Czytania, jakie leksykon projektu daje tej formie, albo nic.

Forma wraca w czytaniu taka, jak stoi w tekście, a nie taka, jak ją wydał słownik, bo tym samym oddaje ją Morfeusz: Program wraca od niego z lematem program i z formą Program.

Source code in olski/projekt.py
196
197
198
199
200
201
202
203
204
def czytania(forma: str) -> tuple[Reading, ...]:
    """Czytania, jakie leksykon projektu daje tej formie, albo nic.

    Forma wraca w czytaniu taka, jak stoi w tekście, a nie taka, jak ją wydał
    słownik, bo tym samym oddaje ją Morfeusz: ``Program`` wraca od niego z
    lematem ``program`` i z formą ``Program``.
    """
    znalezione = _wedle_formy().get(forma.casefold(), ())
    return tuple(replace(czytanie, form=forma) for czytanie in znalezione)

z_leksykonu(segment)

Krawędź wraz z czytaniami, jakie leksykon projektu daje jej formie.

Czytania leksykonu dochodzą do tych, które ma słownik, a nie zastępują ich, bo leksykon orzeka o formie, a nie łata milczenie Morfeusza: forma, którą słownik zna, a leksykon o niej mówi, ma czytania jednego i drugiego, i tyle właśnie czytań ma wtedy w polszczyźnie.

Czytanie ign stąd schodzi, bo mówi ono, że słowa nie zna nikt, a leksykon właśnie je nazwał. Krawędź bez czytań z tego nie wyjdzie: znika ono tylko tam, gdzie leksykon coś dołożył.

Source code in olski/projekt.py
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
def z_leksykonu(segment: Segment) -> Segment:
    """Krawędź wraz z czytaniami, jakie leksykon projektu daje jej formie.

    Czytania leksykonu dochodzą do tych, które ma słownik, a nie zastępują ich,
    bo leksykon orzeka o formie, a nie łata milczenie Morfeusza: forma, którą
    słownik zna, a leksykon o niej mówi, ma czytania jednego i drugiego, i tyle
    właśnie czytań ma wtedy w polszczyźnie.

    Czytanie ``ign`` stąd schodzi, bo mówi ono, że słowa nie zna nikt, a
    leksykon właśnie je nazwał. Krawędź bez czytań z tego nie wyjdzie: znika ono
    tylko tam, gdzie leksykon coś dołożył.
    """
    czytania_leksykonu = czytania(segment.form)
    if not czytania_leksykonu:
        return segment
    znane = tuple(reading for reading in segment.readings if reading.tag.known)
    return replace(segment, readings=znane + czytania_leksykonu)