554 ML {* OuterSyntax.command *} |
554 ML {* OuterSyntax.command *} |
555 |
555 |
556 section {* New Commands and Creating Keyword Files *} |
556 section {* New Commands and Creating Keyword Files *} |
557 |
557 |
558 text {* |
558 text {* |
559 Often new commands, for example for providing a new definitional principle, |
559 Often new commands, for example for providing new definitional principles, |
560 need to be programmed. While this is not difficult to do on the ML-level, |
560 need to be implemented. While this is not difficult on the ML-level, |
561 new commands, in order to be useful, need to be recognised by |
561 new commands, in order to be useful, need to be recognised by |
562 ProofGeneral. This results in some subtle configuration issues, which we |
562 ProofGeneral. This results in some subtle configuration issues, which we |
563 will explain in this section. |
563 will explain in this section. |
564 |
564 |
565 Let us start with a ``silly'' command, which we call \isacommand{foo} in what follows. |
565 Let us start with a ``silly'' command, which we call \isacommand{foobar} in what follows. |
566 To keep things simple this command does nothing at all. On the ML-level it can be defined as |
566 To keep things simple this command does nothing at all. On the ML-level it can be defined as |
567 |
567 *} |
568 @{ML [display] |
568 |
569 "let |
569 ML {* |
570 val do_nothing = Scan.succeed (Toplevel.theory (fn x => x)) |
570 let |
571 val flag = OuterKeyword.thy_decl |
571 val do_nothing = Scan.succeed (Toplevel.theory I) |
|
572 val kind = OuterKeyword.thy_decl |
572 in |
573 in |
573 OuterSyntax.command \"foo\" \"description of foo\" flag do_nothing |
574 OuterSyntax.command "foobar" "description of foobar" kind do_nothing |
574 end"} |
575 end |
575 |
576 *} |
|
577 |
|
578 text {* |
576 The function @{ML OuterSyntax.command} expects a name for the command, a |
579 The function @{ML OuterSyntax.command} expects a name for the command, a |
577 short description, a flag (which we will explain later on more thoroughly) and a |
580 short description, a kind indicator (which we will explain later on more thoroughly) and a |
578 parser for a top-level transition function (its purpose will also explained |
581 parser for a top-level transition function (its purpose will also explained |
579 later). |
582 later). |
580 |
583 |
581 While this is everything we have to do on the ML-level, we need |
584 While this is everything we have to do on the ML-level, we need a keyword |
582 now a keyword file that can be loaded by ProofGeneral. This is to enable ProofGeneral |
585 file that can be loaded by ProofGeneral. This is to enable ProofGeneral to |
583 to recognise \isacommand{foo} as a command. Such a keyword file can be generated with |
586 recognise \isacommand{foobar} as a command. Such a keyword file can be |
584 the command-line |
587 generated with the command-line |
585 |
588 |
586 @{text [display] "$ isabelle keywords -k foo <some-log-files>"} |
589 |
587 |
590 @{text [display] "$ isabelle keywords -k foobar some-log-files"} |
588 The option @{text "-k foo"} indicates which postfix the keyword file will obtain. In |
591 |
589 the case above the generated file will be named @{text "isar-keywords-foo.el"}. This command |
592 The option @{text "-k foobar"} indicates which postfix the keyword file will |
590 requires log files to be present (in order to extract the keywords from them). |
593 obtain. In the case above the generated file will be named @{text |
591 To generate these log files, we first package the code above into a separate theory file |
594 "isar-keywords-foobar.el"}. However, this command requires log files to be |
592 named @{text "Command.thy"}, say---see Figure~\ref{fig:commandtheory} for the complete code. |
595 present (in order to extract the keywords from them). To generate these log |
|
596 files, we first package the code above into a separate theory file named |
|
597 @{text "Command.thy"}, say---see Figure~\ref{fig:commandtheory} for the |
|
598 complete code. |
|
599 |
593 |
600 |
594 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
601 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
595 \begin{figure}[t] |
602 \begin{figure}[t] |
596 \begin{boxedminipage}{\textwidth} |
603 \begin{boxedminipage}{\textwidth}\small |
597 \isacommand{theory}~@{text Command}\\ |
604 \isacommand{theory}~@{text Command}\\ |
598 \isacommand{imports}~@{text Main}\\ |
605 \isacommand{imports}~@{text Main}\\ |
599 \isacommand{begin}\\ |
606 \isacommand{begin}\\ |
600 \isacommand{ML}~\isa{\isacharverbatimopen}\\ |
607 \isacommand{ML}~\isa{\isacharverbatimopen}\\ |
601 @{ML |
608 @{ML |
602 "let |
609 "let |
603 val do_nothing = Scan.succeed (Toplevel.theory (fn x => x)) |
610 val do_nothing = Scan.succeed (Toplevel.theory I) |
604 val flag = OuterKeyword.thy_decl |
611 val kind = OuterKeyword.thy_decl |
605 in |
612 in |
606 OuterSyntax.command \"foo\" \"description of foo\" flag do_nothing |
613 OuterSyntax.command \"foobar\" \"description of foobar\" kind do_nothing |
607 end"}\\ |
614 end"}\\ |
608 \isa{\isacharverbatimclose}\\ |
615 \isa{\isacharverbatimclose}\\ |
609 \isacommand{end} |
616 \isacommand{end} |
610 \end{boxedminipage} |
617 \end{boxedminipage} |
611 \caption{The file @{text "Command.thy"} is necessary for generating a log |
618 \caption{The file @{text "Command.thy"} is necessary for generating a log |
612 file. This log file enables Isabelle to generate a keyword file containing |
619 file. This log file enables Isabelle to generate a keyword file containing |
613 the command \isacommand{foo}.\label{fig:commandtheory}} |
620 the command \isacommand{foobar}.\label{fig:commandtheory}} |
614 \end{figure} |
621 \end{figure} |
615 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
622 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
616 |
623 |
617 For |
624 For our purposes it is sufficient to use the log files for the theories |
618 our purposes it is sufficient to use the log files for the theories @{text "Pure"}, |
625 @{text "Pure"}, @{text "HOL"} and @{text "Pure-ProofGeneral"}, as well as |
619 @{text "HOL"} and @{text "Pure-ProofGeneral"}, as well as the theory @{text "Command.thy"} |
626 the theory @{text "Command.thy"} containing the new |
620 containing the new \isacommand{foo}-command. @{text Pure} and @{text HOL} are usually compiled during the |
627 \isacommand{foobar}-command. @{text Pure} and @{text HOL} are usually |
621 installation of Isabelle. So log files for them are already available. If not, then they |
628 compiled during the installation of Isabelle. So log files for them should be |
622 can be conveniently compiled using build-script from the distribution |
629 already available. If not, then they can be conveniently compiled using |
|
630 build-script from the Isabelle distribution |
623 |
631 |
624 @{text [display] |
632 @{text [display] |
625 "$ ./build -m \"Pure\" |
633 "$ ./build -m \"Pure\" |
626 $ ./build -m \"HOL\""} |
634 $ ./build -m \"HOL\""} |
627 |
635 |
630 @{text [display] "$ ./build -m \"Pure-ProofGeneral\" \"Pure\""} |
638 @{text [display] "$ ./build -m \"Pure-ProofGeneral\" \"Pure\""} |
631 |
639 |
632 For the theory @{text "Command.thy"}, we first create a ``managed'' subdirectory |
640 For the theory @{text "Command.thy"}, we first create a ``managed'' subdirectory |
633 with |
641 with |
634 |
642 |
635 @{text [display] "$ isabelle mkdir FooCommand"} |
643 @{text [display] "$ isabelle mkdir FoobarCommand"} |
636 |
644 |
637 This creates a directory containing the files |
645 This creates a directory containing the files |
638 |
646 |
639 @{text [display] |
647 @{text [display] |
640 "./IsaMakefile |
648 "./IsaMakefile |
641 ./FooCommand/ROOT.ML |
649 ./FoobarCommand/ROOT.ML |
642 ./FooCommand/document |
650 ./FoobarCommand/document |
643 ./FooCommand/document/root.tex"} |
651 ./FoobarCommand/document/root.tex"} |
644 |
652 |
645 |
653 |
646 We need to copy the file @{text "Command.thy"} into the directory @{text "FooCommand"} |
654 We need to copy the file @{text "Command.thy"} into the directory @{text "FoobarCommand"} |
647 and add the line |
655 and add the line |
648 |
656 |
649 @{text [display] "use_thy \"Command\";"} |
657 @{text [display] "use_thy \"Command\";"} |
650 |
658 |
651 to the file @{text "./FooCommand/ROOT.ML"}. We can now compile the theory by just typing |
659 to the file @{text "./FoobarCommand/ROOT.ML"}. We can now compile the theory by just typing |
652 |
660 |
653 @{text [display] "$ isabelle make"} |
661 @{text [display] "$ isabelle make"} |
654 |
662 |
655 We created finally all the necessary log files. They are typically stored |
663 We created finally all the necessary log files. They are typically stored |
656 in the directory |
664 in the directory |
661 Let us assume the name of this directory is stored in the shell variable |
669 Let us assume the name of this directory is stored in the shell variable |
662 @{text "ISABELLE_LOGS"}. One way to assign this shell variable is by typing |
670 @{text "ISABELLE_LOGS"}. One way to assign this shell variable is by typing |
663 |
671 |
664 @{text [display] "$ ISABELLE_LOGS=\"$(isabelle getenv -b ISABELLE_OUTPUT)\"/log"} |
672 @{text [display] "$ ISABELLE_LOGS=\"$(isabelle getenv -b ISABELLE_OUTPUT)\"/log"} |
665 |
673 |
666 In this directory are the files |
674 on the Unix prompt. This directory should include the files |
667 |
675 |
668 @{text [display] |
676 @{text [display] |
669 "Pure.gz |
677 "Pure.gz |
670 HOL.gz |
678 HOL.gz |
671 Pure-ProofGeneral.gz |
679 Pure-ProofGeneral.gz |
672 HOL-FooCommand.gz"} |
680 HOL-FoobarCommand.gz"} |
673 |
681 |
674 They are used for creating the keyword files with the command |
682 They are the ones we use for creating the keyword files. The corresponding Unix command |
|
683 is |
675 |
684 |
676 @{text [display] |
685 @{text [display] |
677 "$ isabelle keywords -k foo |
686 "$ isabelle keywords -k foobar |
678 $ISABELLE_LOGS/{Pure.gz,HOL.gz,Pure-ProofGeneral.gz,HOL-FooCommand.gz}"} |
687 $ISABELLE_LOGS/{Pure.gz,HOL.gz,Pure-ProofGeneral.gz,HOL-FoobarCommand.gz}"} |
679 |
688 |
680 The result is the file @{text "isar-keywords-foo.el"}. This file needs to be |
689 The result is the file @{text "isar-keywords-foobar.el"}. It should contain the |
681 copied to the directory @{text "~/.isabelle/etc"}. To make Isabelle use |
690 string @{text "foobar"} twice (check for example that @{text [quotes] "grep foobar |
682 this keyword file, we have to start it with the option @{text "-k foo"}, i.e. |
691 isar-keywords-foobar.el"} returns something non-empty). This keyword file |
683 |
692 needs to be copied into the directory @{text "~/.isabelle/etc"}. To make |
684 @{text [display] "isabelle -k foo <a-theory-file>"} |
693 Isabelle use this keyword file, we have to start it with the option @{text |
|
694 "-k foobar"}, i.e. |
|
695 |
|
696 @{text [display] "$ isabelle -k foobar a-theory-file"} |
685 |
697 |
686 If we now run the original code |
698 If we now run the original code |
687 |
699 *} |
688 @{ML [display] |
700 |
689 "let |
701 ML {* |
690 val do_nothing = Scan.succeed (Toplevel.theory (fn x => x)) |
702 let |
691 val flag = OuterKeyword.thy_decl |
703 val do_nothing = Scan.succeed (Toplevel.theory I) |
|
704 val kind = OuterKeyword.thy_decl |
692 in |
705 in |
693 OuterSyntax.command \"foo\" \"description of foo\" flag do_nothing |
706 OuterSyntax.command "foobar" "description of foobar" kind do_nothing |
694 end"} |
707 end |
695 |
708 *} |
696 then we can make use of \isacommand{foo}! Similarly with any other new command. |
709 |
|
710 text {* |
|
711 then we can make use of \isacommand{foobar}! Similarly with any other new command. |
697 |
712 |
698 In the example above, we built the theories on top of the HOL-logic. If you |
713 In the example above, we built the theories on top of the HOL-logic. If you |
699 target other logics, such as Nominal or ZF, then you need to change the |
714 target other logics, such as Nominal or ZF, then you need to adapt the |
700 log files appropriately. |
715 log files appropriately. |
701 |
716 |
|
717 At the moment, \isacommand{foobar} is not very useful. Let us refine it a bit |
|
718 by taking a proposition as argument and printing this proposition inside |
|
719 the tracing buffer. |
|
720 |
|
721 The crucial part of a command is the function that determines |
|
722 the behaviour of the command. In the code above we used the the |
|
723 @{text do_nothing}-function, which because of @{ML Scan.succeed} does not parse |
|
724 any argument, but immediately returns the simple toplevel function |
|
725 @{ML "Toplevel.theory I"}. We can replace this code by a function that first |
|
726 parses a proposition (using the parser @{ML OuterParse.prop}), then prints out |
|
727 the tracing information and finally does nothing. For this we can write |
|
728 *} |
|
729 |
|
730 ML {* |
|
731 let |
|
732 val trace_prop = |
|
733 OuterParse.prop >> (fn str => (tracing str; (Toplevel.theory I))) |
|
734 val kind = OuterKeyword.thy_decl |
|
735 in |
|
736 OuterSyntax.command "foobar" "traces a proposition" kind trace_prop |
|
737 end |
|
738 *} |
|
739 |
|
740 text {* |
|
741 Now we can type for example |
|
742 |
|
743 @{ML_response_fake_both [display] "foobar \"True \<and> False\"" "True \<and> False"} |
|
744 |
|
745 and see the proposition in the tracing buffer. |
|
746 |
|
747 Note that so far we used @{ML thy_decl in OuterKeyword} as kind indicator |
|
748 for the command. This means that the command finishes as soon as the |
|
749 arguments are processed. Examples of this kind of commands are |
|
750 \isacommand{definition} and \isacommand{declare}. In other cases, however, |
|
751 commands are expected to parse some arguments, for example a proposition, |
|
752 and then ``open up'' a proof in order to prove the proposition (think of |
|
753 \isacommand{lemma}) or prove some other properties (for example in |
|
754 \isacommand{function}). To achieve this behaviour we have to use the kind |
|
755 indicator @{ML thy_goal in OuterKeyword}. |
|
756 |
|
757 Below we change \isacommand{foobar} is such a way that an proposition as |
|
758 argument and then start a proof in order to prove it. Therefore in Line 13 |
|
759 below, we set the kind indicator to @{ML thy_goal in OuterKeyword}. |
|
760 *} |
|
761 |
|
762 ML %linenumbers {*let |
|
763 fun set_up_thm str ctxt = |
|
764 let |
|
765 val prop = Syntax.read_prop ctxt str |
|
766 in |
|
767 Proof.theorem_i NONE (K I) [[(Syntax.read_prop ctxt str,[])]] ctxt |
|
768 end; |
|
769 |
|
770 val prove_prop = OuterParse.prop >> |
|
771 (fn str => Toplevel.print o |
|
772 Toplevel.local_theory_to_proof NONE (set_up_thm str)) |
|
773 |
|
774 val kind = OuterKeyword.thy_goal |
|
775 in |
|
776 OuterSyntax.command "foobar" "proving a proposition" kind prove_prop |
|
777 end |
|
778 *} |
|
779 |
|
780 text {* |
|
781 The function @{text set_up_thm} takes a string (the proposition) and a context. |
|
782 The context is necessary in order to convert the string into a proper proposition |
|
783 using the function @{ML Syntax.read_prop}. In Line 6 we use the function |
|
784 @{ML Proof.theorem_i} to start the proof for the proposition. In Lines 9 to |
|
785 11 contain the parser for the proposition. |
|
786 |
|
787 If we now type @{text "foobar \"True \<and> True\""}, we obtain the following |
|
788 proof state: |
|
789 |
|
790 @{ML_response_fake_both [display] "foobar \"True \<and> True\"" |
|
791 "goal (1 subgoal): |
|
792 1. True \<and> True"} |
|
793 |
|
794 and we can build the proof |
|
795 |
|
796 @{text [display] "foobar \"True \<and> True\" |
|
797 apply(rule conjI) |
|
798 apply(rule TrueI)+ |
|
799 done"} |
|
800 |
|
801 (FIXME What does @{text "Toplevel.theory"}?) |
|
802 |
702 (FIXME Explain @{text "OuterKeyword.thy_decl"} and parser) |
803 (FIXME Explain @{text "OuterKeyword.thy_decl"} and parser) |
703 |
804 |
704 |
805 *} |
705 *} |
|
706 |
|
707 |
|
708 |
|
709 |
806 |
710 chapter {* Parsing *} |
807 chapter {* Parsing *} |
711 |
808 |
712 text {* |
809 text {* |
713 |
810 |