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

Z Studia Informatyczne
Przejdź do nawigacjiPrzejdź do wyszukiwania
Sl (dyskusja | edycje)
Nie podano opisu zmian
Sl (dyskusja | edycje)
Nie podano opisu zmian
Linia 22: Linia 22:
Zdefiniuj znaczenie wyrażeń za pomocą semantyki operacyjnej,
Zdefiniuj znaczenie wyrażeń za pomocą semantyki operacyjnej,
w stylu małych kroków.
w stylu małych kroków.


==== Rozwiązanie ====
==== Rozwiązanie ====


Przypomnijmy składnię wyrażeń boolowskich i arytmetycznych:
Przypomnijmy składnię wyrażeń boolowskich i arytmetycznych:
Linia 60: Linia 63:
Chcemy, aby tranzycje dla wyrażeń były postaci:
Chcemy, aby tranzycje dla wyrażeń były postaci:


<math>
<math>
e, s \,\Longrightarrow\, e', s
e, s \,\Longrightarrow\, e', s
</math>
</math>
Linia 66: Linia 69:
i podobnie dla wyrażeń boolowskich:
i podobnie dla wyrażeń boolowskich:


<math>
<math>
b, s \,\Longrightarrow\, b', s
b, s \,\Longrightarrow\, b', s
</math>
</math>
Linia 73: Linia 76:
Dodatkowo będziemy potrzebować również tranzycji postaci:
Dodatkowo będziemy potrzebować również tranzycji postaci:


<math>
<math>
e, s \,\Longrightarrow\, n
e, s \,\Longrightarrow\, n
\quad \quad
\quad \quad
Linia 97: Linia 100:
Zacznijmy od (chyba najprostszych) tranzycji dla stałych boolowskich i liczbowych:
Zacznijmy od (chyba najprostszych) tranzycji dla stałych boolowskich i liczbowych:


<math>
<math>
l, s \,\Longrightarrow\, l
l, s \,\Longrightarrow\, l
\quad \quad
\quad \quad
Linia 114: Linia 117:
obliczać <math> b_1 </math> i <math> b_2 </math>. Zacznijmy od strategii lewostronnej:
obliczać <math> b_1 </math> i <math> b_2 </math>. Zacznijmy od strategii lewostronnej:


