CookBook/Parsing.thy
changeset 68 e7519207c2b7
parent 67 5fbeeac2901b
child 69 19106a9975c1
equal deleted inserted replaced
67:5fbeeac2901b 68:e7519207c2b7
     1 theory Parsing
     1 theory Parsing
     2 imports Base
     2 imports Base 
     3 
     3 
     4 begin
     4 begin
     5 
     5 
     6 
     6 
     7 chapter {* Parsing *}
     7 chapter {* Parsing *}
   554 ML {* OuterSyntax.command *}
   554 ML {* OuterSyntax.command *}
   555 
   555 
   556 section {* New Commands and Creating Keyword Files *}
   556 section {* New Commands and Creating Keyword Files *}
   557 
   557 
   558 text {*
   558 text {*
   559   Often new commands, for example for providing a new definitional principle,
   559   Often new commands, for example for providing new definitional principles,
   560   need to be programmed. While this is not difficult to do on the ML-level,
   560   need to be implemented. While this is not difficult on the ML-level,
   561   new commands, in order to be useful, need to be recognised by
   561   new commands, in order to be useful, need to be recognised by
   562   ProofGeneral. This results in some subtle configuration issues, which we
   562   ProofGeneral. This results in some subtle configuration issues, which we
   563   will explain in this section.
   563   will explain in this section.
   564 
   564 
   565   Let us start with a ``silly'' command, which we call \isacommand{foo} in what follows.
   565   Let us start with a ``silly'' command, which we call \isacommand{foobar} in what follows.
   566   To keep things simple this command does nothing at all. On the ML-level it can be defined as
   566   To keep things simple this command does nothing at all. On the ML-level it can be defined as
   567 
   567 *}
   568 @{ML [display]
   568 
   569 "let
   569 ML {* 
   570   val do_nothing = Scan.succeed (Toplevel.theory (fn x => x))
   570 let
   571   val flag = OuterKeyword.thy_decl
   571   val do_nothing = Scan.succeed (Toplevel.theory I)
       
   572   val kind = OuterKeyword.thy_decl
   572 in
   573 in
   573   OuterSyntax.command \"foo\" \"description of foo\" flag do_nothing
   574   OuterSyntax.command "foobar" "description of foobar" kind do_nothing
   574 end"}
   575 end 
   575 
   576 *}
       
   577 
       
   578 text {*
   576   The function @{ML OuterSyntax.command} expects a name for the command, a
   579   The function @{ML OuterSyntax.command} expects a name for the command, a
   577   short description, a flag (which we will explain later on more thoroughly) and a
   580   short description, a kind indicator (which we will explain later on more thoroughly) and a
   578   parser for a top-level transition function (its purpose will also explained
   581   parser for a top-level transition function (its purpose will also explained
   579   later). 
   582   later). 
   580 
   583 
   581   While this is everything we have to do on the ML-level, we need 
   584   While this is everything we have to do on the ML-level, we need a keyword
   582   now a keyword file that can be loaded by ProofGeneral. This is to enable ProofGeneral 
   585   file that can be loaded by ProofGeneral. This is to enable ProofGeneral to
   583   to recognise  \isacommand{foo} as a command. Such a keyword file can be generated with 
   586   recognise \isacommand{foobar} as a command. Such a keyword file can be
   584   the command-line
   587   generated with the command-line
   585 
   588 
   586   @{text [display] "$ isabelle keywords -k foo <some-log-files>"}
   589 
   587 
   590   @{text [display] "$ isabelle keywords -k foobar some-log-files"}
   588   The option @{text "-k foo"} indicates which postfix the keyword file will obtain. In
   591 
   589   the case above the generated file will be named @{text "isar-keywords-foo.el"}. This command
   592   The option @{text "-k foobar"} indicates which postfix the keyword file will
   590   requires log files to be present (in order to extract the keywords from them). 
   593   obtain. In the case above the generated file will be named @{text
   591   To generate these log files, we first package the code above into a separate theory file 
   594   "isar-keywords-foobar.el"}. However, this command requires log files to be
   592   named @{text "Command.thy"}, say---see Figure~\ref{fig:commandtheory} for the complete code. 
   595   present (in order to extract the keywords from them). To generate these log
       
   596   files, we first package the code above into a separate theory file named
       
   597   @{text "Command.thy"}, say---see Figure~\ref{fig:commandtheory} for the
       
   598   complete code.
       
   599 
   593 
   600 
   594   %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   601   %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   595   \begin{figure}[t]
   602   \begin{figure}[t]
   596   \begin{boxedminipage}{\textwidth}
   603   \begin{boxedminipage}{\textwidth}\small
   597   \isacommand{theory}~@{text Command}\\
   604   \isacommand{theory}~@{text Command}\\
   598   \isacommand{imports}~@{text Main}\\
   605   \isacommand{imports}~@{text Main}\\
   599   \isacommand{begin}\\
   606   \isacommand{begin}\\
   600   \isacommand{ML}~\isa{\isacharverbatimopen}\\
   607   \isacommand{ML}~\isa{\isacharverbatimopen}\\
   601   @{ML
   608   @{ML
   602 "let
   609 "let
   603   val do_nothing = Scan.succeed (Toplevel.theory (fn x => x))
   610   val do_nothing = Scan.succeed (Toplevel.theory I)
   604   val flag = OuterKeyword.thy_decl
   611   val kind = OuterKeyword.thy_decl
   605 in
   612 in
   606   OuterSyntax.command \"foo\" \"description of foo\" flag do_nothing
   613   OuterSyntax.command \"foobar\" \"description of foobar\" kind do_nothing
   607 end"}\\
   614 end"}\\
   608   \isa{\isacharverbatimclose}\\
   615   \isa{\isacharverbatimclose}\\
   609   \isacommand{end}
   616   \isacommand{end}
   610   \end{boxedminipage}
   617   \end{boxedminipage}
   611   \caption{The file @{text "Command.thy"} is necessary for generating a log 
   618   \caption{The file @{text "Command.thy"} is necessary for generating a log 
   612   file. This log file enables Isabelle to generate a keyword file containing 
   619   file. This log file enables Isabelle to generate a keyword file containing 
   613   the command \isacommand{foo}.\label{fig:commandtheory}}
   620   the command \isacommand{foobar}.\label{fig:commandtheory}}
   614   \end{figure}
   621   \end{figure}
   615   %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   622   %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   616 
   623 
   617   For 
   624   For our purposes it is sufficient to use the log files for the theories
   618   our purposes it is sufficient to use the log files for the theories @{text "Pure"},
   625   @{text "Pure"}, @{text "HOL"} and @{text "Pure-ProofGeneral"}, as well as
   619   @{text "HOL"} and @{text "Pure-ProofGeneral"}, as well as the theory @{text "Command.thy"}
   626   the theory @{text "Command.thy"} containing the new
   620   containing the new \isacommand{foo}-command. @{text Pure} and @{text HOL} are usually compiled during the 
   627   \isacommand{foobar}-command. @{text Pure} and @{text HOL} are usually
   621   installation of Isabelle. So log files for them are already available. If not, then they 
   628   compiled during the installation of Isabelle. So log files for them should be
   622   can be conveniently compiled using build-script from the distribution
   629   already available. If not, then they can be conveniently compiled using
       
   630   build-script from the Isabelle distribution
   623 
   631 
   624   @{text [display] 
   632   @{text [display] 
   625 "$ ./build -m \"Pure\"
   633 "$ ./build -m \"Pure\"
   626 $ ./build -m \"HOL\""}
   634 $ ./build -m \"HOL\""}
   627   
   635   
   630   @{text [display] "$ ./build -m \"Pure-ProofGeneral\" \"Pure\""}
   638   @{text [display] "$ ./build -m \"Pure-ProofGeneral\" \"Pure\""}
   631 
   639 
   632   For the theory @{text "Command.thy"}, we first create a ``managed'' subdirectory 
   640   For the theory @{text "Command.thy"}, we first create a ``managed'' subdirectory 
   633   with
   641   with
   634 
   642 
   635   @{text [display] "$ isabelle mkdir FooCommand"}
   643   @{text [display] "$ isabelle mkdir FoobarCommand"}
   636 
   644 
   637   This creates a directory containing the files 
   645   This creates a directory containing the files 
   638 
   646 
   639   @{text [display] 
   647   @{text [display] 
   640 "./IsaMakefile
   648 "./IsaMakefile
   641 ./FooCommand/ROOT.ML
   649 ./FoobarCommand/ROOT.ML
   642 ./FooCommand/document
   650 ./FoobarCommand/document
   643 ./FooCommand/document/root.tex"}
   651 ./FoobarCommand/document/root.tex"}
   644 
   652 
   645 
   653 
   646   We need to copy the file @{text "Command.thy"} into the directory @{text "FooCommand"}
   654   We need to copy the file @{text "Command.thy"} into the directory @{text "FoobarCommand"}
   647   and add the line 
   655   and add the line 
   648 
   656 
   649   @{text [display] "use_thy \"Command\";"} 
   657   @{text [display] "use_thy \"Command\";"} 
   650   
   658   
   651   to the file @{text "./FooCommand/ROOT.ML"}. We can now compile the theory by just typing
   659   to the file @{text "./FoobarCommand/ROOT.ML"}. We can now compile the theory by just typing
   652 
   660 
   653   @{text [display] "$ isabelle make"}
   661   @{text [display] "$ isabelle make"}
   654 
   662 
   655   We created finally all the necessary log files. They are typically stored 
   663   We created finally all the necessary log files. They are typically stored 
   656   in the directory 
   664   in the directory 
   661   Let us assume the name of this directory is stored in the shell variable 
   669   Let us assume the name of this directory is stored in the shell variable 
   662   @{text "ISABELLE_LOGS"}. One way to assign this shell variable is by typing
   670   @{text "ISABELLE_LOGS"}. One way to assign this shell variable is by typing
   663 
   671 
   664   @{text [display] "$ ISABELLE_LOGS=\"$(isabelle getenv -b ISABELLE_OUTPUT)\"/log"}
   672   @{text [display] "$ ISABELLE_LOGS=\"$(isabelle getenv -b ISABELLE_OUTPUT)\"/log"}
   665  
   673  
   666   In this directory are the files
   674   on the Unix prompt. This directory should include the files
   667 
   675 
   668   @{text [display] 
   676   @{text [display] 
   669 "Pure.gz
   677 "Pure.gz
   670 HOL.gz
   678 HOL.gz
   671 Pure-ProofGeneral.gz
   679 Pure-ProofGeneral.gz
   672 HOL-FooCommand.gz"} 
   680 HOL-FoobarCommand.gz"} 
   673 
   681 
   674   They are used for creating the keyword files with the command
   682   They are the ones we use for creating the keyword files. The corresponding  Unix command
       
   683   is
   675 
   684 
   676 @{text [display]
   685 @{text [display]
   677 "$ isabelle keywords -k foo 
   686 "$ isabelle keywords -k foobar 
   678        $ISABELLE_LOGS/{Pure.gz,HOL.gz,Pure-ProofGeneral.gz,HOL-FooCommand.gz}"}
   687      $ISABELLE_LOGS/{Pure.gz,HOL.gz,Pure-ProofGeneral.gz,HOL-FoobarCommand.gz}"}
   679 
   688 
   680   The result is the file @{text "isar-keywords-foo.el"}. This file needs to be 
   689   The result is the file @{text "isar-keywords-foobar.el"}. It should contain the
   681   copied to the directory @{text "~/.isabelle/etc"}. To make Isabelle use
   690   string @{text "foobar"} twice (check for example that @{text [quotes] "grep foobar
   682   this keyword file, we have to start it with the option @{text "-k foo"}, i.e.
   691   isar-keywords-foobar.el"} returns something non-empty).  This keyword file
   683 
   692   needs to be copied into the directory @{text "~/.isabelle/etc"}. To make
   684   @{text [display] "isabelle -k foo <a-theory-file>"}
   693   Isabelle use this keyword file, we have to start it with the option @{text
       
   694   "-k foobar"}, i.e.
       
   695 
       
   696   @{text [display] "$ isabelle -k foobar a-theory-file"}
   685 
   697 
   686   If we now run the original code
   698   If we now run the original code
   687 
   699 *}
   688   @{ML [display]
   700 
   689 "let
   701 ML {*
   690   val do_nothing = Scan.succeed (Toplevel.theory (fn x => x))
   702 let
   691   val flag = OuterKeyword.thy_decl
   703   val do_nothing = Scan.succeed (Toplevel.theory I)
       
   704   val kind = OuterKeyword.thy_decl
   692 in
   705 in
   693   OuterSyntax.command \"foo\" \"description of foo\" flag do_nothing
   706   OuterSyntax.command "foobar" "description of foobar" kind do_nothing
   694 end"}
   707 end 
   695 
   708 *}
   696   then we can make use of \isacommand{foo}! Similarly with any other new command. 
   709 
       
   710 text {*
       
   711   then we can make use of \isacommand{foobar}! Similarly with any other new command. 
   697 
   712 
   698   In the example above, we built the theories on top of the HOL-logic. If you
   713   In the example above, we built the theories on top of the HOL-logic. If you
   699   target other logics, such as Nominal or ZF, then you need to change the
   714   target other logics, such as Nominal or ZF, then you need to adapt the
   700   log files appropriately.
   715   log files appropriately.
   701 
   716 
       
   717   At the moment, \isacommand{foobar} is not very useful. Let us refine it a bit 
       
   718   by taking a proposition as argument and printing this proposition inside
       
   719   the tracing buffer. 
       
   720 
       
   721   The crucial part of a command is the function that determines
       
   722   the behaviour of the command. In the code above we used the the 
       
   723   @{text do_nothing}-function, which because of @{ML Scan.succeed} does not parse 
       
   724   any argument, but immediately returns the simple toplevel function 
       
   725   @{ML "Toplevel.theory I"}. We can replace this code by a function that first 
       
   726   parses a proposition (using the parser @{ML OuterParse.prop}), then prints out 
       
   727   the tracing information and finally does nothing. For this we can write
       
   728 *}
       
   729 
       
   730 ML {* 
       
   731 let
       
   732   val trace_prop = 
       
   733          OuterParse.prop >> (fn str => (tracing str; (Toplevel.theory I)))
       
   734   val kind = OuterKeyword.thy_decl
       
   735 in
       
   736   OuterSyntax.command "foobar" "traces a proposition" kind trace_prop
       
   737 end 
       
   738 *}
       
   739 
       
   740 text {*
       
   741   Now we can type for example
       
   742 
       
   743   @{ML_response_fake_both [display] "foobar \"True \<and> False\"" "True \<and> False"}
       
   744   
       
   745   and see the proposition in the tracing buffer.  
       
   746 
       
   747   Note that so far we used @{ML thy_decl in OuterKeyword} as kind indicator
       
   748   for the command.  This means that the command finishes as soon as the
       
   749   arguments are processed. Examples of this kind of commands are
       
   750   \isacommand{definition} and \isacommand{declare}.  In other cases, however,
       
   751   commands are expected to parse some arguments, for example a proposition,
       
   752   and then ``open up'' a proof in order to prove the proposition (think of
       
   753   \isacommand{lemma}) or prove some other properties (for example in
       
   754   \isacommand{function}). To achieve this behaviour we have to use the kind
       
   755   indicator @{ML thy_goal in OuterKeyword}.
       
   756 
       
   757   Below we change \isacommand{foobar} is such a way that an proposition as
       
   758   argument and then start a proof in order to prove it. Therefore in Line 13
       
   759   below, we set the kind indicator to @{ML thy_goal in OuterKeyword}.
       
   760 *}
       
   761 
       
   762 ML %linenumbers {*let
       
   763   fun set_up_thm str ctxt =
       
   764     let
       
   765       val prop = Syntax.read_prop ctxt str
       
   766     in
       
   767       Proof.theorem_i NONE (K I) [[(Syntax.read_prop ctxt str,[])]] ctxt
       
   768     end;
       
   769   
       
   770   val prove_prop = OuterParse.prop >>  
       
   771       (fn str => Toplevel.print o 
       
   772                     Toplevel.local_theory_to_proof NONE (set_up_thm str))
       
   773   
       
   774   val kind = OuterKeyword.thy_goal
       
   775 in
       
   776   OuterSyntax.command "foobar" "proving a proposition" kind prove_prop
       
   777 end 
       
   778 *}
       
   779 
       
   780 text {*
       
   781   The function @{text set_up_thm} takes a string (the proposition) and a context.
       
   782   The context is necessary in order to convert the string into a proper proposition
       
   783   using the function @{ML Syntax.read_prop}. In Line 6 we use the function
       
   784   @{ML Proof.theorem_i} to start the proof for the proposition. In Lines 9 to
       
   785   11 contain the parser for the proposition.
       
   786 
       
   787   If we now type @{text "foobar \"True \<and> True\""}, we obtain the following 
       
   788   proof state:
       
   789  
       
   790   @{ML_response_fake_both [display] "foobar \"True \<and> True\"" 
       
   791 "goal (1 subgoal):
       
   792 1. True \<and> True"}
       
   793 
       
   794   and we can build the proof
       
   795 
       
   796   @{text [display] "foobar \"True \<and> True\"
       
   797 apply(rule conjI)
       
   798 apply(rule TrueI)+
       
   799 done"} 
       
   800   
       
   801   (FIXME What does @{text "Toplevel.theory"}?)
       
   802 
   702   (FIXME Explain @{text "OuterKeyword.thy_decl"} and parser)
   803   (FIXME Explain @{text "OuterKeyword.thy_decl"} and parser)
   703   
   804 
   704 
   805 *}
   705 *}
       
   706 
       
   707 
       
   708 
       
   709 
   806 
   710 chapter {* Parsing *}
   807 chapter {* Parsing *}
   711 
   808 
   712 text {*
   809 text {*
   713 
   810