Semantyka i weryfikacja programów/Ćwiczenia 5: Różnice pomiędzy wersjami

Z Studia Informatyczne
Przejdź do nawigacjiPrzejdź do wyszukiwania
Sl (dyskusja | edycje)
Nie podano opisu zmian
m Zastępowanie tekstu – „.↵</math>” na „</math>”
 
(Nie pokazano 14 wersji utworzonych przez 3 użytkowników)
Linia 2: Linia 2:
== Zawartość ==
== Zawartość ==


Kontynuujemy ćwiczenie semantyki naturalnej,
Kontynuujemy ćwiczenie semantyki naturalnej, dodając pewne konstrukcje do języka TINY.
dodając pewne konstrukcje do języka Tiny.
W szczególności rozszerzymy go o deklaracje zmiennych (bloki).
W szczególności rozszerzymy go o deklaracje zmiennych (bloki).
Po raz pierwszy roszdzielimy informację przechowywaną w konfiguracji
Po raz pierwszy roszdzielimy informację przechowywaną w konfiguracji na ''środowisko'' określające wiązanie identyfikatorów i ''stan'' przechowujący wartości zmiennych.
na ''środowisko'' określające wiązanie identyfikatorów i ''stan''
przechowujący wartości zmiennych.
Będzie to przygotowanie do kolejnych zajęć.
Będzie to przygotowanie do kolejnych zajęć.




== Zadania z rozwiązaniami ==
== Semantyka naturalna pewnej instrukcji <math>\mathbf{for}\ </math>, ==




==== Zadanie 1 ====
{{cwiczenie|1|cw1|




Rozszerzamy język Tiny następująco:
Rozszerzamy język TINY następująco:


<math>
<math>
Linia 26: Linia 23:
</math>
</math>


Znaczenie instrukcji <math> \mathbf{for}\, x = e_1 \,\mathbf{to}\, e_2 \,\mathbf{try}\, I_1 \,\mathbf{else}\, I_2 </math>
Znaczenie instrukcji <math>\mathbf{for}\, x = e_1 \,\mathbf{to}\, e_2 \,\mathbf{try}\, I_1 \,\mathbf{else}\, I_2</math> jest następujące.
jest następujące.
Obliczamy wartości wyrażeń <math>e_1</math> i <math>e_2</math>. Jeśli pierwsza z nich jest mniejsza od lub równa drugiej, podstawiamy pierwszą wartość (wartość wyrażenia <math>e_1</math>) na zmienną <math>x</math> i uruchamiamy <math>I_1</math>.
Obliczamy wartości wyrażeń <math> e_1 </math> i <math> e_2 </math>. Jeśli pierwsza
Jeśli w <math>I_1</math> nie zostanie napotkana instrukcja <math>\mathbf{fail}</math>, kończymy instrukcję <math>\mathbf{for}\ </math>, i przyracamy wartość zmiennej <math>x</math>
z nich jest mniejsza lub równa od drugiej, to podstawiamy pierwszą
wartość (wartość wyrażenia <math> e_1 </math>) na zmienną <math> x </math> i uruchamiamy <math> I_1 </math>.
Jeśli w <math> I_1 </math> nie zostanie napotkana instrukcja <math> \mathbf{fail} </math>,  
kończymy instrukcję <math> \mathbf{for}\, </math> i przyracamy wartość zmiennej <math> x </math>
sprzed tej instrukcji.
sprzed tej instrukcji.
Natomiast jeśli w <math> I_1 </math> zostanie napotkana instrukcja <math> \mathbf{fail} </math>,
Natomiast jeśli w <math>I_1</math> zostanie napotkana instrukcja <math>\mathbf{fail}</math>, podstawiamy na <math>x</math> kolejną, o jeden większą wartość, przywracamy wartości wszystkich
podstawiamy na <math> x </math> kolejną, o jeden większą wartość,  
pozostałych zmiennych sprzed instrukcji <math>\mathbf{for}\ </math>, i ponownie wykonujemy <math>I_1</math>.  
przywracamy wartości wszystkich
Powtarzamy w ten sposób, aż <math>I_1</math> zakończy się nie napotkawszy <math>\mathbf{fail}</math>, albo wartość zmiennej <math>x</math> przekroczy wartość wyrażenia <math>e_2</math> obliczoną na początku.
pozostałych zmiennych sprzed instrukcji <math> \mathbf{for}\, </math>, i ponownie
W pierwszym przypadku kończymy instrukcję <math>\mathbf{for}\ </math>, i przyracamy wartość zmiennej <math>x</math> sprzed tej instrukcji.
wykonujemy <math> I_1 </math>.  
W drugim przywracamy wartości wszystkich zmiennych sprzed instrukcji <math>\mathbf{for}\ </math>, i uruchamiamy <math>I_2</math>.
Powtarzamy w ten sposób aż <math> I_1 </math> zakończy się nie napotkawszy <math> \mathbf{fail} </math>,
}}
albo wartość zmiennej <math> x </math> przekroczy wartość wyrażenia <math> e_2 </math>
 
obliczoną na początku.
W pierwszym przypadku  
kończymy instrukcję <math> \mathbf{for}\, </math> i przyracamy wartość zmiennej <math> x </math>
sprzed tej instrukcji.
W drugim przywracamy wartości wszystkich zmiennych sprzed instrukcji
<math> \mathbf{for}\, </math> i uruchamiamy <math> I_2 </math>.


{{przyklad|||
Oto przykładowy program:
}}


===== Przykład =====
x := 0; y := 1;
<math>\mathbf{for}\ </math>, x := 1 <math>\,\mathbf{to}\ </math>, 5 <math>\,\mathbf{try}\ </math>,
  y := y+1;
  <math>\mathbf{if}\ </math>, x <= 4 <math>\,\mathbf{then}\, \mathbf{fail} \,\mathbf{else}\ </math>, z:= x
<math>\,\mathbf{else}\, \mathbf{skip}</math>


<math>
x := 0; y := 1;
\mathbf{for}\, x := 1 \,\mathbf{to}\, 5 \,\mathbf{try}\,
\quad y := y+1;
\quad \mathbf{if}\, x \leq 4 \,\mathbf{then}\, \mathbf{fail} \,\mathbf{else}\, z:= x
\,\mathbf{else}\, \mathbf{skip}
</math>


Wartości zmiennych po zakończeniu programu to:
Wartości zmiennych po zakończeniu programu to: <math>x = 0, y = 2, z = 5</math>.
<math> x = 0, y = 2, z = 5 </math>.




==== Rozwiązanie ====


<div class="mw-collapsible mw-made=collapsible mw-collapsed">
<span class="mw-collapsible-toogle mw-collapsible-toogle-default style="font-variant:small-caps">Rozwiązanie</span>
<div class="mw-collapsible-content" style="display:none">


