ProgTutorial/Parsing.thy
changeset 572 438703674711
parent 569 f875a25aa72d
child 573 321e220a6baa
equal deleted inserted replaced
571:95b42288294e 572:438703674711
    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