-
Notifications
You must be signed in to change notification settings - Fork 0
Opis składni Rybu
Komentarze umieszczane są po podwójnym znaku minus. Np. -- komentarz
Deklarowane są globalnie przy użyciu następującej składni: const <nazwa> = <wartosc>;
. Np. const STALA = 4;
Zmienne deklarowane są następująco: var <nazwa> : <typ>
.
Zmienne mogą być zadeklarowane globalnie albo lokalnie w serwerach.
Obecnie obsługiwane są następujące typy zmiennych:
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
Deklarowany jako przedział liczbowy np. 0..5
. Przy deklaracji przedziału można korzystać ze stałych np. 1..N
.
Deklarowany następująco: <typ>[<rozmiar>]
np. (0..3)[5]
Dostępne operatory:
-
+
,-
dodawania, odejmowanie -
*
mnożenie -
%
modulo
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 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ą.
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 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.
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 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 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();
}
}
}
process producer() {
loop {
wolne_miejsce.p();
brak_niezatw.p();
sk.p();
-- operacja na buforze
jest_niezatw.v();
sk.v();
}
}
Proces jest kompilowany jako serwer programu Dedan. Rybu automatycznie generuje pośrednie stany pomiędzy każdą operacją. [to be continued]