Metody realizacji języków programowania/MRJP Laboratorium/Semantyka Kotka
Semantyka języka
Poniżej omówione są najważniejsze cechy semantyki języka Kotek. Jeśli jakaś konstrukcja nie jest omówiona, to jej semantyka nie różni się od semantyki analogicznych konstrukcji w innych językach programowania.
Widoczność deklaracji
Wykonywana instrukcja ma dostęp do wszystkich deklaracji funkcji w której była zadeklarowana (lub do deklaracji głównego programu, jeśli instrukcja występuje bezpośrednio w programie), włącznie z deklaracjami parametrów funkcji, oraz do wszystkich deklaracji z wyższego poziomu. Zadeklarowanie zmiennej o nazwie takiej jak jakaś zmienna z wyższego poziomu przesłania zmienną z wyższego poziomu. Nie ma możliwości odwołania się do przysłoniętej zmiennej. Zabroniona jest deklaracja zmiennej o nazwie takiej jak któryś parametr funkcji w której (bezpośrednio) występuje deklaracja. Przyjrzyjmy się przykładowej deklaracji funkcji.
function f(var x: int) : void function a(var s : string) : string { // deklaracja parametru s przysłania deklarację // z funkcji f if x == 0 then return "x jest zerem"; else return b(); endif } function b() : string var x : string; { return s; // zwróci zmienną lokalną funkcji f } var s : string; { s = "napis"; print a("inny napis"); }
W funkcji a widoczna są:
- funkcja b (mimo, że została zadeklarowana później),
- zmienna x - parametr funkcji f,
- zmienna s - parametr funkcji a.
W funkcji b widoczne są:
- zmienna x - lokalna zmienna funkcji b, przysłaniająca parametr funkcji f (zauważ, że przysłaniająca zmienna może być dowolnego typu),
- zmienna s - lokalna zmienna funkcji f,
- funkcja a.
W funkcji f widoczne są:
- funkcja a,
- funkcja b,
- zmienna s - zadeklarowana lokalnie w f,
- zmienna x - parametr f.
Zasady widoczności w przypadku lokalnych deklaracji obiektów są pozostawione jako ćwiczenie.
Alokacja obiektów, rekordów i tablic
Obiekty, rekordy oraz tablice są tworzone dynamicznie na stercie, za pomocą operatora 'new, na przykład:
new int[100];
zwróci tablicę 100 liczb całkowitych,
new mojRekord;
zwróci nowy rekord typu mojRekord, a
new mojObiekt[10];
zwróci tablicę 10 obiektów klasy mojObiekt.
Wywołanie operatora new bez podania rozmiaru tablicy alokuje pojedynczy element. Takie wywołanie jest dozwolone tylko dla typów rekordowych i obiektowych. Tworzenie wielowymiarowych tablic jest możliwe przez zadeklarowanie typu tablicowego:
type tabl = arrayof int;
i alokacja tablicy tego typu:
tablicaTablic = new tabl[10];
i poszczególnych elementów tablicy:
tabl[0] = new int[10]; // ...
Zmienne o typie tablicowym, rekordowym lub obiektowym przechowują referencję do tablicy, rekordu lub obiektu (odpowiednio). Przekazywanie tablic do funkcji, zwracanie ich z funkcji oraz przypisywanie do zmiennej odbywa się przez referencję. Oznacza to, że zmiany elementów tablicy w funkcji do której tablica została przekazana będą zachowane również po powrocie z tej funkcji. Przypisanie tablicy do nowej zmiennej nie kopiuje jej, ale nowa zmienna wskazuje na tą samą tablicę. Dokładnie tak samo jest w przypadku rekordów i obiektów.
Zwalnianie elementów zaalokowanych operatorem new odbywa się przy pomocy operatora delete. Jedynym parametrem delete jest referencja do zaalokowanego elementu (czyli wartość zmiennej wskazującej na element). Nie należy podawać rozmiaru w przypadku tablic. Każde wywołanie delete usuwa dokładnie jeden element zaalokowany operatorem new.
Kontrola typów
Typy są sprawdzane w czasie kompilacji.
Równoważność typów jest przez nazwę.
Semantyka wyrażeń logicznych
Język Kotek nie przewiduje oddzielnego typu logicznego. Zamiast tego w wyrażeniach logicznych używane są zwykłe wyrażenia arytmetyczne. Jeśli wartość wyrażenia jest niezerowa, to jego logiczną wartością jest prawda (reprezentowana jako 1), a jeśli wyrażenie zostanie wyliczone do zera, to wartością logiczną jest fałsz (reprezentowany przez 0). Uwaga: jeśli wyrażenie logiczne jest w postaci czysto arytmetycznej (bez operatorów logicznych), to jego wartość nie może się zmienić, np. wartością wyrażenia 2+2 jest zawsze 4, niezależnie od tego, czy jest to wyrażenie logiczne, czy nie. Natomiast wartością logiczną 2 || 2 jest 1. Ściśle mówiąc, zmiana wartości wyrażenia na binarną (0 lub 1) odbywa się tylko gdy używane są operatory logiczne.
Wyrażenia logiczne wyliczane są w sposób leniwy, od lewej do prawej. To znaczy jeśli wartość wyrażenia jest zdeterminowana już po obliczeniu jego pierwszej części (np. 0 && 1 lub 1 || 1 - wynik jest znany już po wyliczeniu pierwszej wartości), to druga część nie jest wyliczana.
Odwołania do pól rekordów i obiektów (operator kropka)
Semantyka kropki jest identyczna jak w językach Java, czy C++. Kropka może być aplikowana tylko do typów obiektowych lub rekordowych.
Stringi
Ciągi napisowe są w języku dostępne tylko statycznie (nie ma na przykład operacji konkatenacji) i są niezmienialne.