Mercurial > cpdt > repo
comparison src/StackMachine.v @ 207:a7be5d9a2fd4
Converted StackMachine
author | Adam Chlipala <adamc@hcoop.net> |
---|---|
date | Fri, 06 Nov 2009 17:10:27 -0500 |
parents | 3f4576f15130 |
children | b149a07b9b5b |
comparison
equal
deleted
inserted
replaced
206:3f4576f15130 | 207:a7be5d9a2fd4 |
---|---|
585 | true, true => true | 585 | true, true => true |
586 | false, false => true | 586 | false, false => true |
587 | _, _ => false | 587 | _, _ => false |
588 end. | 588 end. |
589 | 589 |
590 Fixpoint eq_nat (n1 n2 : nat) {struct n1} : bool := | 590 Fixpoint eq_nat (n1 n2 : nat) : bool := |
591 match n1, n2 with | 591 match n1, n2 with |
592 | O, O => true | 592 | O, O => true |
593 | S n1', S n2' => eq_nat n1' n2' | 593 | S n1', S n2' => eq_nat n1' n2' |
594 | _, _ => false | 594 | _, _ => false |
595 end. | 595 end. |
596 | 596 |
597 Fixpoint lt (n1 n2 : nat) {struct n1} : bool := | 597 Fixpoint lt (n1 n2 : nat) : bool := |
598 match n1, n2 with | 598 match n1, n2 with |
599 | O, S _ => true | 599 | O, S _ => true |
600 | S n1', S n2' => lt n1' n2' | 600 | S n1', S n2' => lt n1' n2' |
601 | _, _ => false | 601 | _, _ => false |
602 end. | 602 end. |
603 | 603 |
604 (** Now we can interpret binary operators: *) | 604 (** Now we can interpret binary operators: *) |
605 | 605 |
606 Definition tbinopDenote arg1 arg2 res (b : tbinop arg1 arg2 res) | 606 Definition tbinopDenote arg1 arg2 res (b : tbinop arg1 arg2 res) |
607 : typeDenote arg1 -> typeDenote arg2 -> typeDenote res := | 607 : typeDenote arg1 -> typeDenote arg2 -> typeDenote res := |
608 match b in (tbinop arg1 arg2 res) return (typeDenote arg1 -> typeDenote arg2 -> typeDenote res) with | 608 match b in (tbinop arg1 arg2 res) |
609 return (typeDenote arg1 -> typeDenote arg2 -> typeDenote res) with | |
609 | TPlus => plus | 610 | TPlus => plus |
610 | TTimes => mult | 611 | TTimes => mult |
611 | TEq Nat => eq_nat | 612 | TEq Nat => eq_nat |
612 | TEq Bool => eq_bool | 613 | TEq Bool => eq_bool |
613 | TLt => lt | 614 | TLt => lt |
614 end. | 615 end. |
615 | 616 |
616 (** This function has just a few differences from the denotation functions we saw earlier. First, [tbinop] is an indexed type, so its indices become additional arguments to [tbinopDenote]. Second, we need to perform a genuine %\textit{%#<i>#dependent pattern match#</i>#%}% to come up with a definition of this function that type-checks. In each branch of the [match], we need to use branch-specific information about the indices to [tbinop]. General type inference that takes such information into account is undecidable, and Coq avoids pursuing special heuristics for the problem, instead asking users to write annotations, like we see above on the line with [match]. | 617 (** This function has just a few differences from the denotation functions we saw earlier. First, [tbinop] is an indexed type, so its indices become additional arguments to [tbinopDenote]. Second, we need to perform a genuine %\textit{%#<i>#dependent pattern match#</i>#%}% to come up with a definition of this function that type-checks. In each branch of the [match], we need to use branch-specific information about the indices to [tbinop]. General type inference that takes such information into account is undecidable, so it is often necessary to write annotations, like we see above on the line with [match]. |
617 | 618 |
618 The [in] annotation restates the type of the term being case-analyzed. Though we use the same names for the indices as we use in the type of the original argument binder, these are actually fresh variables, and they are %\textit{%#<i>#binding occurrences#</i>#%}%. Their scope is the [return] clause. That is, [arg1], [arg2], and [arg3] are new bound variables bound only within the return clause [typeDenote arg1 -> typeDenote arg2 -> typeDenote res]. By being explicit about the functional relationship between the type indices and the match result, we regain decidable type inference. | 619 The [in] annotation restates the type of the term being case-analyzed. Though we use the same names for the indices as we use in the type of the original argument binder, these are actually fresh variables, and they are %\textit{%#<i>#binding occurrences#</i>#%}%. Their scope is the [return] clause. That is, [arg1], [arg2], and [arg3] are new bound variables bound only within the return clause [typeDenote arg1 -> typeDenote arg2 -> typeDenote res]. By being explicit about the functional relationship between the type indices and the match result, we regain decidable type inference. |
619 | 620 |
621 In fact, recent Coq versions use some heuristics that can save us the trouble of writing [match] annotations, and those heuristics get the job done in this case. We can get away with writing just: *) | |
622 | |
623 (* begin hide *) | |
624 Reset tbinopDenote. | |
625 (* end hide *) | |
626 Definition tbinopDenote arg1 arg2 res (b : tbinop arg1 arg2 res) | |
627 : typeDenote arg1 -> typeDenote arg2 -> typeDenote res := | |
628 match b with | |
629 | TPlus => plus | |
630 | TTimes => mult | |
631 | TEq Nat => eq_nat | |
632 | TEq Bool => eq_bool | |
633 | TLt => lt | |
634 end. | |
635 | |
636 (** | |
620 The same tricks suffice to define an expression denotation function in an unsurprising way: | 637 The same tricks suffice to define an expression denotation function in an unsurprising way: |
621 *) | 638 *) |
622 | 639 |
623 Fixpoint texpDenote t (e : texp t) {struct e} : typeDenote t := | 640 Fixpoint texpDenote t (e : texp t) : typeDenote t := |
624 match e in (texp t) return (typeDenote t) with | 641 match e with |
625 | TNConst n => n | 642 | TNConst n => n |
626 | TBConst b => b | 643 | TBConst b => b |
627 | TBinop _ _ _ b e1 e2 => (tbinopDenote b) (texpDenote e1) (texpDenote e2) | 644 | TBinop _ _ _ b e1 e2 => (tbinopDenote b) (texpDenote e1) (texpDenote e2) |
628 end. | 645 end. |
629 | 646 |
630 (** We can evaluate a few example programs to convince ourselves that this semantics is correct. *) | 647 (** We can evaluate a few example programs to convince ourselves that this semantics is correct. *) |
631 | 648 |
632 Eval simpl in texpDenote (TNConst 42). | 649 Eval simpl in texpDenote (TNConst 42). |
633 (** [[ | 650 (** [= 42 : typeDenote Nat] *) |
634 = 42 : typeDenote Nat | 651 |
635 ]] *) | |
636 Eval simpl in texpDenote (TBConst true). | 652 Eval simpl in texpDenote (TBConst true). |
637 (** [[ | 653 (** [= true : typeDenote Bool] *) |
638 = true : typeDenote Bool | 654 |
639 ]] *) | |
640 Eval simpl in texpDenote (TBinop TTimes (TBinop TPlus (TNConst 2) (TNConst 2)) (TNConst 7)). | 655 Eval simpl in texpDenote (TBinop TTimes (TBinop TPlus (TNConst 2) (TNConst 2)) (TNConst 7)). |
641 (** [[ | 656 (** [= 28 : typeDenote Nat] *) |
642 = 28 : typeDenote Nat | 657 |
643 ]] *) | |
644 Eval simpl in texpDenote (TBinop (TEq Nat) (TBinop TPlus (TNConst 2) (TNConst 2)) (TNConst 7)). | 658 Eval simpl in texpDenote (TBinop (TEq Nat) (TBinop TPlus (TNConst 2) (TNConst 2)) (TNConst 7)). |
645 (** [[ | 659 (** [= false : typeDenote Bool] *) |
646 = false : typeDenote Bool | 660 |
647 ]] *) | |
648 Eval simpl in texpDenote (TBinop TLt (TBinop TPlus (TNConst 2) (TNConst 2)) (TNConst 7)). | 661 Eval simpl in texpDenote (TBinop TLt (TBinop TPlus (TNConst 2) (TNConst 2)) (TNConst 7)). |
649 (** [[ | 662 (** [= true : typeDenote Bool] *) |
650 = true : typeDenote Bool | |
651 ]] *) | |
652 | 663 |
653 | 664 |
654 (** ** Target Language *) | 665 (** ** Target Language *) |
655 | 666 |
656 (** Now we want to define a suitable stack machine target for compilation. In the example of the untyped language, stack machine programs could encounter stack underflows and "get stuck." This was unfortunate, since we had to deal with this complication even though we proved that our compiler never produced underflowing programs. We could have used dependent types to force all stack machine programs to be underflow-free. | 667 (** Now we want to define a suitable stack machine target for compilation. In the example of the untyped language, stack machine programs could encounter stack underflows and "get stuck." This was unfortunate, since we had to deal with this complication even though we proved that our compiler never produced underflowing programs. We could have used dependent types to force all stack machine programs to be underflow-free. |
689 | t :: ts' => typeDenote t * vstack ts' | 700 | t :: ts' => typeDenote t * vstack ts' |
690 end%type. | 701 end%type. |
691 | 702 |
692 (** This is another [Set]-valued function. This time it is recursive, which is perfectly valid, since [Set] is not treated specially in determining which functions may be written. We say that the value stack of an empty stack type is any value of type [unit], which has just a single value, [tt]. A nonempty stack type leads to a value stack that is a pair, whose first element has the proper type and whose second element follows the representation for the remainder of the stack type. | 703 (** This is another [Set]-valued function. This time it is recursive, which is perfectly valid, since [Set] is not treated specially in determining which functions may be written. We say that the value stack of an empty stack type is any value of type [unit], which has just a single value, [tt]. A nonempty stack type leads to a value stack that is a pair, whose first element has the proper type and whose second element follows the representation for the remainder of the stack type. |
693 | 704 |
694 This idea of programming with types can take a while to internalize, but it enables a very simple definition of instruction denotation. We have the same kind of type annotations for the dependent [match], but everything else is like what you might expect from a Lisp-like version of ML that ignored type information. Nonetheless, the fact that [tinstrDenote] passes the type-checker guarantees that our stack machine programs can never go wrong. *) | 705 This idea of programming with types can take a while to internalize, but it enables a very simple definition of instruction denotation. Our definition is like what you might expect from a Lisp-like version of ML that ignored type information. Nonetheless, the fact that [tinstrDenote] passes the type-checker guarantees that our stack machine programs can never go wrong. *) |
695 | 706 |
696 Definition tinstrDenote ts ts' (i : tinstr ts ts') : vstack ts -> vstack ts' := | 707 Definition tinstrDenote ts ts' (i : tinstr ts ts') : vstack ts -> vstack ts' := |
697 match i in (tinstr ts ts') return (vstack ts -> vstack ts') with | 708 match i with |
698 | TINConst _ n => fun s => (n, s) | 709 | TINConst _ n => fun s => (n, s) |
699 | TIBConst _ b => fun s => (b, s) | 710 | TIBConst _ b => fun s => (b, s) |
700 | TIBinop _ _ _ _ b => fun s => | 711 | TIBinop _ _ _ _ b => fun s => |
701 match s with | 712 match s with |
702 (arg1, (arg2, s')) => ((tbinopDenote b) arg1 arg2, s') | 713 (arg1, (arg2, s')) => ((tbinopDenote b) arg1 arg2, s') |
705 | 716 |
706 (** Why do we choose to use an anonymous function to bind the initial stack in every case of the [match]? Consider this well-intentioned but invalid alternative version: | 717 (** Why do we choose to use an anonymous function to bind the initial stack in every case of the [match]? Consider this well-intentioned but invalid alternative version: |
707 | 718 |
708 [[ | 719 [[ |
709 Definition tinstrDenote ts ts' (i : tinstr ts ts') (s : vstack ts) : vstack ts' := | 720 Definition tinstrDenote ts ts' (i : tinstr ts ts') (s : vstack ts) : vstack ts' := |
710 match i in (tinstr ts ts') return (vstack ts') with | 721 match i with |
711 | TINConst _ n => (n, s) | 722 | TINConst _ n => (n, s) |
712 | TIBConst _ b => (b, s) | 723 | TIBConst _ b => (b, s) |
713 | TIBinop _ _ _ _ b => | 724 | TIBinop _ _ _ _ b => |
714 match s with | 725 match s with |
715 (arg1, (arg2, s')) => ((tbinopDenote b) arg1 arg2, s') | 726 (arg1, (arg2, s')) => ((tbinopDenote b) arg1 arg2, s') |
720 | 731 |
721 The Coq type-checker complains that: | 732 The Coq type-checker complains that: |
722 | 733 |
723 [[ | 734 [[ |
724 The term "(n, s)" has type "(nat * vstack ts)%type" | 735 The term "(n, s)" has type "(nat * vstack ts)%type" |
725 while it is expected to have type "vstack (Nat :: t)" | 736 while it is expected to have type "vstack ?119". |
737 | |
738 ]] | |
739 | |
740 The text [?119] stands for a unification variable. We can try to help Coq figure out the value of this variable with an explicit annotation on our [match] expression. | |
741 | |
742 [[ | |
743 Definition tinstrDenote ts ts' (i : tinstr ts ts') (s : vstack ts) : vstack ts' := | |
744 match i in tinstr ts ts' return vstack ts' with | |
745 | TINConst _ n => (n, s) | |
746 | TIBConst _ b => (b, s) | |
747 | TIBinop _ _ _ _ b => | |
748 match s with | |
749 (arg1, (arg2, s')) => ((tbinopDenote b) arg1 arg2, s') | |
750 end | |
751 end. | |
752 | |
753 ]] | |
754 | |
755 Now the error message changes. | |
756 | |
757 [[ | |
758 The term "(n, s)" has type "(nat * vstack ts)%type" | |
759 while it is expected to have type "vstack (Nat :: t)". | |
760 | |
726 ]] | 761 ]] |
727 | 762 |
728 Recall from our earlier discussion of [match] annotations that we write the annotations to express to the type-checker the relationship between the type indices of the case object and the result type of the [match]. Coq chooses to assign to the wildcard [_] after [TINConst] the name [t], and the type error is telling us that the type checker cannot prove that [t] is the same as [ts]. By moving [s] out of the [match], we lose the ability to express, with [in] and [return] clauses, the relationship between the shared index [ts] of [s] and [i]. | 763 Recall from our earlier discussion of [match] annotations that we write the annotations to express to the type-checker the relationship between the type indices of the case object and the result type of the [match]. Coq chooses to assign to the wildcard [_] after [TINConst] the name [t], and the type error is telling us that the type checker cannot prove that [t] is the same as [ts]. By moving [s] out of the [match], we lose the ability to express, with [in] and [return] clauses, the relationship between the shared index [ts] of [s] and [i]. |
729 | 764 |
730 There %\textit{%#<i>#are#</i>#%}% reasonably general ways of getting around this problem without pushing binders inside [match]es. However, the alternatives are significantly more involved, and the technique we use here is almost certainly the best choice, whenever it applies. | 765 There %\textit{%#<i>#are#</i>#%}% reasonably general ways of getting around this problem without pushing binders inside [match]es. However, the alternatives are significantly more involved, and the technique we use here is almost certainly the best choice, whenever it applies. |
731 | 766 |
732 *) | 767 *) |
733 | 768 |
734 (** We finish the semantics with a straightforward definition of program denotation. *) | 769 (** We finish the semantics with a straightforward definition of program denotation. *) |
735 | 770 |
736 Fixpoint tprogDenote ts ts' (p : tprog ts ts') {struct p} : vstack ts -> vstack ts' := | 771 Fixpoint tprogDenote ts ts' (p : tprog ts ts') : vstack ts -> vstack ts' := |
737 match p in (tprog ts ts') return (vstack ts -> vstack ts') with | 772 match p with |
738 | TNil _ => fun s => s | 773 | TNil _ => fun s => s |
739 | TCons _ _ _ i p' => fun s => tprogDenote p' (tinstrDenote i s) | 774 | TCons _ _ _ i p' => fun s => tprogDenote p' (tinstrDenote i s) |
740 end. | 775 end. |
741 | 776 |
742 | 777 |
743 (** ** Translation *) | 778 (** ** Translation *) |
744 | 779 |
745 (** To define our compilation, it is useful to have an auxiliary function for concatenating two stack machine programs. *) | 780 (** To define our compilation, it is useful to have an auxiliary function for concatenating two stack machine programs. *) |
746 | 781 |
747 Fixpoint tconcat ts ts' ts'' (p : tprog ts ts') {struct p} : tprog ts' ts'' -> tprog ts ts'' := | 782 Fixpoint tconcat ts ts' ts'' (p : tprog ts ts') : tprog ts' ts'' -> tprog ts ts'' := |
748 match p in (tprog ts ts') return (tprog ts' ts'' -> tprog ts ts'') with | 783 match p with |
749 | TNil _ => fun p' => p' | 784 | TNil _ => fun p' => p' |
750 | TCons _ _ _ i p1 => fun p' => TCons i (tconcat p1 p') | 785 | TCons _ _ _ i p1 => fun p' => TCons i (tconcat p1 p') |
751 end. | 786 end. |
752 | 787 |
753 (** With that function in place, the compilation is defined very similarly to how it was before, modulo the use of dependent typing. *) | 788 (** With that function in place, the compilation is defined very similarly to how it was before, modulo the use of dependent typing. *) |
754 | 789 |
755 Fixpoint tcompile t (e : texp t) (ts : tstack) {struct e} : tprog ts (t :: ts) := | 790 Fixpoint tcompile t (e : texp t) (ts : tstack) : tprog ts (t :: ts) := |
756 match e in (texp t) return (tprog ts (t :: ts)) with | 791 match e with |
757 | TNConst n => TCons (TINConst _ n) (TNil _) | 792 | TNConst n => TCons (TINConst _ n) (TNil _) |
758 | TBConst b => TCons (TIBConst _ b) (TNil _) | 793 | TBConst b => TCons (TIBConst _ b) (TNil _) |
759 | TBinop _ _ _ b e1 e2 => tconcat (tcompile e2 _) | 794 | TBinop _ _ _ b e1 e2 => tconcat (tcompile e2 _) |
760 (tconcat (tcompile e1 _) (TCons (TIBinop _ b) (TNil _))) | 795 (tconcat (tcompile e1 _) (TCons (TIBinop _ b) (TNil _))) |
761 end. | 796 end. |
763 (** One interesting feature of the definition is the underscores appearing to the right of [=>] arrows. Haskell and ML programmers are quite familiar with compilers that infer type parameters to polymorphic values. In Coq, it is possible to go even further and ask the system to infer arbitrary terms, by writing underscores in place of specific values. You may have noticed that we have been calling functions without specifying all of their arguments. For instance, the recursive calls here to [tcompile] omit the [t] argument. Coq's %\textit{%#<i>#implicit argument#</i>#%}% mechanism automatically inserts underscores for arguments that it will probably be able to infer. Inference of such values is far from complete, though; generally, it only works in cases similar to those encountered with polymorphic type instantiation in Haskell and ML. | 798 (** One interesting feature of the definition is the underscores appearing to the right of [=>] arrows. Haskell and ML programmers are quite familiar with compilers that infer type parameters to polymorphic values. In Coq, it is possible to go even further and ask the system to infer arbitrary terms, by writing underscores in place of specific values. You may have noticed that we have been calling functions without specifying all of their arguments. For instance, the recursive calls here to [tcompile] omit the [t] argument. Coq's %\textit{%#<i>#implicit argument#</i>#%}% mechanism automatically inserts underscores for arguments that it will probably be able to infer. Inference of such values is far from complete, though; generally, it only works in cases similar to those encountered with polymorphic type instantiation in Haskell and ML. |
764 | 799 |
765 The underscores here are being filled in with stack types. That is, the Coq type inferencer is, in a sense, inferring something about the flow of control in the translated programs. We can take a look at exactly which values are filled in: *) | 800 The underscores here are being filled in with stack types. That is, the Coq type inferencer is, in a sense, inferring something about the flow of control in the translated programs. We can take a look at exactly which values are filled in: *) |
766 | 801 |
767 Print tcompile. | 802 Print tcompile. |
768 | 803 (** [[ |
769 (** [[ | |
770 | |
771 tcompile = | 804 tcompile = |
772 fix tcompile (t : type) (e : texp t) (ts : tstack) {struct e} : | 805 fix tcompile (t : type) (e : texp t) (ts : tstack) {struct e} : |
773 tprog ts (t :: ts) := | 806 tprog ts (t :: ts) := |
774 match e in (texp t0) return (tprog ts (t0 :: ts)) with | 807 match e in (texp t0) return (tprog ts (t0 :: ts)) with |
775 | TNConst n => TCons (TINConst ts n) (TNil (Nat :: ts)) | 808 | TNConst n => TCons (TINConst ts n) (TNil (Nat :: ts)) |
784 | 817 |
785 | 818 |
786 (** We can check that the compiler generates programs that behave appropriately on our sample programs from above: *) | 819 (** We can check that the compiler generates programs that behave appropriately on our sample programs from above: *) |
787 | 820 |
788 Eval simpl in tprogDenote (tcompile (TNConst 42) nil) tt. | 821 Eval simpl in tprogDenote (tcompile (TNConst 42) nil) tt. |
789 (** [[ | 822 (** [= (42, tt) : vstack (Nat :: nil)] *) |
790 = (42, tt) : vstack (Nat :: nil) | 823 |
791 ]] *) | |
792 Eval simpl in tprogDenote (tcompile (TBConst true) nil) tt. | 824 Eval simpl in tprogDenote (tcompile (TBConst true) nil) tt. |
793 (** [[ | 825 (** [= (true, tt) : vstack (Bool :: nil)] *) |
794 = (true, tt) : vstack (Bool :: nil) | 826 |
795 ]] *) | |
796 Eval simpl in tprogDenote (tcompile (TBinop TTimes (TBinop TPlus (TNConst 2) (TNConst 2)) (TNConst 7)) nil) tt. | 827 Eval simpl in tprogDenote (tcompile (TBinop TTimes (TBinop TPlus (TNConst 2) (TNConst 2)) (TNConst 7)) nil) tt. |
797 (** [[ | 828 (** [= (28, tt) : vstack (Nat :: nil)] *) |
798 = (28, tt) : vstack (Nat :: nil) | 829 |
799 ]] *) | |
800 Eval simpl in tprogDenote (tcompile (TBinop (TEq Nat) (TBinop TPlus (TNConst 2) (TNConst 2)) (TNConst 7)) nil) tt. | 830 Eval simpl in tprogDenote (tcompile (TBinop (TEq Nat) (TBinop TPlus (TNConst 2) (TNConst 2)) (TNConst 7)) nil) tt. |
801 (** [[ | 831 (** [= (false, tt) : vstack (Bool :: nil)] *) |
802 = (false, tt) : vstack (Bool :: nil) | 832 |
803 ]] *) | |
804 Eval simpl in tprogDenote (tcompile (TBinop TLt (TBinop TPlus (TNConst 2) (TNConst 2)) (TNConst 7)) nil) tt. | 833 Eval simpl in tprogDenote (tcompile (TBinop TLt (TBinop TPlus (TNConst 2) (TNConst 2)) (TNConst 7)) nil) tt. |
805 (** [[ | 834 (** [= (true, tt) : vstack (Bool :: nil)] *) |
806 = (true, tt) : vstack (Bool :: nil) | |
807 ]] *) | |
808 | 835 |
809 | 836 |
810 (** ** Translation Correctness *) | 837 (** ** Translation Correctness *) |
811 | 838 |
812 (** We can state a correctness theorem similar to the last one. *) | 839 (** We can state a correctness theorem similar to the last one. *) |
813 | 840 |
814 Theorem tcompile_correct : forall t (e : texp t), tprogDenote (tcompile e nil) tt = (texpDenote e, tt). | 841 Theorem tcompile_correct : forall t (e : texp t), |
842 tprogDenote (tcompile e nil) tt = (texpDenote e, tt). | |
815 (* begin hide *) | 843 (* begin hide *) |
816 Abort. | 844 Abort. |
817 (* end hide *) | 845 (* end hide *) |
818 (* begin thide *) | 846 (* begin thide *) |
819 | 847 |
820 (** Again, we need to strengthen the theorem statement so that the induction will go through. This time, I will develop an alternative approach to this kind of proof, stating the key lemma as: *) | 848 (** Again, we need to strengthen the theorem statement so that the induction will go through. This time, I will develop an alternative approach to this kind of proof, stating the key lemma as: *) |
821 | 849 |
822 Lemma tcompile_correct' : forall t (e : texp t) | 850 Lemma tcompile_correct' : forall t (e : texp t) ts (s : vstack ts), |
823 ts (s : vstack ts), | 851 tprogDenote (tcompile e ts) s = (texpDenote e, s). |
824 tprogDenote (tcompile e ts) s | |
825 = (texpDenote e, s). | |
826 | 852 |
827 (** While lemma [compile_correct'] quantified over a program that is the "continuation" for the expression we are considering, here we avoid drawing in any extra syntactic elements. In addition to the source expression and its type, we also quantify over an initial stack type and a stack compatible with it. Running the compilation of the program starting from that stack, we should arrive at a stack that differs only in having the program's denotation pushed onto it. | 853 (** While lemma [compile_correct'] quantified over a program that is the "continuation" for the expression we are considering, here we avoid drawing in any extra syntactic elements. In addition to the source expression and its type, we also quantify over an initial stack type and a stack compatible with it. Running the compilation of the program starting from that stack, we should arrive at a stack that differs only in having the program's denotation pushed onto it. |
828 | 854 |
829 Let us try to prove this theorem in the same way that we settled on in the last section. *) | 855 Let us try to prove this theorem in the same way that we settled on in the last section. *) |
830 | 856 |
831 induction e; crush. | 857 induction e; crush. |
832 | 858 |
833 (** We are left with this unproved conclusion: | 859 (** We are left with this unproved conclusion: |
834 | 860 |
835 [[ | 861 [[ |
836 | |
837 tprogDenote | 862 tprogDenote |
838 (tconcat (tcompile e2 ts) | 863 (tconcat (tcompile e2 ts) |
839 (tconcat (tcompile e1 (arg2 :: ts)) | 864 (tconcat (tcompile e1 (arg2 :: ts)) |
840 (TCons (TIBinop ts t) (TNil (res :: ts))))) s = | 865 (TCons (TIBinop ts t) (TNil (res :: ts))))) s = |
841 (tbinopDenote t (texpDenote e1) (texpDenote e2), s) | 866 (tbinopDenote t (texpDenote e1) (texpDenote e2), s) |
867 | |
842 ]] | 868 ]] |
843 | 869 |
844 We need an analogue to the [app_ass] theorem that we used to rewrite the goal in the last section. We can abort this proof and prove such a lemma about [tconcat]. | 870 We need an analogue to the [app_ass] theorem that we used to rewrite the goal in the last section. We can abort this proof and prove such a lemma about [tconcat]. |
845 *) | 871 *) |
872 | |
846 Abort. | 873 Abort. |
847 | 874 |
848 Lemma tconcat_correct : forall ts ts' ts'' (p : tprog ts ts') (p' : tprog ts' ts'') | 875 Lemma tconcat_correct : forall ts ts' ts'' (p : tprog ts ts') (p' : tprog ts' ts'') |
849 (s : vstack ts), | 876 (s : vstack ts), |
850 tprogDenote (tconcat p p') s | 877 tprogDenote (tconcat p p') s |
858 | 885 |
859 Hint Rewrite tconcat_correct : cpdt. | 886 Hint Rewrite tconcat_correct : cpdt. |
860 | 887 |
861 (** We ask that the lemma be used for left-to-right rewriting, and we ask for the hint to be added to the hint database called [cpdt], which is the database used by [crush]. Now we are ready to return to [tcompile_correct'], proving it automatically this time. *) | 888 (** We ask that the lemma be used for left-to-right rewriting, and we ask for the hint to be added to the hint database called [cpdt], which is the database used by [crush]. Now we are ready to return to [tcompile_correct'], proving it automatically this time. *) |
862 | 889 |
863 Lemma tcompile_correct' : forall t (e : texp t) | 890 Lemma tcompile_correct' : forall t (e : texp t) ts (s : vstack ts), |
864 ts (s : vstack ts), | 891 tprogDenote (tcompile e ts) s = (texpDenote e, s). |
865 tprogDenote (tcompile e ts) s | |
866 = (texpDenote e, s). | |
867 induction e; crush. | 892 induction e; crush. |
868 Qed. | 893 Qed. |
869 | 894 |
870 (** We can register this main lemma as another hint, allowing us to prove the final theorem trivially. *) | 895 (** We can register this main lemma as another hint, allowing us to prove the final theorem trivially. *) |
871 | 896 |
872 Hint Rewrite tcompile_correct' : cpdt. | 897 Hint Rewrite tcompile_correct' : cpdt. |
873 | 898 |
874 Theorem tcompile_correct : forall t (e : texp t), tprogDenote (tcompile e nil) tt = (texpDenote e, tt). | 899 Theorem tcompile_correct : forall t (e : texp t), |
900 tprogDenote (tcompile e nil) tt = (texpDenote e, tt). | |
875 crush. | 901 crush. |
876 Qed. | 902 Qed. |
877 (* end thide *) | 903 (* end thide *) |