<math>
<math>
\frac{b_1, s \,\Longrightarrow\, b'_1, s}
\frac{b_1, s \,\Longrightarrow\, b'_1, s}
{b_1 \land b_2, s \,\Longrightarrow\, b'_1 \land b_2, s}
{b_1 \land b_2, s \,\Longrightarrow\, b'_1 \land b_2, s}
Linia 129: Linia 132:
Oto odpowiednio zmodyfikowane reguły:
Oto odpowiednio zmodyfikowane reguły:


<math>
<math>
\frac{b_1, s \,\Longrightarrow\, b'_1, s}
\frac{b_1, s \,\Longrightarrow\, b'_1, s}
{b_1 \land b_2, s \,\Longrightarrow\, b'_1 \land b_2, s}
{b_1 \land b_2, s \,\Longrightarrow\, b'_1 \land b_2, s}
Linia 140: Linia 143:
Analogicznie reguły prawostronne to:
Analogicznie reguły prawostronne to:


<math>
<math>
\frac{b_2, s \,\Longrightarrow\, b'_2, s}
\frac{b_2, s \,\Longrightarrow\, b'_2, s}
{b_1 \land b_2, s \,\Longrightarrow\, b_1 \land b'_2, s}
{b_1 \land b_2, s \,\Longrightarrow\, b_1 \land b'_2, s}
Linia 164: Linia 167:
Oto reguły dla negacji:
Oto reguły dla negacji:


<math>
<math>
\neg \mathbf{true}, s \,\Longrightarrow\, \mathbf{true}, s
\neg \mathbf{true}, s \,\Longrightarrow\, \mathbf{true}, s
\quad \quad \quad
\quad \quad \quad
Linia 175: Linia 178:
Reguły dla <math> e_1 \leq e_2 </math> są następujące:
Reguły dla <math> e_1 \leq e_2 </math> są następujące:


<math>
<math>
\frac{e_1, s \,\Longrightarrow\, e'_1, s}
\frac{e_1, s \,\Longrightarrow\, e'_1, s}
{e_1 \leq e_2, s \,\Longrightarrow\, e'_1 \leq e_2, s}
{e_1 \leq e_2, s \,\Longrightarrow\, e'_1 \leq e_2, s}
Linia 198: Linia 201:
stan:
stan:


<math>
<math>
\frac{e, s \,\Longrightarrow\, e', s}
\frac{e, s \,\Longrightarrow\, e', s}
     {x := e, s \,\Longrightarrow\, x := e', s}
     {x := e, s \,\Longrightarrow\, x := e', s}
Linia 208: Linia 211:
Najpierw obliczamy wartość dozoru:
Najpierw obliczamy wartość dozoru:


<math>
<math>
\frac{b, s \,\Longrightarrow\, b', s}
\frac{b, s \,\Longrightarrow\, b', s}
{\mathbf{if}\, b \,\mathbf{then}\, I_1 \,\mathbf{else}\, I_2, s \,\Longrightarrow\,  
{\mathbf{if}\, b \,\mathbf{then}\, I_1 \,\mathbf{else}\, I_2, s \,\Longrightarrow\,  
Linia 220: Linia 223:
W przypadku instrukcji warunkowej reguły są oczywiste:
W przypadku instrukcji warunkowej reguły są oczywiste:


<math>
<math>
\mathbf{if}\, \mathbf{true} \,\mathbf{then}\, I_1 \,\mathbf{else}\, I_2, s \,\Longrightarrow\, I_1, s
\mathbf{if}\, \mathbf{true} \,\mathbf{then}\, I_1 \,\mathbf{else}\, I_2, s \,\Longrightarrow\, I_1, s
\quad \quad
\quad \quad
Linia 228: Linia 231:
Gorzej jest w przypadku instrukcji pętli. Reguła mogłaby wyglądać tak:
Gorzej jest w przypadku instrukcji pętli. Reguła mogłaby wyglądać tak:


<math>
<math>
\mathbf{while}\, \mathbf{true} \,\mathbf{do}\, I, s \,\Longrightarrow\,  
\mathbf{while}\, \mathbf{true} \,\mathbf{do}\, I, s \,\Longrightarrow\,  
I;\, \mathbf{while}\, ? \,\mathbf{do}\, I, s
I;\, \mathbf{while}\, ? \,\mathbf{do}\, I, s
Linia 238: Linia 241:
<math> \,\Longrightarrow\, </math> (czyli w zadadzie do semantyki dużych kroków):
<math> \,\Longrightarrow\, </math> (czyli w zadadzie do semantyki dużych kroków):


<math>
<math>
\frac{b, s \,\Longrightarrow\,^{*} \mathbf{true}}
\frac{b, s \,\Longrightarrow\,^{*} \mathbf{true}}
{\mathbf{while}\, b \,\mathbf{do}\, I, s \,\Longrightarrow\,  
{\mathbf{while}\, b \,\mathbf{do}\, I, s \,\Longrightarrow\,  
Linia 254: Linia 257:
Jedyną reguła dla pętli <math> \mathbf{while}\, </math> byłaby wtedy reguła:
Jedyną reguła dla pętli <math> \mathbf{while}\, </math> byłaby wtedy reguła:


<math>
<math>
\mathbf{while}\, b \,\mathbf{do}\, I, s \,\Longrightarrow\, \mathbf{if}\, b \,\mathbf{then}\, (I; \mathbf{while}\, b \,\mathbf{do}\, I) \,\mathbf{else}\, \mathbf{skip}, s.
\mathbf{while}\, b \,\mathbf{do}\, I, s \,\Longrightarrow\, \mathbf{if}\, b \,\mathbf{then}\, (I; \mathbf{while}\, b \,\mathbf{do}\, I) \,\mathbf{else}\, \mathbf{skip}, s.
</math>
</math>
Linia 264: Linia 267:


Reguły dla operacji arytmetycznych również pozostawiamy do napisania Czytelnikowi.
Reguły dla operacji arytmetycznych również pozostawiamy do napisania Czytelnikowi.




Linia 283: Linia 288:


==== Rozwiązanie ====
==== Rozwiązanie ====




Linia 292: Linia 299:
Po pierwsze rozwinięcie:
Po pierwsze rozwinięcie:


<math>
<math>
\mathbf{repeat}\, I \,\mathbf{until}\, b, s \,\Longrightarrow\, I; \mathbf{if}\, b \,\mathbf{then}\, (\mathbf{repeat}\, I \,\mathbf{until}\, b) \,\mathbf{else}\, \mathbf{skip}, s.
\mathbf{repeat}\, I \,\mathbf{until}\, b, s \,\Longrightarrow\, I; \mathbf{if}\, b \,\mathbf{then}\, (\mathbf{repeat}\, I \,\mathbf{until}\, b) \,\mathbf{else}\, \mathbf{skip}, s.
</math>
</math>
Linia 298: Linia 305:
Po drugie, spróbujmy odwołać się do <math> \,\Longrightarrow\,^{*} </math> dla dozoru pętli <math> b </math>:
Po drugie, spróbujmy odwołać się do <math> \,\Longrightarrow\,^{*} </math> dla dozoru pętli <math> b </math>:


<math>
<math>
\frac{I, s \,\Longrightarrow\, I', s'}
\frac{I, s \,\Longrightarrow\, I', s'}
     {\mathbf{repeat}\, I \,\mathbf{until}\, b, s \,\Longrightarrow\, \mathbf{repeat}\, I' \,\mathbf{until}\, b, s'}
     {\mathbf{repeat}\, I \,\mathbf{until}\, b, s \,\Longrightarrow\, \mathbf{repeat}\, I' \,\mathbf{until}\, b, s'}
</math>
</math>


<math>
<math>
\frac{I, s \,\Longrightarrow\, s' \quad b, s \,\Longrightarrow\,^{*} \mathbf{true}}
\frac{I, s \,\Longrightarrow\, s' \quad b, s \,\Longrightarrow\,^{*} \mathbf{true}}
     {\mathbf{repeat}\, I \,\mathbf{until}\, b, s \,\Longrightarrow\, s'}
     {\mathbf{repeat}\, I \,\mathbf{until}\, b, s \,\Longrightarrow\, s'}
Linia 323: Linia 330:
Czyli najpierw obliczmy <math> e </math> przy pomocy reguły:
Czyli najpierw obliczmy <math> e </math> przy pomocy reguły:


<math>
<math>
\frac{e, s \,\Longrightarrow\, e', s}
\frac{e, s \,\Longrightarrow\, e', s}
     {\,\mathbf{do}\, e \,\mathbf{times}\, I, s \,\Longrightarrow\, D e' \,\mathbf{times}\, I, s}
     {\,\mathbf{do}\, e \,\mathbf{times}\, I, s \,\Longrightarrow\, D e' \,\mathbf{times}\, I, s}
Linia 355: Linia 362:
Czyli:
Czyli:


<math>
<math>
\frac{e_1, s \,\Longrightarrow\, e'_1, s}
\frac{e_1, s \,\Longrightarrow\, e'_1, s}
     {\mathbf{for}\, x = e_1 \,\mathbf{to}\, e_2 \,\mathbf{do}\, I,s \,\Longrightarrow\, \mathbf{for}\, x = e'_1 \,\mathbf{to}\, e_2 \,\mathbf{do}\, I, s}
     {\mathbf{for}\, x = e_1 \,\mathbf{to}\, e_2 \,\mathbf{do}\, I,s \,\Longrightarrow\, \mathbf{for}\, x = e'_1 \,\mathbf{to}\, e_2 \,\mathbf{do}\, I, s}
</math>
</math>


<math>
<math>
\frac{e_2, s \,\Longrightarrow\, e'_2, s}
\frac{e_2, s \,\Longrightarrow\, e'_2, s}
     {\mathbf{for}\, x = n \,\mathbf{to}\, e_2 \,\mathbf{do}\, I,s \,\Longrightarrow\, \mathbf{for}\, x = n \,\mathbf{to}\, e'_2 \,\mathbf{do}\, I, s}
     {\mathbf{for}\, x = n \,\mathbf{to}\, e_2 \,\mathbf{do}\, I,s \,\Longrightarrow\, \mathbf{for}\, x = n \,\mathbf{to}\, e'_2 \,\mathbf{do}\, I, s}
Linia 368: Linia 375:
w konfiguracji
w konfiguracji


<math>
<math>
\mathbf{for}\, x = n_1 \,\mathbf{to}\, n_2 \,\mathbf{do}\, I, s.
\mathbf{for}\, x = n_1 \,\mathbf{to}\, n_2 \,\mathbf{do}\, I, s.
</math>
</math>
Linia 374: Linia 381:
Dalsze reguły mogą być podobne do reguł dla pętli <math> \,\mathbf{do}\, n \,\mathbf{times}\, I </math>:
Dalsze reguły mogą być podobne do reguł dla pętli <math> \,\mathbf{do}\, n \,\mathbf{times}\, I </math>:


<math>
<math>
\mathbf{for}\, x = n_1 \,\mathbf{to}\, n_2 \,\mathbf{do}\, I, s \,\Longrightarrow\, x:= n_1; I; \mathbf{for}\, x = n_1+1 \,\mathbf{to}\, n_2 \,\mathbf{do}\, I, s
\mathbf{for}\, x = n_1 \,\mathbf{to}\, n_2 \,\mathbf{do}\, I, s \,\Longrightarrow\, x:= n_1; I; \mathbf{for}\, x = n_1+1 \,\mathbf{to}\, n_2 \,\mathbf{do}\, I, s
\quad \mbox{ o ile } n_1 \leq n_2
\quad \mbox{ o ile } n_1 \leq n_2
</math>
</math>


<math>
<math>
\mathbf{for}\, x = n_1 \,\mathbf{to}\, n_2 \,\mathbf{do}\, I, s \,\Longrightarrow\, \mathbf{skip}, s
\mathbf{for}\, x = n_1 \,\mathbf{to}\, n_2 \,\mathbf{do}\, I, s \,\Longrightarrow\, \mathbf{skip}, s
\quad \mbox{ o ile } n_1 > n_2
\quad \mbox{ o ile } n_1 > n_2
Linia 395: Linia 402:
Oczywiście minimalistyczne rozwiązanie to
Oczywiście minimalistyczne rozwiązanie to


<math>
<math>
\,\mathbf{do}\, I \,\mathbf{while}\, b, s \,\Longrightarrow\, \mathbf{repeat}\, I \,\mathbf{until}\, \neg b, s
\,\mathbf{do}\, I \,\mathbf{while}\, b, s \,\Longrightarrow\, \mathbf{repeat}\, I \,\mathbf{until}\, \neg b, s
</math>
</math>




Linia 422: Linia 430:


==== Rozwiązanie ====
==== Rozwiązanie ====


Składnia wyrażeń pozwala na wygodny dostęp do najmniej znaczącego
Składnia wyrażeń pozwala na wygodny dostęp do najmniej znaczącego
Linia 427: Linia 437:
pisemnego:
pisemnego:


<math>
<math>
e_1 0 + e_2 0 \,\Longrightarrow\, (e_1 + e_2) 0
e_1 0 + e_2 0 \,\Longrightarrow\, (e_1 + e_2) 0
</math>
</math>


<math>
<math>
e_1 0 + e_2 1 \,\Longrightarrow\, (e_1 + e_2) 1
e_1 0 + e_2 1 \,\Longrightarrow\, (e_1 + e_2) 1
</math>
</math>


<math>
<math>
e_1 1 + e_2 0 \,\Longrightarrow\, (e_1 + e_2) 1
e_1 1 + e_2 0 \,\Longrightarrow\, (e_1 + e_2) 1
</math>
</math>
Linia 441: Linia 451:
Ale co zrobić z przeniesieniem?  
Ale co zrobić z przeniesieniem?  


<math>
<math>
e_1 1 + e_2 1 \,\Longrightarrow\, ?
e_1 1 + e_2 1 \,\Longrightarrow\, ?
</math>
</math>
Linia 447: Linia 457:
Podstawowy pomysł polega na potraktowaniu przeniesienia jak dodatkowego składnika:
Podstawowy pomysł polega na potraktowaniu przeniesienia jak dodatkowego składnika:


<math>
<math>
e_1 1 + e_2 1 \,\Longrightarrow\, ((e_1 + e_2) + \epsilon 1) 0
e_1 1 + e_2 1 \,\Longrightarrow\, ((e_1 + e_2) + \epsilon 1) 0
</math>
</math>
Linia 478: Linia 488:
potrzebujemy reguły:
potrzebujemy reguły:


<math>
<math>
\epsilon + \epsilon \,\Longrightarrow\, \epsilon.
\epsilon + \epsilon \,\Longrightarrow\, \epsilon.
</math>
</math>
Linia 484: Linia 494:
Gdy jeden ze składników ma mniej cyfr niż drugi, potrzebujemy reguł:
Gdy jeden ze składników ma mniej cyfr niż drugi, potrzebujemy reguł:


<math>
<math>
\epsilon + e 0 \,\Longrightarrow\, e 0
\epsilon + e 0 \,\Longrightarrow\, e 0
\quad \quad
\quad \quad
Linia 492: Linia 502:
oraz ich odpowiedników:
oraz ich odpowiedników:


<math>
<math>
e 0 + \epsilon \,\Longrightarrow\, e 0
e 0 + \epsilon \,\Longrightarrow\, e 0
\quad \quad
\quad \quad
Linia 500: Linia 510:
Niestety, nie możemy użyć reguły przemienności:
Niestety, nie możemy użyć reguły przemienności:


<math>
<math>
e_1 + e_2 \,\Longrightarrow\, e_2 + e_1
e_1 + e_2 \,\Longrightarrow\, e_2 + e_1
</math>
</math>
Linia 509: Linia 519:
krok całego wyrażenia:
krok całego wyrażenia:


<math>
<math>
\frac{e_1 \,\Longrightarrow\, e'_1}
\frac{e_1 \,\Longrightarrow\, e'_1}
     {e_1 + e_2 \,\Longrightarrow\, e'_1 + e_2}
     {e_1 + e_2 \,\Longrightarrow\, e'_1 + e_2}
Linia 523: Linia 533:
\quad \quad
\quad \quad
</math>
</math>




Linia 552: Linia 564:


==== Rozwiązanie ====
==== Rozwiązanie ====


Zadanie dotyczy w zasadzie składni ograniczonej, ale jako konfiguracji
Zadanie dotyczy w zasadzie składni ograniczonej, ale jako konfiguracji
Linia 569: Linia 583:
cyfr, i to ten właśnie składnik odnotował przepełnienie:
cyfr, i to ten właśnie składnik odnotował przepełnienie:


<math>
<math>
p + e 0 \,\Longrightarrow\, e 0
p + e 0 \,\Longrightarrow\, e 0
\quad \quad
\quad \quad
Linia 584: Linia 598:
cyfr, to reguły  
cyfr, to reguły  


<math>
<math>
\epsilon + e 0 \,\Longrightarrow\, e 0
\epsilon + e 0 \,\Longrightarrow\, e 0
\quad \quad
\quad \quad
Linia 601: Linia 615:
powinna zostać zachowana:
powinna zostać zachowana:


<math>
<math>
p + p \,\Longrightarrow\, p.
p + p \,\Longrightarrow\, p.
</math>
</math>
Linia 611: Linia 625:
Oto odpowiednie reguły:
Oto odpowiednie reguły:


<math>
<math>
p + \epsilon \,\Longrightarrow\, \epsilon
p + \epsilon \,\Longrightarrow\, \epsilon
\quad \quad
\quad \quad
Linia 626: Linia 640:
Odpowiednia reguła to
Odpowiednia reguła to


<math>
<math>
e_1 1 + e_2 1 \,\Longrightarrow\, ((e_1 + e_2) + p 1) 0.
e_1 1 + e_2 1 \,\Longrightarrow\, ((e_1 + e_2) + p 1) 0.
</math>
</math>
Linia 636: Linia 650:
to <math> p </math> zostanie usunięte. W przeciwnym wypadku symbol <math> p </math>  
to <math> p </math> zostanie usunięte. W przeciwnym wypadku symbol <math> p </math>  
i przetrwa i będzie poprawnie informował o sytuacji przepełnienia.
i przetrwa i będzie poprawnie informował o sytuacji przepełnienia.





Wersja z 08:54, 7 sie 2006


Zawartość

Ćwiczymy dalej semantykę małych kroków. Uzupełnimy semantykę języka Tiny o semantykę operacyjną wyrażeń boolowskich i arytmetycznych. Następnie rozszerzymy nasz język o róznorodne konstrukcje iteracji. Na koniec zdefiniujemy operacje arytmetyczne liczb binarnych.


Rozszerzenia semantyki języka Tiny

Zadanie 1

Semantyka języka Tiny z wykładu używała funkcji semantycznych B,E:StateState dla określenia znaczenia wyrażeń boolowskich i arytmetycznych. Zdefiniuj znaczenie wyrażeń za pomocą semantyki operacyjnej, w stylu małych kroków.


Rozwiązanie

Przypomnijmy składnię wyrażeń boolowskich i arytmetycznych:

b::=l|e1e2|¬b|b1b2|

l::=𝐭𝐫𝐮𝐞|𝐭𝐫𝐮𝐞

e::=n|x|e1+e2|

n::=0|1|

x::=(identyfikatory)

Niech 𝐁𝐄𝐱𝐩 oznacza zbiór wyrażeń boolowskich, b𝐁𝐄𝐱𝐩. Chcemy, aby tranzycje dla wyrażeń były postaci:

e,se,s

i podobnie dla wyrażeń boolowskich:

b,sb,s

gdzie s𝐒𝐭𝐚𝐭𝐞. Dodatkowo będziemy potrzebować również tranzycji postaci:

e,sn oraz b,sl gdzie n jest liczbą całkowitą, n𝐍𝐮𝐦, a l𝐁𝐨𝐨𝐥={𝐭𝐫𝐮𝐞,𝐭𝐫𝐮𝐞}. Formalnie, zbiór konfiguracji dla semantyki całego języka Tiny to

((𝐄𝐱𝐩𝐁𝐄𝐱𝐩𝐒𝐭𝐦𝐭)×𝐒𝐭𝐚𝐭𝐞)𝐍𝐮𝐦𝐁𝐨𝐨𝐥𝐒𝐭𝐚𝐭𝐞

a konfiguracje końcowe to 𝐒𝐭𝐚𝐭𝐞; aczkolwiek konfiguracje ze zbioru 𝐍𝐮𝐦𝐁𝐨𝐨𝐥 pełnią podobną rolę dla wyrażeń arytmetycznych i boolowskich jako konfiguracje końcowe dla instrukcji. Przypomnijmy, że 𝐒𝐭𝐦𝐭 oznacza zbiór instrukcji, I𝐒𝐭𝐦𝐭.

Zacznijmy od (chyba najprostszych) tranzycji dla stałych boolowskich i liczbowych:

l,sln,sn.

Podobnie jak poprzednio, zakładamy tutaj dla wygody, że 𝐍𝐮𝐦subseteq𝐄𝐱𝐩 oraz 𝐁𝐨𝐨𝐥𝐁𝐄𝐱𝐩. Pozwala nam to nie odróżniać stałych występujących w wyrażeniach a zatem pojawiających się po lewej stonie tranzycji mkrok od wartości im odpowiadających pojawiających się po prawej stronie.

Przejdźmy do spójników logicznych, powiedzmy b1b2. Ponieważ opisujemy teraz pojedyncze (małe) kroki składające się na wykonanie programu, musimy podać w jakiej kolejności będą się obliczać b1 i b2. Zacznijmy od strategii lewostronnej:

b1,sb'1,sb1b2,sb'1b2,sb2,sb'2,sl1b2,sl1b2,sl1l2,sl,s o ile l=l1l2

Możemy zaniechać obliczania b2 jeśli b1 oblicza się do false. Oto odpowiednio zmodyfikowane reguły:

b1,sb'1,sb1b2,sb'1b2,s𝐭𝐫𝐮𝐞b2,s𝐭𝐫𝐮𝐞,s𝐭𝐫𝐮𝐞b2,sb2,s

Analogicznie reguły prawostronne to:

b2,sb'2,sb1b2,sb1b'2,sb1𝐭𝐫𝐮𝐞,s𝐭𝐫𝐮𝐞,sb1𝐭𝐫𝐮𝐞,sb1,s

Reguły równoległe otrzymujemy jako sumę reguł lewo- i prawostronnych (w sumie 6 reguł). Zauważmy, że obliczanie wyrażeń b1 i b2 odbywa się teraz w twz. przeplocie: Pojedynczy krok polega na wykonaniu jednego kroku obliczenia b1 albo jednego kroku obliczenia b2. Zwróćmy też uwagę, że nasze tranzycje nie posiadają teraz własności determinizmu: wyrażenie b1b2 może wyewoluować w pojedyńczym kroku albo do b'1b2 albo do b1b'2. Na szczęście, końcowy wynik, do jakiego oblicza się wyrażenie jest zawsze taki sam, niezależnie od przeplotu.

Oto reguły dla negacji:

¬𝐭𝐫𝐮𝐞,s𝐭𝐫𝐮𝐞,s¬𝐭𝐫𝐮𝐞,s𝐭𝐫𝐮𝐞,sb,sb,s¬b,s¬b,s

Reguły dla e1e2 są następujące:

e1,se'1,se1e2,se'1e2,se2,se'2,se1e2,se1e'2,sn1n2,s𝐭𝐫𝐮𝐞,s o ile n1n2n1n2,s𝐭𝐫𝐮𝐞,s o ile n1>n2

Reguły powyższe zależą od semantyki wyrażen arytmetycznych. Zauważmy, że ponownie pozostawiliśmy dowolność jeśli chodzi o kolejność obliczania wyrażeń arytmetycznych e1 i e2.

Jako pierwszą z instrukcji rozważmy przypisanie. Najpierw obliczamy wyrażenie po prawej stronie przypisania, a gdy wyrażenie to wyewoluuje do stałej (obliczy się), modyfikujemy stan:

e,se,sx:=e,sx:=e,sx:=n,ss[xn]

Rozważmy teraz instrukcję warunkową i instrukcję pętli. Najpierw obliczamy wartość dozoru:

b,sb,s𝐢𝐟b𝐭𝐡𝐞𝐧I1𝐞𝐥𝐬𝐞I2,s𝐢𝐟b𝐭𝐡𝐞𝐧I1𝐞𝐥𝐬𝐞I2,sb,sb,s𝐰𝐡𝐢𝐥𝐞b𝐝𝐨I,s𝐰𝐡𝐢𝐥𝐞b𝐝𝐨I,s

a gdy dozór jest już obliczony, podejmujemy decyzję. W przypadku instrukcji warunkowej reguły są oczywiste:

𝐢𝐟𝐭𝐫𝐮𝐞𝐭𝐡𝐞𝐧I1𝐞𝐥𝐬𝐞I2,sI1,s𝐢𝐟𝐭𝐫𝐮𝐞𝐭𝐡𝐞𝐧I1𝐞𝐥𝐬𝐞I2,sI2,s

Gorzej jest w przypadku instrukcji pętli. Reguła mogłaby wyglądać tak:

𝐰𝐡𝐢𝐥𝐞𝐭𝐫𝐮𝐞𝐝𝐨I,sI;𝐰𝐡𝐢𝐥𝐞?𝐝𝐨I,s

ale nie wiemy już, jaki był dozór pętli (widzimy tylko wynik obliczenia tego dozoru w stanie s, czyli 𝐭𝐫𝐮𝐞). Możemy odwołać się do tranzytywnego domknięcia relacji (czyli w zadadzie do semantyki dużych kroków):

b,s*𝐭𝐫𝐮𝐞𝐰𝐡𝐢𝐥𝐞b𝐝𝐨I,sI;𝐰𝐡𝐢𝐥𝐞b𝐝𝐨I,sb,s*𝐭𝐫𝐮𝐞𝐰𝐡𝐢𝐥𝐞b𝐝𝐨I,ss

Takie rozwiązanie nie jest zatem czystą semantyką małych kroków. Istnieją inne możliwe rozwiązania w stylu małych kroków. Jedno z nich oparte jest na pomyśle, aby rozwinąc pętlę 𝐰𝐡𝐢𝐥𝐞, zanim obliczymy wartość dozoru b. Jedyną reguła dla pętli 𝐰𝐡𝐢𝐥𝐞 byłaby wtedy reguła:

𝐰𝐡𝐢𝐥𝐞b𝐝𝐨I,s𝐢𝐟b𝐭𝐡𝐞𝐧(I;𝐰𝐡𝐢𝐥𝐞b𝐝𝐨I)𝐞𝐥𝐬𝐞𝐬𝐤𝐢𝐩,s.

Dzięki temu obliczany warunek logiczny b jest zawsze jednorazowy. Znalezienie innych rozwiązań, np. opartych na rozszerzeniu składni, pozostawiamy dociekliwemu Czytelnikowi.

Reguły dla operacji arytmetycznych również pozostawiamy do napisania Czytelnikowi.



Zadanie 2

Rozszerzmy język Tiny o następujące dobrze znane konstrukcje iteracji:

I::=𝐫𝐞𝐩𝐞𝐚𝐭I𝐮𝐧𝐭𝐢𝐥b|𝐟𝐨𝐫x:=e1𝐭𝐨e2𝐝𝐨I|𝐝𝐨e𝐭𝐢𝐦𝐞𝐬I|𝐝𝐨I𝐰𝐡𝐢𝐥𝐞b

Napisz semantykę małych kroków dla powyższych konstrukcji.


Rozwiązanie

Instrukcja 𝐫𝐞𝐩𝐞𝐚𝐭

Zacznijmy od pętli 𝐫𝐞𝐩𝐞𝐚𝐭I𝐮𝐧𝐭𝐢𝐥b. Przyjrzyjmy się dwóm podejściom, które zastosowaliśmy dla pętli 𝐰𝐡𝐢𝐥𝐞 w poprzednim zadaniu. Po pierwsze rozwinięcie:

𝐫𝐞𝐩𝐞𝐚𝐭I𝐮𝐧𝐭𝐢𝐥b,sI;𝐢𝐟b𝐭𝐡𝐞𝐧(𝐫𝐞𝐩𝐞𝐚𝐭I𝐮𝐧𝐭𝐢𝐥b)𝐞𝐥𝐬𝐞𝐬𝐤𝐢𝐩,s.

Po drugie, spróbujmy odwołać się do * dla dozoru pętli b:

I,sI,s𝐫𝐞𝐩𝐞𝐚𝐭I𝐮𝐧𝐭𝐢𝐥b,s𝐫𝐞𝐩𝐞𝐚𝐭I𝐮𝐧𝐭𝐢𝐥b,s

I,ssb,s*𝐭𝐫𝐮𝐞𝐫𝐞𝐩𝐞𝐚𝐭I𝐮𝐧𝐭𝐢𝐥b,ssI,ssb,s*𝐭𝐫𝐮𝐞𝐫𝐞𝐩𝐞𝐚𝐭I𝐮𝐧𝐭𝐢𝐥b,s𝐫𝐞𝐩𝐞𝐚𝐭?𝐮𝐧𝐭𝐢𝐥b,s

Okazuje się, że jest jeszcze gorzej niż w przypadku pętli 𝐰𝐡𝐢𝐥𝐞: nie pamiętamy już, jaka była instrukcja wewnętrzna naszej pętli! Czyli takie podejście jest teraz nieskuteczne.


Instrukcja 𝐝𝐨e𝐭𝐢𝐦𝐞𝐬I

Pętla 𝐝𝐨e𝐭𝐢𝐦𝐞𝐬I, w stanie s, oznacza wykonianie instrukcji I n razy, gdzie n to wartość, do której oblicza się e w stanie s. Czyli najpierw obliczmy e przy pomocy reguły:

e,se,s𝐝𝐨e𝐭𝐢𝐦𝐞𝐬I,sDe𝐭𝐢𝐦𝐞𝐬I,s

która doprowadza nas do konfiguracji:

𝐝𝐨n𝐭𝐢𝐦𝐞𝐬I,s.

Teraz jest juz łatwo:

𝐝𝐨n𝐭𝐢𝐦𝐞𝐬I,sI;𝐝𝐨n1𝐭𝐢𝐦𝐞𝐬I,s o ile n>0

𝐝𝐨n𝐭𝐢𝐦𝐞𝐬I,ss o ile n=0.


Instrukcja 𝐟𝐨𝐫

W przypadku pętli 𝐟𝐨𝐫 przyjmijmy, że wartości wyrażeń e1 i e2 obliczane są przed pierwszą iteracją pętli. Dodatkowo ustalmy, że e1 będzie obliczone jako pierwsze. Czyli:

e1,se'1,s𝐟𝐨𝐫x=e1𝐭𝐨e2𝐝𝐨I,s𝐟𝐨𝐫x=e'1𝐭𝐨e2𝐝𝐨I,s

e2,se'2,s𝐟𝐨𝐫x=n𝐭𝐨e2𝐝𝐨I,s𝐟𝐨𝐫x=n𝐭𝐨e'2𝐝𝐨I,s

Zatem zakres zmiennej x mamy już obliczony, tzn. jesteśmy w konfiguracji

𝐟𝐨𝐫x=n1𝐭𝐨n2𝐝𝐨I,s.

Dalsze reguły mogą być podobne do reguł dla pętli 𝐝𝐨n𝐭𝐢𝐦𝐞𝐬I:

𝐟𝐨𝐫x=n1𝐭𝐨n2𝐝𝐨I,sx:=n1;I;𝐟𝐨𝐫x=n1+1𝐭𝐨n2𝐝𝐨I,s o ile n1n2

𝐟𝐨𝐫x=n1𝐭𝐨n2𝐝𝐨I,s𝐬𝐤𝐢𝐩,s o ile n1>n2


Zauważmy, wartość zmiennej x po zakończeniu pętli wynosi n2. Ponieważ nie zostało wyspecyfikowane jaka powinna być wartość tej zmiennej, taką semantykę uważamy za poprawną.


Semantykę dla 𝐝𝐨I𝐰𝐡𝐢𝐥𝐞b pozostawiamy Czytelnikowi jako proste ćwiczenie. Oczywiście minimalistyczne rozwiązanie to

𝐝𝐨I𝐰𝐡𝐢𝐥𝐞b,s𝐫𝐞𝐩𝐞𝐚𝐭I𝐮𝐧𝐭𝐢𝐥¬b,s



Kalkulator binarny

Zadanie 1

Rozważmy następujący język wyrażeń (liczby binarne z dodawaniem):

e::=ϵ|e0|e1|e1+e2

ϵ oznacza słowo puste, czyli np. ϵ1011 oznacza binarną liczbę 1011. Napisz semantykę operacyjną obliczającą wartość wyrażeń.


Rozwiązanie

Składnia wyrażeń pozwala na wygodny dostęp do najmniej znaczącego bitu liczby. Spróbujmy zatem zastosować metodę dodawania pisemnego:

e10+e20(e1+e2)0

e10+e21(e1+e2)1

e11+e20(e1+e2)1

Ale co zrobić z przeniesieniem?

e11+e21?

Podstawowy pomysł polega na potraktowaniu przeniesienia jak dodatkowego składnika:

e11+e21((e1+e2)+ϵ1)0

Zauważmy, że w składni dopuszcza się dowolne przeplatanie operatora dodawania i bitów 0,1. Tę dowolność wykorzystaliśmy właśnie w regułach powyżej. Gdyby nasz język ograniczyć tylko do składni

e::=b|e1+e2

b::=ϵ|b0|b1

(nazwijmy ją składnią ograniczoną) to powyższe reguły byłyby niepoprawne.

Zanim dopiszemy pozostałe reguły, określmy zbiór konfiguracji jako zbiór wyrażeń. Konfiguracje końcowe to wyrażenia bez operatora dodawania (liczby binarne). Nasz pomysł jest taki, że tranzycje stopniowo przesuwają operator dodawania w lewo, aż się go ostatecznie pozbędą.

Gdy obydwa składniki mają tyle samo cyfr, do zakończenia dodawania potrzebujemy reguły:

ϵ+ϵϵ.

Gdy jeden ze składników ma mniej cyfr niż drugi, potrzebujemy reguł:

ϵ+e0e0ϵ+e1e1

oraz ich odpowiedników:

e0+ϵe0e1+ϵe1.

Niestety, nie możemy użyć reguły przemienności:

e1+e2e2+e1

gdyż spowodowałaby ona możliwość pętlenia się, a zatem braku wyniku obliczenia.

Na koniec dodajemy typowe reguły opisujące jak krok podwyrażenia indukuje krok całego wyrażenia:

e1e'1e1+e2e'1+e2e2e'2e1+e2e1+e'2eee0e0eee1e1



Zadanie 2

Rozszerzmy składnię o jeden symbol p oznaczający przepełnienie:

e::=ϵ|p|e0|e1|e1+e2.

Na przykład p101 oznacza tę samą liczbę co ϵ101, ale z dodatkową informacją, że podczas jej obliczania nastąpiło przepełnienie. Rozumiemy przez to sytuację, gdy wynik ma więcej cyfr niż każdy z argumentów. Cyfry zero z lewej strony (najbardziej znaczące) również uważamy za pełnoprawne cyfry, nie należy ich usuwać ani dodawać nowych.

Napisz semantykę operacyjną obliczającą wyrażenie wraz z informacja o ewentualnym przepełnieniu. Wynik powinien byc poprawny przynajmniej dla wyrażeń w składni ograniczonej.

Rozwiązanie

Zadanie dotyczy w zasadzie składni ograniczonej, ale jako konfiguracji pośrednich będziemy zapewne potrzebować wyrażeń wykraczających poza tę składnię, np. (e1+e2)0, podobnie jak w poprzednim zadaniu. Zatem mamy tu do czynienia z typowym zabiegiem rozszerzania składni na użytek semantyki operacyjnej (z tym, że rozszerzenie jest dane z góry i nie musimy go wymyślać :)

Przyjmijmy, że konfiguracjami są dowolne wyrażenia, a konfiguracjami końcowymi wyrażenia bez operatora dodawania (ale teraz mogą to być np. wyrażenia postaci p100). Spróbujmy pozostawić wszystkie reguły z poprzedniego zadania. Dodamy tylko kilka nowych reguł, odpowiedzialnych za przepełnienie.

Zacznijmy od najprostszej sytuacji, gdy jeden ze składników ma mniej cyfr, i to ten właśnie składnik odnotował przepełnienie:

p+e0e0p+e1e1e0+pe0e1+pe1.

W takiej sytuacji oczywiście informacja o przepełnieniu zostaje wymazana. Jeśli przepełnienie zostało odnotowane w składniku o większej liczbie cyfr, to reguły

ϵ+e0e0ϵ+e1e1e0+ϵe0e1+ϵe1.

z poprzedniego zadania są wystarczające.

Rozważmy teraz przypadek, gdy obydwa składniki mają tę samą liczbę cyfr. Jeśli obydwa odnotowały przepełnienie, to oczywiście informacja ta powinna zostać zachowana:

p+pp.

Ale co należy zrobić, gdy tylko jeden ze składników odnotował przepełnienie? p+ϵ? Oczywiście, w tej sytuacji żadnego przepełnienia nie ma, ponieważ drugi składnik ma wystarczająco dużo cyfr by je przesłonić. Oto odpowiednie reguły:

p+ϵϵϵ+pϵ.

Na koniec zostało najważniejsze: kiedy powinniśmy generować sygnał o przepełnieniu? Przypomnijmy sobie reguły dla dadawania pisemnego w poprzednim zadaniu. Jeśli nie ma przeniesienia, to przepełnienie nie może powstać. Natomiast jeśli jest przeniesienie, to stanowi ono potencjalne przepełnienie. Odpowiednia reguła to

e11+e21((e1+e2)+p1)0.

Nowy sztuczny składnik p1 zawiera jakby na wszelki wypadek informacje o potencjalnym przepełnieniu. Jeśli którykolwiek z pozostałych składników e1 i e2 ma przynajmniej jedną cyfrę, to p zostanie usunięte. W przeciwnym wypadku symbol p i przetrwa i będzie poprawnie informował o sytuacji przepełnienia.



Zadania domowe

Zadanie 1

Podaj przykład wyrażenia, które nie policzy się ani przy użyciu strategii lewo- ani prawostronnej, a policzy się przy strategii równoległej.


Zadanie 2

Rozważ inną semantykę pętli 𝐟𝐨𝐫x=e1𝐭𝐨e2doI, w której wyrażenie e2 jest obliczane na nowo przed każdą iteracją pętli. Napisz reguły semantyczne dla tej instrukcji, nie odwołując się do innych istrukcji pętli.

Zadanie 3

Dodaj do wyrażeń binarnych operację odejmowania.


Zadanie 4

Zaproponuj semantykę przypisania równoległego

x1:=e1||||xn:=en

polegającego na obliczeniu najpierw wartości wyrażeń e1,,en a następnie na podstawieniu nowych wartości na zmienne x1,,xn.