From Coq Require Import Lists.List. Import ListNotations.
From Coq Require Import Strings.String.
V následujících úkolech bude využita níže uvedená implementace aritmetických
výrazů.
Jak vidíte, ve výrazech se mohou vyskytovat proměnné. Tyto proměnné jsou
reprezentovány datovým typem var, který je zde implementován pomocí
datového typu string.
Typ state reprezentuje aktuální přiřazení hodnot proměnným.
Pro jednoduchost jsou proměnným přiřazovány pouze hodnoty typu nat.
Definition var := string.
Definition var_eqb : var → var → bool := String.eqb.
Definition state := var → nat.
Definition empty_st (_ : var) := 0.
Definition update_state (st : state) (x : var) (v : nat) :=
fun x' ⇒ if var_eqb x x' then v else st x'.
Notation "x '!->' v ';' st" := (update_state st x v)
(at level 100, v at next level, right associativity).
Notation "x '!->' v" := (x !-> v ; empty_st) (at level 100).
Definice aritmetických výrazů:
Inductive aexp : Type :=
| ANum (n : nat)
| AId (x : var)
| APlus (a1 a2 : aexp)
| AMinus (a1 a2 : aexp)
| AMult (a1 a2 : aexp).
Definition X : var := "X"%string.
Definition Y : var := "Y"%string.
Definice koercí a uživatelsky definovaných notací pro usnadnění
zápisu aritmetických výrazů:
Coercion AId : var >-> aexp.
Coercion ANum : nat >-> aexp.
Declare Custom Entry com.
Declare Scope com_scope.
Notation "<{ e }>" := e (at level 0, e custom com at level 99) : com_scope.
Notation "( x )" := x (in custom com, x at level 99) : com_scope.
Notation "x" := x (in custom com at level 0, x constr at level 0) : com_scope.
Notation "x + y" := (APlus x y) (in custom com at level 50, left associativity).
Notation "x - y" := (AMinus x y) (in custom com at level 50, left associativity).
Notation "x * y" := (AMult x y) (in custom com at level 40, left associativity).
Open Scope com_scope.
Funkce pro vyhodnocení hodnoty aritmetického výrazu:
Fixpoint aeval (st : state) (a : aexp) : nat :=
match a with
| ANum n ⇒ n
| AId x ⇒ st x
| <{a1 + a2}> ⇒ (aeval st a1) + (aeval st a2)
| <{a1 - a2}> ⇒ (aeval st a1) - (aeval st a2)
| <{a1 × a2}> ⇒ (aeval st a1) × (aeval st a2)
end.
Příklad A: 1 bod
(2*3)+(3*(4-2))by byl zapsán jako
2 3 * 3 4 2 - * +a vyhodnocen následujícím způsobem (kde program, který je vyhodnocován, je zobrazen vpravo a obsah zásobníku vlevo):
[ ] | 2 3 * 3 4 2 - * + [2] | 3 * 3 4 2 - * + [3, 2] | * 3 4 2 - * + [6] | 3 4 2 - * + [3, 6] | 4 2 - * + [4, 3, 6] | 2 - * + [2, 4, 3, 6] | - * + [2, 3, 6] | * + [6, 6] | + [12] |Cílem tohoto cvičení je napsat malý kompilátor, který přeloží výrazy typu aexp na instrukce zásobníkového stroje.
- SPush n: Uloží číslo n na zásobník.
- SLoad x: Načte hodnotu proměnné x z aktuálního stavu a uloží ji na zásobník.
- SPlus: Odebere z vrcholu zásobníku dvě čísla, sečte je a výsledek uloží na zásobník.
- SMinus: Podobné, ale odečte první číslo od druhého.
- SMult: Podobné, ale čísla vynásobí.
Inductive sinstr : Type :=
| SPush (n : nat)
| SLoad (x : var)
| SPlus
| SMinus
| SMult.
Napište funkci, která vyhodnotí programy ve výše popsaném zásobníkovém
jazyce. Tato funkce dostane jako vstup aktuální stav přiřazující
hodnoty proměnným, počáteční obsah zásobníku, který je reprezentován
jako seznam tvořený čísly (vrchol zásobníku odpovídá začátku seznamu),
a program reprezentovaný jako seznam tvořený jednotlivými instrukcemi.
Funkce by měla vrátit jako výsledek seznam reprezentující obsah zásobníku
po vykonání daného programu. Otestujte vaši funkci na níže uvedených
příkladech.
Všimněte si, že není specifikováno, co se má udělat v případě, kdy
se má provést instrukce SPlus, SMinus nebo SMult a zásobník
obsahuje méně než dva prvky. Z určitého pohledu se dá říci, že na tom
nezáleží, protože korektní kompilátor nikdy nevyprodukuje takový
chybně utvořený program. Pro jednoduchost bude asi nejsnadnější
takovou problematickou instrukci přeskočit a pokračovat vykonáváním
následujících instrukcí.
Fixpoint s_execute (st : state) (stack : list nat)
(prog : list sinstr)
: list nat
. Admitted.
Check s_execute.
Example s_execute1 :
s_execute empty_st []
[SPush 5; SPush 3; SPush 1; SMinus]
= [2; 5].
Admitted.
Example s_execute2 :
s_execute (X !-> 3) [3;4]
[SPush 4; SLoad X; SMult; SPlus]
= [15; 4].
Admitted.
☐
Příklad B: 1 bod
Fixpoint s_compile (e : aexp) : list sinstr
. Admitted.
Poté, co definujete funkci s_compile, dokažte následující tvrzení, abyste
ověřili, že vaše funkce funguje správně.
Example s_compile1 :
s_compile <{ X - (2 × Y) }>
= [SLoad X; SPush 2; SLoad Y; SMult; SMinus].
Admitted.
☐
Příklad C: 1 bod
Theorem execute_app : ∀ st p1 p2 stack,
s_execute st stack (p1 ++ p2) = s_execute st (s_execute st stack p1) p2.
Proof.
Admitted.
☐
Příklad D: 2 body
Lemma s_compile_correct_aux : ∀ st e stack,
s_execute st stack (s_compile e) = aeval st e :: stack.
Proof.
Admitted.
Hlavní teorém by pak měl být velmi snadno odvoditelným důsledkem výše
uvedeného lemmatu.
Theorem s_compile_correct : ∀ (st : state) (e : aexp),
s_execute st [] (s_compile e) = [ aeval st e ].
Proof.
Admitted.
☐
This page has been generated by coqdoc