2 imports Base "Package/Simple_Inductive_Package" |
2 imports Base "Package/Simple_Inductive_Package" |
3 keywords "foobar" "foobar_trace" :: thy_decl and |
3 keywords "foobar" "foobar_trace" :: thy_decl and |
4 "foobar_goal" "foobar_prove" :: thy_goal |
4 "foobar_goal" "foobar_prove" :: thy_goal |
5 begin |
5 begin |
6 |
6 |
7 chapter {* Parsing\label{chp:parsing} *} |
7 chapter \<open>Parsing\label{chp:parsing}\<close> |
8 |
8 |
9 |
9 |
10 text {* |
10 text \<open> |
11 \begin{flushright} |
11 \begin{flushright} |
12 {\em An important principle underlying the success and popularity of Unix\\ is |
12 {\em An important principle underlying the success and popularity of Unix\\ is |
13 the philosophy of building on the work of others.} \\[1ex] |
13 the philosophy of building on the work of others.} \\[1ex] |
14 Tony Travis in an email about the\\ ``LINUX is obsolete'' debate |
14 Tony Travis in an email about the\\ ``LINUX is obsolete'' debate |
15 \end{flushright} |
15 \end{flushright} |
45 "Pure/Isar/parse.ML"}. In addition specific parsers for packages are |
45 "Pure/Isar/parse.ML"}. In addition specific parsers for packages are |
46 defined in @{ML_file "Pure/Isar/parse_spec.ML"}. Parsers for method arguments |
46 defined in @{ML_file "Pure/Isar/parse_spec.ML"}. Parsers for method arguments |
47 are defined in @{ML_file "Pure/Isar/args.ML"}. |
47 are defined in @{ML_file "Pure/Isar/args.ML"}. |
48 \end{readmore} |
48 \end{readmore} |
49 |
49 |
50 *} |
50 \<close> |
51 |
51 |
52 section {* Building Generic Parsers *} |
52 section \<open>Building Generic Parsers\<close> |
53 |
53 |
54 text {* |
54 text \<open> |
55 |
55 |
56 Let us first have a look at parsing strings using generic parsing |
56 Let us first have a look at parsing strings using generic parsing |
57 combinators. The function @{ML_ind "$$" in Scan} takes a string as argument and will |
57 combinators. The function @{ML_ind "$$" in Scan} takes a string as argument and will |
58 ``consume'' this string from a given input list of strings. ``Consume'' in |
58 ``consume'' this string from a given input list of strings. ``Consume'' in |
59 this context means that it will return a pair consisting of this string and |
59 this context means that it will return a pair consisting of this string and |
64 |
64 |
65 @{ML_response [display,gray] |
65 @{ML_response [display,gray] |
66 "($$ \"w\") (Symbol.explode \"world\")" "(\"w\", [\"o\", \"r\", \"l\", \"d\"])"} |
66 "($$ \"w\") (Symbol.explode \"world\")" "(\"w\", [\"o\", \"r\", \"l\", \"d\"])"} |
67 |
67 |
68 The function @{ML "$$"} will either succeed (as in the two examples above) |
68 The function @{ML "$$"} will either succeed (as in the two examples above) |
69 or raise the exception @{text "FAIL"} if no string can be consumed. For |
69 or raise the exception \<open>FAIL\<close> if no string can be consumed. For |
70 example trying to parse |
70 example trying to parse |
71 |
71 |
72 @{ML_response_fake [display,gray] |
72 @{ML_response_fake [display,gray] |
73 "($$ \"x\") (Symbol.explode \"world\")" |
73 "($$ \"x\") (Symbol.explode \"world\")" |
74 "Exception FAIL raised"} |
74 "Exception FAIL raised"} |
75 |
75 |
76 will raise the exception @{text "FAIL"}. The function @{ML_ind "$$" in Scan} will also |
76 will raise the exception \<open>FAIL\<close>. The function @{ML_ind "$$" in Scan} will also |
77 fail if you try to consume more than a single character. |
77 fail if you try to consume more than a single character. |
78 |
78 |
79 There are three exceptions that are raised by the parsing combinators: |
79 There are three exceptions that are raised by the parsing combinators: |
80 |
80 |
81 \begin{itemize} |
81 \begin{itemize} |
82 \item @{text "FAIL"} indicates that alternative routes of parsing |
82 \item \<open>FAIL\<close> indicates that alternative routes of parsing |
83 might be explored. |
83 might be explored. |
84 \item @{text "MORE"} indicates that there is not enough input for the parser. For example |
84 \item \<open>MORE\<close> indicates that there is not enough input for the parser. For example |
85 in @{text "($$ \"h\") []"}. |
85 in \<open>($$ "h") []\<close>. |
86 \item @{text "ABORT"} indicates that a dead end is reached. |
86 \item \<open>ABORT\<close> indicates that a dead end is reached. |
87 It is used for example in the function @{ML "!!"} (see below). |
87 It is used for example in the function @{ML "!!"} (see below). |
88 \end{itemize} |
88 \end{itemize} |
89 |
89 |
90 However, note that these exceptions are private to the parser and cannot be accessed |
90 However, note that these exceptions are private to the parser and cannot be accessed |
91 by the programmer (for example to handle them). |
91 by the programmer (for example to handle them). |
92 |
92 |
93 In the examples above we use the function @{ML_ind explode in Symbol} from the |
93 In the examples above we use the function @{ML_ind explode in Symbol} from the |
94 structure @{ML_struct Symbol}, instead of the more standard library function |
94 structure @{ML_struct Symbol}, instead of the more standard library function |
95 @{ML_ind explode in String}, for obtaining an input list for the parser. The reason is |
95 @{ML_ind explode in String}, for obtaining an input list for the parser. The reason is |
96 that @{ML explode in Symbol} is aware of character |
96 that @{ML explode in Symbol} is aware of character |
97 sequences, for example @{text "\<foo>"}, that have a special meaning in |
97 sequences, for example \<open>\<foo>\<close>, that have a special meaning in |
98 Isabelle. To see the difference consider |
98 Isabelle. To see the difference consider |
99 |
99 |
100 @{ML_response_fake [display,gray] |
100 @{ML_response_fake [display,gray] |
101 "let |
101 "let |
102 val input = \"\<foo> bar\" |
102 val input = \"\<foo> bar\" |
122 (hw input1, hw input2) |
122 (hw input1, hw input2) |
123 end" |
123 end" |
124 "((\"h\", [\"e\", \"l\", \"l\", \"o\"]),(\"w\", [\"o\", \"r\", \"l\", \"d\"]))"} |
124 "((\"h\", [\"e\", \"l\", \"l\", \"o\"]),(\"w\", [\"o\", \"r\", \"l\", \"d\"]))"} |
125 |
125 |
126 Two parsers can be connected in sequence by using the function @{ML_ind "--" in Scan}. |
126 Two parsers can be connected in sequence by using the function @{ML_ind "--" in Scan}. |
127 For example parsing @{text "h"}, @{text "e"} and @{text "l"} (in this |
127 For example parsing \<open>h\<close>, \<open>e\<close> and \<open>l\<close> (in this |
128 order) you can achieve by: |
128 order) you can achieve by: |
129 |
129 |
130 @{ML_response [display,gray] |
130 @{ML_response [display,gray] |
131 "($$ \"h\" -- $$ \"e\" -- $$ \"l\") (Symbol.explode \"hello\")" |
131 "($$ \"h\" -- $$ \"e\" -- $$ \"l\") (Symbol.explode \"hello\")" |
132 "(((\"h\", \"e\"), \"l\"), [\"l\", \"o\"])"} |
132 "(((\"h\", \"e\"), \"l\"), [\"l\", \"o\"])"} |
140 "Scan.this_string \"hell\" (Symbol.explode \"hello\")" |
140 "Scan.this_string \"hell\" (Symbol.explode \"hello\")" |
141 "(\"hell\", [\"o\"])"} |
141 "(\"hell\", [\"o\"])"} |
142 |
142 |
143 Parsers that explore alternatives can be constructed using the function |
143 Parsers that explore alternatives can be constructed using the function |
144 @{ML_ind "||" in Scan}. The parser @{ML "(p || q)" for p q} returns the |
144 @{ML_ind "||" in Scan}. The parser @{ML "(p || q)" for p q} returns the |
145 result of @{text "p"}, in case it succeeds; otherwise it returns the |
145 result of \<open>p\<close>, in case it succeeds; otherwise it returns the |
146 result of @{text "q"}. For example: |
146 result of \<open>q\<close>. For example: |
147 |
147 |
148 |
148 |
149 @{ML_response [display,gray] |
149 @{ML_response [display,gray] |
150 "let |
150 "let |
151 val hw = $$ \"h\" || $$ \"w\" |
151 val hw = $$ \"h\" || $$ \"w\" |
171 (just_e input, just_h input) |
171 (just_e input, just_h input) |
172 end" |
172 end" |
173 "((\"e\", [\"l\", \"l\", \"o\"]), (\"h\", [\"l\", \"l\", \"o\"]))"} |
173 "((\"e\", [\"l\", \"l\", \"o\"]), (\"h\", [\"l\", \"l\", \"o\"]))"} |
174 |
174 |
175 The parser @{ML "Scan.optional p x" for p x} returns the result of the parser |
175 The parser @{ML "Scan.optional p x" for p x} returns the result of the parser |
176 @{text "p"}, in case it succeeds; otherwise it returns |
176 \<open>p\<close>, in case it succeeds; otherwise it returns |
177 the default value @{text "x"}. For example: |
177 the default value \<open>x\<close>. For example: |
178 |
178 |
179 @{ML_response [display,gray] |
179 @{ML_response [display,gray] |
180 "let |
180 "let |
181 val p = Scan.optional ($$ \"h\") \"x\" |
181 val p = Scan.optional ($$ \"h\") \"x\" |
182 val input1 = Symbol.explode \"hello\" |
182 val input1 = Symbol.explode \"hello\" |
185 (p input1, p input2) |
185 (p input1, p input2) |
186 end" |
186 end" |
187 "((\"h\", [\"e\", \"l\", \"l\", \"o\"]), (\"x\", [\"w\", \"o\", \"r\", \"l\", \"d\"]))"} |
187 "((\"h\", [\"e\", \"l\", \"l\", \"o\"]), (\"x\", [\"w\", \"o\", \"r\", \"l\", \"d\"]))"} |
188 |
188 |
189 The function @{ML_ind option in Scan} works similarly, except no default value can |
189 The function @{ML_ind option in Scan} works similarly, except no default value can |
190 be given. Instead, the result is wrapped as an @{text "option"}-type. For example: |
190 be given. Instead, the result is wrapped as an \<open>option\<close>-type. For example: |
191 |
191 |
192 @{ML_response [display,gray] |
192 @{ML_response [display,gray] |
193 "let |
193 "let |
194 val p = Scan.option ($$ \"h\") |
194 val p = Scan.option ($$ \"h\") |
195 val input1 = Symbol.explode \"hello\" |
195 val input1 = Symbol.explode \"hello\" |
204 @{ML_response [display,gray] |
204 @{ML_response [display,gray] |
205 "Scan.ahead (Scan.this_string \"foo\") (Symbol.explode \"foo\")" |
205 "Scan.ahead (Scan.this_string \"foo\") (Symbol.explode \"foo\")" |
206 "(\"foo\", [\"f\", \"o\", \"o\"])"} |
206 "(\"foo\", [\"f\", \"o\", \"o\"])"} |
207 |
207 |
208 The function @{ML_ind "!!" in Scan} helps with producing appropriate error messages |
208 The function @{ML_ind "!!" in Scan} helps with producing appropriate error messages |
209 during parsing. For example if you want to parse @{text p} immediately |
209 during parsing. For example if you want to parse \<open>p\<close> immediately |
210 followed by @{text q}, or start a completely different parser @{text r}, |
210 followed by \<open>q\<close>, or start a completely different parser \<open>r\<close>, |
211 you might write: |
211 you might write: |
212 |
212 |
213 @{ML [display,gray] "(p -- q) || r" for p q r} |
213 @{ML [display,gray] "(p -- q) || r" for p q r} |
214 |
214 |
215 However, this parser is problematic for producing a useful error |
215 However, this parser is problematic for producing a useful error |
216 message, if the parsing of @{ML "(p -- q)" for p q} fails. Because with the |
216 message, if the parsing of @{ML "(p -- q)" for p q} fails. Because with the |
217 parser above you lose the information that @{text p} should be followed by @{text q}. |
217 parser above you lose the information that \<open>p\<close> should be followed by \<open>q\<close>. |
218 To see this assume that @{text p} is present in the input, but it is not |
218 To see this assume that \<open>p\<close> is present in the input, but it is not |
219 followed by @{text q}. That means @{ML "(p -- q)" for p q} will fail and |
219 followed by \<open>q\<close>. That means @{ML "(p -- q)" for p q} will fail and |
220 hence the alternative parser @{text r} will be tried. However, in many |
220 hence the alternative parser \<open>r\<close> will be tried. However, in many |
221 circumstances this will be the wrong parser for the input ``@{text "p"}-followed-by-something'' |
221 circumstances this will be the wrong parser for the input ``\<open>p\<close>-followed-by-something'' |
222 and therefore will also fail. The error message is then caused by the failure |
222 and therefore will also fail. The error message is then caused by the failure |
223 of @{text r}, not by the absence of @{text q} in the input. Such |
223 of \<open>r\<close>, not by the absence of \<open>q\<close> in the input. Such |
224 situation can be avoided when using the function @{ML "!!"}. This function |
224 situation can be avoided when using the function @{ML "!!"}. This function |
225 aborts the whole process of parsing in case of a failure and prints an error |
225 aborts the whole process of parsing in case of a failure and prints an error |
226 message. For example if you invoke the parser |
226 message. For example if you invoke the parser |
227 |
227 |
228 |
228 |
237 but if you invoke it on @{text [quotes] "world"} |
237 but if you invoke it on @{text [quotes] "world"} |
238 |
238 |
239 @{ML_response_fake [display,gray] "(!! (fn _ => fn _ => \"foo\") ($$ \"h\")) (Symbol.explode \"world\")" |
239 @{ML_response_fake [display,gray] "(!! (fn _ => fn _ => \"foo\") ($$ \"h\")) (Symbol.explode \"world\")" |
240 "Exception ABORT raised"} |
240 "Exception ABORT raised"} |
241 |
241 |
242 then the parsing aborts and the error message @{text "foo"} is printed. In order to |
242 then the parsing aborts and the error message \<open>foo\<close> is printed. In order to |
243 see the error message properly, you need to prefix the parser with the function |
243 see the error message properly, you need to prefix the parser with the function |
244 @{ML_ind error in Scan}. For example: |
244 @{ML_ind error in Scan}. For example: |
245 |
245 |
246 @{ML_response_fake [display,gray] |
246 @{ML_response_fake [display,gray] |
247 "Scan.error (!! (fn _ => fn _ => \"foo\") ($$ \"h\")) (Symbol.explode \"world\")" |
247 "Scan.error (!! (fn _ => fn _ => \"foo\") ($$ \"h\")) (Symbol.explode \"world\")" |
251 such as @{ML_ind local_theory in Outer_Syntax} |
251 such as @{ML_ind local_theory in Outer_Syntax} |
252 (see Section~\ref{sec:newcommand} which explains this function in more detail). |
252 (see Section~\ref{sec:newcommand} which explains this function in more detail). |
253 |
253 |
254 Let us now return to our example of parsing @{ML "(p -- q) || r" for p q |
254 Let us now return to our example of parsing @{ML "(p -- q) || r" for p q |
255 r}. If you want to generate the correct error message for failure |
255 r}. If you want to generate the correct error message for failure |
256 of parsing @{text "p"}-followed-by-@{text "q"}, then you have to write: |
256 of parsing \<open>p\<close>-followed-by-\<open>q\<close>, then you have to write: |
257 *} |
257 \<close> |
258 |
258 |
259 ML %grayML{*fun p_followed_by_q p q r = |
259 ML %grayML\<open>fun p_followed_by_q p q r = |
260 let |
260 let |
261 val err_msg = fn _ => p ^ " is not followed by " ^ q |
261 val err_msg = fn _ => p ^ " is not followed by " ^ q |
262 in |
262 in |
263 ($$ p -- (!! (fn _ => err_msg) ($$ q))) || ($$ r -- $$ r) |
263 ($$ p -- (!! (fn _ => err_msg) ($$ q))) || ($$ r -- $$ r) |
264 end *} |
264 end\<close> |
265 |
265 |
266 |
266 |
267 text {* |
267 text \<open> |
268 Running this parser with the arguments |
268 Running this parser with the arguments |
269 @{text [quotes] "h"}, @{text [quotes] "e"} and @{text [quotes] "w"}, and |
269 @{text [quotes] "h"}, @{text [quotes] "e"} and @{text [quotes] "w"}, and |
270 the input @{text [quotes] "holle"} |
270 the input @{text [quotes] "holle"} |
271 |
271 |
272 @{ML_response_fake [display,gray] "Scan.error (p_followed_by_q \"h\" \"e\" \"w\") (Symbol.explode \"holle\")" |
272 @{ML_response_fake [display,gray] "Scan.error (p_followed_by_q \"h\" \"e\" \"w\") (Symbol.explode \"holle\")" |
277 @{ML_response [display,gray] "Scan.error (p_followed_by_q \"h\" \"e\" \"w\") (Symbol.explode \"wworld\")" |
277 @{ML_response [display,gray] "Scan.error (p_followed_by_q \"h\" \"e\" \"w\") (Symbol.explode \"wworld\")" |
278 "((\"w\", \"w\"), [\"o\", \"r\", \"l\", \"d\"])"} |
278 "((\"w\", \"w\"), [\"o\", \"r\", \"l\", \"d\"])"} |
279 |
279 |
280 yields the expected parsing. |
280 yields the expected parsing. |
281 |
281 |
282 The function @{ML "Scan.repeat p" for p} will apply a parser @{text p} as |
282 The function @{ML "Scan.repeat p" for p} will apply a parser \<open>p\<close> as |
283 long as it succeeds. For example: |
283 long as it succeeds. For example: |
284 |
284 |
285 @{ML_response [display,gray] "Scan.repeat ($$ \"h\") (Symbol.explode \"hhhhello\")" |
285 @{ML_response [display,gray] "Scan.repeat ($$ \"h\") (Symbol.explode \"hhhhello\")" |
286 "([\"h\", \"h\", \"h\", \"h\"], [\"e\", \"l\", \"l\", \"o\"])"} |
286 "([\"h\", \"h\", \"h\", \"h\"], [\"e\", \"l\", \"l\", \"o\"])"} |
287 |
287 |
288 Note that @{ML_ind repeat in Scan} stores the parsed items in a list. The function |
288 Note that @{ML_ind repeat in Scan} stores the parsed items in a list. The function |
289 @{ML_ind repeat1 in Scan} is similar, but requires that the parser @{text "p"} |
289 @{ML_ind repeat1 in Scan} is similar, but requires that the parser \<open>p\<close> |
290 succeeds at least once. |
290 succeeds at least once. |
291 |
291 |
292 Also note that the parser would have aborted with the exception @{text MORE}, if |
292 Also note that the parser would have aborted with the exception \<open>MORE\<close>, if |
293 you had it run with the string @{text [quotes] "hhhh"}. This can be avoided by using |
293 you had it run with the string @{text [quotes] "hhhh"}. This can be avoided by using |
294 the wrapper @{ML_ind finite in Scan} and the ``stopper-token'' |
294 the wrapper @{ML_ind finite in Scan} and the ``stopper-token'' |
295 @{ML_ind stopper in Symbol}. With them you can write: |
295 @{ML_ind stopper in Symbol}. With them you can write: |
296 |
296 |
297 @{ML_response [display,gray] "Scan.finite Symbol.stopper (Scan.repeat ($$ \"h\")) (Symbol.explode \"hhhh\")" |
297 @{ML_response [display,gray] "Scan.finite Symbol.stopper (Scan.repeat ($$ \"h\")) (Symbol.explode \"hhhh\")" |
347 |
347 |
348 |
348 |
349 After parsing is done, you almost always want to apply a function to the parsed |
349 After parsing is done, you almost always want to apply a function to the parsed |
350 items. One way to do this is the function @{ML_ind ">>" in Scan} where |
350 items. One way to do this is the function @{ML_ind ">>" in Scan} where |
351 @{ML "(p >> f)" for p f} runs |
351 @{ML "(p >> f)" for p f} runs |
352 first the parser @{text p} and upon successful completion applies the |
352 first the parser \<open>p\<close> and upon successful completion applies the |
353 function @{text f} to the result. For example |
353 function \<open>f\<close> to the result. For example |
354 |
354 |
355 @{ML_response [display,gray] |
355 @{ML_response [display,gray] |
356 "let |
356 "let |
357 fun double (x, y) = (x ^ x, y ^ y) |
357 fun double (x, y) = (x ^ x, y ^ y) |
358 val parser = $$ \"h\" -- $$ \"e\" |
358 val parser = $$ \"h\" -- $$ \"e\" |
381 |
381 |
382 @{ML_response [display,gray] |
382 @{ML_response [display,gray] |
383 "Scan.lift ($$ \"h\" -- $$ \"e\") (1, Symbol.explode \"hello\")" |
383 "Scan.lift ($$ \"h\" -- $$ \"e\") (1, Symbol.explode \"hello\")" |
384 "((\"h\", \"e\"), (1, [\"l\", \"l\", \"o\"]))"} |
384 "((\"h\", \"e\"), (1, [\"l\", \"l\", \"o\"]))"} |
385 |
385 |
386 \footnote{\bf FIXME: In which situations is @{text "lift"} useful? Give examples.} |
386 \footnote{\bf FIXME: In which situations is \<open>lift\<close> useful? Give examples.} |
387 |
387 |
388 Be aware of recursive parsers. Suppose you want to read strings separated by |
388 Be aware of recursive parsers. Suppose you want to read strings separated by |
389 commas and by parentheses into a tree datastructure; for example, generating |
389 commas and by parentheses into a tree datastructure; for example, generating |
390 the tree corresponding to the string @{text [quotes] "(A, A), (A, A)"} where |
390 the tree corresponding to the string @{text [quotes] "(A, A), (A, A)"} where |
391 the @{text "A"}s will be the leaves. We assume the trees are represented by the |
391 the \<open>A\<close>s will be the leaves. We assume the trees are represented by the |
392 datatype: |
392 datatype: |
393 *} |
393 \<close> |
394 |
394 |
395 ML %grayML{*datatype tree = |
395 ML %grayML\<open>datatype tree = |
396 Lf of string |
396 Lf of string |
397 | Br of tree * tree*} |
397 | Br of tree * tree\<close> |
398 |
398 |
399 text {* |
399 text \<open> |
400 Since nested parentheses should be treated in a meaningful way---for example |
400 Since nested parentheses should be treated in a meaningful way---for example |
401 the string @{text [quotes] "((A))"} should be read into a single |
401 the string @{text [quotes] "((A))"} should be read into a single |
402 leaf---you might implement the following parser. |
402 leaf---you might implement the following parser. |
403 *} |
403 \<close> |
404 |
404 |
405 ML %grayML{*fun parse_basic s = |
405 ML %grayML\<open>fun parse_basic s = |
406 $$ s >> Lf || $$ "(" |-- parse_tree s --| $$ ")" |
406 $$ s >> Lf || $$ "(" |-- parse_tree s --| $$ ")" |
407 |
407 |
408 and parse_tree s = |
408 and parse_tree s = |
409 parse_basic s --| $$ "," -- parse_tree s >> Br || parse_basic s*} |
409 parse_basic s --| $$ "," -- parse_tree s >> Br || parse_basic s\<close> |
410 |
410 |
411 text {* |
411 text \<open> |
412 This parser corresponds to the grammar: |
412 This parser corresponds to the grammar: |
413 |
413 |
414 \begin{center} |
414 \begin{center} |
415 \begin{tabular}{lcl} |
415 \begin{tabular}{lcl} |
416 @{text "<Basic>"} & @{text "::="} & @{text "<String> | (<Tree>)"}\\ |
416 \<open><Basic>\<close> & \<open>::=\<close> & \<open><String> | (<Tree>)\<close>\\ |
417 @{text "<Tree>"} & @{text "::="} & @{text "<Basic>, <Tree> | <Basic>"}\\ |
417 \<open><Tree>\<close> & \<open>::=\<close> & \<open><Basic>, <Tree> | <Basic>\<close>\\ |
418 \end{tabular} |
418 \end{tabular} |
419 \end{center} |
419 \end{center} |
420 |
420 |
421 The parameter @{text "s"} is the string over which the tree is parsed. The |
421 The parameter \<open>s\<close> is the string over which the tree is parsed. The |
422 parser @{ML parse_basic} reads either a leaf or a tree enclosed in |
422 parser @{ML parse_basic} reads either a leaf or a tree enclosed in |
423 parentheses. The parser @{ML parse_tree} reads either a pair of trees |
423 parentheses. The parser @{ML parse_tree} reads either a pair of trees |
424 separated by a comma, or acts like @{ML parse_basic}. Unfortunately, |
424 separated by a comma, or acts like @{ML parse_basic}. Unfortunately, |
425 because of the mutual recursion, this parser will immediately run into a |
425 because of the mutual recursion, this parser will immediately run into a |
426 loop, even if it is called without any input. For example |
426 loop, even if it is called without any input. For example |
433 looping parser are not useful, because of ML's strict evaluation of |
433 looping parser are not useful, because of ML's strict evaluation of |
434 arguments. Therefore we need to delay the execution of the |
434 arguments. Therefore we need to delay the execution of the |
435 parser until an input is given. This can be done by adding the parsed |
435 parser until an input is given. This can be done by adding the parsed |
436 string as an explicit argument. So the parser above should be implemented |
436 string as an explicit argument. So the parser above should be implemented |
437 as follows. |
437 as follows. |
438 *} |
438 \<close> |
439 |
439 |
440 ML %grayML{*fun parse_basic s xs = |
440 ML %grayML\<open>fun parse_basic s xs = |
441 ($$ s >> Lf || $$ "(" |-- parse_tree s --| $$ ")") xs |
441 ($$ s >> Lf || $$ "(" |-- parse_tree s --| $$ ")") xs |
442 |
442 |
443 and parse_tree s xs = |
443 and parse_tree s xs = |
444 (parse_basic s --| $$ "," -- parse_tree s >> Br || parse_basic s) xs*} |
444 (parse_basic s --| $$ "," -- parse_tree s >> Br || parse_basic s) xs\<close> |
445 |
445 |
446 text {* |
446 text \<open> |
447 While the type of the parser is unchanged by the addition, its behaviour |
447 While the type of the parser is unchanged by the addition, its behaviour |
448 changed: with this version of the parser the execution is delayed until |
448 changed: with this version of the parser the execution is delayed until |
449 some string is applied for the argument @{text "xs"}. This gives us |
449 some string is applied for the argument \<open>xs\<close>. This gives us |
450 exactly the parser what we wanted. An example is as follows: |
450 exactly the parser what we wanted. An example is as follows: |
451 |
451 |
452 @{ML_response [display, gray] |
452 @{ML_response [display, gray] |
453 "let |
453 "let |
454 val input = Symbol.explode \"(A,((A))),A\" |
454 val input = Symbol.explode \"(A,((A))),A\" |
458 "(Br (Br (Lf \"A\", Lf \"A\"), Lf \"A\"), [])"} |
458 "(Br (Br (Lf \"A\", Lf \"A\"), Lf \"A\"), [])"} |
459 |
459 |
460 |
460 |
461 \begin{exercise}\label{ex:scancmts} |
461 \begin{exercise}\label{ex:scancmts} |
462 Write a parser that parses an input string so that any comment enclosed |
462 Write a parser that parses an input string so that any comment enclosed |
463 within @{text "(*\<dots>*)"} is replaced by the same comment but enclosed within |
463 within \<open>(*\<dots>*)\<close> is replaced by the same comment but enclosed within |
464 @{text "(**\<dots>**)"} in the output string. To enclose a string, you can use the |
464 \<open>(**\<dots>**)\<close> in the output string. To enclose a string, you can use the |
465 function @{ML "enclose s1 s2 s" for s1 s2 s} which produces the string @{ML |
465 function @{ML "enclose s1 s2 s" for s1 s2 s} which produces the string @{ML |
466 "s1 ^ s ^ s2" for s1 s2 s}. Hint: To simplify the task ignore the proper |
466 "s1 ^ s ^ s2" for s1 s2 s}. Hint: To simplify the task ignore the proper |
467 nesting of comments. |
467 nesting of comments. |
468 \end{exercise} |
468 \end{exercise} |
469 *} |
469 \<close> |
470 |
470 |
471 section {* Parsing Theory Syntax *} |
471 section \<open>Parsing Theory Syntax\<close> |
472 |
472 |
473 text {* |
473 text \<open> |
474 Most of the time, however, Isabelle developers have to deal with parsing |
474 Most of the time, however, Isabelle developers have to deal with parsing |
475 tokens, not strings. These token parsers have the type: |
475 tokens, not strings. These token parsers have the type: |
476 *} |
476 \<close> |
477 |
477 |
478 ML %grayML{*type 'a parser = Token.T list -> 'a * Token.T list*} |
478 ML %grayML\<open>type 'a parser = Token.T list -> 'a * Token.T list\<close> |
479 ML "Thy_Header.get_keywords'" |
479 ML "Thy_Header.get_keywords'" |
480 text {* |
480 text \<open> |
481 {\bf REDO!!} |
481 {\bf REDO!!} |
482 |
482 |
483 |
483 |
484 The reason for using token parsers is that theory syntax, as well as the |
484 The reason for using token parsers is that theory syntax, as well as the |
485 parsers for the arguments of proof methods, use the type @{ML_type |
485 parsers for the arguments of proof methods, use the type @{ML_type |
512 @{text [quotes] "hello"} and @{text [quotes] "world"} do not match any |
512 @{text [quotes] "hello"} and @{text [quotes] "world"} do not match any |
513 other syntactic category. The second indicates a space. |
513 other syntactic category. The second indicates a space. |
514 |
514 |
515 We can easily change what is recognised as a keyword with the function |
515 We can easily change what is recognised as a keyword with the function |
516 @{ML_ind add_keywords in Thy_Header}. For example calling it with |
516 @{ML_ind add_keywords in Thy_Header}. For example calling it with |
517 *} |
517 \<close> |
518 |
518 |
519 |
519 |
520 |
520 |
521 setup {* Thy_Header.add_keywords [(("hello", Position.none),Keyword.no_spec)] *} |
521 setup \<open>Thy_Header.add_keywords [(("hello", Position.none),Keyword.no_spec)]\<close> |
522 |
522 |
523 |
523 |
524 text {* |
524 text \<open> |
525 then lexing @{text [quotes] "hello world"} will produce |
525 then lexing @{text [quotes] "hello world"} will produce |
526 |
526 |
527 @{ML_response_fake [display,gray] "Token.explode |
527 @{ML_response_fake [display,gray] "Token.explode |
528 (Thy_Header.get_keywords' @{context}) Position.none \"hello world\"" |
528 (Thy_Header.get_keywords' @{context}) Position.none \"hello world\"" |
529 "[Token (\<dots>,(Keyword, \"hello\"),\<dots>), |
529 "[Token (\<dots>,(Keyword, \"hello\"),\<dots>), |
542 filter Token.is_proper input |
542 filter Token.is_proper input |
543 end" |
543 end" |
544 "[Token (\<dots>,(Ident, \"hello\"), \<dots>), Token (\<dots>,(Ident, \"world\"), \<dots>)]"} |
544 "[Token (\<dots>,(Ident, \"hello\"), \<dots>), Token (\<dots>,(Ident, \"world\"), \<dots>)]"} |
545 |
545 |
546 For convenience we define the function: |
546 For convenience we define the function: |
547 *} |
547 \<close> |
548 |
548 |
549 ML %grayML{*fun filtered_input str = |
549 ML %grayML\<open>fun filtered_input str = |
550 filter Token.is_proper (Token.explode (Thy_Header.get_keywords' @{context}) Position.none str) *} |
550 filter Token.is_proper (Token.explode (Thy_Header.get_keywords' @{context}) Position.none str)\<close> |
551 |
551 |
552 ML \<open>filtered_input "inductive | for"\<close> |
552 ML \<open>filtered_input "inductive | for"\<close> |
553 text {* |
553 text \<open> |
554 If you now parse |
554 If you now parse |
555 |
555 |
556 @{ML_response_fake [display,gray] |
556 @{ML_response_fake [display,gray] |
557 "filtered_input \"inductive | for\"" |
557 "filtered_input \"inductive | for\"" |
558 "[Token (\<dots>,(Command, \"inductive\"),\<dots>), |
558 "[Token (\<dots>,(Command, \"inductive\"),\<dots>), |
561 |
561 |
562 you obtain a list consisting of only one command and two keyword tokens. |
562 you obtain a list consisting of only one command and two keyword tokens. |
563 If you want to see which keywords and commands are currently known to Isabelle, |
563 If you want to see which keywords and commands are currently known to Isabelle, |
564 use the function @{ML_ind get_keywords' in Thy_Header}: |
564 use the function @{ML_ind get_keywords' in Thy_Header}: |
565 |
565 |
566 You might have to adjust the @{text ML_print_depth} in order to |
566 You might have to adjust the \<open>ML_print_depth\<close> in order to |
567 see the complete list. |
567 see the complete list. |
568 |
568 |
569 The parser @{ML_ind "$$$" in Parse} parses a single keyword. For example: |
569 The parser @{ML_ind "$$$" in Parse} parses a single keyword. For example: |
570 |
570 |
571 @{ML_response [display,gray] |
571 @{ML_response [display,gray] |
598 (Parse.$$$ \"|\" -- Parse.$$$ \"in\") input |
598 (Parse.$$$ \"|\" -- Parse.$$$ \"in\") input |
599 end" |
599 end" |
600 "((\"|\", \"in\"), [])"} |
600 "((\"|\", \"in\"), [])"} |
601 |
601 |
602 The parser @{ML "Parse.enum s p" for s p} parses a possibly empty |
602 The parser @{ML "Parse.enum s p" for s p} parses a possibly empty |
603 list of items recognised by the parser @{text p}, where the items being parsed |
603 list of items recognised by the parser \<open>p\<close>, where the items being parsed |
604 are separated by the string @{text s}. For example: |
604 are separated by the string \<open>s\<close>. For example: |
605 |
605 |
606 @{ML_response [display,gray] |
606 @{ML_response [display,gray] |
607 "let |
607 "let |
608 val input = filtered_input \"in | in | in foo\" |
608 val input = filtered_input \"in | in | in foo\" |
609 in |
609 in |
612 "([\"in\", \"in\", \"in\"], [\<dots>])"} |
612 "([\"in\", \"in\", \"in\"], [\<dots>])"} |
613 |
613 |
614 The function @{ML_ind enum1 in Parse} works similarly, except that the |
614 The function @{ML_ind enum1 in Parse} works similarly, except that the |
615 parsed list must be non-empty. Note that we had to add a string @{text |
615 parsed list must be non-empty. Note that we had to add a string @{text |
616 [quotes] "foo"} at the end of the parsed string, otherwise the parser would |
616 [quotes] "foo"} at the end of the parsed string, otherwise the parser would |
617 have consumed all tokens and then failed with the exception @{text |
617 have consumed all tokens and then failed with the exception \<open>MORE\<close>. Like in the previous section, we can avoid this exception using the |
618 "MORE"}. Like in the previous section, we can avoid this exception using the |
|
619 wrapper @{ML Scan.finite}. This time, however, we have to use the |
618 wrapper @{ML Scan.finite}. This time, however, we have to use the |
620 ``stopper-token'' @{ML Token.stopper}. We can write: |
619 ``stopper-token'' @{ML Token.stopper}. We can write: |
621 |
620 |
622 @{ML_response [display,gray] |
621 @{ML_response [display,gray] |
623 "let |
622 "let |
627 Scan.finite Token.stopper p input |
626 Scan.finite Token.stopper p input |
628 end" |
627 end" |
629 "([\"in\", \"in\", \"in\"], [])"} |
628 "([\"in\", \"in\", \"in\"], [])"} |
630 |
629 |
631 The following function will help to run examples. |
630 The following function will help to run examples. |
632 *} |
631 \<close> |
633 |
632 |
634 ML %grayML{*fun parse p input = Scan.finite Token.stopper (Scan.error p) input *} |
633 ML %grayML\<open>fun parse p input = Scan.finite Token.stopper (Scan.error p) input\<close> |
635 |
634 |
636 text {* |
635 text \<open> |
637 The function @{ML_ind "!!!" in Parse} can be used to force termination |
636 The function @{ML_ind "!!!" in Parse} can be used to force termination |
638 of the parser in case of a dead end, just like @{ML "Scan.!!"} (see previous |
637 of the parser in case of a dead end, just like @{ML "Scan.!!"} (see previous |
639 section). A difference, however, is that the error message of @{ML |
638 section). A difference, however, is that the error message of @{ML |
640 "Parse.!!!"} is fixed to be @{text [quotes] "Outer syntax error"} |
639 "Parse.!!!"} is fixed to be @{text [quotes] "Outer syntax error"} |
641 together with a relatively precise description of the failure. For example: |
640 together with a relatively precise description of the failure. For example: |
662 Whenever there is a possibility that the processing of user input can fail, |
661 Whenever there is a possibility that the processing of user input can fail, |
663 it is a good idea to give all available information about where the error |
662 it is a good idea to give all available information about where the error |
664 occurred. For this Isabelle can attach positional information to tokens |
663 occurred. For this Isabelle can attach positional information to tokens |
665 and then thread this information up the ``processing chain''. To see this, |
664 and then thread this information up the ``processing chain''. To see this, |
666 modify the function @{ML filtered_input}, described earlier, as follows |
665 modify the function @{ML filtered_input}, described earlier, as follows |
667 *} |
666 \<close> |
668 |
667 |
669 ML %grayML{*fun filtered_input' str = |
668 ML %grayML\<open>fun filtered_input' str = |
670 filter Token.is_proper (Token.explode (Thy_Header.get_keywords' @{context}) (Position.line 7) str) *} |
669 filter Token.is_proper (Token.explode (Thy_Header.get_keywords' @{context}) (Position.line 7) str)\<close> |
671 |
670 |
672 text {* |
671 text \<open> |
673 where we pretend the parsed string starts on line 7. An example is |
672 where we pretend the parsed string starts on line 7. An example is |
674 |
673 |
675 @{ML_response_fake [display,gray] |
674 @{ML_response_fake [display,gray] |
676 "filtered_input' \"foo \\n bar\"" |
675 "filtered_input' \"foo \\n bar\"" |
677 "[Token ((\"foo\", ({line=7, end_line=7}, {line=7})), (Ident, \"foo\"), \<dots>), |
676 "[Token ((\"foo\", ({line=7, end_line=7}, {line=7})), (Ident, \"foo\"), \<dots>), |
702 binds stronger than addition, and both of them nest to the right. |
701 binds stronger than addition, and both of them nest to the right. |
703 The context-free grammar is defined as: |
702 The context-free grammar is defined as: |
704 |
703 |
705 \begin{center} |
704 \begin{center} |
706 \begin{tabular}{lcl} |
705 \begin{tabular}{lcl} |
707 @{text "<Basic>"} & @{text "::="} & @{text "<Number> | (<Expr>)"}\\ |
706 \<open><Basic>\<close> & \<open>::=\<close> & \<open><Number> | (<Expr>)\<close>\\ |
708 @{text "<Factor>"} & @{text "::="} & @{text "<Basic> * <Factor> | <Basic>"}\\ |
707 \<open><Factor>\<close> & \<open>::=\<close> & \<open><Basic> * <Factor> | <Basic>\<close>\\ |
709 @{text "<Expr>"} & @{text "::="} & @{text "<Factor> + <Expr> | <Factor>"}\\ |
708 \<open><Expr>\<close> & \<open>::=\<close> & \<open><Factor> + <Expr> | <Factor>\<close>\\ |
710 \end{tabular} |
709 \end{tabular} |
711 \end{center} |
710 \end{center} |
712 |
711 |
713 Hint: Be careful with recursive parsers. |
712 Hint: Be careful with recursive parsers. |
714 \end{exercise} |
713 \end{exercise} |
715 *} |
714 \<close> |
716 |
715 |
717 section {* Parsers for ML-Code (TBD) *} |
716 section \<open>Parsers for ML-Code (TBD)\<close> |
718 |
717 |
719 text {* |
718 text \<open> |
720 @{ML_ind ML_source in Parse} |
719 @{ML_ind ML_source in Parse} |
721 *} |
720 \<close> |
722 |
721 |
723 section {* Context Parser (TBD) *} |
722 section \<open>Context Parser (TBD)\<close> |
724 |
723 |
725 text {* |
724 text \<open> |
726 @{ML_ind Args.context} |
725 @{ML_ind Args.context} |
727 *} |
726 \<close> |
728 (* |
727 (* |
729 ML {* |
728 ML {* |
730 let |
729 let |
731 val parser = Args.context -- Scan.lift Args.name_inner_syntax |
730 val parser = Args.context -- Scan.lift Args.name_inner_syntax |
732 |
731 |
737 |> fst |
736 |> fst |
738 end |
737 end |
739 *} |
738 *} |
740 *) |
739 *) |
741 |
740 |
742 text {* |
741 text \<open> |
743 @{ML_ind Args.context} |
742 @{ML_ind Args.context} |
744 |
743 |
745 Used for example in \isacommand{attribute\_setup} and \isacommand{method\_setup}. |
744 Used for example in \isacommand{attribute\_setup} and \isacommand{method\_setup}. |
746 *} |
745 \<close> |
747 |
746 |
748 section {* Argument and Attribute Parsers (TBD) *} |
747 section \<open>Argument and Attribute Parsers (TBD)\<close> |
749 |
748 |
750 section {* Parsing Inner Syntax *} |
749 section \<open>Parsing Inner Syntax\<close> |
751 |
750 |
752 text {* |
751 text \<open> |
753 There is usually no need to write your own parser for parsing inner syntax, that is |
752 There is usually no need to write your own parser for parsing inner syntax, that is |
754 for terms and types: you can just call the predefined parsers. Terms can |
753 for terms and types: you can just call the predefined parsers. Terms can |
755 be parsed using the function @{ML_ind term in Parse}. For example: |
754 be parsed using the function @{ML_ind term in Parse}. For example: |
756 |
755 |
757 @{ML_response_fake [display,gray] |
756 @{ML_response_fake [display,gray] |
790 @{ML_ind parse_term in Syntax} @{ML_ind check_term in Syntax} |
789 @{ML_ind parse_term in Syntax} @{ML_ind check_term in Syntax} |
791 @{ML_ind parse_typ in Syntax} @{ML_ind check_typ in Syntax} |
790 @{ML_ind parse_typ in Syntax} @{ML_ind check_typ in Syntax} |
792 @{ML_ind read_term in Syntax} @{ML_ind read_term in Syntax} |
791 @{ML_ind read_term in Syntax} @{ML_ind read_term in Syntax} |
793 |
792 |
794 |
793 |
795 *} |
794 \<close> |
796 |
795 |
797 section {* Parsing Specifications\label{sec:parsingspecs} *} |
796 section \<open>Parsing Specifications\label{sec:parsingspecs}\<close> |
798 |
797 |
799 |
798 |
800 text {* |
799 text \<open> |
801 There are a number of special purpose parsers that help with parsing |
800 There are a number of special purpose parsers that help with parsing |
802 specifications of function definitions, inductive predicates and so on. In |
801 specifications of function definitions, inductive predicates and so on. In |
803 Chapter~\ref{chp:package}, for example, we will need to parse specifications |
802 Chapter~\ref{chp:package}, for example, we will need to parse specifications |
804 for inductive predicates of the form: |
803 for inductive predicates of the form: |
805 *} |
804 \<close> |
806 |
805 |
807 |
806 |
808 simple_inductive |
807 simple_inductive |
809 even and odd |
808 even and odd |
810 where |
809 where |
811 even0: "even 0" |
810 even0: "even 0" |
812 | evenS: "odd n \<Longrightarrow> even (Suc n)" |
811 | evenS: "odd n \<Longrightarrow> even (Suc n)" |
813 | oddS: "even n \<Longrightarrow> odd (Suc n)" |
812 | oddS: "even n \<Longrightarrow> odd (Suc n)" |
814 |
813 |
815 text {* |
814 text \<open> |
816 For this we are going to use the parser: |
815 For this we are going to use the parser: |
817 *} |
816 \<close> |
818 |
817 |
819 ML %linenosgray{*val spec_parser = |
818 ML %linenosgray\<open>val spec_parser = |
820 Parse.vars -- |
819 Parse.vars -- |
821 Scan.optional |
820 Scan.optional |
822 (Parse.$$$ "where" |-- |
821 (Parse.$$$ "where" |-- |
823 Parse.!!! |
822 Parse.!!! |
824 (Parse.enum1 "|" |
823 (Parse.enum1 "|" |
825 (Parse_Spec.opt_thm_name ":" -- Parse.prop))) []*} |
824 (Parse_Spec.opt_thm_name ":" -- Parse.prop))) []\<close> |
826 |
825 |
827 text {* |
826 text \<open> |
828 Note that the parser must not parse the keyword \simpleinductive, even if it is |
827 Note that the parser must not parse the keyword \simpleinductive, even if it is |
829 meant to process definitions as shown above. The parser of the keyword |
828 meant to process definitions as shown above. The parser of the keyword |
830 will be given by the infrastructure that will eventually call @{ML spec_parser}. |
829 will be given by the infrastructure that will eventually call @{ML spec_parser}. |
831 |
830 |
832 |
831 |
846 end" |
845 end" |
847 "(([(even, NONE, NoSyn), (odd, NONE, NoSyn)], |
846 "(([(even, NONE, NoSyn), (odd, NONE, NoSyn)], |
848 [((even0,\<dots>),\<dots>), |
847 [((even0,\<dots>),\<dots>), |
849 ((evenS,\<dots>),\<dots>), |
848 ((evenS,\<dots>),\<dots>), |
850 ((oddS,\<dots>),\<dots>)]), [])"} |
849 ((oddS,\<dots>),\<dots>)]), [])"} |
851 *} |
850 \<close> |
852 |
851 |
853 ML \<open>let |
852 ML \<open>let |
854 val input = filtered_input |
853 val input = filtered_input |
855 "foo::\"int \<Rightarrow> bool\" and bar::nat (\"BAR\" 100) and blonk" |
854 "foo::\"int \<Rightarrow> bool\" and bar::nat (\"BAR\" 100) and blonk" |
856 in |
855 in |
857 parse Parse.vars input |
856 parse Parse.vars input |
858 end\<close> |
857 end\<close> |
859 |
858 |
860 text {* |
859 text \<open> |
861 As you see, the result is a pair consisting of a list of |
860 As you see, the result is a pair consisting of a list of |
862 variables with optional type-annotation and syntax-annotation, and a list of |
861 variables with optional type-annotation and syntax-annotation, and a list of |
863 rules where every rule has optionally a name and an attribute. |
862 rules where every rule has optionally a name and an attribute. |
864 |
863 |
865 The function @{ML_ind "vars" in Parse} in Line 2 of the parser reads an |
864 The function @{ML_ind "vars" in Parse} in Line 2 of the parser reads an |
866 \isacommand{and}-separated |
865 \isacommand{and}-separated |
867 list of variables that can include optional type annotations and syntax translations. |
866 list of variables that can include optional type annotations and syntax translations. |
868 For example:\footnote{Note that in the code we need to write |
867 For example:\footnote{Note that in the code we need to write |
869 @{text "\\\"int \<Rightarrow> bool\\\""} in order to properly escape the double quotes |
868 \<open>\"int \<Rightarrow> bool\"\<close> in order to properly escape the double quotes |
870 in the compound type.} |
869 in the compound type.} |
871 |
870 |
872 @{ML_response_fake [display,gray] |
871 @{ML_response_fake [display,gray] |
873 "let |
872 "let |
874 val input = filtered_input |
873 val input = filtered_input |
877 parse Parse.vars input |
876 parse Parse.vars input |
878 end" |
877 end" |
879 "([(foo, SOME \<dots>, NoSyn), |
878 "([(foo, SOME \<dots>, NoSyn), |
880 (bar, SOME \<dots>, Mixfix (Source {text=\"BAR\",...}, [], 100, \<dots>)), |
879 (bar, SOME \<dots>, Mixfix (Source {text=\"BAR\",...}, [], 100, \<dots>)), |
881 (blonk, NONE, NoSyn)],[])"} |
880 (blonk, NONE, NoSyn)],[])"} |
882 *} |
881 \<close> |
883 |
882 |
884 text {* |
883 text \<open> |
885 Whenever types are given, they are stored in the @{ML SOME}s. The types are |
884 Whenever types are given, they are stored in the @{ML SOME}s. The types are |
886 not yet used to type the variables: this must be done by type-inference later |
885 not yet used to type the variables: this must be done by type-inference later |
887 on. Since types are part of the inner syntax they are strings with some |
886 on. Since types are part of the inner syntax they are strings with some |
888 encoded information (see previous section). If a mixfix-syntax is |
887 encoded information (see previous section). If a mixfix-syntax is |
889 present for a variable, then it is stored in the |
888 present for a variable, then it is stored in the |
914 |
913 |
915 \begin{readmore} |
914 \begin{readmore} |
916 Attributes and arguments are implemented in the files @{ML_file "Pure/Isar/attrib.ML"} |
915 Attributes and arguments are implemented in the files @{ML_file "Pure/Isar/attrib.ML"} |
917 and @{ML_file "Pure/Isar/args.ML"}. |
916 and @{ML_file "Pure/Isar/args.ML"}. |
918 \end{readmore} |
917 \end{readmore} |
919 *} |
918 \<close> |
920 |
919 |
921 text_raw {* |
920 text_raw \<open> |
922 \begin{exercise} |
921 \begin{exercise} |
923 Have a look at how the parser @{ML Parse_Spec.where_multi_specs} is implemented |
922 Have a look at how the parser @{ML Parse_Spec.where_multi_specs} is implemented |
924 in file @{ML_file "Pure/Isar/parse_spec.ML"}. This parser corresponds |
923 in file @{ML_file "Pure/Isar/parse_spec.ML"}. This parser corresponds |
925 to the ``where-part'' of the introduction rules given above. Below |
924 to the ``where-part'' of the introduction rules given above. Below |
926 we paraphrase the code of @{ML_ind where_multi_specs in Parse_Spec} adapted to our |
925 we paraphrase the code of @{ML_ind where_multi_specs in Parse_Spec} adapted to our |
927 purposes. |
926 purposes. |
928 \begin{isabelle} |
927 \begin{isabelle} |
929 *} |
928 \<close> |
930 ML %linenosgray{*val spec_parser' = |
929 ML %linenosgray\<open>val spec_parser' = |
931 Parse.vars -- |
930 Parse.vars -- |
932 Scan.optional |
931 Scan.optional |
933 (Parse.$$$ "where" |-- |
932 (Parse.$$$ "where" |-- |
934 Parse.!!! |
933 Parse.!!! |
935 (Parse.enum1 "|" |
934 (Parse.enum1 "|" |
936 ((Parse_Spec.opt_thm_name ":" -- Parse.prop) --| |
935 ((Parse_Spec.opt_thm_name ":" -- Parse.prop) --| |
937 Scan.option (Scan.ahead (Parse.name || |
936 Scan.option (Scan.ahead (Parse.name || |
938 Parse.$$$ "[") -- |
937 Parse.$$$ "[") -- |
939 Parse.!!! (Parse.$$$ "|"))))) [] *} |
938 Parse.!!! (Parse.$$$ "|"))))) []\<close> |
940 text_raw {* |
939 text_raw \<open> |
941 \end{isabelle} |
940 \end{isabelle} |
942 Both parsers accept the same input% that's not true: |
941 Both parsers accept the same input% that's not true: |
943 % spec_parser accepts input that is refuted by spec_parser' |
942 % spec_parser accepts input that is refuted by spec_parser' |
944 , but if you look closely, you can notice |
943 , but if you look closely, you can notice |
945 an additional ``tail'' (Lines 8 to 10) in @{ML spec_parser'}. What is the purpose of |
944 an additional ``tail'' (Lines 8 to 10) in @{ML spec_parser'}. What is the purpose of |
946 this additional ``tail''? |
945 this additional ``tail''? |
947 \end{exercise} |
946 \end{exercise} |
948 *} |
947 \<close> |
949 |
948 |
950 text {* |
949 text \<open> |
951 (FIXME: @{ML Parse.type_args}, @{ML Parse.typ}, @{ML Parse.opt_mixfix}) |
950 (FIXME: @{ML Parse.type_args}, @{ML Parse.typ}, @{ML Parse.opt_mixfix}) |
952 *} |
951 \<close> |
953 |
952 |
954 |
953 |
955 section {* New Commands\label{sec:newcommand} *} |
954 section \<open>New Commands\label{sec:newcommand}\<close> |
956 |
955 |
957 text {* |
956 text \<open> |
958 Often new commands, for example for providing new definitional principles, |
957 Often new commands, for example for providing new definitional principles, |
959 need to be implemented. While this is not difficult on the ML-level and for |
958 need to be implemented. While this is not difficult on the ML-level and for |
960 jEdit, in order to be backwards compatible, new commands need also to be recognised |
959 jEdit, in order to be backwards compatible, new commands need also to be recognised |
961 by Proof-General. This results in some subtle configuration issues, which we will |
960 by Proof-General. This results in some subtle configuration issues, which we will |
962 explain in the next section. Here we just describe how to define new commands |
961 explain in the next section. Here we just describe how to define new commands |
967 implement any new command, you have to ``announce'' it in the |
966 implement any new command, you have to ``announce'' it in the |
968 \isacommand{keywords}-section of your theory header. For \isacommand{foobar} |
967 \isacommand{keywords}-section of your theory header. For \isacommand{foobar} |
969 we need to write something like |
968 we need to write something like |
970 |
969 |
971 \begin{graybox} |
970 \begin{graybox} |
972 \isacommand{theory}~@{text Foo}\\ |
971 \isacommand{theory}~\<open>Foo\<close>\\ |
973 \isacommand{imports}~@{text Main}\\ |
972 \isacommand{imports}~\<open>Main\<close>\\ |
974 \isacommand{keywords} @{text [quotes] "foobar"} @{text "::"} @{text "thy_decl"}\\ |
973 \isacommand{keywords} @{text [quotes] "foobar"} \<open>::\<close> \<open>thy_decl\<close>\\ |
975 ... |
974 ... |
976 \end{graybox} |
975 \end{graybox} |
977 |
976 |
978 where @{ML_ind "thy_decl" in Keyword} indicates the kind of the |
977 where @{ML_ind "thy_decl" in Keyword} indicates the kind of the |
979 command. Another possible kind is @{text "thy_goal"}, or you can |
978 command. Another possible kind is \<open>thy_goal\<close>, or you can |
980 also omit the kind entirely, in which case you declare a keyword |
979 also omit the kind entirely, in which case you declare a keyword |
981 (something that is part of a command). |
980 (something that is part of a command). |
982 |
981 |
983 Now you can implement \isacommand{foobar} as follows. |
982 Now you can implement \isacommand{foobar} as follows. |
984 *} |
983 \<close> |
985 |
984 |
986 ML %grayML{*let |
985 ML %grayML\<open>let |
987 val do_nothing = Scan.succeed (Local_Theory.background_theory I) |
986 val do_nothing = Scan.succeed (Local_Theory.background_theory I) |
988 in |
987 in |
989 Outer_Syntax.local_theory @{command_keyword "foobar"} |
988 Outer_Syntax.local_theory @{command_keyword "foobar"} |
990 "description of foobar" |
989 "description of foobar" |
991 do_nothing |
990 do_nothing |
992 end *} |
991 end\<close> |
993 |
992 |
994 text {* |
993 text \<open> |
995 The crucial function @{ML_ind local_theory in Outer_Syntax} expects |
994 The crucial function @{ML_ind local_theory in Outer_Syntax} expects |
996 the name for the command, a kind indicator, a short description and |
995 the name for the command, a kind indicator, a short description and |
997 a parser producing a local theory transition (explained later). For the |
996 a parser producing a local theory transition (explained later). For the |
998 name and the kind, you can use the ML-antiquotation @{text "@{command_spec ...}"}. |
997 name and the kind, you can use the ML-antiquotation \<open>@{command_spec ...}\<close>. |
999 You can now write in your theory |
998 You can now write in your theory |
1000 *} |
999 \<close> |
1001 |
1000 |
1002 foobar |
1001 foobar |
1003 |
1002 |
1004 text {* |
1003 text \<open> |
1005 but of course you will not see anything since \isacommand{foobar} is |
1004 but of course you will not see anything since \isacommand{foobar} is |
1006 not intended to do anything. Remember, however, that this only |
1005 not intended to do anything. Remember, however, that this only |
1007 works in jEdit. In order to enable also Proof-General recognise this |
1006 works in jEdit. In order to enable also Proof-General recognise this |
1008 command, a keyword file needs to be generated (see next section). |
1007 command, a keyword file needs to be generated (see next section). |
1009 |
1008 |
1011 us refine it a bit next by letting it take a proposition as argument |
1010 us refine it a bit next by letting it take a proposition as argument |
1012 and printing this proposition inside the tracing buffer. We announce |
1011 and printing this proposition inside the tracing buffer. We announce |
1013 the command \isacommand{foobar\_trace} in the theory header as |
1012 the command \isacommand{foobar\_trace} in the theory header as |
1014 |
1013 |
1015 \begin{graybox} |
1014 \begin{graybox} |
1016 \isacommand{keywords} @{text [quotes] "foobar_trace"} @{text "::"} @{text "thy_decl"} |
1015 \isacommand{keywords} @{text [quotes] "foobar_trace"} \<open>::\<close> \<open>thy_decl\<close> |
1017 \end{graybox} |
1016 \end{graybox} |
1018 |
1017 |
1019 The crucial part of a command is the function that determines the |
1018 The crucial part of a command is the function that determines the |
1020 behaviour of the command. In the code above we used a |
1019 behaviour of the command. In the code above we used a |
1021 ``do-nothing''-function, which because of the parser @{ML_ind succeed in Scan} |
1020 ``do-nothing''-function, which because of the parser @{ML_ind succeed in Scan} |
1022 does not parse any argument, but immediately returns the simple |
1021 does not parse any argument, but immediately returns the simple |
1023 function @{ML "Local_Theory.background_theory I"}. We can replace |
1022 function @{ML "Local_Theory.background_theory I"}. We can replace |
1024 this code by a function that first parses a proposition (using the |
1023 this code by a function that first parses a proposition (using the |
1025 parser @{ML Parse.prop}), then prints out some tracing information |
1024 parser @{ML Parse.prop}), then prints out some tracing information |
1026 (using the function @{text trace_prop}) and finally does |
1025 (using the function \<open>trace_prop\<close>) and finally does |
1027 nothing. For this you can write: |
1026 nothing. For this you can write: |
1028 *} |
1027 \<close> |
1029 |
1028 |
1030 ML %grayML{*let |
1029 ML %grayML\<open>let |
1031 fun trace_prop str = |
1030 fun trace_prop str = |
1032 Local_Theory.background_theory (fn ctxt => (tracing str; ctxt)) |
1031 Local_Theory.background_theory (fn ctxt => (tracing str; ctxt)) |
1033 in |
1032 in |
1034 Outer_Syntax.local_theory @{command_keyword "foobar_trace"} |
1033 Outer_Syntax.local_theory @{command_keyword "foobar_trace"} |
1035 "traces a proposition" |
1034 "traces a proposition" |
1036 (Parse.prop >> trace_prop) |
1035 (Parse.prop >> trace_prop) |
1037 end *} |
1036 end\<close> |
1038 |
1037 |
1039 text {* |
1038 text \<open> |
1040 This command can now be used to |
1039 This command can now be used to |
1041 see the proposition in the tracing buffer. |
1040 see the proposition in the tracing buffer. |
1042 *} |
1041 \<close> |
1043 |
1042 |
1044 foobar_trace "True \<and> False" |
1043 foobar_trace "True \<and> False" |
1045 |
1044 |
1046 text {* |
1045 text \<open> |
1047 Note that so far we used @{ML_ind thy_decl in Keyword} as the kind |
1046 Note that so far we used @{ML_ind thy_decl in Keyword} as the kind |
1048 indicator for the new command. This means that the command finishes as soon as |
1047 indicator for the new command. This means that the command finishes as soon as |
1049 the arguments are processed. Examples of this kind of commands are |
1048 the arguments are processed. Examples of this kind of commands are |
1050 \isacommand{definition} and \isacommand{declare}. In other cases, commands |
1049 \isacommand{definition} and \isacommand{declare}. In other cases, commands |
1051 are expected to parse some arguments, for example a proposition, and then |
1050 are expected to parse some arguments, for example a proposition, and then |
1055 the kind indicator @{ML_ind thy_goal in Keyword} and the function @{ML |
1054 the kind indicator @{ML_ind thy_goal in Keyword} and the function @{ML |
1056 "local_theory_to_proof" in Outer_Syntax} to set up the command. |
1055 "local_theory_to_proof" in Outer_Syntax} to set up the command. |
1057 Below we show the command \isacommand{foobar\_goal} which takes a |
1056 Below we show the command \isacommand{foobar\_goal} which takes a |
1058 proposition as argument and then starts a proof in order to prove |
1057 proposition as argument and then starts a proof in order to prove |
1059 it. Therefore, we need to announce this command in the header |
1058 it. Therefore, we need to announce this command in the header |
1060 as @{text "thy_goal"}. |
1059 as \<open>thy_goal\<close>. |
1061 |
1060 |
1062 \begin{graybox} |
1061 \begin{graybox} |
1063 \isacommand{keywords} @{text [quotes] "foobar_goal"} @{text "::"} @{text "thy_goal"} |
1062 \isacommand{keywords} @{text [quotes] "foobar_goal"} \<open>::\<close> \<open>thy_goal\<close> |
1064 \end{graybox} |
1063 \end{graybox} |
1065 |
1064 |
1066 Then we can write: |
1065 Then we can write: |
1067 *} |
1066 \<close> |
1068 |
1067 |
1069 ML%linenosgray{*let |
1068 ML%linenosgray\<open>let |
1070 fun goal_prop str ctxt = |
1069 fun goal_prop str ctxt = |
1071 let |
1070 let |
1072 val prop = Syntax.read_prop ctxt str |
1071 val prop = Syntax.read_prop ctxt str |
1073 in |
1072 in |
1074 Proof.theorem NONE (K I) [[(prop, [])]] ctxt |
1073 Proof.theorem NONE (K I) [[(prop, [])]] ctxt |
1075 end |
1074 end |
1076 in |
1075 in |
1077 Outer_Syntax.local_theory_to_proof @{command_keyword "foobar_goal"} |
1076 Outer_Syntax.local_theory_to_proof @{command_keyword "foobar_goal"} |
1078 "proves a proposition" |
1077 "proves a proposition" |
1079 (Parse.prop >> goal_prop) |
1078 (Parse.prop >> goal_prop) |
1080 end *} |
1079 end\<close> |
1081 |
1080 |
1082 text {* |
1081 text \<open> |
1083 The function @{text goal_prop} in Lines 2 to 7 takes a string (the proposition to be |
1082 The function \<open>goal_prop\<close> in Lines 2 to 7 takes a string (the proposition to be |
1084 proved) and a context as argument. The context is necessary in order to be able to use |
1083 proved) and a context as argument. The context is necessary in order to be able to use |
1085 @{ML_ind read_prop in Syntax}, which converts a string into a proper proposition. |
1084 @{ML_ind read_prop in Syntax}, which converts a string into a proper proposition. |
1086 In Line 6 the function @{ML_ind theorem in Proof} starts the proof for the |
1085 In Line 6 the function @{ML_ind theorem in Proof} starts the proof for the |
1087 proposition. Its argument @{ML NONE} stands for a locale (which we chose to |
1086 proposition. Its argument @{ML NONE} stands for a locale (which we chose to |
1088 omit); the argument @{ML "(K I)"} stands for a function that determines what |
1087 omit); the argument @{ML "(K I)"} stands for a function that determines what |
1089 should be done with the theorem once it is proved (we chose to just forget |
1088 should be done with the theorem once it is proved (we chose to just forget |
1090 about it). |
1089 about it). |
1091 |
1090 |
1092 If you now type \isacommand{foobar\_goal}~@{text [quotes] "True \<and> True"}, |
1091 If you now type \isacommand{foobar\_goal}~@{text [quotes] "True \<and> True"}, |
1093 you obtain the following proof state: |
1092 you obtain the following proof state: |
1094 *} |
1093 \<close> |
1095 |
1094 |
1096 foobar_goal "True \<and> True" |
1095 foobar_goal "True \<and> True" |
1097 txt {* |
1096 txt \<open> |
1098 \begin{minipage}{\textwidth} |
1097 \begin{minipage}{\textwidth} |
1099 @{subgoals [display]} |
1098 @{subgoals [display]} |
1100 \end{minipage}\medskip |
1099 \end{minipage}\medskip |
1101 |
1100 |
1102 and can prove the proposition as follows. |
1101 and can prove the proposition as follows. |
1103 *} |
1102 \<close> |
1104 apply(rule conjI) |
1103 apply(rule conjI) |
1105 apply(rule TrueI)+ |
1104 apply(rule TrueI)+ |
1106 done |
1105 done |
1107 |
1106 |
1108 text {* |
1107 text \<open> |
1109 The last command we describe here is |
1108 The last command we describe here is |
1110 \isacommand{foobar\_proof}. Like \isacommand{foobar\_goal}, its purpose is |
1109 \isacommand{foobar\_proof}. Like \isacommand{foobar\_goal}, its purpose is |
1111 to take a proposition and open a corresponding proof-state that |
1110 to take a proposition and open a corresponding proof-state that |
1112 allows us to give a proof for it. However, unlike |
1111 allows us to give a proof for it. However, unlike |
1113 \isacommand{foobar\_goal}, the proposition will be given as a |
1112 \isacommand{foobar\_goal}, the proposition will be given as a |
1120 text as ML-source and then interpret it as an Isabelle term using |
1119 text as ML-source and then interpret it as an Isabelle term using |
1121 the ML-runtime. For the parsing part, we can use the function |
1120 the ML-runtime. For the parsing part, we can use the function |
1122 @{ML_ind "ML_source" in Parse} in the structure @{ML_struct |
1121 @{ML_ind "ML_source" in Parse} in the structure @{ML_struct |
1123 Parse}. For running the ML-interpreter we need the following |
1122 Parse}. For running the ML-interpreter we need the following |
1124 scaffolding code. |
1123 scaffolding code. |
1125 *} |
1124 \<close> |
1126 |
1125 |
1127 ML %grayML{* |
1126 ML %grayML\<open> |
1128 structure Result = Proof_Data |
1127 structure Result = Proof_Data |
1129 (type T = unit -> term |
1128 (type T = unit -> term |
1130 fun init thy () = error "Result") |
1129 fun init thy () = error "Result") |
1131 |
1130 |
1132 val result_cookie = (Result.get, Result.put, "Result.put") *} |
1131 val result_cookie = (Result.get, Result.put, "Result.put")\<close> |
1133 |
1132 |
1134 text {* |
1133 text \<open> |
1135 With this in place, we can implement the code for \isacommand{foobar\_prove} |
1134 With this in place, we can implement the code for \isacommand{foobar\_prove} |
1136 as follows. |
1135 as follows. |
1137 *} |
1136 \<close> |
1138 |
1137 |
1139 ML %linenosgray{*let |
1138 ML %linenosgray\<open>let |
1140 fun after_qed thm_name thms lthy = |
1139 fun after_qed thm_name thms lthy = |
1141 Local_Theory.note (thm_name, (flat thms)) lthy |> snd |
1140 Local_Theory.note (thm_name, (flat thms)) lthy |> snd |
1142 |
1141 |
1143 fun setup_proof (thm_name, src) lthy = |
1142 fun setup_proof (thm_name, src) lthy = |
1144 let |
1143 let |
1151 val parser = Parse_Spec.opt_thm_name ":" -- Parse.ML_source |
1150 val parser = Parse_Spec.opt_thm_name ":" -- Parse.ML_source |
1152 in |
1151 in |
1153 Outer_Syntax.local_theory_to_proof @{command_keyword "foobar_prove"} |
1152 Outer_Syntax.local_theory_to_proof @{command_keyword "foobar_prove"} |
1154 "proving a proposition" |
1153 "proving a proposition" |
1155 (parser >> setup_proof) |
1154 (parser >> setup_proof) |
1156 end*} |
1155 end\<close> |
1157 |
1156 |
1158 text {* |
1157 text \<open> |
1159 In Line 12, we implement a parser that first reads in an optional lemma name (terminated |
1158 In Line 12, we implement a parser that first reads in an optional lemma name (terminated |
1160 by ``:'') and then some ML-code. The function in Lines 5 to 10 takes the ML-text |
1159 by ``:'') and then some ML-code. The function in Lines 5 to 10 takes the ML-text |
1161 and lets the ML-runtime evaluate it using the function @{ML_ind value in Code_Runtime} |
1160 and lets the ML-runtime evaluate it using the function @{ML_ind value in Code_Runtime} |
1162 in the structure @{ML_struct Code_Runtime}. Once the ML-text has been turned into a term, |
1161 in the structure @{ML_struct Code_Runtime}. Once the ML-text has been turned into a term, |
1163 the function @{ML theorem in Proof} opens a corresponding proof-state. This function takes the |
1162 the function @{ML theorem in Proof} opens a corresponding proof-state. This function takes the |
1164 function @{text "after_qed"} as argument, whose purpose is to store the theorem |
1163 function \<open>after_qed\<close> as argument, whose purpose is to store the theorem |
1165 (once it is proven) under the given name @{text "thm_name"}. |
1164 (once it is proven) under the given name \<open>thm_name\<close>. |
1166 |
1165 |
1167 You can now define a term, for example |
1166 You can now define a term, for example |
1168 *} |
1167 \<close> |
1169 |
1168 |
1170 ML %grayML{*val prop_true = @{prop "True"}*} |
1169 ML %grayML\<open>val prop_true = @{prop "True"}\<close> |
1171 |
1170 |
1172 text {* |
1171 text \<open> |
1173 and give it a proof using \isacommand{foobar\_prove}: |
1172 and give it a proof using \isacommand{foobar\_prove}: |
1174 *} |
1173 \<close> |
1175 |
1174 |
1176 foobar_prove test: prop_true |
1175 foobar_prove test: prop_true |
1177 apply(rule TrueI) |
1176 apply(rule TrueI) |
1178 done |
1177 done |
1179 |
1178 |
1180 text {* |
1179 text \<open> |
1181 Finally you can test whether the lemma has been stored under the given name. |
1180 Finally you can test whether the lemma has been stored under the given name. |
1182 |
1181 |
1183 \begin{isabelle} |
1182 \begin{isabelle} |
1184 \isacommand{thm}~@{text "test"}\\ |
1183 \isacommand{thm}~\<open>test\<close>\\ |
1185 @{text "> "}~@{thm TrueI} |
1184 \<open>> \<close>~@{thm TrueI} |
1186 \end{isabelle} |
1185 \end{isabelle} |
1187 |
1186 |
1188 *} |
1187 \<close> |
1189 |
1188 |
1190 |
1189 |
1191 (* |
1190 (* |
1192 text {* |
1191 text {* |
1193 {\bf TBD below} |
1192 {\bf TBD below} |
1322 ML {* |
1321 ML {* |
1323 Context.set_thread_data (SOME (let fun tactic (facts: thm list) : tactic = (atac 1) in Context.map_proof (Method.set_tactic tactic) end (ML_Context.the_generic_context ()))); |
1322 Context.set_thread_data (SOME (let fun tactic (facts: thm list) : tactic = (atac 1) in Context.map_proof (Method.set_tactic tactic) end (ML_Context.the_generic_context ()))); |
1324 *} |
1323 *} |
1325 *) |
1324 *) |
1326 |
1325 |
1327 section {* Methods (TBD) *} |
1326 section \<open>Methods (TBD)\<close> |
1328 |
1327 |
1329 text {* |
1328 text \<open> |
1330 (FIXME: maybe move to after the tactic section) |
1329 (FIXME: maybe move to after the tactic section) |
1331 |
1330 |
1332 Methods are central to Isabelle. You use them, for example, |
1331 Methods are central to Isabelle. You use them, for example, |
1333 in \isacommand{apply}. To print out all currently known methods you can use the |
1332 in \isacommand{apply}. To print out all currently known methods you can use the |
1334 Isabelle command: |
1333 Isabelle command: |
1335 |
1334 |
1336 \begin{isabelle} |
1335 \begin{isabelle} |
1337 \isacommand{print\_methods}\\ |
1336 \isacommand{print\_methods}\\ |
1338 @{text "> methods:"}\\ |
1337 \<open>> methods:\<close>\\ |
1339 @{text "> -: do nothing (insert current facts only)"}\\ |
1338 \<open>> -: do nothing (insert current facts only)\<close>\\ |
1340 @{text "> HOL.default: apply some intro/elim rule (potentially classical)"}\\ |
1339 \<open>> HOL.default: apply some intro/elim rule (potentially classical)\<close>\\ |
1341 @{text "> ..."} |
1340 \<open>> ...\<close> |
1342 \end{isabelle} |
1341 \end{isabelle} |
1343 |
1342 |
1344 An example of a very simple method is: |
1343 An example of a very simple method is: |
1345 *} |
1344 \<close> |
1346 |
1345 |
1347 method_setup %gray foo = |
1346 method_setup %gray foo = |
1348 {* Scan.succeed |
1347 \<open>Scan.succeed |
1349 (fn ctxt => (SIMPLE_METHOD ((eresolve_tac ctxt [@{thm conjE}] THEN' resolve_tac ctxt [@{thm conjI}]) 1))) *} |
1348 (fn ctxt => (SIMPLE_METHOD ((eresolve_tac ctxt [@{thm conjE}] THEN' resolve_tac ctxt [@{thm conjI}]) 1)))\<close> |
1350 "foo method for conjE and conjI" |
1349 "foo method for conjE and conjI" |
1351 |
1350 |
1352 text {* |
1351 text \<open> |
1353 It defines the method @{text foo}, which takes no arguments (therefore the |
1352 It defines the method \<open>foo\<close>, which takes no arguments (therefore the |
1354 parser @{ML Scan.succeed}) and only applies a single tactic, namely the tactic which |
1353 parser @{ML Scan.succeed}) and only applies a single tactic, namely the tactic which |
1355 applies @{thm [source] conjE} and then @{thm [source] conjI}. The function |
1354 applies @{thm [source] conjE} and then @{thm [source] conjI}. The function |
1356 @{ML_ind SIMPLE_METHOD in Method} |
1355 @{ML_ind SIMPLE_METHOD in Method} |
1357 turns such a tactic into a method. The method @{text "foo"} can be used as follows |
1356 turns such a tactic into a method. The method \<open>foo\<close> can be used as follows |
1358 *} |
1357 \<close> |
1359 |
1358 |
1360 lemma shows "A \<and> B \<Longrightarrow> C \<and> D" |
1359 lemma shows "A \<and> B \<Longrightarrow> C \<and> D" |
1361 apply(foo) |
1360 apply(foo) |
1362 txt {* |
1361 txt \<open> |
1363 where it results in the goal state |
1362 where it results in the goal state |
1364 |
1363 |
1365 \begin{minipage}{\textwidth} |
1364 \begin{minipage}{\textwidth} |
1366 @{subgoals} |
1365 @{subgoals} |
1367 \end{minipage} *} |
1366 \end{minipage}\<close> |
1368 (*<*)oops(*>*) |
1367 (*<*)oops(*>*) |
1369 |
1368 |
1370 method_setup test = {* |
1369 method_setup test = \<open> |
1371 Scan.lift (Scan.succeed (K Method.succeed)) *} {* bla *} |
1370 Scan.lift (Scan.succeed (K Method.succeed))\<close> \<open>bla\<close> |
1372 |
1371 |
1373 lemma "True" |
1372 lemma "True" |
1374 apply(test) |
1373 apply(test) |
1375 oops |
1374 oops |
1376 |
1375 |
1377 method_setup joker = {* |
1376 method_setup joker = \<open> |
1378 Scan.lift (Scan.succeed (fn ctxt => Method.cheating true)) *} {* bla *} |
1377 Scan.lift (Scan.succeed (fn ctxt => Method.cheating true))\<close> \<open>bla\<close> |
1379 |
1378 |
1380 lemma "False" |
1379 lemma "False" |
1381 apply(joker) |
1380 apply(joker) |
1382 oops |
1381 oops |
1383 |
1382 |
1384 text {* if true is set then always works *} |
1383 text \<open>if true is set then always works\<close> |
1385 |
1384 |
1386 ML {* assume_tac *} |
1385 ML \<open>assume_tac\<close> |
1387 |
1386 |
1388 method_setup first_atac = {* Scan.lift (Scan.succeed (fn ctxt => (SIMPLE_METHOD (assume_tac ctxt 1)))) *} {* bla *} |
1387 method_setup first_atac = \<open>Scan.lift (Scan.succeed (fn ctxt => (SIMPLE_METHOD (assume_tac ctxt 1))))\<close> \<open>bla\<close> |
1389 |
1388 |
1390 ML {* HEADGOAL *} |
1389 ML \<open>HEADGOAL\<close> |
1391 |
1390 |
1392 lemma "A \<Longrightarrow> A" |
1391 lemma "A \<Longrightarrow> A" |
1393 apply(first_atac) |
1392 apply(first_atac) |
1394 oops |
1393 oops |
1395 |
1394 |
1396 method_setup my_atac = {* Scan.lift (Scan.succeed (fn ctxt => (SIMPLE_METHOD' (assume_tac ctxt)))) *} {* bla *} |
1395 method_setup my_atac = \<open>Scan.lift (Scan.succeed (fn ctxt => (SIMPLE_METHOD' (assume_tac ctxt))))\<close> \<open>bla\<close> |
1397 |
1396 |
1398 lemma "A \<Longrightarrow> A" |
1397 lemma "A \<Longrightarrow> A" |
1399 apply(my_atac) |
1398 apply(my_atac) |
1400 oops |
1399 oops |
1401 |
1400 |
1410 ML {* METHOD *} |
1409 ML {* METHOD *} |
1411 ML {* K (SIMPLE_METHOD ((etac @{thm conjE} THEN' rtac @{thm conjI}) 1)) *} |
1410 ML {* K (SIMPLE_METHOD ((etac @{thm conjE} THEN' rtac @{thm conjI}) 1)) *} |
1412 ML {* Scan.succeed *} |
1411 ML {* Scan.succeed *} |
1413 *) |
1412 *) |
1414 |
1413 |
1415 ML {* resolve_tac *} |
1414 ML \<open>resolve_tac\<close> |
1416 |
1415 |
1417 method_setup myrule = |
1416 method_setup myrule = |
1418 {* Scan.lift (Scan.succeed (fn ctxt => (METHOD (fn thms => resolve_tac ctxt thms 1)))) *} |
1417 \<open>Scan.lift (Scan.succeed (fn ctxt => (METHOD (fn thms => resolve_tac ctxt thms 1))))\<close> |
1419 {* bla *} |
1418 \<open>bla\<close> |
1420 |
1419 |
1421 lemma |
1420 lemma |
1422 assumes a: "A \<Longrightarrow> B \<Longrightarrow> C" |
1421 assumes a: "A \<Longrightarrow> B \<Longrightarrow> C" |
1423 shows "C" |
1422 shows "C" |
1424 using a |
1423 using a |
1425 apply(myrule) |
1424 apply(myrule) |
1426 oops |
1425 oops |
1427 |
1426 |
1428 |
1427 |
1429 |
1428 |
1430 text {* |
1429 text \<open> |
1431 (********************************************************) |
1430 (********************************************************) |
1432 (FIXME: explain a version of rule-tac) |
1431 (FIXME: explain a version of rule-tac) |
1433 *} |
1432 \<close> |
1434 |
1433 |
1435 end |
1434 end |