Kto to zaproponował — niech poda przykłady użycia lub zamilknie na wieki.

Chodzi o scenariusze, kiedy te rozszerzenia będą potrzebne.

  1. Operator liczący odległość między pozycjami. Np. distance(2,-1) == 3. AR: wyjąłem to z wymagań dopóki ktoś nie poda zastosowania
  2. Funkcje ze zmiennymi lokalnymi. Uwaga: to dość mocny mechanizm. Może da się zrobić to prościej. Podać przypadki użycia. # Łatwiejszy wariant: funkcje bez argumentów, które działają jak inne operatory. Zwracają true/false lub zbiory czegoś.

Rzeczy już postanowione

Składnia zbiorów

W dotychczasowych implementacjach mieliśmy typ zbiorów, który mógł zawierać elementy typu string i typu symbol z tagsetu. Dało się utworzyć zbiór {subst, "pies", acc}. W C++ do tego typ boolowski też był tego samego typu. Teraz chcemy rozdzielić te typy, by wprowadzić większy porządek i wcześniejsze wykrywanie błędów. Jedynym problemem są stałe określające zbiory puste. Naturalna składnia {} jest niejednoznaczna, nie wiadomo jakiego typu będzie to zbiór: stringów czy symboli.

Przyjęte rozwiązanie: zbiory stringów zawsze zapisujemy w nawiasach kwadratowych: ["pies", "psa"], ["pies"], []. Zbiory symboli pozostają w nawiasach wąsowych, np. {subst, ger}, {subst}, {}.

Składnia warunków

Dotychczasowa składnia jest nietypowa. Chodzi o operatory and, or, warunkowy (?):

and(
   warunek1,
   …
   warunekn
)
? zwrócić_jeśli_tak ? czy_tak

Np.

and(
   or(
      inter(flex[0], {subst, fin}),
      inter(flex[0], {adj, ppas, pact})
   ),
   or(
      inter(flex[1], {subst, fin}),
      inter(flex[1], {adj, ppas, pact})
   )
)

? flex[$P] ? llook(…, $P, and( … )

Osoby spoza naszego kręgu mogą mieć trudności z przyzwyczajeniem się. Inna składnia być może nawet dla nas byłaby czytelniejsza.

if: gdy dodamy ifa, jego składnia musiałaby być albo podobna do tej u góry (czyli mało czytelna), a jeśli zrobimy bardziej klasycznie, to będzie niespójnie.

Przyjęte rozwiązanie: zachowaliśmy składnię and, tymczasowo zostawiliśmy składnię ze znakami zapytania (raczej do wyrzucenia) i wprowadziliśmy dwa warianty operatora if: z klauzulą else oraz bez niej. Bez klauzuli else działa jak dotychczasowe znaki zapytania, tj. w zależności od typu, zwraca domyślną wartość dla tego typu.

and(
   pred1,
   pred2
)

if(
  pred,
  when_true,
  when_false
)

if(
  pred,
  when_true
) // when false == default_value(type(when_true))

Operatory uzgodnienia a liczba atrybutów

Chodzi o parametr, który zwykle jest trójką w kodzie (zwany bits w implementacji c++)

Np.

agr(-1,1,{cas,gnd,num},3)
wagr(-1,1,{cas,gnd,num},3)
agrpp(-1,1,{cas,gnd,num},3)
agrflt(-1,1,{cas,nmb,gnd},3,{cas})

W implementacji C++-owej operatora agr przekazana liczność nie zmienia działania. By zachować kompatybilność, Disaster też wymaga podania liczności, ale implementacja z tego nie korzystała.

W implementacji pozostałych operatorów liczba jest brana pod uwagę. Ta liczba teoretycznie pozwala na słabsze ograniczenie — że tokeny nie mają np. wartości przypadka, ale uzgodnione są na liczbie i rodzaju. To jest jednak mało użyteczne, bo wymagane jest, by dla każdego tokenu to był dokładnie ten sam zbiór — w tym przykładzie wszystkie tokeny z zakresu musiałyby mieć tę samą wartość liczby i rodzaju, a nie mieć określonego przypadka.

Żadne z użyć tego operatora w ramach TaKIPI nie korzysta z powyższej możliwości. Założę się, że nigdzie indziej nikt z tego nie korzysta — choćby dlatego, że działanie w takiej sytuacji jest nieintuicyjne — np. nie dopasuje się do ciągu {[acc,f,sg], [f,sg], [acc,f,sg]}.

Przyjęte rozwiązanie: wyrzuciliśmy ten liczbowy argument operatora. Policzenie różnych atrybutów obecnych w zbiorze nie jest operacją kosztowną. Nieświadome podanie innej wartości może prowadzić do błędnego działania operatora.

Regex

Dotychczas mieliśmy dwa specyficzne operatory sprawdzające stringi: hasnum, isbig. Pierwszy z nich sprawdzał, czy wszystkie elementy zbioru mają w sobie cyfrę, drugi — czy wszystkie elmenty zbioru rozpoczynają się wielką literą. Teraz mamy jedynie operator regex.

Nazwa i semantyka in

Decyzja: zostawiamy jak było.

Operator zwróci True tylko, gdy pierwszy zbiór będzie niepusty. To nietypowe z punktu widzenia matematycznego, ale wygodne praktycznie — jeśli pytamy o wartość atrybutu, podczas gdy atrybut w ogóle nie pasuje do tokenu, to lepiej powiedzieć, że całe stwierdzenie jest nieprawdziwe (niespełniona presupozycja).

Wątpliwości budziła też nazwa, ponieważ sugeruje ona sprawdzanie przynależności elementu do zbioru — a w rzeczywistości operator sprawdza czy zbiór pierwszy jest niepusty i jest podzbiorem zbioru drugiego. Nie jest to jednak aż tak straszne, bo w JOSKIPI nie ma typu reprezentującego pojedynczy element zbioru.

Nazwa flex

Decyzja: zmieniamy na class.

Nazwa flex w starym JOSKIPI wzięła się stąd, że pos była już zajęta (to jedna z wartości atrybutu deg — stopień równy), więc trzeba było wymyślić coś innego. flex, czyli klasa fleksyjna. Klasy fleksyjne są specyficzne dla tagsetu KIPI. W ogólności to klasy gramatyczne albo klasy słów. Zresztą, interp, ign czy nasze rozszerzenia (tsym itp.) to nie jest fleksja.

Propozycja nowej składni dla zmiennych różnego typu

W starym JOSKIPI: stałe typu pozycja to np. 0, -3, a odwołania do zmiennych to $V, $-2V.

Teraz wprowadzamy zmienne kilku typów, które chcemy rozróżnić składniowo (by zachować założenie, że każde wyrażenie JOSKIPI ma jednoznacznie określony typ).

Typ pozycja: zachowujemy składnię zmiennych bez przesunięcia ($V). Przesunięcia można wyrazić operatorem +, np. $V + 2. Tak jest czytelniej i prościej.

Typ zbiór stringów: składnia $s:V.

Typ zbiór symboli z tagsetu: składnia $t:V.

Typ wartość logiczna: składnia: $b:V.

Dlaczego tak? Dotychczas mieliśmy tylko zmienne po pozycjach, więc prawdopodobnie dalej będą to najczęściej używane zmienne — mają więc najprostszą składnię. W szczególności nie chcemy, by składnia, która dotychczas oznaczała pozycje, teraz oznaczała coś innego. Użycie zmiennych pozostałych typów wymaga jawnego podania typu poprzez prefiksy.