-
Notifications
You must be signed in to change notification settings - Fork 0
/
Projet.v
343 lines (284 loc) · 11.7 KB
/
Projet.v
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
(* Projet SVP - Edwin ANSARI *)
Set Warnings "-notation-overridden,-parsing,-deprecated-hint-without-locality".
From Coq Require Import Bool.Bool.
From Coq Require Import Init.Nat.
From Coq Require Import Arith.Arith.
From Coq Require Import Arith.EqNat. Import Nat.
From Coq Require Import Lia.
From Coq Require Import Lists.List. Import ListNotations.
From Coq Require Import Strings.String.
From PLF Require Import Maps Imp.
Module BreakImp.
(** **** Exercise: 4 stars, advanced (break_imp)
Imperative languages like C and Java often include a [break] or
similar statement for interrupting the execution of loops. In this
exercise we consider how to add [break] to Imp and "downgrading"
the while statement into the loop statement (equivalanet to while
true). First, we need to change the language of commands *)
Inductive com : Type :=
| CSkip
| CBreak (* <--- NEW *)
| CAsgn (x : string) (a : aexp)
| CSeq (c1 c2 : com)
| CIf (b : bexp) (c1 c2 : com)
| CLoop (c : com). (* <--- DOWNGRADED *)
Notation "'break'" := CBreak (in custom com at level 0).
Notation "'skip'" :=
CSkip (in custom com at level 0) : com_scope.
Notation "x := y" :=
(CAsgn x y)
(in custom com at level 0, x constr at level 0,
y at level 85, no associativity) : com_scope.
Notation "x ; y" :=
(CSeq x y)
(in custom com at level 90, right associativity) : com_scope.
Notation "'if' x 'then' y 'else' z 'end'" :=
(CIf x y z)
(in custom com at level 89, x at level 99,
y at level 99, z at level 99) : com_scope.
Notation "'loop' y 'endloop'" :=
(CLoop y)
(in custom com at level 89, y at level 99) : com_scope.
(** Next, we need to define the behavior of [break]. Informally,
whenever [break] is executed in a sequence of commands, it stops
the execution of that sequence and signals that the innermost
enclosing loop should terminate. (If there aren't any
enclosing loops, then the whole program simply terminates.) The
final state should be the same as the one in which the [break]
statement was executed.
One important point is what to do when there are multiple loops
enclosing a given [break]. In those cases, [break] should only
terminate the _innermost_ loop. Thus, after executing the
following...
X := 0;
Y := 1;
loop
if ~(0 = Y) then
loop
break
endloop;
X := 1;
Y := Y - 1
else break
endloop
... the value of [X] should be [1], and not [0].
One way of expressing this behavior is to add another parameter to
the evaluation relation that specifies whether evaluation of a
command executes a [break] statement: *)
Inductive result : Type :=
| SContinue
| SBreak.
Reserved Notation "st '=[' c ']=>' st' '/' s"
(at level 40, c custom com at level 99, st' constr at next level).
(** Intuitively, [st =[ c ]=> st' / s] means that, if [c] is started in
state [st], then it terminates in state [st'] and either signals
that the innermost surrounding loop (or the whole program) should
exit immediately ([s = SBreak]) or that execution should continue
normally ([s = SContinue]).
The definition of the "[st =[ c ]=> st' / s]" relation is very
similar to the one we gave above for the regular evaluation
relation ([st =[ c ]=> st']) -- we just need to handle the
termination signals appropriately:
- If the command is [skip], then the state doesn't change and
execution of any enclosing loop can continue normally.
- If the command is [break], the state stays unchanged but we
signal a [SBreak].
- If the command is an assignment, then we update the binding for
that variable in the state accordingly and signal that execution
can continue normally.
- If the command is of the form [if b then c1 else c2 end], then
the state is updated as in the original semantics of Imp, except
that we also propagate the signal from the execution of
whichever branch was taken.
(** [] *)
- If the command is a sequence [c1 ; c2], we first execute
[c1]. If this yields a [SBreak], we skip the execution of [c2]
and propagate the [SBreak] signal to the surrounding context;
the resulting state is the same as the one obtained by
executing [c1] alone. Otherwise, we execute [c2] on the state
obtained after executing [c1], and propagate the signal
generated there.
- Finally, for a loop of the form [loop c end], the semantics is
as follows: The only difference is that we execute [c] and check
the signal that it raises. If that signal is [SContinue], then
the execution proceeds (the loop iterates once again). Otherwise,
we stop the execution of the loop, and the resulting state is
the same as the one resulting from the execution of the current
iteration. In either case, since [break] only terminates the
innermost loop, [loop] signals [SContinue]. *)
(** Based on the above description, complete the definition of the
[ceval] relation. *)
Inductive ceval : com -> state -> result -> state -> Prop :=
| E_Skip : forall st,
st =[CSkip]=> st / SContinue
| E_Break : forall st,
st =[CBreak]=> st / SBreak
| E_Asgn : forall st a n x,
aeval st a = n ->
st =[ x := a ]=> (x !-> n ; st) / SContinue
| E_Seq_Continue : forall c1 c2 st st' st'' s',
st =[ c1 ]=> st' / SContinue ->
st' =[ c2 ]=> st'' / s'->
st =[ c1 ; c2 ]=> st'' / s'
| E_Seq_Break : forall c1 c2 st st',
st =[ c1 ]=> st' / SBreak ->
st =[ c1 ; c2 ]=> st' / SBreak
| E_IfTrue : forall st st' s' b c1 c2,
beval st b = true ->
st =[ c1 ]=> st' / s'->
st =[ if b then c1 else c2 end]=> st' / s'
| E_IfFalse : forall st st' s' b c1 c2,
beval st b = false ->
st =[ c2 ]=> st' / s'->
st =[ if b then c1 else c2 end]=> st' / s'
| E_Loop_Continue : forall st st' st'' s' c,
st =[c]=> st' / SContinue->
st' =[ loop c endloop ]=> st'' / s' ->
st =[ loop c endloop ]=> st'' / s'
| E_Loop_Break : forall st st' c,
st =[c]=> st' / SBreak->
st =[ loop c endloop ]=> st' / SContinue
where "st '=[' c ']=>' st' '/' s" := (ceval c st s st').
(** Now prove the following properties of your definition of [ceval]: *)
Theorem break_ignore : forall c st st' s,
st =[ break; c ]=> st' / s ->
st = st'.
Proof.
intros.
inversion H; subst.
- inversion H2.
- inversion H5. reflexivity.
Qed.
Theorem loop_continue : forall c st st' s,
st =[ loop c endloop ]=> st' / s ->
s = SContinue.
Proof.
intros.
remember <{loop c endloop}> as p.
induction H; auto; try discriminate.
Qed.
Theorem while_stops_on_break : forall c st st',
st =[ c ]=> st' / SBreak ->
st =[ loop c endloop ]=> st' / SContinue.
Proof.
intros.
inversion H; subst; apply E_Loop_Break; assumption.
Qed.
Theorem seq_continue : forall c1 c2 st st' st'',
st =[ c1 ]=> st' / SContinue ->
st' =[ c2 ]=> st'' / SContinue ->
st =[ c1 ; c2 ]=> st'' / SContinue.
Proof.
intros.
apply (E_Seq_Continue c1 c2 st st'); assumption.
Qed.
Theorem seq_stops_on_break : forall c1 c2 st st',
st =[ c1 ]=> st' / SBreak ->
st =[ c1 ; c2 ]=> st' / SBreak.
Proof.
intros.
eapply E_Seq_Break. assumption.
Qed.
(** [] *)
(** **** Exercise: 3 stars, advanced, optional (while_break_true) *)
Theorem while_break_true : forall c st st',
st =[ loop c endloop ]=> st' / SContinue ->
exists st'', st'' =[ c ]=> st' / SBreak.
Proof.
intros.
remember <{loop c endloop}> as p.
induction H; try discriminate; auto.
- inversion Heqp. subst. exists st. assumption.
Qed.
(** **** Exercise: 4 stars, advanced, optional (ceval_deterministic) *)
Theorem ceval_deterministic: forall (c:com) st st1 st2 s1 s2,
st =[ c ]=> st1 / s1 ->
st =[ c ]=> st2 / s2 ->
st1 = st2 /\ s1 = s2.
Proof.
intros.
generalize dependent st1.
generalize dependent s1.
induction H0; intros.
- try (inversion H; auto).
- try (inversion H; auto).
- inversion H. inversion H0. subst. auto.
- inversion H; subst.
+ specialize (IHceval1 _ _ H2). destruct IHceval1. subst. auto.
+ specialize (IHceval1 _ _ H5). destruct IHceval1. subst. discriminate H1.
- inversion H; subst.
+ specialize (IHceval _ _ H3). destruct IHceval. discriminate H2.
+ specialize (IHceval _ _ H6). destruct IHceval. auto.
- inversion H1; subst.
+ specialize (IHceval _ _ H9). destruct IHceval. subst. auto.
+ rewrite H in H8. discriminate H8.
- inversion H1; subst.
+ rewrite H in H8. discriminate H8.
+ specialize (IHceval _ _ H9). destruct IHceval. subst. auto.
- inversion H; subst.
+ specialize (IHceval1 _ _ H1). destruct IHceval1. subst. auto.
+ specialize (IHceval1 _ _ H1). destruct IHceval1. discriminate H2.
- inversion H; subst.
+ specialize (IHceval _ _ H2). destruct IHceval. discriminate H4.
+ specialize (IHceval _ _ H2). destruct IHceval. subst. auto.
Qed.
End BreakImp.
(* From now on regular Imp has a nice syntax, but not the variant with loop and break. *)
(* Let us define the translation from Imp to the variant: *)
Fixpoint traduct_while (pw: Imp.com): BreakImp.com :=
match pw with
| <{ skip }> => BreakImp.CSkip
| <{ x := a }> => BreakImp.CAsgn x a
| <{ c1; c2 }> => BreakImp.CSeq (traduct_while c1) (traduct_while c2)
| <{ if b then c1 else c2 end }> => BreakImp.CIf b (traduct_while c1) (traduct_while c2)
| <{ while b do c end }> =>
BreakImp.CLoop (BreakImp.CIf b (traduct_while c) BreakImp.CBreak)
end.
(* Now let us prove that we can simulate a regular imp program with a
simple translated program in the variant. *)
Theorem equiv_while_loopbreak: forall (c:Imp.com)(c':BreakImp.com) st st1,
c' = traduct_while c ->
(st =[ c ]=> st1) ->
BreakImp.ceval c' st BreakImp.SContinue st1.
Proof.
intros.
induction H0.
- simpl in H. subst. apply BreakImp.E_Skip.
- simpl in H. subst. apply BreakImp.E_Asgn. reflexivity.
- admit.
- simpl in H. subst. apply (BreakImp.E_IfTrue).
+ assumption.
+ inversion H1.
* simpl in *. apply BreakImp.E_Skip.
* simpl in *. apply BreakImp.E_Asgn. assumption.
* simpl in *. subst.
Admitted.
(** **** BONUS Exercise: 3 stars, standard, optional (short_circuit)
Most modern programming languages use a "short-circuit" evaluation
rule for boolean [and]: to evaluate [BAnd b1 b2], first evaluate
[b1]. If it evaluates to [false], then the entire [BAnd]
expression evaluates to [false] immediately, without evaluating
[b2]. Otherwise, [b2] is evaluated to determine the result of the
[BAnd] expression.
Write an alternate version of [beval] that performs short-circuit
evaluation of [BAnd] in this manner, and prove that it is
equivalent to [beval]. (N.b. This is only true because expression
evaluation in Imp is rather simple. In a bigger language where
evaluating an expression might diverge, the short-circuiting [BAnd]
would _not_ be equivalent to the original, since it would make more
programs terminate.) *)
Fixpoint beval_lazy (st : state) (b : bexp) : bool :=
match b with
| <{true}> => true
| <{false}> => false
| <{a1 = a2}> => (aeval st a1) =? (aeval st a2)
| <{a1 <= a2}> => (aeval st a1) <=? (aeval st a2)
| <{~ b1}> => negb (beval_lazy st b1)
| <{b1 && b2}> => if (beval_lazy st b1) then (beval_lazy st b2) else false
end.
Theorem equiv_beval_lazy: forall (st : state) (b : bexp),
beval st b = beval_lazy st b.
Proof.
intros.
induction b; trivial.
Qed.