comparison src/StackMachine.v @ 206:3f4576f15130

Revising for 8.2 through first big example
author Adam Chlipala <adamc@hcoop.net>
date Fri, 06 Nov 2009 16:44:06 -0500
parents f05514cc6c0d
children a7be5d9a2fd4
comparison
equal deleted inserted replaced
205:f05514cc6c0d 206:3f4576f15130
1 (* Copyright (c) 2008, Adam Chlipala 1 (* Copyright (c) 2008-2009, Adam Chlipala
2 * 2 *
3 * This work is licensed under a 3 * This work is licensed under a
4 * Creative Commons Attribution-Noncommercial-No Derivative Works 3.0 4 * Creative Commons Attribution-Noncommercial-No Derivative Works 3.0
5 * Unported License. 5 * Unported License.
6 * The license text is available at: 6 * The license text is available at:
17 17
18 18
19 (** %\chapter{Some Quick Examples}% *) 19 (** %\chapter{Some Quick Examples}% *)
20 20
21 21
22 (** I will start off by jumping right in to a fully-worked set of examples, building certified compilers from increasingly complicated source languages to stack machines. We will meet a few useful tactics and see how they can be used in manual proofs, and we will also see how easily these proofs can be automated instead. I assume that you have installed Coq and Proof General. The code in this book is tested with Coq version 8.1pl3, though parts may work with other versions. 22 (** I will start off by jumping right in to a fully-worked set of examples, building certified compilers from increasingly complicated source languages to stack machines. We will meet a few useful tactics and see how they can be used in manual proofs, and we will also see how easily these proofs can be automated instead. I assume that you have installed Coq and Proof General. The code in this book is tested with Coq version 8.2pl1, though parts may work with other versions.
23 23
24 As always, you can step through the source file %\texttt{%#<tt>#StackMachine.v#</tt>#%}% for this chapter interactively in Proof General. Alternatively, to get a feel for the whole lifecycle of creating a Coq development, you can enter the pieces of source code in this chapter in a new %\texttt{%#<tt>#.v#</tt>#%}% file in an Emacs buffer. If you do the latter, include a line [Require Import List Tactics.] at the start of the file, to match some code hidden in this rendering of the chapter source, and be sure to run the Coq binary %\texttt{%#<tt>#coqtop#</tt>#%}% with the command-line argument %\texttt{%#<tt>#-I SRC#</tt>#%}%, where %\texttt{%#<tt>#SRC#</tt>#%}% is the path to a directory containing the source for this book. In either case, you will need to run %\texttt{%#<tt>#make#</tt>#%}% in the root directory of the source distribution for the book before getting started. If you have installed Proof General properly, it should start automatically when you visit a %\texttt{%#<tt>#.v#</tt>#%}% buffer in Emacs. 24 As always, you can step through the source file %\texttt{%#<tt>#StackMachine.v#</tt>#%}% for this chapter interactively in Proof General. Alternatively, to get a feel for the whole lifecycle of creating a Coq development, you can enter the pieces of source code in this chapter in a new %\texttt{%#<tt>#.v#</tt>#%}% file in an Emacs buffer. If you do the latter, include a line [Require Import List Tactics.] at the start of the file, to match some code hidden in this rendering of the chapter source, and be sure to run the Coq binary %\texttt{%#<tt>#coqtop#</tt>#%}% with the command-line argument %\texttt{%#<tt>#-I SRC#</tt>#%}%, where %\texttt{%#<tt>#SRC#</tt>#%}% is the path to a directory containing the source for this book. In either case, you will need to run %\texttt{%#<tt>#make#</tt>#%}% in the root directory of the source distribution for the book before getting started. If you have installed Proof General properly, it should start automatically when you visit a %\texttt{%#<tt>#.v#</tt>#%}% buffer in Emacs.
25 25
26 There are some minor headaches associated with getting Proof General to pass the proper command line arguments to the %\texttt{%#<tt>#coqtop#</tt>#%}% program. The best way to add settings that will be shared by many source files is to add a custom variable setting to your %\texttt{%#<tt>#.emacs#</tt>#%}% file, like this: 26 There are some minor headaches associated with getting Proof General to pass the proper command line arguments to the %\texttt{%#<tt>#coqtop#</tt>#%}% program. The best way to add settings that will be shared by many source files is to add a custom variable setting to your %\texttt{%#<tt>#.emacs#</tt>#%}% file, like this:
27 %\begin{verbatim}%#<pre>#(custom-set-variables 27 %\begin{verbatim}%#<pre>#(custom-set-variables
28 ... 28 ...
29 '(coq-prog-args '("-impredicative-set" "-I" "/path/to/cpdt/src")) 29 '(coq-prog-args '("-I" "/path/to/cpdt/src"))
30 ... 30 ...
31 )#</pre>#%\end{verbatim}% 31 )#</pre>#%\end{verbatim}%
32 The extra arguments demonstrated here are the proper choices for working with the code for this book. The ellipses stand for other Emacs customization settings you may already have. It can be helpful to save several alternate sets of flags in your %\texttt{%#<tt>#.emacs#</tt>#%}% file, with all but one commented out within the %\texttt{%#<tt>#custom-set-variables#</tt>#%}% block at any given time. 32 The extra arguments demonstrated here are the proper choices for working with the code for this book. The ellipses stand for other Emacs customization settings you may already have. It can be helpful to save several alternate sets of flags in your %\texttt{%#<tt>#.emacs#</tt>#%}% file, with all but one commented out within the %\texttt{%#<tt>#custom-set-variables#</tt>#%}% block at any given time.
33 33
34 With Proof General, the portion of a buffer that Coq has processed is highlighted in some way, like being given a blue background. You step through Coq source files by positioning the point at the position you want Coq to run to and pressing C-C C-RET. This can be used both for normal step-by-step coding, by placing the point inside some command past the end of the highlighted region; and for undoing, by placing the point inside the highlighted region. *) 34 With Proof General, the portion of a buffer that Coq has processed is highlighted in some way, like being given a blue background. You step through Coq source files by positioning the point at the position you want Coq to run to and pressing C-C C-RET. This can be used both for normal step-by-step coding, by placing the point inside some command past the end of the highlighted region; and for undoing, by placing the point inside the highlighted region. *)
142 | arg1 :: arg2 :: s' => Some ((binopDenote b) arg1 arg2 :: s') 142 | arg1 :: arg2 :: s' => Some ((binopDenote b) arg1 arg2 :: s')
143 | _ => None 143 | _ => None
144 end 144 end
145 end. 145 end.
146 146
147 (** With [instrDenote] defined, it is easy to define a function [progDenote], which iterates application of [instrDenote] through a whole program. *) 147 (** With [instrDenote] defined, it is easy to define a function [progDenote], which iterates application of [instrDenote] through a whole program.
148 148
149 [[
149 Fixpoint progDenote (p : prog) (s : stack) {struct p} : option stack := 150 Fixpoint progDenote (p : prog) (s : stack) {struct p} : option stack :=
150 match p with 151 match p with
151 | nil => Some s 152 | nil => Some s
152 | i :: p' => 153 | i :: p' =>
153 match instrDenote i s with 154 match instrDenote i s with
154 | None => None 155 | None => None
155 | Some s' => progDenote p' s' 156 | Some s' => progDenote p' s'
156 end 157 end
157 end. 158 end.
158 159
159 (** There is one interesting difference compared to our previous example of a [Fixpoint]. This recursive function takes two arguments, [p] and [s]. It is critical for the soundness of Coq that every program terminate, so a shallow syntactic termination check is imposed on every recursive function definition. One of the function parameters must be designated to decrease monotonically across recursive calls. That is, every recursive call must use a version of that argument that has been pulled out of the current argument by some number of [match] expressions. [expDenote] has only one argument, so we did not need to specify which of its arguments decreases. For [progDenote], we resolve the ambiguity by writing [{struct p}] to indicate that argument [p] decreases structurally. *) 160 ]]
161
162 There is one interesting difference compared to our previous example of a [Fixpoint]. This recursive function takes two arguments, [p] and [s]. It is critical for the soundness of Coq that every program terminate, so a shallow syntactic termination check is imposed on every recursive function definition. One of the function parameters must be designated to decrease monotonically across recursive calls. That is, every recursive call must use a version of that argument that has been pulled out of the current argument by some number of [match] expressions. [expDenote] has only one argument, so we did not need to specify which of its arguments decreases. For [progDenote], we resolve the ambiguity by writing [{struct p}] to indicate that argument [p] decreases structurally.
163
164 Recent versions of Coq will also infer a termination argument, so that we may write simply: *)
165
166 Fixpoint progDenote (p : prog) (s : stack) : option stack :=
167 match p with
168 | nil => Some s
169 | i :: p' =>
170 match instrDenote i s with
171 | None => None
172 | Some s' => progDenote p' s'
173 end
174 end.
160 175
161 176
162 (** ** Translation *) 177 (** ** Translation *)
163 178
164 (** Our compiler itself is now unsurprising. [++] is the list concatenation operator from the Coq standard library. *) 179 (** Our compiler itself is now unsurprising. [++] is the list concatenation operator from the Coq standard library. *)
171 186
172 187
173 (** Before we set about proving that this compiler is correct, we can try a few test runs, using our sample programs from earlier. *) 188 (** Before we set about proving that this compiler is correct, we can try a few test runs, using our sample programs from earlier. *)
174 189
175 Eval simpl in compile (Const 42). 190 Eval simpl in compile (Const 42).
176 (** [[ 191 (** [= IConst 42 :: nil : prog] *)
177 = IConst 42 :: nil : prog 192
178 ]] *)
179 Eval simpl in compile (Binop Plus (Const 2) (Const 2)). 193 Eval simpl in compile (Binop Plus (Const 2) (Const 2)).
180 (** [[ 194 (** [= IConst 2 :: IConst 2 :: IBinop Plus :: nil : prog] *)
181 = IConst 2 :: IConst 2 :: IBinop Plus :: nil : prog 195
182 ]] *)
183 Eval simpl in compile (Binop Times (Binop Plus (Const 2) (Const 2)) (Const 7)). 196 Eval simpl in compile (Binop Times (Binop Plus (Const 2) (Const 2)) (Const 7)).
184 (** [[ 197 (** [= IConst 7 :: IConst 2 :: IConst 2 :: IBinop Plus :: IBinop Times :: nil : prog] *)
185 = IConst 7 :: IConst 2 :: IConst 2 :: IBinop Plus :: IBinop Times :: nil : prog
186 ]] *)
187 198
188 (** We can also run our compiled programs and check that they give the right results. *) 199 (** We can also run our compiled programs and check that they give the right results. *)
189 200
190 Eval simpl in progDenote (compile (Const 42)) nil. 201 Eval simpl in progDenote (compile (Const 42)) nil.
191 (** [[ 202 (** [= Some (42 :: nil) : option stack] *)
192 = Some (42 :: nil) : option stack 203
193 ]] *)
194 Eval simpl in progDenote (compile (Binop Plus (Const 2) (Const 2))) nil. 204 Eval simpl in progDenote (compile (Binop Plus (Const 2) (Const 2))) nil.
195 (** [[ 205 (** [= Some (4 :: nil) : option stack] *)
196 = Some (4 :: nil) : option stack 206
197 ]] *)
198 Eval simpl in progDenote (compile (Binop Times (Binop Plus (Const 2) (Const 2)) (Const 7))) nil. 207 Eval simpl in progDenote (compile (Binop Times (Binop Plus (Const 2) (Const 2)) (Const 7))) nil.
199 (** [[ 208 (** [= Some (28 :: nil) : option stack] *)
200 = Some (28 :: nil) : option stack
201 ]] *)
202 209
203 210
204 (** ** Translation Correctness *) 211 (** ** Translation Correctness *)
205 212
206 (** We are ready to prove that our compiler is implemented correctly. We can use a new vernacular command [Theorem] to start a correctness proof, in terms of the semantics we defined earlier: *) 213 (** We are ready to prove that our compiler is implemented correctly. We can use a new vernacular command [Theorem] to start a correctness proof, in terms of the semantics we defined earlier: *)
212 (* begin thide *) 219 (* begin thide *)
213 220
214 (** Though a pencil-and-paper proof might clock out at this point, writing "by a routine induction on [e]," it turns out not to make sense to attack this proof directly. We need to use the standard trick of %\textit{%#<i>#strengthening the induction hypothesis#</i>#%}%. We do that by proving an auxiliary lemma: 221 (** Though a pencil-and-paper proof might clock out at this point, writing "by a routine induction on [e]," it turns out not to make sense to attack this proof directly. We need to use the standard trick of %\textit{%#<i>#strengthening the induction hypothesis#</i>#%}%. We do that by proving an auxiliary lemma:
215 *) 222 *)
216 223
217 Lemma compile_correct' : forall e p s, progDenote (compile e ++ p) s = progDenote p (expDenote e :: s). 224 Lemma compile_correct' : forall e p s,
225 progDenote (compile e ++ p) s = progDenote p (expDenote e :: s).
218 226
219 (** After the period in the [Lemma] command, we are in %\textit{%#<i>#the interactive proof-editing mode#</i>#%}%. We find ourselves staring at this ominous screen of text: 227 (** After the period in the [Lemma] command, we are in %\textit{%#<i>#the interactive proof-editing mode#</i>#%}%. We find ourselves staring at this ominous screen of text:
220 228
221 [[ 229 [[
222 1 subgoal 230 1 subgoal
223 231
224 ============================ 232 ============================
225 forall (e : exp) (p : list instr) (s : stack), 233 forall (e : exp) (p : list instr) (s : stack),
226 progDenote (compile e ++ p) s = progDenote p (expDenote e :: s) 234 progDenote (compile e ++ p) s = progDenote p (expDenote e :: s)
235
227 ]] 236 ]]
228 237
229 Coq seems to be restating the lemma for us. What we are seeing is a limited case of a more general protocol for describing where we are in a proof. We are told that we have a single subgoal. In general, during a proof, we can have many pending subgoals, each of which is a logical proposition to prove. Subgoals can be proved in any order, but it usually works best to prove them in the order that Coq chooses. 238 Coq seems to be restating the lemma for us. What we are seeing is a limited case of a more general protocol for describing where we are in a proof. We are told that we have a single subgoal. In general, during a proof, we can have many pending subgoals, each of which is a logical proposition to prove. Subgoals can be proved in any order, but it usually works best to prove them in the order that Coq chooses.
230 239
231 Next in the output, we see our single subgoal described in full detail. There is a double-dashed line, above which would be our free variables and hypotheses, if we had any. Below the line is the conclusion, which, in general, is to be proved from the hypotheses. 240 Next in the output, we see our single subgoal described in full detail. There is a double-dashed line, above which would be our free variables and hypotheses, if we had any. Below the line is the conclusion, which, in general, is to be proved from the hypotheses.
249 [[ 258 [[
250 subgoal 2 is: 259 subgoal 2 is:
251 forall (s : stack) (p : list instr), 260 forall (s : stack) (p : list instr),
252 progDenote (compile (Binop b e1 e2) ++ p) s = 261 progDenote (compile (Binop b e1 e2) ++ p) s =
253 progDenote p (expDenote (Binop b e1 e2) :: s) 262 progDenote p (expDenote (Binop b e1 e2) :: s)
263
254 ]] 264 ]]
255 265
256 The first and current subgoal is displayed with the double-dashed line below free variables and hypotheses, while later subgoals are only summarized with their conclusions. We see an example of a free variable in the first subgoal; [n] is a free variable of type [nat]. The conclusion is the original theorem statement where [e] has been replaced by [Const n]. In a similar manner, the second case has [e] replaced by a generalized invocation of the [Binop] expression constructor. We can see that proving both cases corresponds to a standard proof by structural induction. 266 The first and current subgoal is displayed with the double-dashed line below free variables and hypotheses, while later subgoals are only summarized with their conclusions. We see an example of a free variable in the first subgoal; [n] is a free variable of type [nat]. The conclusion is the original theorem statement where [e] has been replaced by [Const n]. In a similar manner, the second case has [e] replaced by a generalized invocation of the [Binop] expression constructor. We can see that proving both cases corresponds to a standard proof by structural induction.
257 267
258 We begin the first case with another very common tactic. 268 We begin the first case with another very common tactic.
267 s : stack 277 s : stack
268 p : list instr 278 p : list instr
269 ============================ 279 ============================
270 progDenote (compile (Const n) ++ p) s = 280 progDenote (compile (Const n) ++ p) s =
271 progDenote p (expDenote (Const n) :: s) 281 progDenote p (expDenote (Const n) :: s)
282
272 ]] 283 ]]
273 284
274 We see that [intros] changes [forall]-bound variables at the beginning of a goal into free variables. 285 We see that [intros] changes [forall]-bound variables at the beginning of a goal into free variables.
275 286
276 To progress further, we need to use the definitions of some of the functions appearing in the goal. The [unfold] tactic replaces an identifier with its definition. 287 To progress further, we need to use the definitions of some of the functions appearing in the goal. The [unfold] tactic replaces an identifier with its definition.
282 s : stack 293 s : stack
283 p : list instr 294 p : list instr
284 ============================ 295 ============================
285 progDenote ((IConst n :: nil) ++ p) s = 296 progDenote ((IConst n :: nil) ++ p) s =
286 progDenote p (expDenote (Const n) :: s) 297 progDenote p (expDenote (Const n) :: s)
298
287 ]] *) 299 ]] *)
288 300
289 unfold expDenote. 301 unfold expDenote.
290 (** [[ 302 (** [[
291 n : nat 303 n : nat
292 s : stack 304 s : stack
293 p : list instr 305 p : list instr
294 ============================ 306 ============================
295 progDenote ((IConst n :: nil) ++ p) s = progDenote p (n :: s) 307 progDenote ((IConst n :: nil) ++ p) s = progDenote p (n :: s)
308
296 ]] 309 ]]
297 310
298 We only need to unfold the first occurrence of [progDenote] to prove the goal: *) 311 We only need to unfold the first occurrence of [progDenote] to prove the goal: *)
299 312
300 unfold progDenote at 1. 313 unfold progDenote at 1.
314 | Some s' => progDenote p' s' 327 | Some s' => progDenote p' s'
315 | None => None (A:=stack) 328 | None => None (A:=stack)
316 end 329 end
317 end) ((IConst n :: nil) ++ p) s = 330 end) ((IConst n :: nil) ++ p) s =
318 progDenote p (n :: s) 331 progDenote p (n :: s)
332
319 ]] 333 ]]
320 334
321 This last [unfold] has left us with an anonymous fixpoint version of [progDenote], which will generally happen when unfolding recursive definitions. Fortunately, in this case, we can eliminate such complications right away, since the structure of the argument [(IConst n :: nil) ++ p] is known, allowing us to simplify the internal pattern match with the [simpl] tactic: 335 This last [unfold] has left us with an anonymous fixpoint version of [progDenote], which will generally happen when unfolding recursive definitions. Fortunately, in this case, we can eliminate such complications right away, since the structure of the argument [(IConst n :: nil) ++ p] is known, allowing us to simplify the internal pattern match with the [simpl] tactic:
322 *) 336 *)
323 337
324 simpl. 338 simpl.
325 339 (** [[
326 (** [[
327
328 n : nat 340 n : nat
329 s : stack 341 s : stack
330 p : list instr 342 p : list instr
331 ============================ 343 ============================
332 (fix progDenote (p0 : prog) (s0 : stack) {struct p0} : 344 (fix progDenote (p0 : prog) (s0 : stack) {struct p0} :
337 match instrDenote i s0 with 349 match instrDenote i s0 with
338 | Some s' => progDenote p' s' 350 | Some s' => progDenote p' s'
339 | None => None (A:=stack) 351 | None => None (A:=stack)
340 end 352 end
341 end) p (n :: s) = progDenote p (n :: s) 353 end) p (n :: s) = progDenote p (n :: s)
354
342 ]] 355 ]]
343 356
344 Now we can unexpand the definition of [progDenote]: 357 Now we can unexpand the definition of [progDenote]:
345 *) 358 *)
346 359
351 n : nat 364 n : nat
352 s : stack 365 s : stack
353 p : list instr 366 p : list instr
354 ============================ 367 ============================
355 progDenote p (n :: s) = progDenote p (n :: s) 368 progDenote p (n :: s) = progDenote p (n :: s)
369
356 ]] 370 ]]
357 371
358 It looks like we are at the end of this case, since we have a trivial equality. Indeed, a single tactic finishes us off: 372 It looks like we are at the end of this case, since we have a trivial equality. Indeed, a single tactic finishes us off:
359 *) 373 *)
360 374
361 reflexivity. 375 reflexivity.
362 376
363 (** On to the second inductive case: 377 (** On to the second inductive case:
364 378
365 [[ 379 [[
366
367 b : binop 380 b : binop
368 e1 : exp 381 e1 : exp
369 IHe1 : forall (s : stack) (p : list instr), 382 IHe1 : forall (s : stack) (p : list instr),
370 progDenote (compile e1 ++ p) s = progDenote p (expDenote e1 :: s) 383 progDenote (compile e1 ++ p) s = progDenote p (expDenote e1 :: s)
371 e2 : exp 384 e2 : exp
373 progDenote (compile e2 ++ p) s = progDenote p (expDenote e2 :: s) 386 progDenote (compile e2 ++ p) s = progDenote p (expDenote e2 :: s)
374 ============================ 387 ============================
375 forall (s : stack) (p : list instr), 388 forall (s : stack) (p : list instr),
376 progDenote (compile (Binop b e1 e2) ++ p) s = 389 progDenote (compile (Binop b e1 e2) ++ p) s =
377 progDenote p (expDenote (Binop b e1 e2) :: s) 390 progDenote p (expDenote (Binop b e1 e2) :: s)
391
378 ]] 392 ]]
379 393
380 We see our first example of hypotheses above the double-dashed line. They are the inductive hypotheses [IHe1] and [IHe2] corresponding to the subterms [e1] and [e2], respectively. 394 We see our first example of hypotheses above the double-dashed line. They are the inductive hypotheses [IHe1] and [IHe2] corresponding to the subterms [e1] and [e2], respectively.
381 395
382 We start out the same way as before, introducing new free variables and unfolding and folding the appropriate definitions. The seemingly frivolous [unfold]/[fold] pairs are actually accomplishing useful work, because [unfold] will sometimes perform easy simplifications. *) 396 We start out the same way as before, introducing new free variables and unfolding and folding the appropriate definitions. The seemingly frivolous [unfold]/[fold] pairs are actually accomplishing useful work, because [unfold] will sometimes perform easy simplifications. *)
388 fold expDenote. 402 fold expDenote.
389 403
390 (** Now we arrive at a point where the tactics we have seen so far are insufficient. No further definition unfoldings get us anywhere, so we will need to try something different. 404 (** Now we arrive at a point where the tactics we have seen so far are insufficient. No further definition unfoldings get us anywhere, so we will need to try something different.
391 405
392 [[ 406 [[
393
394 b : binop 407 b : binop
395 e1 : exp 408 e1 : exp
396 IHe1 : forall (s : stack) (p : list instr), 409 IHe1 : forall (s : stack) (p : list instr),
397 progDenote (compile e1 ++ p) s = progDenote p (expDenote e1 :: s) 410 progDenote (compile e1 ++ p) s = progDenote p (expDenote e1 :: s)
398 e2 : exp 411 e2 : exp
401 s : stack 414 s : stack
402 p : list instr 415 p : list instr
403 ============================ 416 ============================
404 progDenote ((compile e2 ++ compile e1 ++ IBinop b :: nil) ++ p) s = 417 progDenote ((compile e2 ++ compile e1 ++ IBinop b :: nil) ++ p) s =
405 progDenote p (binopDenote b (expDenote e1) (expDenote e2) :: s) 418 progDenote p (binopDenote b (expDenote e1) (expDenote e2) :: s)
419
406 ]] 420 ]]
407 421
408 What we need is the associative law of list concatenation, available as a theorem [app_ass] in the standard library. *) 422 What we need is the associative law of list concatenation, available as a theorem [app_ass] in the standard library. *)
409 423
410 Check app_ass. 424 Check app_ass.
411 425 (** [[
412 (** [[
413
414 app_ass 426 app_ass
415 : forall (A : Type) (l m n : list A), (l ++ m) ++ n = l ++ m ++ n 427 : forall (A : Type) (l m n : list A), (l ++ m) ++ n = l ++ m ++ n
428
416 ]] 429 ]]
417 430
418 We use it to perform a rewrite: *) 431 We use it to perform a rewrite: *)
419 432
420 rewrite app_ass. 433 rewrite app_ass.
421 434
422 (** changing the conclusion to: [[ 435 (** changing the conclusion to:
423 436
437 [[
424 progDenote (compile e2 ++ (compile e1 ++ IBinop b :: nil) ++ p) s = 438 progDenote (compile e2 ++ (compile e1 ++ IBinop b :: nil) ++ p) s =
425 progDenote p (binopDenote b (expDenote e1) (expDenote e2) :: s) 439 progDenote p (binopDenote b (expDenote e1) (expDenote e2) :: s)
440
426 ]] 441 ]]
427 442
428 Now we can notice that the lefthand side of the equality matches the lefthand side of the second inductive hypothesis, so we can rewrite with that hypothesis, too: *) 443 Now we can notice that the lefthand side of the equality matches the lefthand side of the second inductive hypothesis, so we can rewrite with that hypothesis, too: *)
429 444
430 rewrite IHe2. 445 rewrite IHe2.
431 446 (** [[
432 (** [[
433
434 progDenote ((compile e1 ++ IBinop b :: nil) ++ p) (expDenote e2 :: s) = 447 progDenote ((compile e1 ++ IBinop b :: nil) ++ p) (expDenote e2 :: s) =
435 progDenote p (binopDenote b (expDenote e1) (expDenote e2) :: s) 448 progDenote p (binopDenote b (expDenote e1) (expDenote e2) :: s)
449
436 ]] 450 ]]
437 451
438 The same process lets us apply the remaining hypothesis. *) 452 The same process lets us apply the remaining hypothesis. *)
439 453
440 rewrite app_ass. 454 rewrite app_ass.
441 rewrite IHe1. 455 rewrite IHe1.
442 456 (** [[
443 (** [[
444
445 progDenote ((IBinop b :: nil) ++ p) (expDenote e1 :: expDenote e2 :: s) = 457 progDenote ((IBinop b :: nil) ++ p) (expDenote e1 :: expDenote e2 :: s) =
446 progDenote p (binopDenote b (expDenote e1) (expDenote e2) :: s) 458 progDenote p (binopDenote b (expDenote e1) (expDenote e2) :: s)
459
447 ]] 460 ]]
448 461
449 Now we can apply a similar sequence of tactics to that that ended the proof of the first case. 462 Now we can apply a similar sequence of tactics to that that ended the proof of the first case.
450 *) 463 *)
451 464
475 488
476 The proof of our main theorem is now easy. We prove it with four period-terminated tactics, though separating them with semicolons would work as well; the version here is easier to step through. *) 489 The proof of our main theorem is now easy. We prove it with four period-terminated tactics, though separating them with semicolons would work as well; the version here is easier to step through. *)
477 490
478 Theorem compile_correct : forall e, progDenote (compile e) nil = Some (expDenote e :: nil). 491 Theorem compile_correct : forall e, progDenote (compile e) nil = Some (expDenote e :: nil).
479 intros. 492 intros.
480 493 (** [[
481 (** [[
482
483 e : exp 494 e : exp
484 ============================ 495 ============================
485 progDenote (compile e) nil = Some (expDenote e :: nil) 496 progDenote (compile e) nil = Some (expDenote e :: nil)
497
486 ]] 498 ]]
487 499
488 At this point, we want to massage the lefthand side to match the statement of [compile_correct']. A theorem from the standard library is useful: *) 500 At this point, we want to massage the lefthand side to match the statement of [compile_correct']. A theorem from the standard library is useful: *)
489 501
490 Check app_nil_end. 502 Check app_nil_end.
491 503 (** [[
492 (** [[
493
494 app_nil_end 504 app_nil_end
495 : forall (A : Type) (l : list A), l = l ++ nil 505 : forall (A : Type) (l : list A), l = l ++ nil
496 ]] *) 506 ]] *)
497 507
498 rewrite (app_nil_end (compile e)). 508 rewrite (app_nil_end (compile e)).
499 509
500 (** This time, we explicitly specify the value of the variable [l] from the theorem statement, since multiple expressions of list type appear in the conclusion. [rewrite] might choose the wrong place to rewrite if we did not specify which we want. 510 (** This time, we explicitly specify the value of the variable [l] from the theorem statement, since multiple expressions of list type appear in the conclusion. [rewrite] might choose the wrong place to rewrite if we did not specify which we want.
501 511
502 [[ 512 [[
503
504 e : exp 513 e : exp
505 ============================ 514 ============================
506 progDenote (compile e ++ nil) nil = Some (expDenote e :: nil) 515 progDenote (compile e ++ nil) nil = Some (expDenote e :: nil)
516
507 ]] 517 ]]
508 518
509 Now we can apply the lemma. *) 519 Now we can apply the lemma. *)
510 520
511 rewrite compile_correct'. 521 rewrite compile_correct'.
512 522 (** [[
513 (** [[
514
515 e : exp 523 e : exp
516 ============================ 524 ============================
517 progDenote nil (expDenote e :: nil) = Some (expDenote e :: nil) 525 progDenote nil (expDenote e :: nil) = Some (expDenote e :: nil)
526
518 ]] 527 ]]
519 528
520 We are almost done. The lefthand and righthand sides can be seen to match by simple symbolic evaluation. That means we are in luck, because Coq identifies any pair of terms as equal whenever they normalize to the same result by symbolic evaluation. By the definition of [progDenote], that is the case here, but we do not need to worry about such details. A simple invocation of [reflexivity] does the normalization and checks that the two results are syntactically equal. *) 529 We are almost done. The lefthand and righthand sides can be seen to match by simple symbolic evaluation. That means we are in luck, because Coq identifies any pair of terms as equal whenever they normalize to the same result by symbolic evaluation. By the definition of [progDenote], that is the case here, but we do not need to worry about such details. A simple invocation of [reflexivity] does the normalization and checks that the two results are syntactically equal. *)
521 530
522 reflexivity. 531 reflexivity.