Przejdź do treści

olski.werdykt

Werdykt o zdaniu: znalezisko wraz z tym, co autor ma przeczytać.

Werdykt mówi o zdaniu więcej niż to, do której klasy je liczy, bo autor ma na niego zareagować. Zdanie o dwóch odczytaniach jest znaleziskiem (docs/subset.md#wieloznaczność-jest-znaleziskiem-a-nie-definicją-olskiego), a Verdict.explain pokazuje, gdzie te odczytania się rozchodzą; znaleziskiem jest też zdanie, które od odczytania dzieli jeden znak (Naprawa); zdanie odrzucone dostaje miejsce, na którym rozbiór stanął, a zatrzymania każde takie miejsce, bo pierwsze zasłania następne. Skąd te odczytania się biorą, mówi Verdict.morfologia: rozchodzą się w rolach, a zaczynają w lemacie i znaczniku formy.

Kto pyta o cały tekst, dostaje check i Podsumowanie, czyli tyle werdyktów, ile zdań, oraz jedną odpowiedź policzoną z nich regułą.

Warstwa ta ani nie wnosi wieloznaczności, ani jej nie zdejmuje, bo jest wypowiedzią o warstwach pod nią (docs/architecture.md). Gramatykę czyta gotową z olski/subset/, a segmentację, po której werdykt pada, z olski/segmentacja.py.

FRAGMENT = 'fragment' module-attribute

NIEDOMKNIĘTE = 'unclosed' module-attribute

DOMKNIĘCIA = {'.': 'kropka na końcu', '?': 'pytajnik na końcu'} module-attribute

Naprawa dataclass

Poprawka jednego znaku, po której olski to zdanie czyta.

Klasa jest jedna na wszystkie takie poprawki, bo autorowi mówią one to samo: olski tego zdania nie czyta, a od czytania dzieli je jeden znak. Świadkiem jest w każdej z nich gramatyka, bo poprawka wchodzi tutaj dopiero wtedy, gdy rozbiór poprawionego napisu daje odczytanie. Reguła stojąca na takim świadku nie żąda kalibracji, której brak zamknął pakiet reguł (docs/linter.md#co-zamknęło-pakiet-reguł).

Liczba odczytań idzie razem z poprawką, bo policzona drugi raz żądałaby trzeciego rozbioru nad zdaniem, które werdykt rozebrał już dwa razy.

Source code in olski/werdykt.py
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
@dataclass(frozen=True)
class Naprawa:
    """Poprawka jednego znaku, po której olski to zdanie czyta.

    Klasa jest jedna na wszystkie takie poprawki, bo autorowi mówią one to samo:
    olski tego zdania nie czyta, a od czytania dzieli je jeden znak. Świadkiem
    jest w każdej z nich gramatyka, bo poprawka wchodzi tutaj dopiero wtedy, gdy
    rozbiór poprawionego napisu daje odczytanie. Reguła stojąca na takim świadku
    nie żąda kalibracji, której brak zamknął pakiet reguł
    (docs/linter.md#co-zamknęło-pakiet-reguł).

    Liczba odczytań idzie razem z poprawką, bo policzona drugi raz żądałaby
    trzeciego rozbioru nad zdaniem, które werdykt rozebrał już dwa razy.
    """

    #: Co autor ma poprawić, tak jak to stoi w wierszu werdyktu.
    poprawka: str
    #: Liczba odczytań, które olski nad poprawionym napisem czyta.
    czytań: int

poprawka instance-attribute

czytań instance-attribute

__init__(poprawka, czytań)

Verdict dataclass

What olski says about one sentence.

Source code in olski/werdykt.py
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
@dataclass(frozen=True)
class Verdict:
    """What olski says about one sentence."""

    #: Zdanie tak, jak stoi w tekście. Segmenty są krawędziami grafu, a nie
    #: listą, więc sklejone dają naraz każdy podział, jaki Morfeusz na formie
    #: widzi: ``ktoś`` wychodzi wtedy jako ``kto ktoś ś``.
    text: str
    result: Result
    #: Formy nie do ominięcia, którym żadna produkcja nie bierze ani jednego
    #: czytania: odrzucenie stanęło na nich, a nie na strukturze. Pola bez
    #: wartości domyślnej, bo pusta krotka jest tu twierdzeniem o zdaniu, a
    #: ``Nowa program zapisuje ustawienia.`` ma je puste i jest odrzucone.
    nielicencjonowane: tuple[str, ...]
    #: Forma, której nie wzięła ani jedna analiza częściowa, czyli miejsce, na
    #: którym odrzucenie stanęło; ``None``, gdy analiza doszła do ostatniego
    #: znaku zdania. Pola bez wartości domyślnej z tego samego powodu co wyżej:
    #: ``None`` jest tu twierdzeniem, a nie brakiem odpowiedzi.
    #: Twierdzeniem jest tylko wtedy, gdy o zatrzymanie pytano.
    #: Przebieg, który nie pytał (:func:`werdykt`), zostawia tu również ``None``.
    #: Rozdziela te dwa ``Result.furthest`` i o niego pyta :meth:`explain`.
    zatrzymanie: str | None
    #: Poprawka jednego znaku, po której olski ten napis czyta, albo ``None``.
    #: Pole, a nie właściwość, bo :func:`_naprawa` kosztuje rozbiór, a właściwość
    #: płaciłaby tyle razy, ile razy ktoś ją przeczyta.
    naprawa: Naprawa | None

    @property
    def punktowane(self) -> bool:
        """Czy tekst punktuje ten napis jako zdanie.

        Mianownik pomiaru pyta o to, a nie o :attr:`status`, bo odpowiedź jest ta
        sama nad oboma werdyktami o napisie niepunktowanym i nie kosztuje rozbioru,
        którego kosztuje :attr:`domknięcie` (:class:`Podsumowanie`).
        """
        return bool(SENTENCE_CLOSE.search(self.text))

    @property
    def znalezisko(self) -> bool:
        """Czy narzędzie ma o tym zdaniu coś do powiedzenia.

        Znaleziska widoczne z jednego zdania są dwa: wieloznaczność oraz poprawka
        jednego znaku
        (docs/subset.md#wieloznaczność-jest-znaleziskiem-a-nie-definicją-olskiego).
        Trzecie — zaimek wskazujący na dwie rzeczy naraz — pada obok werdyktu,
        bo rzeczy nazywa zdanie obok (``olski/odniesienia.py``), więc tutaj go nie ma.
        Warunek stoi tu raz, bo pyta o niego wydruk nad każdym zdaniem osobno;
        :class:`Podsumowanie` liczy każde z tych znalezisk nad tekstem i liczy je osobno.

        Napis niepunktowany zostaje poza znaleziskiem także wtedy, gdy poprawkę
        ma, bo nagłówek i pozycja listy dochodzą tu jako takie napisy i żadne z
        nich nie jest zdaniem, którego autor nie domknął
        (docs/extraction.md); wydruk pokazuje je do flagi ``--zatrzymania``.
        """
        return self.punktowane and (self.result.ambiguous or self.naprawa is not None)

    @property
    def czytane(self) -> bool:
        """Czy olski to zdanie czyta.

        Nieczytane są trzy: zdanie odrzucone, napis, którego nic nie domyka,
        i fragment. O polszczyźnie żadnego z nich olski nie orzeka,
        więc wydruk milczy o nich do flagi (``olski/check.py``).
        """
        return self.punktowane and not self.result.rejected

    @property
    def status(self) -> str:
        """Klasa, do której werdykt to zdanie liczy, czyli słowo pomiaru.

        Wydruk komendy go nie podaje, bo dzieli zdania po liczbie odczytań,
        a nie po tym, czy narzędzie ma o zdaniu co powiedzieć (:attr:`znalezisko`).
        Czytają je pomiar pokrycia (``olski/pokrycie.py``), sondy w ``harness/``
        i znaczek na stronie (``witryna/skrypt.js``).
        """
        if not self.punktowane:
            return NIEDOMKNIĘTE if self.naprawa else FRAGMENT
        return self.result.status

    @property
    def readings(self) -> list[tuple[dict[str, str], ...]]:
        """Streszczenia odczytań, każde raz (:func:`streszczenia`).

        Odczytanie jest krotką streszczeń, po jednym na zdanie składowe, więc
        wpisem tej listy jest krotka, a nie jedno streszczenie.

        Lista jest po to, żeby pokazać różnicę między odczytaniami,
        a różnicę spoza zasięgu streszczenia nazywa wiersz o konstytuencie
        (:func:`_rozbieżny`), więc powtórzony napis nie zostawia jej nienazwanej.
        Liczbę odczytań podaje las (:attr:`Result.ile`),
        więc skrócenie tej listy jej nie rusza.
        """
        return [
            tuple(map(_po_szkolnemu, streszczenie))
            for streszczenie in streszczenia(self.result.readings, DEKLARACJA)
        ]

    @property
    def morfologia(self) -> list[tuple[OdczytaniaFormy, ...]]:
        """Czym formy stoją w każdym odczytaniu: wpis na streszczenie z :attr:`readings`.

        Po co ta odpowiedź autorowi, mówi
        docs/pisanie-po-olsku.md#skąd-bierze-się-odczytanie-którego-autor-nie-widzi.

        Wiersz dostaje forma czytana więcej niż jednym sposobem, a odczytania w
        nim licencjonują ją w tym kształcie (:attr:`olski.parse.Leaf.odczytania`);
        wiersze składa :func:`_pod_streszczeniem`, a nad streszczeniem, które
        zbiera kilka kształtów, bierze odczytania z każdego z nich.
        Wpisów jest tyle, ile streszczeń, więc nad zdaniem urwanym na
        :data:`olski.parse.MAX_READINGS` mówią one o odczytaniach wypisanych.

        Zdanie bez ani jednego odczytania dostaje jeden wpis, a w nim każde
        odczytanie każdej formy (:func:`_morfologia_zdania`): odsiać ich nie ma
        czym. Rozstrzyga się to tutaj, a nie w wydruku, bo wydruki są dwa
        (``olski/check.py`` i ``witryna/werdykty.py``) i rozjechałyby się po cichu.
        """
        if self.result.rejected or not self.punktowane:
            return [_morfologia_zdania(self.text)]
        return [
            _pod_streszczeniem(drzewa)
            for _streszczenie, drzewa in streszczone(self.result.readings, DEKLARACJA)
        ]

    @property
    def rachunki(self) -> list[tuple[tuple[str, int], ...]]:
        """Za co płaci każde odczytanie: wpis na streszczenie z :attr:`readings`.

        Wychodzą stąd pozycje policzone, a nie ich suma, bo kolejność czytań
        rozstrzyga koszt czytany od góry drzewa
        (``test_koszt_produkcji_nie_sumuje_się_do_kosztu_rodzica``),
        więc suma czytałaby się na miejsce w kolejce, którym nie jest.

        Streszczenie zbiera czasem kilka kształtów, a rachunek jest tego, który
        wyszedł z lasu pierwszy, czyli tego, przez którego to streszczenie stoi
        tam, gdzie stoi.
        """
        return [
            cennik.rachunek(_koszty_drzewa(drzewa[0]))
            for _streszczenie, drzewa in streszczone(self.result.readings, DEKLARACJA)
        ]

    @property
    def żądania(self) -> list[tuple[Żądanie, ...]]:
        """Czego czasownik żąda od obsadzonych pozycji: wpis na streszczenie z :attr:`readings`.

        Po co ta odpowiedź autorowi i czego ona nie mówi, wywodzi
        ``olski/żądania.py``; wpis jest na streszczenie z tego samego powodu, co
        przy :attr:`morfologia`, i grupuje go to samo pytanie o las.

        Warunku na zdanie odrzucone nie ma i nie ma go czemu stawiać: pozycję
        obsadza czytanie, a takie zdanie nie ma ani jednego, więc lista wychodzi
        stąd pusta sama. Morfologia ma tam wpis, bo mówi o formach, a nie o rolach.
        """
        return [
            _żądania_streszczenia(drzewa)
            for _streszczenie, drzewa in streszczone(self.result.readings, DEKLARACJA)
        ]

    @property
    def rozbieżne(self) -> list[Rozbieżność]:
        """Konstytuenty rozbieżne, którym streszczenia naprawdę się różnią.

        Jedno streszczenie znaczy, że streszczenie tej różnicy nie widzi
        (:class:`Rozbieżność`), a wypisane byłoby wierszem bez treści.
        Warunek stoi tu raz na oba wydruki, na wiersz poleceń i na witrynę,
        bo napisany dwa razy rozjechałby się po cichu.
        """
        return [r for r in self.result.rozbieżności if len(r.czytania) > 1]

    def explain(self) -> str:
        #  Poprawka wyprzedza każde inne wyjaśnienie i wyprzedza je nad zdaniem
        #  odrzuconym tak samo jak nad napisem niedomkniętym: zatrzymanie mówi,
        #  dokąd doszła analiza, a poprawka mówi, co z tym zrobić.
        if self.naprawa is not None:
            return _naprawiony(self.naprawa)
        if self.status == FRAGMENT:
            return "to nie zdanie: nic go nie punktuje jako zdania"
        if self.result.valid:
            return _odczytań(1)
        if self.result.rejected:
            if self.nielicencjonowane:
                # Cudzysłów jest treścią: najczęstszą formą bez licencji jest
                # przecinek, a lista rozdzielana przecinkami gubi bez niego granice.
                formy = ", ".join(f"„{forma}”" for forma in self.nielicencjonowane)
                return f"brak odczytania: żadna produkcja nie bierze {formy}"
            if self.result.furthest is None:
                #  Tak samo odmawia ``bloker`` w ``olski/pokrycie.py`` i z tego
                #  samego powodu: milczenie o zatrzymaniu czytałoby się tu jako
                #  zdanie o analizie, która doszła do końca.
                raise ValueError(
                    "wyjaśnienie odrzucenia nazywa miejsce zatrzymania, "
                    "a ten przebieg o zatrzymanie nie pytał (werdykt w olski/werdykt.py)"
                )
            if self.zatrzymanie is None:
                return "brak odczytania: analiza dochodzi do końca, a nic nie domyka zdania"
            return f"brak odczytania: analiza staje na „{self.zatrzymanie}”"
        przyłączenia = self.result.przyłączenia
        # Przekład idzie i tutaj (:func:`_nazwy_szkolne`), bo wiersz ten nie ma
        # nazywać roli, której lista czytań pod nim nie nazywa.
        różne = sorted(
            {
                nazwa
                for role in self.result.różniące
                # Przyłączenie nazwane niżej mówi o tej roli więcej niż sama jej
                # nazwa, więc wypisana obok byłaby tym samym zdaniem dwa razy.
                if not (przyłączenia and role == WYRAŻENIE_PRZYIMKOWE)
                for nazwa in _nazwy_szkolne(role)
            }
        )
        # Liczba i role wychodzą z lasu, więc granica wyliczania sięga listy
        # czytań i nie sięga tego wiersza: liczba jest liczbą, a nie „64+”.
        wiersz = _odczytań(self.result.ile)
        if różne:
            # Dwukropek oddziela nazwy od zdania: bez niego „różne w dopełnienie”
            # czyta się jak rzeczownik, którego przyimek nie odmienił.
            wiersz += f", różne w {'roli' if len(różne) == 1 else 'rolach'}: {', '.join(różne)}"
        return "; ".join(
            [
                wiersz,
                *map(_nierozstrzygnięte, przyłączenia),
                *map(_rozbieżny, self.result.rozbieżności),
            ]
        )

text instance-attribute

result instance-attribute

nielicencjonowane instance-attribute

zatrzymanie instance-attribute

naprawa instance-attribute

punktowane property

Czy tekst punktuje ten napis jako zdanie.

Mianownik pomiaru pyta o to, a nie o status, bo odpowiedź jest ta sama nad oboma werdyktami o napisie niepunktowanym i nie kosztuje rozbioru, którego kosztuje domknięcie (Podsumowanie).

znalezisko property

Czy narzędzie ma o tym zdaniu coś do powiedzenia.

Znaleziska widoczne z jednego zdania są dwa: wieloznaczność oraz poprawka jednego znaku (docs/subset.md#wieloznaczność-jest-znaleziskiem-a-nie-definicją-olskiego). Trzecie — zaimek wskazujący na dwie rzeczy naraz — pada obok werdyktu, bo rzeczy nazywa zdanie obok (olski/odniesienia.py), więc tutaj go nie ma. Warunek stoi tu raz, bo pyta o niego wydruk nad każdym zdaniem osobno; Podsumowanie liczy każde z tych znalezisk nad tekstem i liczy je osobno.

Napis niepunktowany zostaje poza znaleziskiem także wtedy, gdy poprawkę ma, bo nagłówek i pozycja listy dochodzą tu jako takie napisy i żadne z nich nie jest zdaniem, którego autor nie domknął (docs/extraction.md); wydruk pokazuje je do flagi --zatrzymania.

czytane property

Czy olski to zdanie czyta.

Nieczytane są trzy: zdanie odrzucone, napis, którego nic nie domyka, i fragment. O polszczyźnie żadnego z nich olski nie orzeka, więc wydruk milczy o nich do flagi (olski/check.py).

status property

Klasa, do której werdykt to zdanie liczy, czyli słowo pomiaru.

Wydruk komendy go nie podaje, bo dzieli zdania po liczbie odczytań, a nie po tym, czy narzędzie ma o zdaniu co powiedzieć (znalezisko). Czytają je pomiar pokrycia (olski/pokrycie.py), sondy w harness/ i znaczek na stronie (witryna/skrypt.js).

readings property

Streszczenia odczytań, każde raz (streszczenia).

Odczytanie jest krotką streszczeń, po jednym na zdanie składowe, więc wpisem tej listy jest krotka, a nie jedno streszczenie.

Lista jest po to, żeby pokazać różnicę między odczytaniami, a różnicę spoza zasięgu streszczenia nazywa wiersz o konstytuencie (_rozbieżny), więc powtórzony napis nie zostawia jej nienazwanej. Liczbę odczytań podaje las (Result.ile), więc skrócenie tej listy jej nie rusza.

morfologia property

Czym formy stoją w każdym odczytaniu: wpis na streszczenie z readings.

Po co ta odpowiedź autorowi, mówi docs/pisanie-po-olsku.md#skąd-bierze-się-odczytanie-którego-autor-nie-widzi.

Wiersz dostaje forma czytana więcej niż jednym sposobem, a odczytania w nim licencjonują ją w tym kształcie (olski.parse.Leaf.odczytania); wiersze składa _pod_streszczeniem, a nad streszczeniem, które zbiera kilka kształtów, bierze odczytania z każdego z nich. Wpisów jest tyle, ile streszczeń, więc nad zdaniem urwanym na olski.parse.MAX_READINGS mówią one o odczytaniach wypisanych.

Zdanie bez ani jednego odczytania dostaje jeden wpis, a w nim każde odczytanie każdej formy (_morfologia_zdania): odsiać ich nie ma czym. Rozstrzyga się to tutaj, a nie w wydruku, bo wydruki są dwa (olski/check.py i witryna/werdykty.py) i rozjechałyby się po cichu.

rachunki property

Za co płaci każde odczytanie: wpis na streszczenie z readings.

Wychodzą stąd pozycje policzone, a nie ich suma, bo kolejność czytań rozstrzyga koszt czytany od góry drzewa (test_koszt_produkcji_nie_sumuje_się_do_kosztu_rodzica), więc suma czytałaby się na miejsce w kolejce, którym nie jest.

Streszczenie zbiera czasem kilka kształtów, a rachunek jest tego, który wyszedł z lasu pierwszy, czyli tego, przez którego to streszczenie stoi tam, gdzie stoi.

żądania property

Czego czasownik żąda od obsadzonych pozycji: wpis na streszczenie z readings.

Po co ta odpowiedź autorowi i czego ona nie mówi, wywodzi olski/żądania.py; wpis jest na streszczenie z tego samego powodu, co przy morfologia, i grupuje go to samo pytanie o las.

Warunku na zdanie odrzucone nie ma i nie ma go czemu stawiać: pozycję obsadza czytanie, a takie zdanie nie ma ani jednego, więc lista wychodzi stąd pusta sama. Morfologia ma tam wpis, bo mówi o formach, a nie o rolach.

rozbieżne property

Konstytuenty rozbieżne, którym streszczenia naprawdę się różnią.

Jedno streszczenie znaczy, że streszczenie tej różnicy nie widzi (Rozbieżność), a wypisane byłoby wierszem bez treści. Warunek stoi tu raz na oba wydruki, na wiersz poleceń i na witrynę, bo napisany dwa razy rozjechałby się po cichu.

__init__(text, result, nielicencjonowane, zatrzymanie, naprawa)

explain()

Source code in olski/werdykt.py
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
def explain(self) -> str:
    #  Poprawka wyprzedza każde inne wyjaśnienie i wyprzedza je nad zdaniem
    #  odrzuconym tak samo jak nad napisem niedomkniętym: zatrzymanie mówi,
    #  dokąd doszła analiza, a poprawka mówi, co z tym zrobić.
    if self.naprawa is not None:
        return _naprawiony(self.naprawa)
    if self.status == FRAGMENT:
        return "to nie zdanie: nic go nie punktuje jako zdania"
    if self.result.valid:
        return _odczytań(1)
    if self.result.rejected:
        if self.nielicencjonowane:
            # Cudzysłów jest treścią: najczęstszą formą bez licencji jest
            # przecinek, a lista rozdzielana przecinkami gubi bez niego granice.
            formy = ", ".join(f"„{forma}”" for forma in self.nielicencjonowane)
            return f"brak odczytania: żadna produkcja nie bierze {formy}"
        if self.result.furthest is None:
            #  Tak samo odmawia ``bloker`` w ``olski/pokrycie.py`` i z tego
            #  samego powodu: milczenie o zatrzymaniu czytałoby się tu jako
            #  zdanie o analizie, która doszła do końca.
            raise ValueError(
                "wyjaśnienie odrzucenia nazywa miejsce zatrzymania, "
                "a ten przebieg o zatrzymanie nie pytał (werdykt w olski/werdykt.py)"
            )
        if self.zatrzymanie is None:
            return "brak odczytania: analiza dochodzi do końca, a nic nie domyka zdania"
        return f"brak odczytania: analiza staje na „{self.zatrzymanie}”"
    przyłączenia = self.result.przyłączenia
    # Przekład idzie i tutaj (:func:`_nazwy_szkolne`), bo wiersz ten nie ma
    # nazywać roli, której lista czytań pod nim nie nazywa.
    różne = sorted(
        {
            nazwa
            for role in self.result.różniące
            # Przyłączenie nazwane niżej mówi o tej roli więcej niż sama jej
            # nazwa, więc wypisana obok byłaby tym samym zdaniem dwa razy.
            if not (przyłączenia and role == WYRAŻENIE_PRZYIMKOWE)
            for nazwa in _nazwy_szkolne(role)
        }
    )
    # Liczba i role wychodzą z lasu, więc granica wyliczania sięga listy
    # czytań i nie sięga tego wiersza: liczba jest liczbą, a nie „64+”.
    wiersz = _odczytań(self.result.ile)
    if różne:
        # Dwukropek oddziela nazwy od zdania: bez niego „różne w dopełnienie”
        # czyta się jak rzeczownik, którego przyimek nie odmienił.
        wiersz += f", różne w {'roli' if len(różne) == 1 else 'rolach'}: {', '.join(różne)}"
    return "; ".join(
        [
            wiersz,
            *map(_nierozstrzygnięte, przyłączenia),
            *map(_rozbieżny, self.result.rozbieżności),
        ]
    )

OdczytaniaFormy dataclass

Forma zdania wraz z odczytaniami, którymi tam stać może, każde napisem.

Odczytanie jest napisem, a nie parą lematu i znacznika, bo oba wydruki wypisują je razem, a rozdzielone kazałyby każdemu z nich składać ten napis osobno.

Source code in olski/werdykt.py
598
599
600
601
602
603
604
605
606
607
608
@dataclass(frozen=True)
class OdczytaniaFormy:
    """Forma zdania wraz z odczytaniami, którymi tam stać może, każde napisem.

    Odczytanie jest napisem, a nie parą lematu i znacznika, bo oba wydruki
    wypisują je razem, a rozdzielone kazałyby każdemu z nich składać ten napis
    osobno.
    """

    forma: str
    odczytania: tuple[str, ...]

forma instance-attribute

odczytania instance-attribute

__init__(forma, odczytania)

Żądanie dataclass

Czego czasownik zdania żąda od tego, co w jego pozycji stanęło.

Wiersz jest jeden na obsadzoną pozycję, a nie jeden na rolę, bo jedna rola obsadza czasem dwie pozycje naraz: Autor doradza czytelnikowi poprawkę. ma dopełnienie w celowniku obok dopełnienia w bierniku, a czasownik żąda od nich czego innego.

Source code in olski/werdykt.py
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
@dataclass(frozen=True)
class Żądanie:
    """Czego czasownik zdania żąda od tego, co w jego pozycji stanęło.

    Wiersz jest jeden na obsadzoną pozycję, a nie jeden na rolę, bo jedna rola
    obsadza czasem dwie pozycje naraz: `Autor doradza czytelnikowi poprawkę.`
    ma dopełnienie w celowniku obok dopełnienia w bierniku, a czasownik żąda
    od nich czego innego.
    """

    #: Rola, którą streszczenie nazywa to wypełnienie.
    rola: str
    #: Formy wypełnienia i forma czasownika, tak jak stoją w zdaniu.
    wypełnienie: str
    czasownik: str
    #: Żądane klasy jako alternatywa, nazwane przed nienazwanymi
    #: (:data:`olski.żądania.NIENAZWANE`), a w każdej z tych grup alfabetycznie.
    klasy: tuple[str, ...]
    #: Lematy głowy wypełnienia, czyli słowa, którymi ta pozycja stoi. O nie pyta
    #: deklaracja osób (``olski/osoby.py``), bo deklaruje się lemat, a nie formę.
    #: Poza porównaniem, bo wiersz jest o pozycji, a nie o głowie, i dwa kształty
    #: dają czasem tę samą pozycję o dwóch głowach (:func:`_zwinięte`).
    lematy: frozenset[str] = field(compare=False)

rola instance-attribute

wypełnienie instance-attribute

czasownik instance-attribute

klasy instance-attribute

lematy = field(compare=False) class-attribute instance-attribute

__init__(rola, wypełnienie, czasownik, klasy, lematy)

Podsumowanie dataclass

Znaleziska nad tekstem i to, o czym olski milczy, dla tego, kto pyta o cały tekst.

Liczby te wychodzą z werdyktów jedną regułą — fragment nie jest zdaniem, więc nie wchodzi do mianownika, a zdanie odrzucone jest milczeniem, dopóki nie ma poprawki — i pyta o nie więcej niż jeden wołający, więc policzone u każdego z nich rozjeżdżają się po cichu: mianownik mniejszy o fragment czyta się jak pomiar, a nie jak pomyłka.

Zdanie naprawialne stoi w dwóch licznikach naraz, w naprawialne i w bez_odczytania, bo gramatyka go nie wyprowadza i pokrycie liczy je tak samo jak przedtem: znalezisko mówi o autorze, a nie o podzbiorze.

Source code in olski/werdykt.py
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
@dataclass(frozen=True)
class Podsumowanie:
    """Znaleziska nad tekstem i to, o czym olski milczy, dla tego, kto pyta o cały tekst.

    Liczby te wychodzą z werdyktów jedną regułą — fragment nie jest zdaniem, więc
    nie wchodzi do mianownika, a zdanie odrzucone jest milczeniem, dopóki nie ma
    poprawki — i pyta o nie więcej niż jeden wołający, więc policzone u każdego z
    nich rozjeżdżają się po cichu: mianownik mniejszy o fragment czyta się jak
    pomiar, a nie jak pomyłka.

    Zdanie naprawialne stoi w dwóch licznikach naraz, w :attr:`naprawialne` i w
    :attr:`bez_odczytania`, bo gramatyka go nie wyprowadza i pokrycie liczy je
    tak samo jak przedtem: znalezisko mówi o autorze, a nie o podzbiorze.
    """

    #: Zdania, czyli to, o czym werdykt orzeka: fragmentów nie ma tu ani w liczniku.
    zdań: int
    #: Zdania o kilku odczytaniach, czyli znaleziska wieloznaczności.
    wieloznaczne: int
    #: Zdania, które od odczytania dzieli jeden znak, czyli drugie ze znalezisk
    #: (:class:`Naprawa`). Liczba jest osobna od :attr:`wieloznaczne`, bo mówi o
    #: zdaniu rzecz przeciwną: tamto olski czyta i ma o nim za dużo do
    #: powiedzenia, a to zdanie czyta dopiero po poprawce.
    naprawialne: int
    #: Zdania, których gramatyka nie wyprowadza. Olski o nich milczy, a milczenie
    #: liczy się osobno, bo bez tej liczby przebieg nad tekstem, którego nie
    #: przeczytał, czytałby się jak czysty.
    bez_odczytania: int
    #: Napisy, których nic nie interpunkuje jako zdania. Liczba jest jedna na oba
    #: werdykty o takim napisie, :data:`FRAGMENT` i :data:`NIEDOMKNIĘTE`, bo o
    #: mianowniku rozstrzyga jedno i to samo: domknięcia nie postawił nikt.
    fragmentów: int
    #: Zdania z zaimkiem, który wskazuje na dwie rzeczy naraz, czyli trzecie ze
    #: znalezisk (``olski/odniesienia.py``). Podawane, a nie liczone z werdyktów:
    #: znalezisko to pada o zdaniu, którego werdykt sam z siebie nie zna, bo
    #: rzeczy nazywa zdanie obok. Zdanie wieloznaczne z takim zaimkiem stoi w
    #: dwóch licznikach naraz, tak samo jak zdanie naprawialne, i z tego samego
    #: powodu: znaleziska są dwa i mówią o zdaniu co innego.
    niejasnych_odniesień: int = 0

    @property
    def znalezisk(self) -> int:
        """Ile zdań tekstu narzędzie zgłasza, bez względu na to, które znalezisko.

        Pyta o to kod wyjścia (``olski/check.py``), bo o samym zgłoszeniu
        rozstrzyga tu jedno miejsce, a znalezisko dopisane później dostaje ten
        kod wyjścia razem z własnym licznikiem.
        """
        return self.wieloznaczne + self.naprawialne + self.niejasnych_odniesień

    @classmethod
    def z_werdyktów(
        cls, werdykty: Sequence[Verdict], niejasnych_odniesień: int = 0
    ) -> Podsumowanie:
        zdania = [verdict for verdict in werdykty if verdict.punktowane]
        return cls(
            zdań=len(zdania),
            wieloznaczne=sum(verdict.result.ambiguous for verdict in zdania),
            naprawialne=sum(verdict.naprawa is not None for verdict in zdania),
            bez_odczytania=sum(verdict.result.rejected for verdict in zdania),
            fragmentów=len(werdykty) - len(zdania),
            niejasnych_odniesień=niejasnych_odniesień,
        )

    def explain(self) -> str:
        #  Wiersz jest listą par, a nie zdaniem: liczba stoi za dwukropkiem, więc
        #  nie żąda zgody od słowa przed sobą i nic tu się nie odmienia.
        podsumowanie = (
            f"zdań: {self.zdań}; wieloznaczne: {self.wieloznaczne};"
            f" bez odczytania: {self.bez_odczytania}"
        )
        #  Wiersz rośnie o tę parę dopiero tam, gdzie poprawka pada, bo nad
        #  tekstem bez ani jednej mówiłaby zero o znalezisku, którego nie ma.
        if self.naprawialne:
            podsumowanie += f"; do poprawki jednym znakiem: {self.naprawialne}"
        #  Ta para rośnie pod tym samym warunkiem i z tego samego powodu.
        if self.niejasnych_odniesień:
            podsumowanie += f"; niejasne odniesienia: {self.niejasnych_odniesień}"
        if self.fragmentów:
            #  Nie „fragmenty, które nie są zdaniami”: napis niedomknięty jest w tej
            #  liczbie, a werdykt nad nim mówi, że olski to zdanie czyta.
            podsumowanie += f"; fragmenty, których nic nie punktuje jako zdania: {self.fragmentów}"
        return podsumowanie

zdań instance-attribute

wieloznaczne instance-attribute

naprawialne instance-attribute

bez_odczytania instance-attribute

fragmentów instance-attribute

niejasnych_odniesień = 0 class-attribute instance-attribute

znalezisk property

Ile zdań tekstu narzędzie zgłasza, bez względu na to, które znalezisko.

Pyta o to kod wyjścia (olski/check.py), bo o samym zgłoszeniu rozstrzyga tu jedno miejsce, a znalezisko dopisane później dostaje ten kod wyjścia razem z własnym licznikiem.

__init__(zdań, wieloznaczne, naprawialne, bez_odczytania, fragmentów, niejasnych_odniesień=0)

z_werdyktów(werdykty, niejasnych_odniesień=0) classmethod

Source code in olski/werdykt.py
927
928
929
930
931
932
933
934
935
936
937
938
939
@classmethod
def z_werdyktów(
    cls, werdykty: Sequence[Verdict], niejasnych_odniesień: int = 0
) -> Podsumowanie:
    zdania = [verdict for verdict in werdykty if verdict.punktowane]
    return cls(
        zdań=len(zdania),
        wieloznaczne=sum(verdict.result.ambiguous for verdict in zdania),
        naprawialne=sum(verdict.naprawa is not None for verdict in zdania),
        bez_odczytania=sum(verdict.result.rejected for verdict in zdania),
        fragmentów=len(werdykty) - len(zdania),
        niejasnych_odniesień=niejasnych_odniesień,
    )

explain()

Source code in olski/werdykt.py
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
def explain(self) -> str:
    #  Wiersz jest listą par, a nie zdaniem: liczba stoi za dwukropkiem, więc
    #  nie żąda zgody od słowa przed sobą i nic tu się nie odmienia.
    podsumowanie = (
        f"zdań: {self.zdań}; wieloznaczne: {self.wieloznaczne};"
        f" bez odczytania: {self.bez_odczytania}"
    )
    #  Wiersz rośnie o tę parę dopiero tam, gdzie poprawka pada, bo nad
    #  tekstem bez ani jednej mówiłaby zero o znalezisku, którego nie ma.
    if self.naprawialne:
        podsumowanie += f"; do poprawki jednym znakiem: {self.naprawialne}"
    #  Ta para rośnie pod tym samym warunkiem i z tego samego powodu.
    if self.niejasnych_odniesień:
        podsumowanie += f"; niejasne odniesienia: {self.niejasnych_odniesień}"
    if self.fragmentów:
        #  Nie „fragmenty, które nie są zdaniami”: napis niedomknięty jest w tej
        #  liczbie, a werdykt nad nim mówi, że olski to zdanie czyta.
        podsumowanie += f"; fragmenty, których nic nie punktuje jako zdania: {self.fragmentów}"
    return podsumowanie

zatrzymania(segmenty, grammar=None)

Każde zatrzymanie odrzuconego zdania, a nie samo pierwsze.

Werdykt nazywa jedno miejsce (na_czym_stanęło), a zdanie długie ma ich kilka i pierwsze zasłania resztę, więc kto pisze pod tę gramatykę, nie widzi z werdyktu, ile jeszcze poprawek to zdanie zabierze; po co ta odpowiedź jest, mówi docs/pisanie-po-olsku.md.

Analiza rusza od nowa za formą zatrzymania, a nie na niej: formy, której nie wzięła żadna analiza częściowa, nie weźmie też analiza zaczęta od niej, a przebieg stałby na miejscu. Krawędź przekraczającą cięcie trzeba przy tym zdjąć, bo graf segmentacji rozchodzi się na kilka dróg — ktoś wychodzi także jako kto i ś — a takiej krawędzi nie ma z czym w kawałku złożyć.

Cięcie nie wskazuje usterki ani granicy konstrukcji, tak samo jak jedno zatrzymanie jej nie wskazuje.

Source code in olski/werdykt.py
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
def zatrzymania(segmenty: list[Segment], grammar: Grammar | None = None) -> tuple[str, ...]:
    """Każde zatrzymanie odrzuconego zdania, a nie samo pierwsze.

    Werdykt nazywa jedno miejsce (:func:`na_czym_stanęło`), a zdanie długie ma ich
    kilka i pierwsze zasłania resztę, więc kto pisze pod tę gramatykę, nie widzi z
    werdyktu, ile jeszcze poprawek to zdanie zabierze; po co ta odpowiedź jest,
    mówi docs/pisanie-po-olsku.md.

    Analiza rusza od nowa **za** formą zatrzymania, a nie na niej: formy, której
    nie wzięła żadna analiza częściowa, nie weźmie też analiza zaczęta od niej, a
    przebieg stałby na miejscu. Krawędź przekraczającą cięcie trzeba przy tym
    zdjąć, bo graf segmentacji rozchodzi się na kilka dróg — ``ktoś`` wychodzi
    także jako ``kto`` i ``ś`` — a takiej krawędzi nie ma z czym w kawałku złożyć.

    Cięcie nie wskazuje usterki ani granicy konstrukcji, tak samo jak jedno
    zatrzymanie jej nie wskazuje.
    """
    grammar = grammar or GRAMMAR
    formy: list[str] = []
    while segmenty:
        stanęło = na_czym_stanęło(segmenty, parse(grammar, segmenty).furthest)
        if stanęło is None:
            break
        formy.append(stanęło.form)
        segmenty = [
            replace(segment, start=segment.start - stanęło.end, end=segment.end - stanęło.end)
            for segment in segmenty
            if segment.start >= stanęło.end
        ]
    return tuple(formy)

werdykt(zdanie, segmenty, grammar=None, zatrzymanie=True)

Werdykt o zdaniu już zsegmentowanym, wraz z całym podsumowaniem.

Segmenty przychodzą argumentem, a nie powstają tutaj, bo zależą od napisu, a nie od gramatyki: kto pyta o jedno zdanie kilka gramatyk — sonda różnicowa nad prozą — segmentuje je raz i pyta tyle razy, ile ma wariantów.

Zatrzymanie jest najdroższym z podsumowań i jedynym, które wolno pominąć. Nad zdaniem odrzuconym przechodzi tablicę drugi raz i unifikuje przy tym przebyte ciała (olski.parse.podsumuj), a nad prozą, której olski w większości nie wyprowadza, odrzuconych jest osiem zdań na dziesięć, więc waży w takim przebiegu więcej niż każde inne podsumowanie. Czytają je Verdict.explain i kolejka blokerów (olski/pokrycie.py); kto go nie czyta, prosi o werdykt bez niego.

Flagą, a nie pytaniem leniwym: Result trzymający las liczyłby punkt przy pierwszym pytaniu, ale przebieg nad bankiem drzew pyta o niego przy każdym zdaniu odrzuconym, a las porzuca rozmyślnie, bo waży tyle, ile jego tablica (zmierz_zdanie w harness/pomiar.py).

Napis bez znaku kończącego pyta o zatrzymanie mimo flagi, bo od niego zależy wtedy sam status: poprawki domykającej szuka się nad analizą, która doszła do końca (_naprawa), a nad napisem punktowanym nie szuka się jej wcale.

Source code in olski/werdykt.py
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
def werdykt(
    zdanie: str,
    segmenty: list[Segment],
    grammar: Grammar | None = None,
    zatrzymanie: bool = True,
) -> Verdict:
    """Werdykt o zdaniu już zsegmentowanym, wraz z całym podsumowaniem.

    Segmenty przychodzą argumentem, a nie powstają tutaj, bo zależą od napisu, a
    nie od gramatyki: kto pyta o jedno zdanie kilka gramatyk — sonda różnicowa
    nad prozą — segmentuje je raz i pyta tyle razy, ile ma wariantów.

    Zatrzymanie jest najdroższym z podsumowań i jedynym, które wolno pominąć.
    Nad zdaniem odrzuconym przechodzi tablicę drugi raz
    i unifikuje przy tym przebyte ciała (:func:`olski.parse.podsumuj`),
    a nad prozą, której olski w większości nie wyprowadza,
    odrzuconych jest osiem zdań na dziesięć,
    więc waży w takim przebiegu więcej niż każde inne podsumowanie.
    Czytają je :meth:`Verdict.explain` i kolejka blokerów (``olski/pokrycie.py``);
    kto go nie czyta, prosi o werdykt bez niego.

    Flagą, a nie pytaniem leniwym: ``Result`` trzymający las liczyłby punkt przy
    pierwszym pytaniu, ale przebieg nad bankiem drzew pyta o niego przy każdym
    zdaniu odrzuconym, a las porzuca rozmyślnie, bo waży tyle, ile jego tablica
    (``zmierz_zdanie`` w ``harness/pomiar.py``).

    Napis bez znaku kończącego pyta o zatrzymanie mimo flagi, bo od niego zależy
    wtedy sam status: poprawki domykającej szuka się nad analizą, która doszła
    do końca (:func:`_naprawa`), a nad napisem punktowanym nie szuka się jej wcale.
    """
    grammar = grammar or GRAMMAR
    pytane = zatrzymanie or not SENTENCE_CLOSE.search(zdanie)
    result = parse(grammar, segmenty, deklaracja=DEKLARACJA, zatrzymanie=pytane)
    stanęło = na_czym_stanęło(segmenty, result.furthest) if pytane else None
    #  Brak formy jest dojściem do końca dopiero wtedy, gdy pytano:
    #  bez pytania ``stanęło`` jest puste także nad analizą, która stanęła.
    doszło_do_końca = pytane and stanęło is None
    nielicencjonowane = bez_licencji(segmenty, grammar)
    return Verdict(
        text=zdanie,
        result=result,
        nielicencjonowane=nielicencjonowane,
        zatrzymanie=stanęło.form if stanęło is not None else None,
        naprawa=_naprawa(zdanie, grammar, result.rejected, nielicencjonowane, doszło_do_końca),
    )

dalsze_zatrzymania(verdict, grammar=None)

Zatrzymania tego zdania poza tym, które nazwał już werdykt.

Zdanie z czytaniem nie stanęło nigdzie, więc krotka jest wtedy pusta, i tak samo pusta jest nad fragmentem. Segmentacja idzie tu drugi raz, bo werdykt segmentów nie niesie (werdykt).

Source code in olski/werdykt.py
586
587
588
589
590
591
592
593
594
595
def dalsze_zatrzymania(verdict: Verdict, grammar: Grammar | None = None) -> tuple[str, ...]:
    """Zatrzymania tego zdania poza tym, które nazwał już werdykt.

    Zdanie z czytaniem nie stanęło nigdzie, więc krotka jest wtedy pusta, i tak
    samo pusta jest nad fragmentem. Segmentacja idzie tu drugi raz, bo werdykt
    segmentów nie niesie (:func:`werdykt`).
    """
    if not verdict.punktowane or not verdict.result.rejected:
        return ()
    return zatrzymania(morphology(verdict.text), grammar)[1:]

niespełnione_żądania(verdict, deklaracja=OSOBY_PROJEKTU)

Pozycje, w których czasownik żąda kogoś, a nikt w nich nie stoi.

Obie połowy pytania spotykają się tutaj: żądanie przychodzi z pliku żądań (olski.żądania.żąda_osoby), a spełnienie z deklaracji projektu (olski/osoby.py). Wiersz zostaje po tej pozycji, której żądania nie spełnia nic z tego, czym jest jej głowa.

Wiersz jest o zdaniu, a nie o odczytaniu, i tym różni się ten wykaz od Verdict.żądania. Pozycję obsadza czytanie, więc wiersz mówi tyle, że w którymś z nich stoi rzecz tam, gdzie czasownik żąda kogoś. Zdanie wieloznaczne ma czytań kilkanaście, a wiersz o jednej pozycji w każdym z nich ten sam, więc wykaz na odczytanie kazałby przeczytać kilkanaście kopii jednego wiersza.

Source code in olski/werdykt.py
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
def niespełnione_żądania(
    verdict: Verdict, deklaracja: Osoby = OSOBY_PROJEKTU
) -> tuple[Żądanie, ...]:
    """Pozycje, w których czasownik żąda kogoś, a nikt w nich nie stoi.

    Obie połowy pytania spotykają się tutaj: żądanie przychodzi z pliku żądań
    (:func:`olski.żądania.żąda_osoby`), a spełnienie z deklaracji projektu
    (``olski/osoby.py``). Wiersz zostaje po tej pozycji, której żądania nie
    spełnia nic z tego, czym jest jej głowa.

    **Wiersz jest o zdaniu, a nie o odczytaniu**, i tym różni się ten wykaz od
    :attr:`Verdict.żądania`. Pozycję obsadza czytanie, więc wiersz mówi tyle,
    że w którymś z nich stoi rzecz tam, gdzie czasownik żąda kogoś. Zdanie
    wieloznaczne ma czytań kilkanaście, a wiersz o jednej pozycji w każdym z nich
    ten sam, więc wykaz na odczytanie kazałby przeczytać kilkanaście kopii
    jednego wiersza.
    """
    return tuple(
        żądanie
        for żądanie in _zwinięte(żądanie for tabela in verdict.żądania for żądanie in tabela)
        if żąda_osoby(żądanie.klasy) and not deklaracja.nazywają(żądanie.lematy)
    )

check(text, grammar=None)

Check every sentence of a text against the grammar.

Source code in olski/werdykt.py
872
873
874
def check(text: str, grammar: Grammar | None = None) -> list[Verdict]:
    """Check every sentence of a text against the grammar."""
    return [werdykt(zdanie, morphology(zdanie), grammar) for zdanie in sentences(text)]