Będziemy posługiwać się jak zwykle tranzycjami postaci:
Będziemy posługiwać się jak zwykle tranzycjami postaci:


<math>
<math>
I, s \,\longrightarrow\, s' \quad \quad \quad e, s \,\longrightarrow\, n.
I, s \,\longrightarrow\, s' \quad \quad \quad e, s \,\longrightarrow\, n</math>
</math>


Zacznijmy od najprostszego przypadku, gdy nie ma wogóle potrzeby
Zacznijmy od najprostszego przypadku, gdy nie ma wogóle potrzeby uruchamiania instrukcji wewnętrznej <math>I_1</math>, ponieważ wartość wyrażenia <math>e_1</math> jest większa od <math>e_2</math>:
uruchamiania instrukcji wewnętrznej <math> I_1 </math>, ponieważ
wartość wyrażenia <math> e_1 </math> jest większa od <math> e_2 </math>:


<math>
<math>
Linia 83: Linia 68:
       I_2, s \,\longrightarrow\, s'}
       I_2, s \,\longrightarrow\, s'}
     {\mathbf{for}\, x = e_1 \,\mathbf{to}\, e_2 \,\mathbf{try}\, I_1 \,\mathbf{else}\, I_2, s \,\longrightarrow\, s'}
     {\mathbf{for}\, x = e_1 \,\mathbf{to}\, e_2 \,\mathbf{try}\, I_1 \,\mathbf{else}\, I_2, s \,\longrightarrow\, s'}
\quad \mbox{ o ile } n_1 > n_2.
\quad \mbox{ o ile } n_1 > n_2</math>
</math>


Po prostu uruchamiamy instrukcję <math> I_2 </math> w stanie <math> s </math>, ignorując
Po prostu uruchamiamy instrukcję <math>I_2</math> w stanie <math>s</math>, ignorując <math>I_1</math>.
<math> I_1 </math>.


Kolejny nietrudny przypadek to sytuacja, w której  
Kolejny nietrudny przypadek to sytuacja, w której <math>n_1 \leq n_2</math>, a wykonanie instrukcji wewnętrznej <math>I_1</math> kończy się sukcesem, tzn. nie następuje wywołanie instrukcji <math>\mathbf{fail}</math>.  
<math> n_1 \leq n_2 </math> a wykonanie instrukcji wewnętrznej <math> I_1 </math>
Oto reguła dla tego przypadku:
kończy się sukcesem, tzn. nie następuje wywołanie instrukcji  
<math> \mathbf{fail} </math>. Oto reguła dla tego przypadku:


<math>
<math>
Linia 101: Linia 82:
       I_1, s[x \mapsto n_1]  \,\longrightarrow\, s'}
       I_1, s[x \mapsto n_1]  \,\longrightarrow\, s'}
     {\mathbf{for}\, x = e_1 \,\mathbf{to}\, e_2 \,\mathbf{try}\, I_1 \,\mathbf{else}\, I_2, s \,\longrightarrow\, s'[x \mapsto s(x)]}
     {\mathbf{for}\, x = e_1 \,\mathbf{to}\, e_2 \,\mathbf{try}\, I_1 \,\mathbf{else}\, I_2, s \,\longrightarrow\, s'[x \mapsto s(x)]}
\quad \mbox{ o ile } n_1 \leq n_2.
\quad \mbox{ o ile } n_1 \leq n_2</math>
</math>


Tym razem uruchamiamy instrukcję <math> I_1 </math>, podstawiając
Tym razem uruchamiamy instrukcję <math>I_1</math>, podstawiając na zmienną <math>x</math> wartość <math>n_1</math>.
pod zmienną <math> x </math> wartość <math> n_1 </math>.
Zwróćmy uwagę, że po zakończeniu <math>I_1</math> przywracamy wartość zmiennej <math>x</math> sprzed jej wykonania.
Zwróćmy uwagę, że po zakończeniu <math> I_1 </math> przywracamy
wartość zmiennej <math> x </math> sprzed jej wykonania.


Pozostał nam trzeci przypadek, gdy <math> n_1 \leq n_2 </math>
Pozostał nam trzeci przypadek, gdy <math>n_1 \leq n_2</math>, a wykonanie instrukcji <math>I_1</math> zakończyło się wykonaniem instrukcji <math>\mathbf{fail}</math> gdzieś wewnątrz <math>I_1</math>.
a wykonanie instrukcji <math> I_1 </math> zakończyło się
Aby poprawnie opisać takie zdarzenie, potrzebujemy dodatkowych tranzycji postaci:
wykonaniem instrukcji <math> \mathbf{fail} </math> gdzieś wewnątrz <math> I_1 </math>.
Aby poprawnie opisać takie zdarzenie, potrzebujemy dodatkowych
tranzycji postaci:


<math>
<math>
I, s \,\longrightarrow\, \mbox{było-}\mathbf{fail}.
I, s \,\longrightarrow\, \mbox{było-}\mathbf{fail}</math>
</math>


Mówiąc formalnie, poszerzamy zbiór konfiguracji o jeden element
Mówiąc formalnie, poszerzamy zbiór konfiguracji o jeden element <math>\mbox{było-}\mathbf{fail}</math>.
<math> \mbox{było-}\mathbf{fail} </math>.
Zauważmy, że po prawej stronie tranzycji nie ma wogóle stanu.
Zauważmy, że po prawej stronie tranzycji <math> dkrok </math> nie ma wogóle
Nie potrzebujemy go dla opisania semantyki instrukcji <math>\mathbf{for}\ </math>,: jeśli wystąpi <math>\mathbf{fail}</math>, powtarzamy <math>I_1</math> dla większej o <math>1</math> wartości zmiennej <math>x</math>, ale pozostałym zmiennym przywracamy wartość ''sprzed'' <math>I_1</math>.
stanu.
Nie potrzebujemy go dla opisania semantyki instrukcji <math> \mathbf{for}\, </math>:
jeśli wystąpi <math> \mathbf{fail} </math>, powtarzamy <math> I_1 </math> dla większej o <math> 1 </math> wartości
zmiennej <math> x </math>, ale pozostałym zmiennym przywracamy wartość ''sprzed''
<math> I_1 </math>.
A więc załóżmy, że  
A więc załóżmy, że  


<math> I_1, s[x \mapsto n_1] \,\longrightarrow\, \mbox{było-}\mathbf{fail}. </math>
<math>I_1, s[x \mapsto n_1] \,\longrightarrow\, \mbox{było-}\mathbf{fail}</math>.


W takiej sytuacji powinniśmy przypisać <math> n_1 + 1 </math>
W takiej sytuacji powinniśmy przypisać <math>n_1 + 1</math> na zmienną <math>x</math> i spróbować ponownie wykonać <math>I_1</math> przy wartościach wszystkich pozostałych zmiennych, takich jak na początku instrukcji <math>\mathbf{for}\ </math>,.
na zmienną <math> x </math> i spróbować ponownie wykonać
Powtarzamy ten schemat aż do skutku, tzn. aż do mementu, gdy zaistnieje któraś z poprzednich dwóch (prostych) sytuacji.
<math> I_1 </math> przy wartościach wszystkich pozostałych
zmiennych takich, jak na początku instrukcji <math> \mathbf{for}\, </math>.
I powtarzać ten schemat aż do skutku, tzn. aż do mementu,
gdy zaistnieje któraś z poprzednich dwóch (prostych) sytuacji.
Oto odpowiednia reguła:
Oto odpowiednia reguła:


<math>
<math>
\frac{e_1, s \,\longrightarrow\, n_1
\frac{e_1, s \,\longrightarrow\, n_1
       \quad \quad
       \quad
       e_2, s \,\longrightarrow\, n_2
       e_2, s \,\longrightarrow\, n_2
       \quad \quad
       \quad
       I_1, s[x \mapsto n_1]  \,\longrightarrow\, \mbox{było-}\mathbf{fail}
       I_1, s[x \mapsto n_1]  \,\longrightarrow\, \mbox{było-}\mathbf{fail}
       \quad \quad
       \quad
       \mathbf{for}\, x = e_1 + 1 \,\mathbf{to}\, e_2 \,\mathbf{try}\, I_1 \,\mathbf{else}\, I_2, s \drok s'}
       \mathbf{for}\, x = e_1 + 1 \,\mathbf{to}\, e_2 \,\mathbf{try}\, I_1 \,\mathbf{else}\, I_2, s \,\longrightarrow\, s'}
     {\mathbf{for}\, x = e_1 \,\mathbf{to}\, e_2 \,\mathbf{try}\, I_1 \,\mathbf{else}\, I_2, s \,\longrightarrow\, s'}
     {\mathbf{for}\, x = e_1 \,\mathbf{to}\, e_2 \,\mathbf{try}\, I_1 \,\mathbf{else}\, I_2, s \,\longrightarrow\, s'}
\quad \mbox{ o ile } n_1 \leq n_2.
\quad \mbox{ o ile } n_1 \leq n_2</math>
</math>
 
Zwróćmy uwagę na pewnien niezwykle istotny szczegół: po zakończeniu wykonania instrukcji <math>\mathbf{for}\, x = e_1 + 1 \,\mathbf{to}\, e_2 \,\mathbf{try}\, I_1 \,\mathbf{else}\, I_2</math> otrzymujemy stan <math>s'</math>, w którym '''nie przywracamy''' wartości zmiennej <math>x</math>.
Gdybyśmy tak zrobili, tzn. gdybyśmy zastąpili <math>s'</math> przez <math>s'[x \mapsto s(x)]</math>, nasze semantyka byłaby niepoprawna.  
Dlaczego? Dlatego, że nie wiemy tak naprawdę, czy powinniśmy przywracać wartość zmiennej <math>x</math>, czy nie.
Jeśli ostatecznie nasza instrukcja <math>\mathbf{for}\ </math>, zakończyła się przez bezbłędne zakończenie instrukcji <math>I_1</math> (przypadek drugi), to powinniśmy to zrobić; ale jeśli zakończyła się wykonaniem instrukcji <math>I_2</math> (przypadek pierwszy), powinniśmy pozostawić wartość zmiennej <math>x</math> taką, jaka jest ona po zakończeniu <math>I_2</math>.
A zatem w powyższej regule dla przypadku trzeciego nie przywracamy wartości zmiennej <math>x</math>; jeśli było to konieczne, to zostało już wykonane "głębiej", dzięki regule dla przypadku drugiego oraz dzięki temu, że instrukcja <math>\mathbf{for}\, x = e_1 + 1 \,\mathbf{to}\, e_2 \,\mathbf{try}\, I_1 \,\mathbf{else}\, I_2</math> wykonywana jest w orginalnym stanie <math>s</math>, a nie w stanie, w którym kończy się <math>I_1</math> (tego ostatniego stanu nawet nie znamy).


Zwróćmy uwagę na pewnien niezwykle istotny szczegół: po zakończeniu
Jeszcze jeden drobiazg: zamiast <math>e_1 {+} 1</math> w <math>\mathbf{for}\, x = e_1 + 1 \,\mathbf{to}\, e_2 \,\mathbf{try}\, I_1 \,\mathbf{else}\, I_2, s \,\longrightarrow\, s'</math> w regule powyżej, moglibyśmy podstawić policzoną już wartość, czyli <math>n_1 {+} 1</math>.
wykonania instrukcji <math> \mathbf{for}\, x = e_1 + 1 \,\mathbf{to}\, e_2 \,\mathbf{try}\, I_1 \,\mathbf{else}\, I_2 </math>
otrzymujemy stan <math> s' </math>, w którym '''nie przywracamy''' wartości
zmiennej <math> x </math>. Gdybyśmy tak zrobili, tzn. gdybyśmy zastąpili <math> s'
</math> przez <math> s'[x \mapsto s(x)] </math>, nasze semantyka byłaby
niepoprawna. Dlaczego? Dlatego, że nie wiemy tak naprawdę,
czy powinniśmy przywracać wartość zmiennej <math> x </math> czy nie.
Jeśli ostatecznie nasza instrukcja <math> \mathbf{for}\, </math> zakończyla
się przez bezbłędne zakończenie instrukcji <math> I_1 </math>
(przypadek drugi), to powinniśmy to zrobić;
ale jeśli zakończyła się wykonaniem instrukcji <math> I_2 </math>
(przypadek pierwszy),
to powinniśmy pozostawić wartość zmiennej <math> x </math> taką,
jaka jest ona po zakończeniu <math> I_2 </math>.
A zatem w powyższej regule dla przypadku trzeciego
nie przywracamy wartości zmiennej <math> x </math>; jeśli było
to konieczne, to zostało już wykonane ,,głębiej",
dzięki regule dla przypadku drugiego oraz dzięki temu,
że instrukcja <math> \mathbf{for}\, x = e_1 + 1 \,\mathbf{to}\, e_2 \,\mathbf{try}\, I_1 \,\mathbf{else}\, I_2 </math>
wykonywana jest w orginalnym stanie <math> s </math>,
a nie w stanie, w którym kończy się <math> I_1 </math>
(tego ostatniego stanu nawet nie znamy).


Na zakończenie prezentujemy reguły niebędne do tego,
Na zakończenie prezentujemy reguły niebędne do tego, aby wygenerować <math>\mbox{było-}\mathbf{fail}</math>:
aby wygenerować <math> \mbox{było-}\mathbf{fail} </math>:


<math>
<math>
Linia 181: Linia 129:
</math>
</math>


oraz aby wystąpienie <math> \mathbf{fail} </math> umiejscowione gdzieś ,,głęboko"
oraz aby wystąpienie <math>\mathbf{fail}</math> umiejscowione gdzieś "głęboko" zostało rozpropagowane do najbliższej otaczającej instrukcji <math>\mathbf{for}\ </math>,.
zostało rozpropagowane do najbliższej otaczającej
Jeśli pojawił się <math>\mathbf{fail}</math>, powinniśmy zaniechać dalszego wykonania instrukcji, a w przypadku pętli, powinniśmy zaniechać dalszego iterowania tej pętli.
instrukcji <math> \mathbf{for}\, </math>.
Jeśli pojawił się <math> \mathbf{fail} </math>, powinniśmy zaniechać dalszego
wykonania instrukcji a w przypadku pętli, powinniśmy
zaniechać dalszego iterowania tej pętli.
Oto odpowiednie reguły:
Oto odpowiednie reguły:


<math>
<math>
\frac{I_1, s \,\longrightarrow\, s', \mbox{było-}\mathbf{fail}}
\frac{I_1, s \,\longrightarrow\, \mbox{było-}\mathbf{fail}}
     {I_1;\, I_2, s \,\longrightarrow\, s', \mbox{było-}\mathbf{fail}}
     {I_1;\, I_2, s \,\longrightarrow\, \mbox{było-}\mathbf{fail}}
\quad \quad
\quad \quad
\frac{I_1, s \,\longrightarrow\, s' \quad \quad I_2, s' \,\longrightarrow\, s'', \mbox{było-}\mathbf{fail}}
\frac{I_1, s \,\longrightarrow\, s' \quad \quad I_2, s' \,\longrightarrow\, \mbox{było-}\mathbf{fail}}
     {I_1;\, I_2, s \,\longrightarrow\, s'', \mbox{było-}\mathbf{fail}}
     {I_1;\, I_2, s \,\longrightarrow\, \mbox{było-}\mathbf{fail}}
</math>
</math>


<math>
<math>
\frac{b, s \,\longrightarrow\, \mathbf{true} \quad \quad I_1, s \,\longrightarrow\, s', \mbox{było-}\mathbf{fail}}
\frac{b, s \,\longrightarrow\, \mathbf{true} \quad \quad I_1, s \,\longrightarrow\, \mbox{było-}\mathbf{fail}}
     {\mathbf{if}\, b \,\mathbf{then}\, I_1 \,\mathbf{else}\, I_2, s \,\longrightarrow\, s', \mbox{było-}\mathbf{fail}}
     {\mathbf{if}\, b \,\mathbf{then}\, I_1 \,\mathbf{else}\, I_2, s \,\longrightarrow\, \mbox{było-}\mathbf{fail}}
\quad \quad
\quad \quad
\mbox{ i analogicznie gdy } b, s \,\longrightarrow\, \mathbf{true}
\mbox{ i analogicznie gdy } b, s \,\longrightarrow\, \mathbf{false}
</math>
</math>


<math>
<math>
\frac{b, s \,\longrightarrow\, \mathbf{true} \quad \quad I, s \,\longrightarrow\, s', \mbox{było-}\mathbf{fail}}
\frac{b, s \,\longrightarrow\, \mathbf{true} \quad \quad I, s \,\longrightarrow\, \mbox{było-}\mathbf{fail}}
     {\mathbf{while}\, b \,\mathbf{do}\, I, s \,\longrightarrow\, s', \mbox{było-}\mathbf{fail}}
     {\mathbf{while}\, b \,\mathbf{do}\, I, s \,\longrightarrow\, \mbox{było-}\mathbf{fail}}
\quad \quad
\quad \quad
\frac{b, s \,\longrightarrow\, \mathbf{true} \quad \quad I, s \,\longrightarrow\, s' \quad \quad
\frac{b, s \,\longrightarrow\, \mathbf{true} \quad \quad I, s \,\longrightarrow\, s' \quad \quad
       \mathbf{while}\, b \,\mathbf{do}\, I, s' \,\longrightarrow\, s'', \mbox{było-}\mathbf{fail}}
       \mathbf{while}\, b \,\mathbf{do}\, I, s' \,\longrightarrow\, \mbox{było-}\mathbf{fail}}
     {\mathbf{while}\, b \,\mathbf{do}\, I, s \,\longrightarrow\, s', \mbox{było-}\mathbf{fail}}
     {\mathbf{while}\, b \,\mathbf{do}\, I, s \,\longrightarrow\, \mbox{było-}\mathbf{fail}}
</math>
</math>


Widać podobieństwo do analogicznych reguł dla
Widać podobieństwo do analogicznych reguł dla pętli <math>\mathbf{loop}\, I</math>, którą zajmowaliśmy się na wcześniejszych zajęciach.
pętli <math> \mathbf{loop}\, I </math>, którą zajmowaliśmy się na wcześniejszych
 
zajęciach.
Zauważmy, że jeśli <math>\mathbf{fail}</math> zostało wykonane poza jakąkolwiek pętlą <math>\mathbf{for}\ </math>,, to program "zakończy się" w konfiguracji <math>\mbox{było-}\mathbf{fail}</math>.
Możemy zatem tę właśnie konfigurację uznać za konfigurację końcową, informującą o porażce wykonania programu.
 
</div></div>


Zauważmy, że jesli <math> \mathbf{fail} </math> zostało wykonane poza jakąkolwiek
== Semantyka naturalna bloków ==
pętlą <math> \mathbf{for}\, </math>, to program ,,zakończy się" w konfiguracji
<math> \mbox{było-}\mathbf{fail} </math>. Możemy zatem tę właśnie konfigurację
uznać za konfigurację końcową, informującą o porażce
wykonania programu.




==== Zadanie 2 (bloki i deklaracje zmiennych) ====
{{cwiczenie|2 (bloki i deklaracje zmiennych)|cw2|


Rozszerz semantykę języka Tiny o deklarację zmiennej:
Rozszerz semantykę języka TINY o deklarację zmiennej:


<math>
<math>
Linia 234: Linia 177:
</math>
</math>


Zasięgiem zmiennej <math> x </math> jest instrukcja <math> I </math>
Zasięgiem zmiennej <math>x</math> jest instrukcja <math>I</math>, czyli wnętrze bloku, w którym jest zadeklarowana.
czyli wnętrze bloku, w którym jest zadeklarowana.
Zakładamy zwykłe (statyczne) reguły widoczności, przesłaniania, itp.
Zakładamy zwykłe (statyczne) reguły widoczności, przesłaniania, itp.
}}




==== Rozwiązanie 1 (tylko stan) ====
<div class="mw-collapsible mw-made=collapsible mw-collapsed">
<span class="mw-collapsible-toogle mw-collapsible-toogle-default style="font-variant:small-caps">Rozwiązanie</span>
<div class="mw-collapsible-content" style="display:none">


'''Wariant 1 (tylko stan)'''
<br>


Oczywiście powinniśmy odróżniać zmienne zadeklarowane (i zarazem
Oczywiście powinniśmy odróżniać zmienne zadeklarowane (i zarazem zainicjowane) od tych niezadeklarowanych.
zainicjowane) od tych niezadeklarowanych.
Zatem przyjmijmy, że  
Zatem przyjmijmy, że  


<math> \mathbf{State} = \mathbf{Var} \to_{\mathrm{fin}} \mathbf{Num} </math>.
<math>\mathbf{State} = \mathbf{Var} \to_{\mathrm{fin}} \mathbf{Num}</math>.


Zachowuujemy wszystkie reguły semantyczne dla języka Tiny
Zachowujemy wszystkie reguły semantyczne dla języka Tiny i dodajemy jedną nową:
i dodajemy jedną nową:


<math>
<math>
Linia 256: Linia 201:
</math>
</math>


mówiącą, że instrukcja wewnętrzna <math> I </math> ewaluowana jest
mówiącą, że instrukcja wewnętrzna <math>I</math> ewaluowana jest w stanie, w którym dodatkowo zaznaczono wartość zmiennej <math>x</math> równą wartości, do której oblicza się <math>e</math>.
w stanie, w którym dodatkow zaznaczono wartość zmiennej <math> x </math>
Oczywiście takie rozwiązanie jest niepoprawne, gdyż wartość zmiennej <math>x</math> pozostaje w stanie nawet po wyjściu z bloku ("wycieka" z bloku).
równą wartości, do której oblicza się <math> e </math>.
Musimy dodać "dealokację" zmiennej <math>x</math>:
Oczywiście takie rozwiązanie jest niepoprawne, gdyż wartość
zmiennej <math> x </math> pozostaje w stanie nawet po wyjściu z bloku
(,,wycieka" z bloku).
Musimy dodać ,,dealokację" zmiennej <math> x </math>:


<math>
<math>
Linia 270: Linia 211:
</math>
</math>


I znów napotykamy podobnie trudności jak na wcześniejszych zajęciach:
I znów napotykamy podobne trudności jak na wcześniejszych zajęciach: powyższa reguła nie obejmuje przypadku, gdy <math>s(x)</math> jest nieokreślone.
powyższa reguła nie obejmuje przypadku, gdy <math> s(x) </math> jest
I choć moglibyśmy naprawić ten mankament podobnie jak kiedyś (co pozostawiamy Czytelnikowi), istnieje inne, bardzo eleganckie i elastyczne rozwiązanie tego problemu, które teraz omówimy.
nieokreślone.
I choć moglibyśmy naprawić ten mankament podobnie jak kiedyś
(co pozostawiamy Czytelnikowi), istnieje inne, bardzo eleganckie
i elastyczne rozwiązanie tego problemu, które teraz omówimy.
 


==== Rozwiązanie 2 (stan i środowisko) ====
<br>
'''Wariant 2 (stan i środowisko)'''
<br>


Podstawowy pomysł polega na rozdzielenie informacji przechowywanej
Podstawowy pomysł polega na rozdzieleniu informacji przechowywanej dotychczas w stanie, czyli odwzorowania nazw zmiennych w wartości, na dwa "etapy".
dotychczas w stanie na dwa ,,etapy".
Pierwszy z nich odwzorowuje identyfikatory zmiennych w ''lokacje'', a drugi lokacje w wartości.
Pierwszy z nich, odwzorowuje identyfikatory zmiennych na ''lokacje'', a drugi
Mamy więc ''środowiska'' <math>E \in \mathbf{Env}</math>, będące funkcjami częściowymi z <math>\mathbf{Var}</math> do <math>\mathbf{Loc}</math>, zbioru lokacji:
lokacje na wartości.
Mamy więc ''środowiska'' <math> E \in \mathbf{Env}], będące funkcjami częściowymi z <math> \mathbf{Var} </math>
do <math> \mathbf{Loc} </math>, zbioru lokacji:


<math>
<math>
Linia 291: Linia 226:
</math>
</math>


oraz stany, będące teraz funkcjami częściowymi z <math> \mathbf{Loc} </math> do <math> \mathbf{Num} </math>:
oraz stany, będące teraz funkcjami częściowymi z <math>\mathbf{Loc}</math> do <math>\mathbf{Num}</math>:


<math>  
<math>
\mathbf{State} = \mathbf{Loc} \to_{\mathrm{fin}} \mathbf{Num}.
\mathbf{Store} = \mathbf{Loc} \to_{\mathrm{fin}} \mathbf{Num}</math>
</math>


Intuicyjnie można myśleć o lokacjach jak o lokacjach w pamięci
Dla ścisłości używamy innej nazwy (<math>\mathbf{Store}</math>), ale będziemy zwykle używać podobnych symboli jak dotychczas <math>s, s', s_1, \ldots \in \mathbf{Store}</math> itp. do nazywania stanów.
operacyjnej maszyny. Środowisko zawiera informację o lokacji, w której
Intuicyjnie można myśleć o lokacjach w pamięci operacyjnej maszyny. Środowisko zawiera informację o lokacji, w której
przechowywana jest wartość danej zmiennej.
przechowywana jest wartość danej zmiennej a stan opisuje właśnie zawartość używanych lokacji.
A stan opisuje właśnie zawartość używanych lokacji.


Zakładamy przy tym, że mamy do dyspozycji nieskończony zbiór
Zakładamy przy tym, że mamy do dyspozycji nieskończony zbiór lokacji <math>\mathbf{Loc} = \{ l_0, l_1, \ldots \}</math> i że w każdym momencie tylko skończenie wiele spośród nich jest wykorzystywane.
lokacji <math> \mathbf{Loc} = \{ l_0, l_1, \ldots \} </math> i że  
Formalnie mowiąc, dziedzina funkcji częściowej <math>s</math> jest zawsze skończona.  
w każdym momencie tylko skończenie wiele spośród nich jest
wykorzystywane.
Formalnie mowiąc, dziedzina funkcji częściowej <math> s </math> jest zawsze skończony.  
Daje nam to pewność, że zawsze jest jakaś nieużywana lokacja.  
Daje nam to pewność, że zawsze jest jakaś nieużywana lokacja.  


Środowisko początkowe, w którym uruchamiany
Środowisko początkowe, w którym uruchamiany będzie program, będzie z reguły puste.
będzie program, będzie z reguły puste.
Ponadto obraz funkcji częściowej <math>E</math> będzie zawsze zawarty w zbiorze aktualnie używanych lokacji, czyli zawarty w dziedzinie funkcji częściowej <math>s</math>, oznaczanej <math>\mathrm{dom}(s)</math>.
Ponadto obraz funkcji częściowej <math> E </math>  
będzie zawsze zawarty w zbiorze aktualnie używanych lokacji,
czyli zawarty w dziedzinie funkcji częściowej <math> s </math>, oznaczanej
<math> \dom(s) </math>.


Pożytek z tego podziału na dwa etapy będzie taki, że
Pożytek z tego podziału na dwa etapy będzie taki, że będziemy umieli łatwo i elastycznie opisywać deklaracje zmiennych, przesłanianie identyfikatorów, itp.
będziemy umieli łatwo i elastycznie opisywać deklaracje zmiennych,
przesłanianie identyfikatorów, itp.
Tranzycje będą teraz postaci:
Tranzycje będą teraz postaci:


Linia 325: Linia 249:
</math>
</math>


czyli instrukcja <math> I </math> będzie modyfikować stan ale nie będzie
czyli instrukcja <math>I</math> będzie modyfikować stan, ale nie będzie zmieniać środowiska <math>E</math>. Dla czytelności będziemy zapisywać nasze reguły w następujący sposób:
zmieniać środowiska <math> E </math>. Dla czytelności będziemy zapisywać
nasze reguły w następujący sposób:


<math>
<math>
Linia 334: Linia 256:


podkreślając w ten sposób, że środowisko nie ulega zmianie.
podkreślając w ten sposób, że środowisko nie ulega zmianie.
Ale należy pamiętać, że konfiguracja, w której ,,uruchamiamy"
Ale należy pamiętać, że konfiguracja, w której "uruchamiamy" instrukcję <math>I</math> składa się naprawdę z trójki <math>(I, s, E)</math>.
instrukcję <math> I </math> składa się naprawdę z pary <math> (E, s) </math>.


Deklarując nową zmienną <math> x </math> dodamy po prostu do <math> E </math> parę
Deklarując nową zmienną <math>x</math> dodamy po prostu do <math>E</math> parę <math>(x, l)</math>, gdzie <math>l</math> jest nową, nieużywaną dotychczas lokacją.
<math> (x, l) </math>, gdzie <math> l </math> jest nową, nieużywaną dotychczas lokacją.
Dla wygody zapisu załóżmy, że mamy do dyspozycji funkcję
Dla wygody zapisu załóżmy, że mamy do dyspozycji funkcję
funkcję


<math>  
<math>
\mathtt{new} : \mathbf{State} \to \mathbf{Loc}
\mathtt{newloc} : \mathbf{Store} \to \mathbf{Loc}
</math>
</math>


Linia 349: Linia 268:


<math>
<math>
\mathtt{new}(s) \notin \dom(s).
\mathtt{newloc}(s) \notin \mathrm{dom}(s)</math>
</math>


Dla ilustracji popatrzmy na przykładową regułę dla deklaracji.
Dla ilustracji popatrzmy na przykładową regułę dla deklaracji.
Linia 358: Linia 276:
       E[x \mapsto l] \,\vdash\, I, s[l \mapsto n] \,\longrightarrow\, s'}
       E[x \mapsto l] \,\vdash\, I, s[l \mapsto n] \,\longrightarrow\, s'}
     {E \,\vdash\, \mathbf{begin}\, \mathbf{var}\, x=e;\, I \,\mathbf{end}, s \,\longrightarrow\, s'}
     {E \,\vdash\, \mathbf{begin}\, \mathbf{var}\, x=e;\, I \,\mathbf{end}, s \,\longrightarrow\, s'}
\quad \mbox{ gdzie } l = \mathtt{new}(s)
\quad \mbox{ gdzie } l = \mathtt{newloc}(s)</math>
</math>
 
Zauważmy, że stan <math> s' </math> po zakończeniu instrukcji
<math> \mathbf{begin}\, \mathbf{var}\, x=e;\, I \,\mathbf{end} </math> zawiera informację o zmiennej
lokalnej <math> x </math>, tzn. <math> s(l) </math> jest określone.
Ale lokacja <math> l </math> jest ,,nieosiągalna" w środowisku
<math> E </math>, gdyż para <math> x \mapsto l </math> została dodana tylko do
środowiska, w którym ewaluuje się wnętrze bloku, a środowisko
<math> E </math> całego bloku nie jest modyfikowane.


Zauważmy, że stan <math>s'</math> po zakończeniu instrukcji <math>\mathbf{begin}\, \mathbf{var}\, x=e;\, I \,\mathbf{end}</math> zawiera informację o zmiennej lokalnej <math>x</math>, tzn. <math>s(l)</math> jest określone.
Ale lokacja <math>l</math> jest "nieosiągalna" w środowisku <math>E</math>, gdyż para <math>x \mapsto l</math> została dodana tylko do środowiska, w którym ewaluuje się wnętrze bloku, a środowisko <math>E</math> całego bloku nie jest modyfikowane.


Poniżej przedstawiamy reguły dla pozostałych instrukcji.
Poniżej przedstawiamy reguły dla pozostałych instrukcji.
Linia 380: Linia 291:
</math>  
</math>  


Reguła ta  uzmysławia nam różnicę pomiędzy środowiskiem a stanem:
Reguła ta  uzmysławia nam różnicę pomiędzy środowiskiem a stanem: środowisko pozostaje to samo, gdy przechodzimy od jednej instrukcji do następnej, a stan oczywiście ewoluuje wraz ze zmieniającymi się wartościami zmiennych.
środowisko pozostaje to samo, gdy przechodzimy od jednej instrukcji do
następn\ej, a stan oczywiście ewoluuje wraz ze zmieniającymi się
wartościami zmiennych.


Reguły dla przypisania, instrukcji warunkowej i pętli
Reguły dla przypisania, instrukcji warunkowej i pętli nie przedstawiają żadnych nowych trudności.  
nie przedstawiają żadnych nowych trudności.  
Musimy tylko najpierw ustalić postać reguł dla wyrażeń:
Musimy tylko najpierw ustalić postać reguł dla wyrażeń:


Linia 393: Linia 300:
</math>
</math>
   
   
która jest zupełnie naturalna w naszym podejściu opartym o środowiska
która jest zupełnie naturalna w naszym podejściu opartym o środowiska i stany.  
i stany.  
Reguły mogą wyglądać np. tak:
Reguły mogą wyglądać np. tak:


Linia 410: Linia 316:
     {E \,\vdash\, \mathbf{if}\, b \,\mathbf{then}\, I_1 \,\mathbf{else}\, I_2, s \,\longrightarrow\, s'}
     {E \,\vdash\, \mathbf{if}\, b \,\mathbf{then}\, I_1 \,\mathbf{else}\, I_2, s \,\longrightarrow\, s'}
\quad \quad
\quad \quad
\frac{E \,\vdash\, b, s \,\longrightarrow\, \mathbf{true}
\frac{E \,\vdash\, b, s \,\longrightarrow\, \mathbf{false}
       \quad \quad
       \quad \quad
       E \,\vdash\, I_2, s \,\longrightarrow\, s'}
       E \,\vdash\, I_2, s \,\longrightarrow\, s'}
Linia 417: Linia 323:


<math>
<math>
{E \,\vdash\, \mathbf{if}\, b \,\mathbf{then}\, (\mathbf{while}\, b \,\mathbf{do}\, I) \,\mathbf{else}\, \mathbf{skip}, s \,\longrightarrow\, s'}
\frac{E \,\vdash\, \mathbf{if}\, b \,\mathbf{then}\, (\mathbf{while}\, b \,\mathbf{do}\, I) \,\mathbf{else}\, \mathbf{skip}, s \,\longrightarrow\, s'}
{E \,\vdash\, \mathbf{while}\, b \,\mathbf{do}\, I, s \,\longrightarrow\, s'}
    {E \,\vdash\, \mathbf{while}\, b \,\mathbf{do}\, I, s \,\longrightarrow\, s'}
</math>
</math>


Linia 432: Linia 338:
i tak dalej -- pomijamy pozostałe reguły.
i tak dalej -- pomijamy pozostałe reguły.


 
<br>
 
'''Wariant 3 (dealokacja)'''
===== Wariant (dealokacja) =====
<br>


Przypomnijmy sobie semantykę bloku
Przypomnijmy sobie semantykę bloku
Linia 442: Linia 348:
       E[x \mapsto l] \,\vdash\, I, s[l \mapsto n] \,\longrightarrow\, s'}
       E[x \mapsto l] \,\vdash\, I, s[l \mapsto n] \,\longrightarrow\, s'}
     {E \,\vdash\, \mathbf{begin}\, \mathbf{var}\, x=e;\, I \,\mathbf{end}, s \,\longrightarrow\, s'}
     {E \,\vdash\, \mathbf{begin}\, \mathbf{var}\, x=e;\, I \,\mathbf{end}, s \,\longrightarrow\, s'}
\quad \mbox{ gdzie } l = \mathtt{new}(s)
\quad \mbox{ gdzie } l = \mathtt{newloc}(s)
</math>
</math>


i zastanówmy się, jak ,,posprzątać" po zakończeniu wykonania bloku,
i zastanówmy się, jak "posprzątać" po zakończeniu wykonania bloku, tzn. zwolnić lokację <math>l</math>, która była używana tylko w tym bloku i w związku z tym nie będzie już potrzebna.
tzn. zwolnić lokację <math> l </math>, która była używana tylko w tym bloku
Oznaczałoby to przywrócenie lokacji <math>l</math> do puli wolnych (nieużywanych) lokacji.
i w związku z tym nie będzie już potrzebne.
Oznaczałoby to przywrócenie lokacji <math> l </math> do puli wolnych
(nieużywanych) lokacji.


Zmodyfikowana reguła dla instrukcji bloku powinna wyglądać mniej więcej tak:
Zmodyfikowana reguła dla instrukcji bloku powinna wyglądać mniej więcej tak:
Linia 460: Linia 363:
</math>
</math>


gdzie <math> \bar{s} </math> to stan <math> s'' </math> ,,okrojony" do dziedziny stanu <math> s </math>.
gdzie <math>\bar{s}</math> to stan <math>s''</math> "okrojony" do dziedziny stanu <math>s</math>.
Zatem powinniśmy po prostu przywrócić nieokreśloność stanu dla lokacji
Powinniśmy po prostu przywrócić nieokreśloność stanu dla lokacji <math>l</math>.
<math> l </math>.
Natomiast oczywiście nie ma potrzeby dealokownia środowiska!
Natomiast oczywiście nie ma potrzeby dealokownia środowiska!
Oto rozwiązanie:
Oto rozwiązanie:
Linia 470: Linia 372:
       E[x \mapsto l] \,\vdash\, I, s[l \mapsto n] \,\longrightarrow\, s'}
       E[x \mapsto l] \,\vdash\, I, s[l \mapsto n] \,\longrightarrow\, s'}
     {E \,\vdash\, \mathbf{begin}\, \mathbf{var}\, x=e;\, I \,\mathbf{end}, s \,\longrightarrow\, s' \setminus \{ (l, s'(l)) \}}
     {E \,\vdash\, \mathbf{begin}\, \mathbf{var}\, x=e;\, I \,\mathbf{end}, s \,\longrightarrow\, s' \setminus \{ (l, s'(l)) \}}
\quad \mbox{ gdzie } l = \mathtt{new}(s)
\quad \mbox{ gdzie } l = \mathtt{newloc}(s)</math>
</math>


</div></div>


== Zadania domowe ==
== Zadania domowe ==




==== Zadanie 1 ====
{{cwiczenie|1|cw1.dom|


Napisz semantykę naturalną dla następującego
Napisz semantykę naturalną dla następującego rozszerzenia języka TINY:
rozszerzenia języka Tiny:


<math>
<math>
I \, ::= \,\,
I \, ::= \,\,
       \ldotes \,\,|\,\,
       \ldots \,\,|\,\,
       \mathbf{throw}\, x \,\,|\,\,
       \mathbf{throw}\, x \,\,|\,\,
       \,\mathbf{try}\, I_1 \,\mathbf{catch}\, exc\, I_2
       \,\mathbf{try}\, I_1 \,\mathbf{catch}\, exc\, I_2
Linia 495: Linia 396:
</math>
</math>


Instrukcja <math> \mathbf{throw}\, x </math> oznacza podniesienie wyjątku o nazwie <math> x </math>.
Instrukcja <math>\mathbf{throw}\, x</math> oznacza podniesienie wyjątku o nazwie <math>x</math>.
Instrukcja <math> I = \,\mathbf{try}\, I_1 \,\mathbf{catch}\, exc\, I_2 </math> wykonuje <math> I_1 </math>.
Instrukcja <math>I = \,\mathbf{try}\, I_1 \,\mathbf{catch}\, exc\, I_2</math> wykonuje <math>I_1</math>.
Jeśli podczas wykonania <math> I_1 </math> zostanie podniesiony wyjątej <math> x
Jeśli podczas wykonania <math>I_1</math> zostanie podniesiony wyjątek <math>x</math>, i <math>exc = x</math> albo <math>exc = \mathbf{any}</math>, to następuje przerwanie <math>I_1</math> i sterowanie zostaje przeniesione do <math>I_2</math> (następuje ''obsługa wyjątku'').  
</math>, i <math> exc = x </math> albo <math> exc = \mathbf{any} </math>, to następuje przerwanie <math>
Jeśli zaś podczas wykonania <math>I_1</math> zostanie podniesiony wyjątek <math>x</math> oraz <math>exc \neq x</math> i <math>exc \neq \mathbf{any}</math>, to obsługa wyjątku przekazana jest do najbliższej instrukcji <math>\,\mathbf{try}\ </math>, otaczającej <math>I</math>.
I_1 </math> i sterowanie zostaje przeniesione do <math> I_2 </math>
Umawiamy się, że <math>\,\mathbf{try}\, I_1 \,\mathbf{catch}\, exc\, I_2</math> ''otacza'' <math>I_1</math> i wszystkie instrukcje wewnątrz <math>I_1</math>, ale ''nie'' otacza <math>I_2</math>.
(następuje ''obsługa wyjątku'').  
}}
Jeśli zaś podczas wykonania <math> I_1 </math> zostanie podniesiony wyjątej <math> x
</math>, i <math> exc \neq x </math> i <math> exc \neq \mathbf{any} </math>, to obsługa wyjątku
przekazana jest do najbliższej instrukcji <math> \,\mathbf{try}\, </math> otaczającej <math> I
</math>.
Umawiamy się, że <math> \,\mathbf{try}\, I_1 \,\mathbf{catch}\, exc\, I_2 </math> ''otacza'' <math> I_1 </math>
i wszystkie instrukcje wewnątrz <math> I_1 </math>,
ale ''nie'' otacza <math> I_2 </math>.




==== Zadanie 2 ====
{{cwiczenie|2|cw2.dom|


Zaproponuj modyfikację semantyki języka Small, w której deklaracja
Zaproponuj modyfikację semantyki, w której deklaracja jest wykonywana "równolegle", analogicznie do przypisania równoległego.  
jest wykonywana ,,równolegle\'', analogicznie do przypisania
Przy takiej semantyce kolejność poszczególnych deklaracji powinna być nieistotna.
równoległego. Przy takiej semantyce kolejność poszczególnych
}}
deklaracji nie powinna wpływać na wynik programu.

Aktualna wersja na dzień 21:30, 11 wrz 2023

Zawartość

Kontynuujemy ćwiczenie semantyki naturalnej, dodając pewne konstrukcje do języka TINY. W szczególności rozszerzymy go o deklaracje zmiennych (bloki). Po raz pierwszy roszdzielimy informację przechowywaną w konfiguracji na środowisko określające wiązanie identyfikatorów i stan przechowujący wartości zmiennych. Będzie to przygotowanie do kolejnych zajęć.


Semantyka naturalna pewnej instrukcji 𝐟𝐨𝐫 ,

Ćwiczenie 1


Rozszerzamy język TINY następująco:

I::=|𝐟𝐨𝐫x=e1𝐭𝐨e2𝐭𝐫𝐲I1𝐞𝐥𝐬𝐞I2|𝐟𝐚𝐢𝐥

Znaczenie instrukcji 𝐟𝐨𝐫x=e1𝐭𝐨e2𝐭𝐫𝐲I1𝐞𝐥𝐬𝐞I2 jest następujące. Obliczamy wartości wyrażeń e1 i e2. Jeśli pierwsza z nich jest mniejsza od lub równa drugiej, podstawiamy pierwszą wartość (wartość wyrażenia e1) na zmienną x i uruchamiamy I1. Jeśli w I1 nie zostanie napotkana instrukcja 𝐟𝐚𝐢𝐥, kończymy instrukcję 𝐟𝐨𝐫 , i przyracamy wartość zmiennej x sprzed tej instrukcji. Natomiast jeśli w I1 zostanie napotkana instrukcja 𝐟𝐚𝐢𝐥, podstawiamy na x kolejną, o jeden większą wartość, przywracamy wartości wszystkich pozostałych zmiennych sprzed instrukcji 𝐟𝐨𝐫 , i ponownie wykonujemy I1. Powtarzamy w ten sposób, aż I1 zakończy się nie napotkawszy 𝐟𝐚𝐢𝐥, albo wartość zmiennej x przekroczy wartość wyrażenia e2 obliczoną na początku. W pierwszym przypadku kończymy instrukcję 𝐟𝐨𝐫 , i przyracamy wartość zmiennej x sprzed tej instrukcji. W drugim przywracamy wartości wszystkich zmiennych sprzed instrukcji 𝐟𝐨𝐫 , i uruchamiamy I2.


Przykład

Oto przykładowy program:

x := 0; y := 1;
𝐟𝐨𝐫 , x := 1 𝐭𝐨 , 5 𝐭𝐫𝐲 ,
  y := y+1;
  𝐢𝐟 , x <= 4 𝐭𝐡𝐞𝐧𝐟𝐚𝐢𝐥𝐞𝐥𝐬𝐞 , z:= x
𝐞𝐥𝐬𝐞𝐬𝐤𝐢𝐩


Wartości zmiennych po zakończeniu programu to: x=0,y=2,z=5.


Rozwiązanie

Semantyka naturalna bloków

Ćwiczenie 2 (bloki i deklaracje zmiennych)

Rozszerz semantykę języka TINY o deklarację zmiennej:

I::=|𝐛𝐞𝐠𝐢𝐧𝐯𝐚𝐫x=e;I𝐞𝐧𝐝

Zasięgiem zmiennej x jest instrukcja I, czyli wnętrze bloku, w którym jest zadeklarowana. Zakładamy zwykłe (statyczne) reguły widoczności, przesłaniania, itp.


Rozwiązanie

Zadania domowe

Ćwiczenie 1

Napisz semantykę naturalną dla następującego rozszerzenia języka TINY:

I::=|𝐭𝐡𝐫𝐨𝐰x|𝐭𝐫𝐲I1𝐜𝐚𝐭𝐜𝐡excI2

exc::=x|𝐚𝐧𝐲

Instrukcja 𝐭𝐡𝐫𝐨𝐰x oznacza podniesienie wyjątku o nazwie x. Instrukcja I=𝐭𝐫𝐲I1𝐜𝐚𝐭𝐜𝐡excI2 wykonuje I1. Jeśli podczas wykonania I1 zostanie podniesiony wyjątek x, i exc=x albo exc=𝐚𝐧𝐲, to następuje przerwanie I1 i sterowanie zostaje przeniesione do I2 (następuje obsługa wyjątku). Jeśli zaś podczas wykonania I1 zostanie podniesiony wyjątek x oraz excx i exc𝐚𝐧𝐲, to obsługa wyjątku przekazana jest do najbliższej instrukcji 𝐭𝐫𝐲 , otaczającej I. Umawiamy się, że 𝐭𝐫𝐲I1𝐜𝐚𝐭𝐜𝐡excI2 otacza I1 i wszystkie instrukcje wewnątrz I1, ale nie otacza I2.


Ćwiczenie 2

Zaproponuj modyfikację semantyki, w której deklaracja jest wykonywana "równolegle", analogicznie do przypisania równoległego. Przy takiej semantyce kolejność poszczególnych deklaracji powinna być nieistotna.