Skip to content

Opis składni Rybu

Jan Michalski edited this page May 27, 2016 · 5 revisions

Opis składni Rybu

Komentarze

Komentarze umieszczane są po podwójnym znaku minus. Np. -- komentarz

Stałe

Deklarowane są globalnie przy użyciu następującej składni: const <nazwa> = <wartosc>;. Np. const STALA = 4;

Zmienne

Zmienne deklarowane są następująco: var <nazwa> : <typ>. Zmienne mogą być zadeklarowane globalnie albo lokalnie w serwerach.

Typy zmiennych

Obecnie obsługiwane są następujące typy zmiennych:

Typ wyliczeniowy enum

Deklarowany jako lista wartości w nawiasach klamrowych np. { wartosc1, wartosc2, wartosc3 }.

Przypisanie konkretnej wartości do zmiennej wymaga użycia dwukropka przed wartością np. zmienna = :wartosc1

Typ całkowitoliczbowy integer

Deklarowany jako przedział liczbowy np. 0..5. Przy deklaracji przedziału można korzystać ze stałych np. 1..N.

Typ tablicowy

Deklarowany następująco: <typ>[<rozmiar>] np. (0..3)[5]

Wyrażenia

Wyrażenia liczbowe

Dostępne operatory:

  • +, - dodawania, odejmowanie
  • * mnożenie
  • % modulo

Wyrażenia tablicowe

Dostępne operatory:

  • tablica[<indeks>] pobranie elementu z o danym indeksie z tablicy
  • tablica[<poczatek>..<koniec>] "array slice" pobranie fragmentu tablicy pomiędzy indeksami <poczatek> i <koniec>
  • [<element>] utworzenie tablicy jednoelementowej
  • [<element1>, <element2>] utworzenie tablicy wieloelementowej
  • + łączenie tablic (konkatenacja)

Warunki

Warunki to wyrażenia boolowskie. Dostępne operatory to:

  • = równość
  • != nierówność
  • && iloczyn logiczny AND
  • || suma logiczna OR
  • >, <, >=, <= porównania zmiennych typu liczbowego

W warunkach można korzystać również z wyrażeń liczbowych i tablicowych -- jednak musi być użyty któryś z powyższych operatorów zwracający wartość boolowską.

Serwery

W Rybu, tak jak w Dedanie, występują serwery. Można o nich myśleć jako o klasach, które mają wewnętrzny stan i zapewniają przy jego wykorzystaniu pewne usługi. Przykłady serwerów to semafor, bufor itp.

W deklaracji serwera najpierw występują zmienne, a następnie zmiany stanu jakie są obsługiwane przez ten serwer.

Zmiany stanu (przejścia)

Zmiany stanu zapisywane są następująco (nawiasy kwadratowe oznaczają część opcjonalną):

{ komunikat [| warunek]} -> {wartosc_zwracana}

albo

{ komunikat [| warunek]} -> {<zmiana stanu>}

albo

{ komunikat [| warunek]} -> {wartosc_zwracana; <zmiana stanu>}

Jeżeli nie podana jest wartość zwracana zwrócona zostanie domyślna wartość ok.

Można wykorzystywać

Przykładowy kod semafora:

server semaphore {
    var state : {up, down};

    {v} -> {state = :up}
    {p | state = :up} -> {state = :down}
    {getState | state = :up} -> {up}
    {getState | state = :down} -> {down}
}

var state : {up, down}; Deklaracja zmiennej state jako enum o wartościach up albo down

{v} -> {state = :up} Pod wpływem komunikatu v następuje przypisanie do zmiennej state wartości up (Uwaga! Użycie wartości pola enum wymaga poprzedzenia ich dwukropkiem -- tak jak w np. Ruby, a więc state = :up).

{p | state = :up} -> {state = :down} Zmiana wartości state wystąpi w tym przejściu pod wpływem komunikatu p tylko jeżeli spełniony jest warunek po znaku | czyli czy state ma wartość up. Jeżeli ten warunek nie jest spełniony to proces wysyłający komunikat -- żądający opuszczenia semafora -- zostanie zawieszony (nie nastąpi zmiana stanu serwera) do czasu, aż warunek będzie spełniony -- wtedy nastąpi zmiana stanu serwera, a proces zostanie odblokowany.

Instancje serwerów

Instancja serwera jest dla serwera tym czym obiekt dla klasy w programowaniu obiektowym.

Instancje deklarowane są podobnie jak zmienne, ale nie podaje się ich typu po dwukropku, zamiast tego przypisuje się im serwer z nawiasami okrągłymi, a w nawiasach klamrowych należy podać wartości początkowe wszystkich zmiennych danego serwera (w kolejności deklaracji w serwerze).

var instancja = serwer() {zmienna1 = :wartosc1, zmienna2 = 2};

Przykład:

var sk = semaphore() {state = :up};
var wolne_miejsce = semaphore() {state = :up};
var brak_niezatw = semaphore() {state = :up};
var jest_niezatw = semaphore() {state = :down};
var do_odbioru = semaphore() {state = :down};
var ostatnioNP = semaphore() {state = :down};
var ostatnioP = semaphore() {state = :up};

Procesy

Procesy to imperatywny zapis działania modelu. Procesy kompilują się do serwerów programu Dedan, ale ich stany i komunikaty są generowane automatycznie. Jedyną funkcjonalnością procesu jest korzystanie z serwerów. Przepływ sterowania jest realizowany przez pattern matching. Operacje na instancjach serwerów przypominają wywołanie metody klasy.

Pattern matching

Pattern matching służy do kontroli przepływu sterowania. Pozwala na uzależnienie wykonywanej operacji od wartości zwracanej z serwera.

Istnieje specjalna instrukcja skip, pozwalająca na niewykonywanie żadnej operacji dla niektórych wartości.

Pattern matching może być zagnieżdżany.

match serwer1.operacja() {
    :wartosc1 => serwer2.operacja2();
    :wartosc2 => {
        serwer3.operacja3();
        match serwer4.operacja4() {
            :wartosc1 => skip;
            :wartosc2 => serwer5.operacja5();
        }
    }
}

Przykład

process producer() {
    loop {
        wolne_miejsce.p();
        brak_niezatw.p();
        sk.p();
            -- operacja na buforze
            jest_niezatw.v();
        sk.v();
    }
}

Jak to działa?

Proces jest kompilowany jako serwer programu Dedan. Rybu automatycznie generuje pośrednie stany pomiędzy każdą operacją. [to be continued]