144 val just_h = $$ \"h\" --| $$ \"e\" |
144 val just_h = $$ \"h\" --| $$ \"e\" |
145 val input = Symbol.explode \"hello\" |
145 val input = Symbol.explode \"hello\" |
146 in |
146 in |
147 (just_e input, just_h input) |
147 (just_e input, just_h input) |
148 end" |
148 end" |
149 "((\"e\", [\"l\", \"l\", \"o\"]),(\"h\", [\"l\", \"l\", \"o\"]))"} |
149 "((\"e\", [\"l\", \"l\", \"o\"]), (\"h\", [\"l\", \"l\", \"o\"]))"} |
150 |
150 |
151 The parser @{ML "Scan.optional p x" for p x} returns the result of the parser |
151 The parser @{ML "Scan.optional p x" for p x} returns the result of the parser |
152 @{text "p"}, if it succeeds; otherwise it returns |
152 @{text "p"}, if it succeeds; otherwise it returns |
153 the default value @{text "x"}. For example: |
153 the default value @{text "x"}. For example: |
154 |
154 |
422 @{ML_response_fake [display,gray] "OuterSyntax.scan Position.none \"hello world\"" |
422 @{ML_response_fake [display,gray] "OuterSyntax.scan Position.none \"hello world\"" |
423 "[Token (\<dots>,(Keyword, \"hello\"),\<dots>), |
423 "[Token (\<dots>,(Keyword, \"hello\"),\<dots>), |
424 Token (\<dots>,(Space, \" \"),\<dots>), |
424 Token (\<dots>,(Space, \" \"),\<dots>), |
425 Token (\<dots>,(Ident, \"world\"),\<dots>)]"} |
425 Token (\<dots>,(Ident, \"world\"),\<dots>)]"} |
426 |
426 |
427 Many parsing functions later on will require spaces, comments and the like |
427 Many parsing functions later on will require white space, comments and the like |
428 to have already been filtered out. So from now on we are going to use the |
428 to have already been filtered out. So from now on we are going to use the |
429 functions @{ML filter} and @{ML OuterLex.is_proper} to do this. For example: |
429 functions @{ML filter} and @{ML OuterLex.is_proper} to do this. For example: |
430 |
430 |
431 @{ML_response_fake [display,gray] |
431 @{ML_response_fake [display,gray] |
432 "let |
432 "let |
450 "[Token (\<dots>,(Command, \"inductive\"),\<dots>), |
450 "[Token (\<dots>,(Command, \"inductive\"),\<dots>), |
451 Token (\<dots>,(Keyword, \"|\"),\<dots>), |
451 Token (\<dots>,(Keyword, \"|\"),\<dots>), |
452 Token (\<dots>,(Keyword, \"for\"),\<dots>)]"} |
452 Token (\<dots>,(Keyword, \"for\"),\<dots>)]"} |
453 |
453 |
454 you obtain a list consisting of only one command and two keyword tokens. |
454 you obtain a list consisting of only one command and two keyword tokens. |
455 If you want to see which keywords and commands are currently known to Isabelle, type in |
455 If you want to see which keywords and commands are currently known to Isabelle, |
456 the following code (you might have to adjust the @{ML print_depth} in order to |
456 type: |
457 see the complete list): |
|
458 |
457 |
459 @{ML_response_fake [display,gray] |
458 @{ML_response_fake [display,gray] |
460 "let |
459 "let |
461 val (keywords, commands) = OuterKeyword.get_lexicons () |
460 val (keywords, commands) = OuterKeyword.get_lexicons () |
462 in |
461 in |
463 (Scan.dest_lexicon commands, Scan.dest_lexicon keywords) |
462 (Scan.dest_lexicon commands, Scan.dest_lexicon keywords) |
464 end" |
463 end" |
465 "([\"}\", \"{\", \<dots>], [\"\<rightleftharpoons>\", \"\<leftharpoondown>\", \<dots>])"} |
464 "([\"}\", \"{\", \<dots>], [\"\<rightleftharpoons>\", \"\<leftharpoondown>\", \<dots>])"} |
|
465 |
|
466 You might have to adjust the @{ML print_depth} in order to |
|
467 see the complete list. |
466 |
468 |
467 The parser @{ML "OuterParse.$$$"} parses a single keyword. For example: |
469 The parser @{ML "OuterParse.$$$"} parses a single keyword. For example: |
468 |
470 |
469 @{ML_response [display,gray] |
471 @{ML_response [display,gray] |
470 "let |
472 "let |
577 Token ((\"bar\", ({line=8, end_line=8}, {line=8})), (Ident, \"bar\"), \<dots>)]"} |
579 Token ((\"bar\", ({line=8, end_line=8}, {line=8})), (Ident, \"bar\"), \<dots>)]"} |
578 |
580 |
579 in which the @{text [quotes] "\\n"} causes the second token to be in |
581 in which the @{text [quotes] "\\n"} causes the second token to be in |
580 line 8. |
582 line 8. |
581 |
583 |
582 By using the parser @{ML OuterParse.position} you can decode the positional |
584 By using the parser @{ML OuterParse.position} you can access the token position |
583 information and return it as part of the parsed input. For example |
585 and return it as part of the parser result. For example |
584 |
586 |
585 @{ML_response_fake [display,gray] |
587 @{ML_response_fake [display,gray] |
586 "let |
588 "let |
587 val input = (filtered_input' \"where\") |
589 val input = filtered_input' \"where\" |
588 in |
590 in |
589 parse (OuterParse.position (OuterParse.$$$ \"where\")) input |
591 parse (OuterParse.position (OuterParse.$$$ \"where\")) input |
590 end" |
592 end" |
591 "((\"where\", {line=7, end_line=7}), [])"} |
593 "((\"where\", {line=7, end_line=7}), [])"} |
592 |
594 |
681 OuterParse.!!! |
683 OuterParse.!!! |
682 (OuterParse.enum1 "|" |
684 (OuterParse.enum1 "|" |
683 (SpecParse.opt_thm_name ":" -- OuterParse.prop))) []*} |
685 (SpecParse.opt_thm_name ":" -- OuterParse.prop))) []*} |
684 |
686 |
685 text {* |
687 text {* |
686 Note that the parser does not parse the keyword \simpleinductive, even if it is |
688 Note that the parser must not parse the keyword \simpleinductive, even if it is |
687 meant to process definitions as shown above. The parser of the keyword |
689 meant to process definitions as shown above. The parser of the keyword |
688 will be given by the infrastructure that will eventually call @{ML spec_parser}. |
690 will be given by the infrastructure that will eventually call @{ML spec_parser}. |
689 |
691 |
690 |
692 |
691 To see what the parser returns, let us parse the string corresponding to the |
693 To see what the parser returns, let us parse the string corresponding to the |
732 |
734 |
733 text {* |
735 text {* |
734 Whenever types are given, they are stored in the @{ML SOME}s. The types are |
736 Whenever types are given, they are stored in the @{ML SOME}s. The types are |
735 not yet used to type the variables: this must be done by type-inference later |
737 not yet used to type the variables: this must be done by type-inference later |
736 on. Since types are part of the inner syntax they are strings with some |
738 on. Since types are part of the inner syntax they are strings with some |
737 encoded information (see previous section). If a syntax translation is |
739 encoded information (see previous section). If a mixfix-syntax is |
738 present for a variable, then it is stored in the @{ML Mixfix} data structure; |
740 present for a variable, then it is stored in the @{ML Mixfix} data structure; |
739 no syntax translation is indicated by @{ML NoSyn}. |
741 no syntax translation is indicated by @{ML NoSyn}. |
740 |
742 |
741 \begin{readmore} |
743 \begin{readmore} |
742 The data structure for syntax annotations is defined in @{ML_file "Pure/Syntax/mixfix.ML"}. |
744 The data structure for mixfix annotations is defined in @{ML_file "Pure/Syntax/mixfix.ML"}. |
743 \end{readmore} |
745 \end{readmore} |
744 |
746 |
745 Lines 3 to 7 in the function @{ML spec_parser} implement the parser for a |
747 Lines 3 to 7 in the function @{ML spec_parser} implement the parser for a |
746 list of introduction rules, that is propositions with theorem annotations |
748 list of introduction rules, that is propositions with theorem annotations |
747 such as rule names and attributes. The introduction rules are propositions |
749 such as rule names and attributes. The introduction rules are propositions |
856 OuterSyntax.local_theory \"foobar\" \"description of foobar\" kind do_nothing |
858 OuterSyntax.local_theory \"foobar\" \"description of foobar\" kind do_nothing |
857 end"}\\ |
859 end"}\\ |
858 @{text "\<verbclose>"}\\ |
860 @{text "\<verbclose>"}\\ |
859 \isacommand{end} |
861 \isacommand{end} |
860 \end{graybox} |
862 \end{graybox} |
861 \caption{The file @{text "Command.thy"} is necessary for generating a log |
863 \caption{This file can be used to generate a log file. This log file in turn can |
862 file. This log file enables Isabelle to generate a keyword file containing |
864 be used to generate a keyword file containing the command \isacommand{foobar}. |
863 the command \isacommand{foobar}.\label{fig:commandtheory}} |
865 \label{fig:commandtheory}} |
864 \end{figure} |
866 \end{figure} |
865 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
867 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
866 |
868 |
867 For our purposes it is sufficient to use the log files of the theories |
869 For our purposes it is sufficient to use the log files of the theories |
868 @{text "Pure"}, @{text "HOL"} and @{text "Pure-ProofGeneral"}, as well as |
870 @{text "Pure"}, @{text "HOL"} and @{text "Pure-ProofGeneral"}, as well as |
907 @{text [display] "$ isabelle make"} |
909 @{text [display] "$ isabelle make"} |
908 |
910 |
909 If the compilation succeeds, you have finally created all the necessary log files. |
911 If the compilation succeeds, you have finally created all the necessary log files. |
910 They are stored in the directory |
912 They are stored in the directory |
911 |
913 |
912 @{text [display] "~/.isabelle/heaps/Isabelle2008/polyml-5.2.1_x86-linux/log"} |
914 @{text [display] "~/.isabelle/heaps/Isabelle2009/polyml-5.2.1_x86-linux/log"} |
913 |
915 |
914 or something similar depending on your Isabelle distribution and architecture. |
916 or something similar depending on your Isabelle distribution and architecture. |
915 One quick way to assign a shell variable to this directory is by typing |
917 One quick way to assign a shell variable to this directory is by typing |
916 |
918 |
917 @{text [display] "$ ISABELLE_LOGS=\"$(isabelle getenv -b ISABELLE_OUTPUT)\"/log"} |
919 @{text [display] "$ ISABELLE_LOGS=\"$(isabelle getenv -b ISABELLE_OUTPUT)\"/log"} |
935 |
937 |
936 The result is the file @{text "isar-keywords-foobar.el"}. It should contain |
938 The result is the file @{text "isar-keywords-foobar.el"}. It should contain |
937 the string @{text "foobar"} twice.\footnote{To see whether things are fine, check |
939 the string @{text "foobar"} twice.\footnote{To see whether things are fine, check |
938 that @{text "grep foobar"} on this file returns something |
940 that @{text "grep foobar"} on this file returns something |
939 non-empty.} This keyword file needs to |
941 non-empty.} This keyword file needs to |
940 be copied into the directory @{text "~/.isabelle/etc"}. To make Isabelle |
942 be copied into the directory @{text "~/.isabelle/etc"}. To make ProofGeneral |
941 aware of this keyword file, you have to start Isabelle with the option @{text |
943 aware of this keyword file, you have to start Isabelle with the option @{text |
942 "-k foobar"}, that is: |
944 "-k foobar"}, that is: |
943 |
945 |
944 |
946 |
945 @{text [display] "$ isabelle emacs -k foobar a_theory_file"} |
947 @{text [display] "$ isabelle emacs -k foobar a_theory_file"} |
1008 argument and then starts a proof in order to prove it. Therefore in Line 13, |
1010 argument and then starts a proof in order to prove it. Therefore in Line 13, |
1009 we set the kind indicator to @{ML thy_goal in OuterKeyword}. |
1011 we set the kind indicator to @{ML thy_goal in OuterKeyword}. |
1010 *} |
1012 *} |
1011 |
1013 |
1012 ML%linenosgray{*let |
1014 ML%linenosgray{*let |
1013 fun prove_prop str ctxt = |
1015 fun prove_prop str lthy = |
1014 let |
1016 let |
1015 val prop = Syntax.read_prop ctxt str |
1017 val prop = Syntax.read_prop lthy str |
1016 in |
1018 in |
1017 Proof.theorem_i NONE (K I) [[(prop,[])]] ctxt |
1019 Proof.theorem_i NONE (K I) [[(prop,[])]] lthy |
1018 end; |
1020 end; |
1019 |
1021 |
1020 val prove_prop_parser = OuterParse.prop >> prove_prop |
1022 val prove_prop_parser = OuterParse.prop >> prove_prop |
1021 val kind = OuterKeyword.thy_goal |
1023 val kind = OuterKeyword.thy_goal |
1022 in |
1024 in |
1073 \end{isabelle} |
1076 \end{isabelle} |
1074 |
1077 |
1075 An example of a very simple method is: |
1078 An example of a very simple method is: |
1076 *} |
1079 *} |
1077 |
1080 |
1078 method_setup %gray foobar_meth = |
1081 method_setup %gray foobar = |
1079 {* Scan.succeed |
1082 {* Scan.succeed |
1080 (K (SIMPLE_METHOD ((etac @{thm conjE} THEN' rtac @{thm conjI}) 1))) *} |
1083 (K (SIMPLE_METHOD ((etac @{thm conjE} THEN' rtac @{thm conjI}) 1))) *} |
1081 "foobar method for conjE and conjI" |
1084 "foobar method for conjE and conjI" |
1082 |
1085 |
1083 text {* |
1086 text {* |
1084 It defines the method @{text foobar_meth}, which takes no arguments (therefore the |
1087 It defines the method @{text foobar}, which takes no arguments (therefore the |
1085 parser @{ML Scan.succeed}) and only applies a single tactic, namely the tactic which |
1088 parser @{ML Scan.succeed}) and only applies a single tactic, namely the tactic which |
1086 applies @{thm [source] conjE} and then @{thm [source] conjI}. The function @{ML SIMPLE_METHOD} |
1089 applies @{thm [source] conjE} and then @{thm [source] conjI}. The function @{ML SIMPLE_METHOD} |
1087 turns such a tactic into a method. @{text "Foobar_meth"} can be used as follows |
1090 turns such a tactic into a method. The method @{text "foobar"} can be used as follows |
1088 *} |
1091 *} |
1089 |
1092 |
1090 lemma shows "A \<and> B \<Longrightarrow> C \<and> D" |
1093 lemma shows "A \<and> B \<Longrightarrow> C \<and> D" |
1091 apply(foobar_meth) |
1094 apply(foobar) |
1092 txt {* |
1095 txt {* |
1093 where it results in the goal state |
1096 where it results in the goal state |
1094 |
1097 |
1095 \begin{minipage}{\textwidth} |
1098 \begin{minipage}{\textwidth} |
1096 @{subgoals} |
1099 @{subgoals} |