1 theory Parsing |
1 theory Parsing |
2 imports Base "Package/Simple_Inductive_Package" |
2 imports Base "Helper/Command/Command" "Package/Simple_Inductive_Package" |
3 uses "Parsing.ML" |
3 uses "Parsing.ML" |
4 begin |
4 begin |
5 |
5 |
6 chapter {* Parsing *} |
6 chapter {* Parsing *} |
7 |
7 |
8 text {* |
8 text {* |
9 |
9 Isabelle distinguishes between \emph{outer} and \emph{inner} |
10 Isabelle distinguishes between \emph{outer} and \emph{inner} syntax. |
10 syntax. Commands, such as \isacommand{definition}, \isacommand{inductive} |
11 Commands, such as \isacommand{definition}, \isacommand{inductive} and so |
11 and so on, belong to the outer syntax, whereas terms, types and so on belong |
12 on, belong to the outer syntax, whereas terms, types and so on belong to the |
12 to the inner syntax. For parsing inner syntax, Isabelle uses a rather |
13 inner syntax. For parsing inner syntax, |
13 general and sophisticated algorithm, which is driven by priority |
14 Isabelle uses a rather general and sophisticated algorithm, which |
14 grammars. Parsers for outer syntax are built up by functional parsing |
15 is driven by priority grammars. Parsers for outer syntax are built up by functional |
15 combinators. These combinators are a well-established technique for parsing, |
16 parsing combinators. These combinators are a well-established technique for parsing, |
16 which has, for example, been described in Paulson's classic ML-book |
17 which has, for example, been described in Paulson's classic ML-book \cite{paulson-ml2}. |
17 \cite{paulson-ml2}. Isabelle developers are usually concerned with writing |
18 Isabelle developers are usually concerned with writing these outer syntax parsers, |
18 these outer syntax parsers, either for new definitional packages or for |
19 either for new definitional packages or for calling methods with specific arguments. |
19 calling methods with specific arguments. |
20 |
20 |
21 \begin{readmore} |
21 \begin{readmore} |
22 The library for writing parser combinators is split up, roughly, into two |
22 The library for writing parser combinators is split up, roughly, into two |
23 parts. The first part consists of a collection of generic parser combinators |
23 parts. The first part consists of a collection of generic parser combinators |
24 defined in the structure @{ML_struct Scan} in the file @{ML_file |
24 defined in the structure @{ML_struct Scan} in the file @{ML_file |
25 "Pure/General/scan.ML"}. The second part of the library consists of |
25 "Pure/General/scan.ML"}. The second part of the library consists of |
26 combinators for dealing with specific token types, which are defined in the |
26 combinators for dealing with specific token types, which are defined in the |
27 structure @{ML_struct OuterParse} in the file @{ML_file |
27 structure @{ML_struct OuterParse} in the file @{ML_file |
28 "Pure/Isar/outer_parse.ML"}. Specific parsers for packages are defined |
28 "Pure/Isar/outer_parse.ML"}. Specific parsers for packages are defined in |
29 in @{ML_file "Pure/Isar/spec_parse.ML"}. Parsers for method arguments |
29 @{ML_file "Pure/Isar/spec_parse.ML"}. Parsers for method arguments are |
30 are defined in @{ML_file "Pure/Isar/args.ML"}. |
30 defined in @{ML_file "Pure/Isar/args.ML"}. |
31 \end{readmore} |
31 \end{readmore} |
32 |
32 |
33 *} |
33 *} |
34 |
34 |
35 section {* Building Generic Parsers *} |
35 section {* Building Generic Parsers *} |
134 in |
134 in |
135 (hw input1, hw input2) |
135 (hw input1, hw input2) |
136 end" |
136 end" |
137 "((\"h\", [\"e\", \"l\", \"l\", \"o\"]), (\"w\", [\"o\", \"r\", \"l\", \"d\"]))"} |
137 "((\"h\", [\"e\", \"l\", \"l\", \"o\"]), (\"w\", [\"o\", \"r\", \"l\", \"d\"]))"} |
138 |
138 |
139 The functions @{ML_ind "|--"} and @{ML_ind "--|"} work like the sequencing function |
139 The functions @{ML_ind "|--"} and @{ML_ind "--|"} work like the sequencing |
140 for parsers, except that they discard the item being parsed by the first (respectively second) |
140 function for parsers, except that they discard the item being parsed by the |
141 parser. For example: |
141 first (respectively second) parser. For example: |
142 |
142 |
143 @{ML_response [display,gray] |
143 @{ML_response [display,gray] |
144 "let |
144 "let |
145 val just_e = $$ \"h\" |-- $$ \"e\" |
145 val just_e = $$ \"h\" |-- $$ \"e\" |
146 val just_h = $$ \"h\" --| $$ \"e\" |
146 val just_h = $$ \"h\" --| $$ \"e\" |
942 @{text [display] |
942 @{text [display] |
943 "$ isabelle keywords -k foobar |
943 "$ isabelle keywords -k foobar |
944 $ISABELLE_LOGS/{Pure.gz,HOL.gz,Pure-ProofGeneral.gz,HOL-FoobarCommand.gz}"} |
944 $ISABELLE_LOGS/{Pure.gz,HOL.gz,Pure-ProofGeneral.gz,HOL-FoobarCommand.gz}"} |
945 |
945 |
946 The result is the file @{text "isar-keywords-foobar.el"}. It should contain |
946 The result is the file @{text "isar-keywords-foobar.el"}. It should contain |
947 the string @{text "foobar"} twice.\footnote{To see whether things are fine, check |
947 the string @{text "foobar"} twice.\footnote{To see whether things are fine, |
948 that @{text "grep foobar"} on this file returns something |
948 check that @{text "grep foobar"} on this file returns something non-empty.} |
949 non-empty.} This keyword file needs to |
949 This keyword file needs to be copied into the directory @{text |
950 be copied into the directory @{text "~/.isabelle/etc"}. To make ProofGeneral |
950 "~/.isabelle/etc"}. To make ProofGeneral aware of it, you have to start |
951 aware of this keyword file, you have to start Isabelle with the option @{text |
951 Isabelle with the option @{text "-k foobar"}, that is: |
952 "-k foobar"}, that is: |
|
953 |
952 |
954 |
953 |
955 @{text [display] "$ isabelle emacs -k foobar a_theory_file"} |
954 @{text [display] "$ isabelle emacs -k foobar a_theory_file"} |
956 |
955 |
957 If you now build a theory on top of @{text "Command.thy"}, |
956 If you now build a theory on top of @{text "Command.thy"}, |
958 then the command \isacommand{foobar} can be used. |
957 then the command \isacommand{foobar} can be used. You can just write |
959 Similarly with any other new command, and also any new keyword that is |
958 *} |
960 introduced with |
959 |
|
960 foobar |
|
961 |
|
962 text {* |
|
963 but you will not see any action as we chose to implement this command to do |
|
964 nothing. A similarly procedure has to be done with any other new command, and |
|
965 also any new keyword that is introduced with @{ML_ind OuterKeyword.keyword}. |
|
966 |
961 *} |
967 *} |
962 |
968 |
963 ML{*val _ = OuterKeyword.keyword "blink" *} |
969 ML{*val _ = OuterKeyword.keyword "blink" *} |
964 |
970 |
965 text {* |
971 text {* |
966 At the moment the command \isacommand{foobar} is not very useful. Let us refine |
972 At the moment the command \isacommand{foobar} is not very useful. Let us |
967 it a bit |
973 refine it a bit next by letting it take a proposition as argument and |
968 next by letting it take a proposition as argument and printing this proposition |
974 printing this proposition inside the tracing buffer. |
969 inside the tracing buffer. |
|
970 |
975 |
971 The crucial part of a command is the function that determines the behaviour |
976 The crucial part of a command is the function that determines the behaviour |
972 of the command. In the code above we used a ``do-nothing''-function, which |
977 of the command. In the code above we used a ``do-nothing''-function, which |
973 because of @{ML_ind succeed in Scan} does not parse any argument, but immediately |
978 because of @{ML_ind succeed in Scan} does not parse any argument, but immediately |
974 returns the simple function @{ML "LocalTheory.theory I"}. We can |
979 returns the simple function @{ML "LocalTheory.theory I"}. We can |
983 LocalTheory.theory (fn lthy => (tracing str; lthy)) |
988 LocalTheory.theory (fn lthy => (tracing str; lthy)) |
984 |
989 |
985 val trace_prop_parser = OuterParse.prop >> trace_prop |
990 val trace_prop_parser = OuterParse.prop >> trace_prop |
986 val kind = OuterKeyword.thy_decl |
991 val kind = OuterKeyword.thy_decl |
987 in |
992 in |
988 OuterSyntax.local_theory "foobar" "traces a proposition" |
993 OuterSyntax.local_theory "foobar_trace" "traces a proposition" |
989 kind trace_prop_parser |
994 kind trace_prop_parser |
990 end *} |
995 end *} |
991 |
996 |
992 |
997 text {* |
993 text {* |
998 The command is now \isacommand{foobar\_trace} and can be used to |
994 Now you can type |
999 see the proposition in the tracing buffer. |
995 |
1000 *} |
996 \begin{isabelle} |
1001 |
997 \isacommand{foobar}~@{text [quotes] "True \<and> False"}\\ |
1002 foobar_trace "True \<and> False" |
998 @{text "> \"True \<and> False\""} |
1003 |
999 \end{isabelle} |
1004 text {* |
1000 |
|
1001 and see the proposition in the tracing buffer. |
|
1002 |
|
1003 Note that so far we used @{ML_ind thy_decl in OuterKeyword} as the kind |
1005 Note that so far we used @{ML_ind thy_decl in OuterKeyword} as the kind |
1004 indicator for the command. This means that the command finishes as soon as |
1006 indicator for the command. This means that the command finishes as soon as |
1005 the arguments are processed. Examples of this kind of commands are |
1007 the arguments are processed. Examples of this kind of commands are |
1006 \isacommand{definition} and \isacommand{declare}. In other cases, commands |
1008 \isacommand{definition} and \isacommand{declare}. In other cases, commands |
1007 are expected to parse some arguments, for example a proposition, and then |
1009 are expected to parse some arguments, for example a proposition, and then |
1012 "local_theory_to_proof" in OuterSyntax} to set up the command. Note, |
1014 "local_theory_to_proof" in OuterSyntax} to set up the command. Note, |
1013 however, once you change the ``kind'' of a command from @{ML thy_decl in |
1015 however, once you change the ``kind'' of a command from @{ML thy_decl in |
1014 OuterKeyword} to @{ML thy_goal in OuterKeyword} then the keyword file needs |
1016 OuterKeyword} to @{ML thy_goal in OuterKeyword} then the keyword file needs |
1015 to be re-created! |
1017 to be re-created! |
1016 |
1018 |
1017 Below we change \isacommand{foobar} so that it takes a proposition as |
1019 Below we change \isacommand{foobar\_trace} so that it takes a proposition as |
1018 argument and then starts a proof in order to prove it. Therefore in Line 13, |
1020 argument and then starts a proof in order to prove it. Therefore in Line 13, |
1019 we set the kind indicator to @{ML thy_goal in OuterKeyword}. |
1021 we set the kind indicator to @{ML thy_goal in OuterKeyword}. |
1020 *} |
1022 *} |
1021 |
1023 |
1022 ML%linenosgray{*let |
1024 ML%linenosgray{*let |
1042 proposition. Its argument @{ML NONE} stands for a locale (which we chose to |
1044 proposition. Its argument @{ML NONE} stands for a locale (which we chose to |
1043 omit); the argument @{ML "(K I)"} stands for a function that determines what |
1045 omit); the argument @{ML "(K I)"} stands for a function that determines what |
1044 should be done with the theorem once it is proved (we chose to just forget |
1046 should be done with the theorem once it is proved (we chose to just forget |
1045 about it). Line 9 contains the parser for the proposition. |
1047 about it). Line 9 contains the parser for the proposition. |
1046 |
1048 |
1047 If you now type \isacommand{foobar}~@{text [quotes] "True \<and> True"}, you obtain the following |
1049 If you now type \isacommand{foobar\_goal}~@{text [quotes] "True \<and> True"}, |
1048 proof state |
1050 you obtain the following proof state |
1049 |
1051 *} |
1050 \begin{isabelle} |
1052 |
1051 \isacommand{foobar}~@{text [quotes] "True \<and> True"}\\ |
1053 foobar_goal "True \<and> True" |
1052 @{text "goal (1 subgoal):"}\\ |
1054 txt {* |
1053 @{text "1. True \<and> True"} |
1055 \begin{minipage}{\textwidth} |
1054 \end{isabelle} |
1056 @{subgoals [display]} |
1055 |
1057 \end{minipage}\medskip |
1056 and you can build the following proof |
1058 |
1057 |
1059 and can prove the proposition as follows. |
1058 \begin{isabelle} |
1060 *} |
1059 \isacommand{foobar}~@{text [quotes] "True \<and> True"}\\ |
1061 apply(rule conjI) |
1060 \isacommand{apply}@{text "(rule conjI)"}\\ |
1062 apply(rule TrueI)+ |
1061 \isacommand{apply}@{text "(rule TrueI)+"}\\ |
1063 done |
1062 \isacommand{done} |
1064 |
1063 \end{isabelle} |
1065 text {* |
1064 |
1066 |
1065 (FIXME: read a name and show how to store theorems; see @{ML_ind note in LocalTheory}) |
1067 (FIXME: read a name and show how to store theorems; see @{ML_ind note in LocalTheory}) |
1066 |
1068 |
1067 *} |
1069 *} |
|
1070 |
|
1071 ML_val{*val r = ref (NONE:(unit -> term) option)*} |
|
1072 ML{*let |
|
1073 fun setup_proof (txt, pos) lthy = |
|
1074 let |
|
1075 val trm = ML_Context.evaluate lthy true ("r", r) txt |
|
1076 in |
|
1077 Proof.theorem_i NONE (K I) [[(trm,[])]] lthy |
|
1078 end; |
|
1079 |
|
1080 val setup_proof_parser = OuterParse.ML_source >> setup_proof |
|
1081 |
|
1082 val kind = OuterKeyword.thy_goal |
|
1083 in |
|
1084 OuterSyntax.local_theory_to_proof "foobar_prove" "proving a proposition" |
|
1085 kind setup_proof_parser |
|
1086 end*} |
|
1087 |
|
1088 foobar_prove {* @{prop "True"} *} |
|
1089 apply(rule TrueI) |
|
1090 done |
1068 |
1091 |
1069 |
1092 |
1070 |
1093 |
1071 |
1094 |
1072 section {* Methods (TBD) *} |
1095 section {* Methods (TBD) *} |