868 lines
26 KiB
Coq
868 lines
26 KiB
Coq
(* term-project.v *)
|
|
(* LPP 2024 - CS3234 2023-2024, Sem2 *)
|
|
(* Olivier Danvy <danvy@yale-nus.edu.sg> *)
|
|
(* Version of 12 Apr 2024 *)
|
|
|
|
(* ********** *)
|
|
|
|
(* Three language processors for arithmetic expressions. *)
|
|
|
|
(*
|
|
group name:
|
|
|
|
student name:
|
|
e-mail address:
|
|
student ID number:
|
|
|
|
student name:
|
|
e-mail address:
|
|
student ID number:
|
|
|
|
student name:
|
|
e-mail address:
|
|
student ID number:
|
|
|
|
student name:
|
|
e-mail address:
|
|
student ID number:
|
|
|
|
student name:
|
|
e-mail address:
|
|
student ID number:
|
|
*)
|
|
|
|
(* ********** *)
|
|
|
|
Ltac fold_unfold_tactic name := intros; unfold name; fold name; reflexivity.
|
|
|
|
Require Import Arith Bool List String Ascii.
|
|
|
|
(* ***** *)
|
|
|
|
Check (1 =? 2).
|
|
Check Nat.eqb.
|
|
Check (Nat.eqb 1 2).
|
|
|
|
Check (1 <=? 2).
|
|
Check Nat.leb.
|
|
Check (Nat.leb 1 2).
|
|
|
|
Check (1 <? 2).
|
|
Check Nat.ltb.
|
|
Check (Nat.ltb 1 2).
|
|
|
|
(* caution: only use natural numbers up to 5000 -- caveat emptor *)
|
|
Definition string_of_nat (q0 : nat) : string :=
|
|
let s0 := String (ascii_of_nat (48 + (q0 mod 10))) EmptyString
|
|
in if q0 <? 10
|
|
then s0
|
|
else let q1 := q0 / 10
|
|
in let s1 := String (ascii_of_nat (48 + (q1 mod 10))) s0
|
|
in if q1 <? 10
|
|
then s1
|
|
else let q2 := q1 / 10
|
|
in let s2 := String (ascii_of_nat (48 + (q2 mod 10))) s1
|
|
in if q2 <? 10
|
|
then s2
|
|
else let q3 := q2 / 10
|
|
in let s2 := String (ascii_of_nat (48 + (q3 mod 10))) s2
|
|
in if q3 <? 10
|
|
then s2
|
|
else "00000".
|
|
|
|
Compute (string_of_nat 100).
|
|
(* ********** *)
|
|
|
|
Lemma fold_unfold_list_append_nil :
|
|
forall (V : Type)
|
|
(v2s : list V),
|
|
nil ++ v2s = v2s.
|
|
Proof.
|
|
fold_unfold_tactic List.app.
|
|
Qed.
|
|
|
|
Lemma fold_unfold_list_append_cons :
|
|
forall (V : Type)
|
|
(v1 : V)
|
|
(v1s' v2s : list V),
|
|
(v1 :: v1s') ++ v2s = v1 :: v1s' ++ v2s.
|
|
Proof.
|
|
fold_unfold_tactic List.app.
|
|
Qed.
|
|
|
|
(* ********** *)
|
|
|
|
(* Arithmetic expressions: *)
|
|
|
|
Inductive arithmetic_expression : Type :=
|
|
Literal : nat -> arithmetic_expression
|
|
| Plus : arithmetic_expression -> arithmetic_expression -> arithmetic_expression
|
|
| Minus : arithmetic_expression -> arithmetic_expression -> arithmetic_expression.
|
|
|
|
(* Source programs: *)
|
|
|
|
Inductive source_program : Type :=
|
|
Source_program : arithmetic_expression -> source_program.
|
|
|
|
(* ********** *)
|
|
|
|
(* Semantics: *)
|
|
|
|
Inductive expressible_value : Type :=
|
|
Expressible_nat : nat -> expressible_value
|
|
| Expressible_msg : string -> expressible_value.
|
|
|
|
(* ********** *)
|
|
|
|
(* With the assistance of Ravern *)
|
|
Definition specification_of_evaluate (evaluate : arithmetic_expression -> expressible_value) :=
|
|
(forall n : nat,
|
|
evaluate (Literal n) = Expressible_nat n)
|
|
/\
|
|
((forall (ae1 ae2 : arithmetic_expression)
|
|
(s1 : string),
|
|
evaluate ae1 = Expressible_msg s1 ->
|
|
evaluate (Plus ae1 ae2) = Expressible_msg s1)
|
|
/\
|
|
(forall (ae1 ae2 : arithmetic_expression)
|
|
(n1 : nat)
|
|
(s2 : string),
|
|
evaluate ae1 = Expressible_nat n1 ->
|
|
evaluate ae2 = Expressible_msg s2 ->
|
|
evaluate (Plus ae1 ae2) = Expressible_msg s2)
|
|
/\
|
|
(forall (ae1 ae2 : arithmetic_expression)
|
|
(n1 n2 : nat),
|
|
evaluate ae1 = Expressible_nat n1 ->
|
|
evaluate ae2 = Expressible_nat n2 ->
|
|
evaluate (Plus ae1 ae2) = Expressible_nat (n1 + n2)))
|
|
/\
|
|
((forall (ae1 ae2 : arithmetic_expression)
|
|
(s1 : string),
|
|
evaluate ae1 = Expressible_msg s1 ->
|
|
evaluate (Minus ae1 ae2) = Expressible_msg s1)
|
|
/\
|
|
(forall (ae1 ae2 : arithmetic_expression)
|
|
(n1 : nat)
|
|
(s2 : string),
|
|
evaluate ae1 = Expressible_nat n1 ->
|
|
evaluate ae2 = Expressible_msg s2 ->
|
|
evaluate (Minus ae1 ae2) = Expressible_msg s2)
|
|
/\
|
|
(forall (ae1 ae2 : arithmetic_expression)
|
|
(n1 n2 : nat),
|
|
evaluate ae1 = Expressible_nat n1 ->
|
|
evaluate ae2 = Expressible_nat n2 ->
|
|
n1 <? n2 = true ->
|
|
evaluate (Minus ae1 ae2) = Expressible_msg (String.append "numerical underflow: -" (string_of_nat (n2 - n1))))
|
|
/\
|
|
(forall (ae1 ae2 : arithmetic_expression)
|
|
(n1 n2 : nat),
|
|
evaluate ae1 = Expressible_nat n1 ->
|
|
evaluate ae2 = Expressible_nat n2 ->
|
|
n1 <? n2 = false ->
|
|
evaluate (Minus ae1 ae2) = Expressible_nat (n1 - n2))).
|
|
|
|
Theorem there_is_at_most_one_evaluate_function :
|
|
forall f g : arithmetic_expression -> expressible_value,
|
|
specification_of_evaluate f ->
|
|
specification_of_evaluate g ->
|
|
forall ae : arithmetic_expression,
|
|
f ae = g ae.
|
|
Proof.
|
|
intros f g.
|
|
unfold specification_of_evaluate.
|
|
intros [S_f_Literal
|
|
[[S_f_Plus_msg [S_f_Plus_msg2 S_f_Plus_nat]]
|
|
[S_f_Minus_msg [S_f_Minus_msg2 [S_f_Minus_nat_lt S_f_Minus_nat_gte]]]]].
|
|
intros [S_g_Literal
|
|
[[S_g_Plus_msg [S_g_Plus_msg2 S_g_Plus_nat]]
|
|
[S_g_Minus_msg [S_g_Minus_msg2 [S_g_Minus_nat_lt S_g_Minus_nat_gte]]]]].
|
|
intros ae.
|
|
induction ae as [n | ae1 IHae1 ae2 IHae2 | ae1 IHae1 ae2 IHae2 ].
|
|
- rewrite -> S_g_Literal.
|
|
exact (S_f_Literal n).
|
|
- Check (f (Plus ae1 ae2) ).
|
|
case (f ae1) as [n1 | s1] eqn:H_f_ae1.
|
|
case (f ae2) as [n2 | s2] eqn:H_f_ae2.
|
|
+ rewrite -> (S_f_Plus_nat ae1 ae2 n1 n2 H_f_ae1 H_f_ae2).
|
|
rewrite -> (S_g_Plus_nat ae1 ae2 n1 n2 (eq_sym IHae1) (eq_sym IHae2)).
|
|
reflexivity.
|
|
+ rewrite -> (S_f_Plus_msg2 ae1 ae2 n1 s2 H_f_ae1 H_f_ae2).
|
|
rewrite -> (S_g_Plus_msg2 ae1 ae2 n1 s2 (eq_sym IHae1) (eq_sym IHae2)).
|
|
reflexivity.
|
|
+ rewrite -> (S_f_Plus_msg ae1 ae2 s1 H_f_ae1).
|
|
rewrite -> (S_g_Plus_msg ae1 ae2 s1 (eq_sym IHae1)).
|
|
reflexivity.
|
|
- case (f ae1) as [n1 | s1] eqn:H_f_ae1.
|
|
case (f ae2) as [n2 | s2] eqn:H_f_ae2.
|
|
+ case (n1 <? n2) eqn:H_n1_lt_n2.
|
|
* rewrite -> (S_f_Minus_nat_lt ae1 ae2 n1 n2 H_f_ae1 H_f_ae2 H_n1_lt_n2).
|
|
rewrite -> (S_g_Minus_nat_lt ae1 ae2 n1 n2 (eq_sym IHae1) (eq_sym IHae2) H_n1_lt_n2).
|
|
reflexivity.
|
|
* rewrite -> (S_f_Minus_nat_gte ae1 ae2 n1 n2 H_f_ae1 H_f_ae2 H_n1_lt_n2).
|
|
rewrite -> (S_g_Minus_nat_gte ae1 ae2 n1 n2 (eq_sym IHae1) (eq_sym IHae2) H_n1_lt_n2).
|
|
reflexivity.
|
|
+ rewrite -> (S_f_Minus_msg2 ae1 ae2 n1 s2 H_f_ae1 H_f_ae2).
|
|
rewrite -> (S_g_Minus_msg2 ae1 ae2 n1 s2 (eq_sym IHae1) (eq_sym IHae2)).
|
|
reflexivity.
|
|
+ rewrite -> (S_f_Minus_msg ae1 ae2 s1 H_f_ae1).
|
|
rewrite -> (S_g_Minus_msg ae1 ae2 s1 (eq_sym IHae1)).
|
|
reflexivity.
|
|
Qed.
|
|
|
|
Check arithmetic_expression_ind.
|
|
|
|
Lemma nat_ind2 :
|
|
forall P : nat -> Prop,
|
|
P 0 ->
|
|
P 1 ->
|
|
(forall k : nat, P k -> P (S k) -> P (S (S k))) ->
|
|
forall n : nat, P n.
|
|
Proof.
|
|
intros P H_P0 H_P1 H_PSS [ | [ | n'' ] ].
|
|
- exact H_P0.
|
|
- exact H_P1.
|
|
- assert (both : forall m : nat, P m /\ P(S m)).
|
|
{ intro m.
|
|
induction m as [ | m' [ IHm' IHSm' ] ].
|
|
- exact (conj H_P0 H_P1).
|
|
- split.
|
|
+ exact IHSm'.
|
|
+ exact (H_PSS m' IHm' IHSm' ).
|
|
}
|
|
destruct (both n'') as [Pn'' PSn''].
|
|
exact (H_PSS n'' Pn'' PSn'').
|
|
Qed.
|
|
|
|
Fixpoint evaluate (ae : arithmetic_expression) : expressible_value :=
|
|
match ae with
|
|
| Literal n => Expressible_nat n
|
|
| Plus ae1 ae2 =>
|
|
match evaluate ae1 with
|
|
| Expressible_msg s1 => Expressible_msg s1
|
|
| Expressible_nat n1 =>
|
|
match evaluate ae2 with
|
|
| Expressible_msg s2 => Expressible_msg s2
|
|
| Expressible_nat n2 => Expressible_nat (n1 + n2)
|
|
end
|
|
end
|
|
| Minus ae1 ae2 =>
|
|
match evaluate ae1 with
|
|
| Expressible_msg s1 => Expressible_msg s1
|
|
| Expressible_nat n1 =>
|
|
match evaluate ae2 with
|
|
| Expressible_msg s2 => Expressible_msg s2
|
|
| Expressible_nat n2 =>
|
|
if n1 <? n2
|
|
then Expressible_msg (String.append "numerical underflow: -" (string_of_nat (n2 - n1)))
|
|
else Expressible_nat (n1 - n2)
|
|
end
|
|
end
|
|
end.
|
|
|
|
Lemma fold_unfold_evaluate_Literal :
|
|
forall (n : nat),
|
|
evaluate (Literal n) = Expressible_nat n.
|
|
Proof.
|
|
intros.
|
|
fold_unfold_tactic evaluate.
|
|
Qed.
|
|
|
|
Lemma fold_unfold_evaluate_Plus :
|
|
forall (ae1 ae2 : arithmetic_expression),
|
|
evaluate (Plus ae1 ae2) =
|
|
match (evaluate ae1) with
|
|
| Expressible_msg s1 => Expressible_msg s1
|
|
| Expressible_nat n1 =>
|
|
match (evaluate ae2) with
|
|
| Expressible_msg s2 => Expressible_msg s2
|
|
| Expressible_nat n2 => Expressible_nat (n1 + n2)
|
|
end
|
|
end.
|
|
|
|
Proof.
|
|
intros.
|
|
fold_unfold_tactic evaluate.
|
|
Qed.
|
|
|
|
Lemma fold_unfold_evaluate_Minus :
|
|
forall (ae1 ae2 : arithmetic_expression),
|
|
evaluate (Minus ae1 ae2) =
|
|
match (evaluate ae1) with
|
|
| Expressible_msg s1 => Expressible_msg s1
|
|
| Expressible_nat n1 =>
|
|
match (evaluate ae2) with
|
|
| Expressible_msg s2 => Expressible_msg s2
|
|
| Expressible_nat n2 =>
|
|
if n1 <? n2
|
|
then Expressible_msg (String.append "numerical underflow: -" (string_of_nat (n2 - n1)))
|
|
else Expressible_nat (n1 - n2)
|
|
end
|
|
end.
|
|
Proof.
|
|
intros.
|
|
fold_unfold_tactic evaluate.
|
|
Qed.
|
|
|
|
Theorem evaluate_satisfies_the_specification_of_evaluate :
|
|
specification_of_evaluate evaluate.
|
|
Proof.
|
|
unfold specification_of_evaluate.
|
|
split.
|
|
exact fold_unfold_evaluate_Literal.
|
|
split.
|
|
split.
|
|
{ intros ae1 ae2 s1 H_ae1.
|
|
rewrite -> fold_unfold_evaluate_Plus.
|
|
rewrite -> H_ae1.
|
|
reflexivity. }
|
|
split.
|
|
{ intros ae1 ae2 n1 s2 H_ae1 H_ae2.
|
|
rewrite -> fold_unfold_evaluate_Plus.
|
|
rewrite -> H_ae1.
|
|
rewrite -> H_ae2.
|
|
reflexivity. }
|
|
{ intros ae1 ae2 n1 n2 H_ae1 H_ae2.
|
|
rewrite -> fold_unfold_evaluate_Plus.
|
|
rewrite -> H_ae1.
|
|
rewrite -> H_ae2.
|
|
reflexivity. }
|
|
split.
|
|
{ intros ae1 ae2 s1 H_ae1.
|
|
rewrite -> fold_unfold_evaluate_Minus.
|
|
rewrite -> H_ae1.
|
|
reflexivity. }
|
|
split.
|
|
{ intros ae1 ae2 n1 s2 H_ae1 H_ae2.
|
|
rewrite -> fold_unfold_evaluate_Minus.
|
|
rewrite -> H_ae1.
|
|
rewrite -> H_ae2.
|
|
reflexivity. }
|
|
split.
|
|
{ intros ae1 ae2 n1 n2 H_ae1 H_ae2 H_n1_le_n2.
|
|
rewrite -> fold_unfold_evaluate_Minus.
|
|
rewrite -> H_ae1.
|
|
rewrite -> H_ae2.
|
|
rewrite -> H_n1_le_n2.
|
|
reflexivity. }
|
|
intros ae1 ae2 n1 n2 H_ae1 H_ae2 H_n1_le_n2.
|
|
rewrite -> fold_unfold_evaluate_Minus.
|
|
rewrite -> H_ae1.
|
|
rewrite -> H_ae2.
|
|
rewrite -> H_n1_le_n2.
|
|
reflexivity.
|
|
Qed.
|
|
|
|
Definition specification_of_interpret (interpret : source_program -> expressible_value) :=
|
|
forall evaluate : arithmetic_expression -> expressible_value,
|
|
specification_of_evaluate evaluate ->
|
|
forall ae : arithmetic_expression,
|
|
interpret (Source_program ae) = evaluate ae.
|
|
|
|
Definition there_is_at_most_one_interpret :
|
|
forall (f g : source_program -> expressible_value),
|
|
(specification_of_interpret f) ->
|
|
(specification_of_interpret g) ->
|
|
forall (ae : arithmetic_expression),
|
|
f (Source_program ae) = g (Source_program ae).
|
|
Proof.
|
|
intros f g.
|
|
unfold specification_of_interpret.
|
|
intros S_f S_g.
|
|
intros ae.
|
|
rewrite -> (S_g evaluate evaluate_satisfies_the_specification_of_evaluate ae).
|
|
exact (S_f evaluate evaluate_satisfies_the_specification_of_evaluate ae).
|
|
Qed.
|
|
|
|
(* Task 1:
|
|
a. time permitting, prove that each of the definitions above specifies at most one function;
|
|
b. implement these two functions; and
|
|
c. verify that each of your functions satisfies its specification.
|
|
*)
|
|
|
|
|
|
Definition interpret (sp : source_program) : expressible_value :=
|
|
match sp with
|
|
| Source_program ae => evaluate ae
|
|
end.
|
|
|
|
Theorem interpret_satisfies_the_specification_of_interpret :
|
|
specification_of_interpret interpret.
|
|
Proof.
|
|
unfold specification_of_interpret.
|
|
intros eval S_evaluate ae.
|
|
unfold interpret.
|
|
exact (there_is_at_most_one_evaluate_function
|
|
evaluate eval
|
|
evaluate_satisfies_the_specification_of_evaluate S_evaluate ae).
|
|
Qed.
|
|
|
|
(* ********** *)
|
|
|
|
(* Byte-code instructions: *)
|
|
|
|
Inductive byte_code_instruction : Type :=
|
|
PUSH : nat -> byte_code_instruction
|
|
| ADD : byte_code_instruction
|
|
| SUB : byte_code_instruction.
|
|
|
|
(* Target programs: *)
|
|
|
|
Inductive target_program : Type :=
|
|
Target_program : list byte_code_instruction -> target_program.
|
|
|
|
(* Data stack: *)
|
|
|
|
Definition data_stack := list nat.
|
|
|
|
(* ********** *)
|
|
|
|
Inductive result_of_decoding_and_execution : Type :=
|
|
OK : data_stack -> result_of_decoding_and_execution
|
|
| KO : string -> result_of_decoding_and_execution.
|
|
|
|
|
|
(* Task 2:
|
|
implement decode_execute
|
|
*)
|
|
|
|
|
|
|
|
|
|
|
|
Definition specification_of_decode_execute (decode_execute : byte_code_instruction -> data_stack -> result_of_decoding_and_execution) :=
|
|
(* given a nat n and a data_stack ds,
|
|
evaluating
|
|
decode_execute (PUSH n) ds
|
|
should successfully return a stack where n is pushed on ds *)
|
|
(forall (n : nat)
|
|
(ds : data_stack),
|
|
decode_execute (PUSH n) ds = OK (n :: ds))
|
|
/\
|
|
(* given a data stack ds that contains at least two nats,
|
|
evaluating
|
|
decode_execute ADD ds
|
|
should
|
|
- pop these two nats off the data stack,
|
|
- add them,
|
|
- push the result of the addition on the data stack, and
|
|
- successfully return the resulting data stack *)
|
|
(forall (n1 n2 : nat)
|
|
(ds : data_stack),
|
|
decode_execute ADD (n2 :: n1 :: ds) = OK ((n1 + n2) :: ds)
|
|
)
|
|
/\
|
|
(* if the data stack does not contain at least two nats,
|
|
evaluating
|
|
decode_execute ADD ds
|
|
should return the error message "ADD: stack underflow" *)
|
|
(forall (ds : data_stack),
|
|
decode_execute ADD nil = KO ("ADD: stack underflow")
|
|
)
|
|
/\
|
|
(forall (n : nat)
|
|
(ds : data_stack),
|
|
decode_execute ADD nil = KO ("ADD: stack underflow")
|
|
)
|
|
/\
|
|
(* given a data stack ds that contains at least two nats,
|
|
evaluating
|
|
decode_execute SUB ds
|
|
should
|
|
- pop these two nats off the data stack,
|
|
- subtract one from the other if the result is non-negative,
|
|
- push the result of the subtraction on the data stack, and
|
|
- successfully return the resulting data stack *)
|
|
(forall (n1 n2 : nat)
|
|
(ds : data_stack),
|
|
n1 <? n2 = true ->
|
|
decode_execute SUB (n2 :: n1 :: ds) = KO (String.append "numerical underflow: -" (string_of_nat (n2 - n1))))
|
|
/\
|
|
(* if the data stack contain at least two nats
|
|
and
|
|
if subtracting one nat from the other gives a negative result,
|
|
evaluating
|
|
decode_execute SUB ds
|
|
should return the same error message as the evaluator *)
|
|
(forall (n1 n2 : nat)
|
|
(ds : data_stack),
|
|
n1 <? n2 = false ->
|
|
decode_execute SUB (n2 :: n1 :: ds) = OK ((n2 - n1) :: ds))
|
|
/\
|
|
(* if the data stack does not contain at least two nats
|
|
evaluating
|
|
decode_execute SUB ds
|
|
should return the error message "SUB: stack underflow" *)
|
|
(forall (ds : data_stack),
|
|
decode_execute SUB nil = KO ("ADD: stack underflow")
|
|
)
|
|
/\
|
|
(forall (n : nat)
|
|
(ds : data_stack),
|
|
decode_execute SUB nil = KO ("ADD: stack underflow")
|
|
).
|
|
|
|
Definition decode_execute (bci : byte_code_instruction) (ds : data_stack) : result_of_decoding_and_execution :=
|
|
match bci with
|
|
| PUSH n => OK (n :: ds)
|
|
| ADD =>
|
|
match ds with
|
|
| n2 :: n1 :: ds' => OK ((n1 + n2) :: ds')
|
|
| _ => KO "ADD: stack underflow"
|
|
end
|
|
| SUB =>
|
|
match ds with
|
|
| n2 :: n1 :: ds' =>
|
|
if n1 <? n2
|
|
then KO (String.append "numerical underflow: -" (string_of_nat (n2 - n1)))
|
|
else OK ((n2 - n1) :: ds')
|
|
| _ => KO "ADD: stack underflow"
|
|
end
|
|
end.
|
|
|
|
Theorem decode_execute_satisfies_the_specification_of_decode_execute :
|
|
specification_of_decode_execute decode_execute.
|
|
Proof.
|
|
unfold specification_of_decode_execute.
|
|
split; unfold decode_execute.
|
|
{ reflexivity. }
|
|
split.
|
|
{ reflexivity. }
|
|
split.
|
|
{ reflexivity. }
|
|
split.
|
|
{ reflexivity. }
|
|
split.
|
|
{ intros n1 n2 ds n1_lt_n2.
|
|
rewrite -> n1_lt_n2.
|
|
reflexivity. }
|
|
split.
|
|
{ intros n1 n2 ds n1_lt_n2.
|
|
rewrite -> n1_lt_n2.
|
|
reflexivity. }
|
|
split.
|
|
{ reflexivity. }
|
|
reflexivity.
|
|
Qed.
|
|
|
|
(* ********** *)
|
|
|
|
(* Specification of the virtual machine: *)
|
|
|
|
Definition specification_of_fetch_decode_execute_loop (fetch_decode_execute_loop : list byte_code_instruction -> data_stack -> result_of_decoding_and_execution) :=
|
|
(forall ds : data_stack,
|
|
fetch_decode_execute_loop nil ds = OK ds)
|
|
/\
|
|
(forall (bci : byte_code_instruction)
|
|
(bcis' : list byte_code_instruction)
|
|
(ds ds' : data_stack),
|
|
decode_execute bci ds = OK ds' ->
|
|
fetch_decode_execute_loop (bci :: bcis') ds =
|
|
fetch_decode_execute_loop bcis' ds')
|
|
/\
|
|
(forall (bci : byte_code_instruction)
|
|
(bcis' : list byte_code_instruction)
|
|
(ds : data_stack)
|
|
(s : string),
|
|
decode_execute bci ds = KO s ->
|
|
fetch_decode_execute_loop (bci :: bcis') ds =
|
|
KO s).
|
|
|
|
Theorem there_is_at_most_one_fetch_decode_execute_loop :
|
|
forall (f g : list byte_code_instruction -> data_stack -> result_of_decoding_and_execution),
|
|
specification_of_fetch_decode_execute_loop f ->
|
|
specification_of_fetch_decode_execute_loop g ->
|
|
forall (bcis : list byte_code_instruction)
|
|
(ds : data_stack),
|
|
f bcis ds = g bcis ds.
|
|
Proof.
|
|
intros f g.
|
|
unfold specification_of_fetch_decode_execute_loop.
|
|
intros [S_f_nil [S_f_OK S_f_KO]] [S_g_nil [S_g_OK S_g_KO]] bcis.
|
|
induction bcis as [ | bci bcis' IHbcis' ]; intros ds.
|
|
- rewrite -> S_g_nil.
|
|
exact (S_f_nil ds).
|
|
- case (decode_execute bci ds) as [ ds' | s ] eqn:H_decode_execute.
|
|
+ rewrite -> (S_f_OK bci bcis' ds ds' H_decode_execute).
|
|
rewrite -> (S_g_OK bci bcis' ds ds' H_decode_execute).
|
|
exact (IHbcis' ds').
|
|
+ rewrite -> (S_g_KO bci bcis' ds s H_decode_execute).
|
|
exact (S_f_KO bci bcis' ds s H_decode_execute).
|
|
Qed.
|
|
|
|
Fixpoint fetch_decode_execute_loop (bcis : list byte_code_instruction) (ds : data_stack) : result_of_decoding_and_execution :=
|
|
match bcis with
|
|
| nil => OK ds
|
|
| bci :: bcis' =>
|
|
match (decode_execute bci ds) with
|
|
| OK ds' => fetch_decode_execute_loop bcis' ds'
|
|
| KO s => KO s
|
|
end
|
|
end.
|
|
|
|
Lemma fold_unfold_fetch_decode_execute_loop_nil :
|
|
forall (ds : data_stack),
|
|
fetch_decode_execute_loop nil ds = OK ds.
|
|
Proof.
|
|
fold_unfold_tactic fetch_decode_execute_loop.
|
|
Qed.
|
|
|
|
Lemma fold_unfold_fetch_decode_execute_loop_cons :
|
|
forall (bci : byte_code_instruction)
|
|
(bcis : list byte_code_instruction)
|
|
(ds : data_stack),
|
|
fetch_decode_execute_loop (bci :: bcis) ds =
|
|
match (decode_execute bci ds) with
|
|
| OK ds' => fetch_decode_execute_loop bcis ds'
|
|
| KO s => KO s
|
|
end.
|
|
Proof.
|
|
fold_unfold_tactic fetch_decode_execute_loop.
|
|
Qed.
|
|
|
|
|
|
Theorem fetch_decode_execute_loop_satifies_the_specification :
|
|
specification_of_fetch_decode_execute_loop fetch_decode_execute_loop.
|
|
Proof.
|
|
unfold specification_of_fetch_decode_execute_loop.
|
|
split.
|
|
{ intros ds.
|
|
exact (fold_unfold_fetch_decode_execute_loop_nil ds). }
|
|
split.
|
|
{ intros bci bcis' ds ds' H_decode_execute.
|
|
rewrite -> fold_unfold_fetch_decode_execute_loop_cons.
|
|
rewrite -> H_decode_execute.
|
|
reflexivity.
|
|
}
|
|
intros bci bcis' ds s H_decode_execute.
|
|
rewrite -> fold_unfold_fetch_decode_execute_loop_cons.
|
|
rewrite -> H_decode_execute.
|
|
reflexivity.
|
|
Qed.
|
|
|
|
(* Task 3:
|
|
(DONE) a. time permitting, prove that the definition above specifies at most one function;
|
|
(DONE) b. implement this function; and
|
|
(DONE) c. verify that your function satisfies the specification.
|
|
*)
|
|
|
|
(* ********** *)
|
|
|
|
(* Food for thought:
|
|
For any lists of byte-code instructions bci1s and bci2s,
|
|
and for any data stack ds,
|
|
characterize the execution of the concatenation of bci1s and bci2s (i.e., bci1s ++ bci2s) with ds
|
|
in terms of executing bci1s and of executing bci2s.
|
|
*)
|
|
|
|
|
|
Proposition about_fetch_decode_execute_loop :
|
|
forall (bci1s bci2s : list byte_code_instruction)
|
|
(ds : data_stack),
|
|
(forall (ds' : data_stack),
|
|
fetch_decode_execute_loop bci1s ds = OK ds' ->
|
|
fetch_decode_execute_loop (bci1s ++ bci2s) ds = fetch_decode_execute_loop bci2s (ds'))
|
|
/\
|
|
(forall (s : string),
|
|
fetch_decode_execute_loop bci1s ds = KO s ->
|
|
fetch_decode_execute_loop (bci1s ++ bci2s) ds = KO s).
|
|
Proof.
|
|
intros bci1s bci2s.
|
|
induction bci1s as [ | bci bci1s' IHbci1s' ]; intros ds.
|
|
- split.
|
|
+ intros ds' H_fdel.
|
|
rewrite -> fold_unfold_list_append_nil.
|
|
rewrite -> fold_unfold_fetch_decode_execute_loop_nil in H_fdel.
|
|
injection H_fdel as ds_eq_ds'.
|
|
rewrite -> ds_eq_ds'.
|
|
reflexivity.
|
|
+ intros s H_fdel.
|
|
rewrite -> fold_unfold_fetch_decode_execute_loop_nil in H_fdel.
|
|
discriminate H_fdel.
|
|
- split.
|
|
+ intros ds' H_fdel.
|
|
rewrite -> fold_unfold_list_append_cons.
|
|
rewrite -> fold_unfold_fetch_decode_execute_loop_cons.
|
|
rewrite -> fold_unfold_fetch_decode_execute_loop_cons in H_fdel.
|
|
case (decode_execute bci ds) as [ ds1 | s ] eqn:H_decode_execute.
|
|
* destruct (IHbci1s' ds1) as [IH_ok _].
|
|
exact (IH_ok ds' H_fdel).
|
|
* discriminate H_fdel.
|
|
+ intros s H_fdel.
|
|
rewrite -> fold_unfold_list_append_cons.
|
|
rewrite -> fold_unfold_fetch_decode_execute_loop_cons.
|
|
rewrite -> fold_unfold_fetch_decode_execute_loop_cons in H_fdel.
|
|
case (decode_execute bci ds) as [ ds1 | s1 ] eqn:H_decode_execute.
|
|
* destruct (IHbci1s' ds1) as [_ IH_ko].
|
|
exact (IH_ko s H_fdel).
|
|
* exact H_fdel.
|
|
Qed.
|
|
|
|
(* ********** *)
|
|
|
|
Definition specification_of_run (run : target_program -> expressible_value) :=
|
|
forall fetch_decode_execute_loop : list byte_code_instruction -> data_stack -> result_of_decoding_and_execution,
|
|
specification_of_fetch_decode_execute_loop fetch_decode_execute_loop ->
|
|
(forall (bcis : list byte_code_instruction),
|
|
fetch_decode_execute_loop bcis nil = OK nil ->
|
|
run (Target_program bcis) = Expressible_msg "no result on the data stack")
|
|
/\
|
|
(forall (bcis : list byte_code_instruction)
|
|
(n : nat),
|
|
fetch_decode_execute_loop bcis nil = OK (n :: nil) ->
|
|
run (Target_program bcis) = Expressible_nat n)
|
|
/\
|
|
(forall (bcis : list byte_code_instruction)
|
|
(n n' : nat)
|
|
(ds'' : data_stack),
|
|
fetch_decode_execute_loop bcis nil = OK (n :: n' :: ds'') ->
|
|
run (Target_program bcis) = Expressible_msg "too many results on the data stack")
|
|
/\
|
|
(forall (bcis : list byte_code_instruction)
|
|
(s : string),
|
|
fetch_decode_execute_loop bcis nil = KO s ->
|
|
run (Target_program bcis) = Expressible_msg s).
|
|
|
|
|
|
Theorem there_is_at_most_one_run_function :
|
|
forall (f g : target_program -> expressible_value),
|
|
specification_of_run f ->
|
|
specification_of_run g ->
|
|
forall (t: target_program),
|
|
f t = g t.
|
|
Proof.
|
|
intros f g.
|
|
unfold specification_of_run.
|
|
intros S_f S_g [bcis].
|
|
case (fetch_decode_execute_loop bcis nil) as [ds | s] eqn:H_fdel.
|
|
- destruct (S_f fetch_decode_execute_loop fetch_decode_execute_loop_satifies_the_specification) as [S_f_nil _].
|
|
destruct (S_g fetch_decode_execute_loop fetch_decode_execute_loop_satifies_the_specification) as [S_g_nil _].
|
|
case ds as [ | n ds' ] eqn:H_ds.
|
|
+ rewrite -> (S_g_nil bcis H_fdel).
|
|
exact (S_f_nil bcis H_fdel).
|
|
+ Check (S_f_nil bcis).
|
|
|
|
|
|
(* Task 4:
|
|
a. time permitting, prove that the definition above specifies at most one function;
|
|
b. implement this function; and
|
|
c. verify that your function satisfies the specification.
|
|
*)
|
|
|
|
(* ********** *)
|
|
|
|
Definition specification_of_compile_aux (compile_aux : arithmetic_expression -> list byte_code_instruction) :=
|
|
(forall n : nat,
|
|
compile_aux (Literal n) = PUSH n :: nil)
|
|
/\
|
|
(forall ae1 ae2 : arithmetic_expression,
|
|
compile_aux (Plus ae1 ae2) = (compile_aux ae1) ++ (compile_aux ae2) ++ (ADD :: nil))
|
|
/\
|
|
(forall ae1 ae2 : arithmetic_expression,
|
|
compile_aux (Minus ae1 ae2) = (compile_aux ae1) ++ (compile_aux ae2) ++ (SUB :: nil)).
|
|
|
|
(* Task 5:
|
|
a. time permitting, prove that the definition above specifies at most one function;
|
|
b. implement this function using list concatenation, i.e., ++; and
|
|
c. verify that your function satisfies the specification.
|
|
*)
|
|
|
|
Definition specification_of_compile (compile : source_program -> target_program) :=
|
|
forall compile_aux : arithmetic_expression -> list byte_code_instruction,
|
|
specification_of_compile_aux compile_aux ->
|
|
forall ae : arithmetic_expression,
|
|
compile (Source_program ae) = Target_program (compile_aux ae).
|
|
|
|
(* Task 6:
|
|
a. time permitting, prove that the definition above specifies at most one function;
|
|
b. implement this function; and
|
|
c. verify that your function satisfies the specification.
|
|
*)
|
|
|
|
(* ********** *)
|
|
|
|
(* Task 7:
|
|
implement an alternative compiler
|
|
using an auxiliary function with an accumulator
|
|
and that does not use ++ but :: instead,
|
|
and prove that it satisfies the specification.
|
|
|
|
Subsidiary question:
|
|
Are your compiler and your alternative compiler equivalent?
|
|
How can you tell?
|
|
*)
|
|
|
|
(* ********** *)
|
|
|
|
(* Task 8 (the capstone):
|
|
Prove that interpreting an arithmetic expression gives the same result
|
|
as first compiling it and then executing the compiled program.
|
|
*)
|
|
|
|
(* ********** *)
|
|
|
|
(* Byte-code verification:
|
|
the following verifier symbolically executes a byte-code program
|
|
to check whether no underflow occurs during execution
|
|
and whether when the program completes,
|
|
there is one and one only natural number on top of the stack.
|
|
The second argument of verify_aux is a natural number
|
|
that represents the size of the stack.
|
|
*)
|
|
|
|
Fixpoint verify_aux (bcis : list byte_code_instruction) (n : nat) : option nat :=
|
|
match bcis with
|
|
| nil =>
|
|
Some n
|
|
| bci :: bcis' =>
|
|
match bci with
|
|
| PUSH _ =>
|
|
verify_aux bcis' (S n)
|
|
| _ =>
|
|
match n with
|
|
| S (S n') =>
|
|
verify_aux bcis' (S n')
|
|
| _ =>
|
|
None
|
|
end
|
|
end
|
|
end.
|
|
|
|
Definition verify (p : target_program) : bool :=
|
|
match p with
|
|
| Target_program bcis =>
|
|
match verify_aux bcis 0 with
|
|
| Some n =>
|
|
match n with
|
|
| 1 =>
|
|
true
|
|
| _ =>
|
|
false
|
|
end
|
|
| _ =>
|
|
false
|
|
end
|
|
end.
|
|
|
|
(* Task 9:
|
|
Prove that the compiler emits code
|
|
that is accepted by the verifier.
|
|
*)
|
|
|
|
(*
|
|
Theorem the_compiler_emits_well_behaved_code :
|
|
forall ae : arithmetic_expression,
|
|
verify (compile ae) = true.
|
|
Proof.
|
|
Abort.
|
|
*)
|
|
|
|
(* Subsidiary question:
|
|
What is the practical consequence of this theorem?
|
|
*)
|
|
|
|
(* ********** *)
|
|
|
|
(* end of term-project.v *)
|