Mercurial > cpdt > repo
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. |