67 |
67 |
68 The function @{ML \<open>$$\<close>} will either succeed (as in the two examples above) |
68 The function @{ML \<open>$$\<close>} will either succeed (as in the two examples above) |
69 or raise the exception \<open>FAIL\<close> 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_matchresult_fake [display,gray] |
72 @{ML_response [display,gray] |
73 \<open>($$ "x") (Symbol.explode "world")\<close> |
73 \<open>($$ "x") (Symbol.explode "world")\<close> |
74 \<open>Exception FAIL raised\<close>} |
74 \<open>exception FAIL NONE raised\<close>} |
75 |
75 |
76 will raise the exception \<open>FAIL\<close>. 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: |
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 \<open>\<foo>\<close>, 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_matchresult_fake [display,gray] |
100 @{ML_matchresult [display,gray] |
101 \<open>let |
101 \<open>String.explode "\<foo> bar"\<close> |
102 val input = "\<foo> bar" |
102 \<open>[#"\\", #"<", #"f", #"o", #"o", #">", #" ", #"b", #"a", #"r"]\<close>} |
103 in |
103 |
104 (String.explode input, Symbol.explode input) |
104 @{ML_matchresult [display,gray] |
105 end\<close> |
105 \<open>Symbol.explode "\<foo> bar"\<close> |
106 \<open>(["\\", "<", "f", "o", "o", ">", " ", "b", "a", "r"], |
106 \<open>["\<foo>", " ", "b", "a", "r"]\<close>} |
107 ["\<foo>", " ", "b", "a", "r"])\<close>} |
|
108 |
107 |
109 Slightly more general than the parser @{ML \<open>$$\<close>} is the function |
108 Slightly more general than the parser @{ML \<open>$$\<close>} is the function |
110 @{ML_ind one in Scan}, in that it takes a predicate as argument and |
109 @{ML_ind one in Scan}, in that it takes a predicate as argument and |
111 then parses exactly |
110 then parses exactly |
112 one item from the input list satisfying this predicate. For example the |
111 one item from the input list satisfying this predicate. For example the |
234 \<open>(!! (fn _ => fn _ => "foo") ($$ "h")) (Symbol.explode "hello")\<close> |
233 \<open>(!! (fn _ => fn _ => "foo") ($$ "h")) (Symbol.explode "hello")\<close> |
235 \<open>("h", ["e", "l", "l", "o"])\<close>} |
234 \<open>("h", ["e", "l", "l", "o"])\<close>} |
236 |
235 |
237 but if you invoke it on @{text [quotes] "world"} |
236 but if you invoke it on @{text [quotes] "world"} |
238 |
237 |
239 @{ML_matchresult_fake [display,gray] \<open>(!! (fn _ => fn _ => "foo") ($$ "h")) (Symbol.explode "world")\<close> |
238 @{ML_response [display,gray] \<open>(!! (fn _ => fn _ => "foo") ($$ "h")) (Symbol.explode "world")\<close> |
240 \<open>Exception ABORT raised\<close>} |
239 \<open>exception ABORT fn raised\<close>} |
241 |
240 |
242 then the parsing aborts and the error message \<open>foo\<close> is printed. In order to |
241 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 |
242 see the error message properly, you need to prefix the parser with the function |
244 @{ML_ind error in Scan}. For example: |
243 @{ML_ind error in Scan}. For example: |
245 |
244 |
246 @{ML_matchresult_fake [display,gray] |
245 @{ML_response [display,gray] |
247 \<open>Scan.error (!! (fn _ => fn _ => "foo") ($$ "h")) (Symbol.explode "world")\<close> |
246 \<open>Scan.error (!! (fn _ => fn _ => "foo") ($$ "h")) (Symbol.explode "world")\<close> |
248 \<open>Exception Error "foo" raised\<close>} |
247 \<open>foo\<close>} |
249 |
248 |
250 This kind of ``prefixing'' to see the correct error message is usually done by wrappers |
249 This kind of ``prefixing'' to see the correct error message is usually done by wrappers |
251 such as @{ML_ind local_theory in Outer_Syntax} |
250 such as @{ML_ind local_theory in Outer_Syntax} |
252 (see Section~\ref{sec:newcommand} which explains this function in more detail). |
251 (see Section~\ref{sec:newcommand} which explains this function in more detail). |
253 |
252 |
267 text \<open> |
266 text \<open> |
268 Running this parser with the arguments |
267 Running this parser with the arguments |
269 @{text [quotes] "h"}, @{text [quotes] "e"} and @{text [quotes] "w"}, and |
268 @{text [quotes] "h"}, @{text [quotes] "e"} and @{text [quotes] "w"}, and |
270 the input @{text [quotes] "holle"} |
269 the input @{text [quotes] "holle"} |
271 |
270 |
272 @{ML_matchresult_fake [display,gray] \<open>Scan.error (p_followed_by_q "h" "e" "w") (Symbol.explode "holle")\<close> |
271 @{ML_response [display,gray] \<open>Scan.error (p_followed_by_q "h" "e" "w") (Symbol.explode "holle")\<close> |
273 \<open>Exception ERROR "h is not followed by e" raised\<close>} |
272 \<open>h is not followed by e\<close>} |
274 |
273 |
275 produces the correct error message. Running it with |
274 produces the correct error message. Running it with |
276 |
275 |
277 @{ML_matchresult [display,gray] \<open>Scan.error (p_followed_by_q "h" "e" "w") (Symbol.explode "wworld")\<close> |
276 @{ML_matchresult [display,gray] \<open>Scan.error (p_followed_by_q "h" "e" "w") (Symbol.explode "wworld")\<close> |
278 \<open>(("w", "w"), ["o", "r", "l", "d"])\<close>} |
277 \<open>(("w", "w"), ["o", "r", "l", "d"])\<close>} |
316 where the function @{ML_ind not_eof in Symbol} ensures that we do not read beyond the |
315 where the function @{ML_ind not_eof in Symbol} ensures that we do not read beyond the |
317 end of the input string (i.e.~stopper symbol). |
316 end of the input string (i.e.~stopper symbol). |
318 |
317 |
319 The function @{ML_ind unless in Scan} takes two parsers: if the first one can |
318 The function @{ML_ind unless in Scan} takes two parsers: if the first one can |
320 parse the input, then the whole parser fails; if not, then the second is tried. Therefore |
319 parse the input, then the whole parser fails; if not, then the second is tried. Therefore |
321 |
320 |
322 @{ML_matchresult_fake_both [display,gray] \<open>Scan.unless ($$ "h") ($$ "w") (Symbol.explode "hello")\<close> |
321 @{ML_response [display,gray] \<open>Scan.unless ($$ "h") ($$ "w") (Symbol.explode "hello")\<close> |
323 \<open>Exception FAIL raised\<close>} |
322 "exception FAIL NONE raised"} |
324 |
323 |
325 fails, while |
324 fails, while |
326 |
325 |
327 @{ML_matchresult [display,gray] \<open>Scan.unless ($$ "h") ($$ "w") (Symbol.explode "world")\<close> |
326 @{ML_matchresult [display,gray] \<open>Scan.unless ($$ "h") ($$ "w") (Symbol.explode "world")\<close> |
328 \<open>("w",["o", "r", "l", "d"])\<close>} |
327 \<open>("w",["o", "r", "l", "d"])\<close>} |
499 @{ML \<open>Position.none\<close>} since, |
498 @{ML \<open>Position.none\<close>} since, |
500 at the moment, we are not interested in generating precise error |
499 at the moment, we are not interested in generating precise error |
501 messages. The following code |
500 messages. The following code |
502 |
501 |
503 |
502 |
504 @{ML_matchresult_fake [display,gray] \<open>Token.explode |
503 @{ML_response [display,gray] \<open>Token.explode |
505 (Thy_Header.get_keywords' @{context}) Position.none "hello world"\<close> |
504 (Thy_Header.get_keywords' @{context}) Position.none "hello world"\<close> |
506 \<open>[Token (_,(Ident, "hello"),_), |
505 \<open>[Token (\<dots>(Ident, "hello"),\<dots>), |
507 Token (_,(Space, " "),_), |
506 Token (\<dots>(Space, " "),\<dots>), |
508 Token (_,(Ident, "world"),_)]\<close>} |
507 Token (\<dots>(Ident, "world"),\<dots>)]\<close>} |
509 |
508 |
510 produces three tokens where the first and the last are identifiers, since |
509 produces three tokens where the first and the last are identifiers, since |
511 @{text [quotes] "hello"} and @{text [quotes] "world"} do not match any |
510 @{text [quotes] "hello"} and @{text [quotes] "world"} do not match any |
512 other syntactic category. The second indicates a space. |
511 other syntactic category. The second indicates a space. |
513 |
512 |
521 |
520 |
522 |
521 |
523 text \<open> |
522 text \<open> |
524 then lexing @{text [quotes] "hello world"} will produce |
523 then lexing @{text [quotes] "hello world"} will produce |
525 |
524 |
526 @{ML_matchresult_fake [display,gray] \<open>Token.explode |
525 @{ML_response [display,gray] \<open>Token.explode |
527 (Thy_Header.get_keywords' @{context}) Position.none "hello world"\<close> |
526 (Thy_Header.get_keywords' @{context}) Position.none "hello world"\<close> |
528 \<open>[Token (_,(Keyword, "hello"),_), |
527 \<open>[Token (\<dots>(Keyword, "hello"),\<dots>), |
529 Token (_,(Space, " "),_), |
528 Token (\<dots>(Space, " "),\<dots>), |
530 Token (_,(Ident, "world"),_)]\<close>} |
529 Token (\<dots>(Ident, "world"),\<dots>)]\<close>} |
531 |
530 |
532 Many parsing functions later on will require white space, comments and the like |
531 Many parsing functions later on will require white space, comments and the like |
533 to have already been filtered out. So from now on we are going to use the |
532 to have already been filtered out. So from now on we are going to use the |
534 functions @{ML filter} and @{ML_ind is_proper in Token} to do this. |
533 functions @{ML filter} and @{ML_ind is_proper in Token} to do this. |
535 For example: |
534 For example: |
536 |
535 |
537 @{ML_matchresult_fake [display,gray] |
536 @{ML_response [display,gray] |
538 \<open>let |
537 \<open>let |
539 val input = Token.explode (Thy_Header.get_keywords' @{context}) Position.none "hello world" |
538 val input = Token.explode (Thy_Header.get_keywords' @{context}) Position.none "hello world" |
540 in |
539 in |
541 filter Token.is_proper input |
540 filter Token.is_proper input |
542 end\<close> |
541 end\<close> |
543 \<open>[Token (_,(Ident, "hello"), _), Token (_,(Ident, "world"), _)]\<close>} |
542 \<open>[Token (\<dots>(Keyword, "hello"), \<dots>), Token (\<dots>(Ident, "world"), \<dots>)]\<close>} |
544 |
543 |
545 For convenience we define the function: |
544 For convenience we define the function: |
546 \<close> |
545 \<close> |
547 |
546 |
548 ML %grayML\<open>fun filtered_input str = |
547 ML %grayML\<open>fun filtered_input str = |
550 |
549 |
551 ML \<open>filtered_input "inductive | for"\<close> |
550 ML \<open>filtered_input "inductive | for"\<close> |
552 text \<open> |
551 text \<open> |
553 If you now parse |
552 If you now parse |
554 |
553 |
555 @{ML_matchresult_fake [display,gray] |
554 @{ML_response [display,gray] |
556 \<open>filtered_input "inductive | for"\<close> |
555 \<open>filtered_input "inductive | for"\<close> |
557 \<open>[Token (_,(Command, "inductive"),_), |
556 \<open>[Token (\<dots>(Command, "inductive"),\<dots>), |
558 Token (_,(Keyword, "|"),_), |
557 Token (\<dots>(Keyword, "|"),\<dots>), |
559 Token (_,(Keyword, "for"),_)]\<close>} |
558 Token (\<dots>(Keyword, "for"),\<dots>)]\<close>} |
560 |
559 |
561 you obtain a list consisting of only one command and two keyword tokens. |
560 you obtain a list consisting of only one command and two keyword tokens. |
562 If you want to see which keywords and commands are currently known to Isabelle, |
561 If you want to see which keywords and commands are currently known to Isabelle, |
563 use the function @{ML_ind get_keywords' in Thy_Header}: |
562 use the function @{ML_ind get_keywords' in Thy_Header}: |
564 |
563 |
635 The function @{ML_ind "!!!" in Parse} can be used to force termination |
634 The function @{ML_ind "!!!" in Parse} can be used to force termination |
636 of the parser in case of a dead end, just like @{ML \<open>Scan.!!\<close>} (see previous |
635 of the parser in case of a dead end, just like @{ML \<open>Scan.!!\<close>} (see previous |
637 section). A difference, however, is that the error message of @{ML \<open>Parse.!!!\<close>} is fixed to be @{text [quotes] "Outer syntax error"} |
636 section). A difference, however, is that the error message of @{ML \<open>Parse.!!!\<close>} is fixed to be @{text [quotes] "Outer syntax error"} |
638 together with a relatively precise description of the failure. For example: |
637 together with a relatively precise description of the failure. For example: |
639 |
638 |
640 @{ML_matchresult_fake [display,gray] |
639 @{ML_response [display,gray] |
641 \<open>let |
640 \<open>let |
642 val input = filtered_input "in |" |
641 val input = filtered_input "in |" |
643 val parse_bar_then_in = Parse.$$$ "|" -- Parse.$$$ "in" |
642 val parse_bar_then_in = Parse.$$$ "|" -- Parse.$$$ "in" |
644 in |
643 in |
645 parse (Parse.!!! parse_bar_then_in) input |
644 parse (Parse.!!! parse_bar_then_in) input |
646 end\<close> |
645 end\<close> |
647 \<open>Exception ERROR "Outer syntax error: keyword "|" expected, |
646 \<open>Outer syntax error: keyword "|" expected, |
648 but keyword in was found" raised\<close> |
647 but keyword in was found\<close> |
649 } |
648 } |
650 |
649 |
651 \begin{exercise} (FIXME) |
650 \begin{exercise} (FIXME) |
652 A type-identifier, for example @{typ "'a"}, is a token of |
651 A type-identifier, for example @{typ "'a"}, is a token of |
653 kind @{ML_ind Keyword in Token}. It can be parsed using |
652 kind @{ML_ind Keyword in Token}. It can be parsed using |
667 filter Token.is_proper (Token.explode (Thy_Header.get_keywords' @{context}) (Position.line 7) str)\<close> |
666 filter Token.is_proper (Token.explode (Thy_Header.get_keywords' @{context}) (Position.line 7) str)\<close> |
668 |
667 |
669 text \<open> |
668 text \<open> |
670 where we pretend the parsed string starts on line 7. An example is |
669 where we pretend the parsed string starts on line 7. An example is |
671 |
670 |
672 @{ML_matchresult_fake [display,gray] |
671 @{ML_response [display,gray] |
673 \<open>filtered_input' "foo \\n bar"\<close> |
672 \<open>filtered_input' "foo \n bar"\<close> |
674 \<open>[Token (("foo", ({line=7, end_line=7}, {line=7})), (Ident, "foo"), _), |
673 \<open>[Token (("foo", ({line=7, offset=1, end_offset=4}, {line=7, offset=4})), (Ident, "foo"),\<dots>), |
675 Token (("bar", ({line=8, end_line=8}, {line=8})), (Ident, "bar"), _)]\<close>} |
674 Token (("bar", ({line=8, offset=7, end_offset=10}, {line=8, offset=10})), (Ident, "bar"),\<dots>)]\<close>} |
676 |
675 |
677 in which the @{text [quotes] "\\n"} causes the second token to be in |
676 in which the @{text [quotes] "\\n"} causes the second token to be in |
678 line 8. |
677 line 8. |
679 |
678 |
680 By using the parser @{ML position in Parse} you can access the token |
679 By using the parser @{ML position in Parse} you can access the token |
681 position and return it as part of the parser result. For example |
680 position and return it as part of the parser result. For example |
682 |
681 |
683 @{ML_matchresult_fake [display,gray] |
682 @{ML_response [display,gray] |
684 \<open>let |
683 \<open>let |
685 val input = filtered_input' "where" |
684 val input = filtered_input' "where" |
686 in |
685 in |
687 parse (Parse.position (Parse.$$$ "where")) input |
686 parse (Parse.position (Parse.$$$ "where")) input |
688 end\<close> |
687 end\<close> |
689 \<open>(("where", {line=7, end_line=7}), [])\<close>} |
688 \<open>(("where", {line=7, offset=1, end_offset=6}), [])\<close>} |
690 |
689 |
691 \begin{readmore} |
690 \begin{readmore} |
692 The functions related to positions are implemented in the file |
691 The functions related to positions are implemented in the file |
693 @{ML_file "Pure/General/position.ML"}. |
692 @{ML_file "Pure/General/position.ML"}. |
694 \end{readmore} |
693 \end{readmore} |
749 text \<open> |
748 text \<open> |
750 There is usually no need to write your own parser for parsing inner syntax, that is |
749 There is usually no need to write your own parser for parsing inner syntax, that is |
751 for terms and types: you can just call the predefined parsers. Terms can |
750 for terms and types: you can just call the predefined parsers. Terms can |
752 be parsed using the function @{ML_ind term in Parse}. For example: |
751 be parsed using the function @{ML_ind term in Parse}. For example: |
753 |
752 |
754 @{ML_matchresult_fake [display,gray] |
753 @{ML_response [display,gray] |
755 \<open>let |
754 \<open>let |
756 val input = Token.explode (Thy_Header.get_keywords' @{context}) Position.none "foo" |
755 val input = Token.explode (Thy_Header.get_keywords' @{context}) Position.none "foo" |
757 in |
756 in |
758 Parse.term input |
757 Parse.term input |
759 end\<close> |
758 end\<close> |
765 the parsed string, but also some markup information. You can decode the |
764 the parsed string, but also some markup information. You can decode the |
766 information with the function @{ML_ind parse in YXML} in @{ML_structure YXML}. |
765 information with the function @{ML_ind parse in YXML} in @{ML_structure YXML}. |
767 The result of the decoding is an XML-tree. You can see better what is going on if |
766 The result of the decoding is an XML-tree. You can see better what is going on if |
768 you replace @{ML Position.none} by @{ML \<open>Position.line 42\<close>}, say: |
767 you replace @{ML Position.none} by @{ML \<open>Position.line 42\<close>}, say: |
769 |
768 |
770 @{ML_matchresult_fake [display,gray] |
769 @{ML_response [display,gray] |
771 \<open>let |
770 \<open>let |
772 val input = Token.explode (Thy_Header.get_keywords' @{context}) (Position.line 42) "foo" |
771 val input = Token.explode (Thy_Header.get_keywords' @{context}) (Position.line 42) "foo" |
773 in |
772 in |
774 YXML.parse (fst (Parse.term input)) |
773 YXML.parse (fst (Parse.term input)) |
775 end\<close> |
774 end\<close> |
776 \<open>Elem ("token", [("line", "42"), ("end_line", "42")], [XML.Text "foo"])\<close>} |
775 \<open><input delimited="false" line="42" offset="1" end_offset="4">foo</input>\<close>} |
777 |
776 |
778 The positional information is stored as part of an XML-tree so that code |
777 The positional information is stored as part of an XML-tree so that code |
779 called later on will be able to give more precise error messages. |
778 called later on will be able to give more precise error messages. |
780 |
779 |
781 \begin{readmore} |
780 \begin{readmore} |
845 [((even0,_),_), |
844 [((even0,_),_), |
846 ((evenS,_),_), |
845 ((evenS,_),_), |
847 ((oddS,_),_)]), [])\<close>} |
846 ((oddS,_),_)]), [])\<close>} |
848 \<close> |
847 \<close> |
849 |
848 |
850 ML \<open>let |
|
851 val input = filtered_input |
|
852 "foo::\"int \<Rightarrow> bool\" and bar::nat (\"BAR\" 100) and blonk" |
|
853 in |
|
854 parse Parse.vars input |
|
855 end\<close> |
|
856 |
|
857 text \<open> |
849 text \<open> |
858 As you see, the result is a pair consisting of a list of |
850 As you see, the result is a pair consisting of a list of |
859 variables with optional type-annotation and syntax-annotation, and a list of |
851 variables with optional type-annotation and syntax-annotation, and a list of |
860 rules where every rule has optionally a name and an attribute. |
852 rules where every rule has optionally a name and an attribute. |
861 |
853 |
864 list of variables that can include optional type annotations and syntax translations. |
856 list of variables that can include optional type annotations and syntax translations. |
865 For example:\footnote{Note that in the code we need to write |
857 For example:\footnote{Note that in the code we need to write |
866 \<open>"int \<Rightarrow> bool"\<close> in order to properly escape the double quotes |
858 \<open>"int \<Rightarrow> bool"\<close> in order to properly escape the double quotes |
867 in the compound type.} |
859 in the compound type.} |
868 |
860 |
869 @{ML_matchresult_fake [display,gray] |
861 @{ML_response [display,gray] |
870 \<open>let |
862 \<open>let |
871 val input = filtered_input |
863 val input = filtered_input |
872 "foo::\"int \<Rightarrow> bool\" and bar::nat (\"BAR\" 100) and blonk" |
864 "foo::\"int \<Rightarrow> bool\" and bar::nat (\"BAR\" 100) and blonk" |
873 in |
865 in |
874 parse Parse.vars input |
866 parse Parse.vars input |
875 end\<close> |
867 end\<close> |
876 \<open>([(foo, SOME _, NoSyn), |
868 \<open>([("foo", SOME \<dots>, NoSyn), |
877 (bar, SOME _, Mixfix (Source {text="BAR",...}, [], 100, _)), |
869 ("bar", SOME \<dots>, Mixfix (Source {\<dots>text = "BAR"}, [], 100, \<dots>)), |
878 (blonk, NONE, NoSyn)],[])\<close>} |
870 ("blonk", NONE, NoSyn)], [])\<close>} |
879 \<close> |
871 \<close> |
880 |
872 |
881 text \<open> |
873 text \<open> |
882 Whenever types are given, they are stored in the @{ML SOME}s. The types are |
874 Whenever types are given, they are stored in the @{ML SOME}s. The types are |
883 not yet used to type the variables: this must be done by type-inference later |
875 not yet used to type the variables: this must be done by type-inference later |
951 |
943 |
952 section \<open>New Commands\label{sec:newcommand}\<close> |
944 section \<open>New Commands\label{sec:newcommand}\<close> |
953 |
945 |
954 text \<open> |
946 text \<open> |
955 Often new commands, for example for providing new definitional principles, |
947 Often new commands, for example for providing new definitional principles, |
956 need to be implemented. While this is not difficult on the ML-level and for |
948 need to be implemented. |
957 jEdit, in order to be backwards compatible, new commands need also to be recognised |
|
958 by Proof-General. This results in some subtle configuration issues, which we will |
|
959 explain in the next section. Here we just describe how to define new commands |
|
960 to work with jEdit. |
|
961 |
949 |
962 Let us start with a ``silly'' command that does nothing at all. We |
950 Let us start with a ``silly'' command that does nothing at all. We |
963 shall name this command \isacommand{foobar}. Before you can |
951 shall name this command \isacommand{foobar}. Before you can |
964 implement any new command, you have to ``announce'' it in the |
952 implement any new command, you have to ``announce'' it in the |
965 \isacommand{keywords}-section of your theory header. For \isacommand{foobar} |
953 \isacommand{keywords}-section of your theory header. For \isacommand{foobar} |
998 |
986 |
999 foobar |
987 foobar |
1000 |
988 |
1001 text \<open> |
989 text \<open> |
1002 but of course you will not see anything since \isacommand{foobar} is |
990 but of course you will not see anything since \isacommand{foobar} is |
1003 not intended to do anything. Remember, however, that this only |
991 not intended to do anything. |
1004 works in jEdit. In order to enable also Proof-General recognise this |
|
1005 command, a keyword file needs to be generated (see next section). |
|
1006 |
992 |
1007 As it stands, the command \isacommand{foobar} is not very useful. Let |
993 As it stands, the command \isacommand{foobar} is not very useful. Let |
1008 us refine it a bit next by letting it take a proposition as argument |
994 us refine it a bit next by letting it take a proposition as argument |
1009 and printing this proposition inside the tracing buffer. We announce |
995 and printing this proposition inside the tracing buffer. We announce |
1010 the command \isacommand{foobar\_trace} in the theory header as |
996 the command \isacommand{foobar\_trace} in the theory header as |