ProgTutorial/Parsing.thy
changeset 520 615762b8d8cb
parent 519 cf471fa86091
child 521 9728b0432fb2
equal deleted inserted replaced
519:cf471fa86091 520:615762b8d8cb
     1 theory Parsing
     1 theory Parsing
     2 imports Base "Helper/Command/Command" "Package/Simple_Inductive_Package"
     2 imports Base "Helper/Command/Command" "Package/Simple_Inductive_Package"
     3 begin
     3 begin
     4 
     4 
     5 chapter {* Parsing\label{chp:parsing} *}
     5 chapter {* Parsing\label{chp:parsing} *}
       
     6 
     6 
     7 
     7 text {*
     8 text {*
     8   \begin{flushright}
     9   \begin{flushright}
     9   {\em An important principle underlying the success and popularity of Unix\\ is
    10   {\em An important principle underlying the success and popularity of Unix\\ is
    10   the philosophy of building on the work of others.} \\[1ex]
    11   the philosophy of building on the work of others.} \\[1ex]
   938 section {* New Commands\label{sec:newcommand} *}
   939 section {* New Commands\label{sec:newcommand} *}
   939 
   940 
   940 text {*
   941 text {*
   941   Often new commands, for example for providing new definitional principles,
   942   Often new commands, for example for providing new definitional principles,
   942   need to be implemented. While this is not difficult on the ML-level and
   943   need to be implemented. While this is not difficult on the ML-level and
   943   jEdit, in order to be compatible, new commands need also to be recognised 
   944   jEdit, in order to be backwards compatible, new commands need also to be recognised 
   944   by ProofGeneral. This results in some subtle configuration issues, which we will 
   945   by Proof-General. This results in some subtle configuration issues, which we will 
   945   explain in the next section. Here we just describe how to define new commands
   946   explain in the next section. Here we just describe how to define new commands
   946   to work with jEdit.
   947   to work with jEdit.
   947 
   948 
   948   Let us start with a ``silly'' command that does nothing 
   949   Let us start with a ``silly'' command that does nothing at all. We
   949   at all. We shall name this command \isacommand{foobar}. On the ML-level it can be 
   950   shall name this command \isacommand{foobar}.  Before you can
   950   defined as:
   951   implement any new command, you have to ``announce'' it in the
   951 *}
   952   \isacommand{keyword}-section of theory header. For \isacommand{foobar}
   952 
   953   we need to write something like
   953 ML %grayML{*let
       
   954   val do_nothing = Scan.succeed (Local_Theory.background_theory I)
       
   955   val kind = Keyword.thy_decl
       
   956 in
       
   957   Outer_Syntax.local_theory ("foobar", kind) 
       
   958     "description of foobar" 
       
   959       do_nothing
       
   960 end *}
       
   961 
       
   962 text {*
       
   963   The crucial function @{ML_ind local_theory in Outer_Syntax} expects a name for the command, 
       
   964   a kind indicator (which we will explain later more thoroughly), a
       
   965   short description and a
       
   966   parser producing a local theory transition (its purpose will also explained
       
   967   later). Before you can use any new command, you have to ``announce'' it in the
       
   968   \isacommand{keyword}-section of theory header. In our running example we need
       
   969   to write
       
   970 
   954 
   971   \begin{graybox}
   955   \begin{graybox}
   972   \isacommand{theory}~@{text Foo}\\
   956   \isacommand{theory}~@{text Foo}\\
   973   \isacommand{imports}~@{text Main}\\
   957   \isacommand{imports}~@{text Main}\\
   974   \isacommand{keywords} @{text [quotes] "foobar"} @{text "::"} @{text "thy_decl"}\\
   958   \isacommand{keywords} @{text [quotes] "foobar"} @{text "::"} @{text "thy_decl"}\\
   975   ...
   959   ...
   976   \end{graybox}
   960   \end{graybox}
   977 
   961 
   978   whereby you have to declare again that the new keyword is of the appropriate
   962   whereby @{ML_ind "thy_decl" in Keyword} indicates the kind of the
   979   kind---for \isacommand{foobar} it is @{text "thy_decl"}. Another possible kind 
   963   command.  Another possible kind is @{text "thy_goal"}, or you can
   980   is @{text "thy_goal"} and also to omit the kind entirely, in which case you declare a 
   964   also to omit the kind entirely, in which case you declare a keyword
   981   keyword (something that is part of a command). After you announced the new command, 
   965   (something that is part of a command).
   982   you can type in your theory
   966 
   983 *}
   967   Now you can implement \isacommand{foobar} as:
       
   968 *}
       
   969 
       
   970 ML %grayML{*let
       
   971   val do_nothing = Scan.succeed (Local_Theory.background_theory I)
       
   972 in
       
   973   Outer_Syntax.local_theory @{command_spec "foobar"} 
       
   974     "description of foobar" 
       
   975       do_nothing
       
   976 end *}
       
   977 
       
   978 text {*
       
   979   The crucial function @{ML_ind local_theory in Outer_Syntax} expects
       
   980   the name for the command, a kind indicator, a short description and 
       
   981   a parser producing a local theory transition (explained later). For the
       
   982   name and the kind, you can use the ML-antiquotation @{text "@{command_spec ...}"}.
       
   983   After this, you can write in your theory 
       
   984 *}
       
   985 
   984 
   986 
   985 foobar
   987 foobar
   986 
   988 
   987 text {* 
   989 
       
   990 text {*
       
   991   Remember that this only works in jEdit. In order to let Proof-General 
       
   992   recognise this command, a keyword file needs to be generated (see next
       
   993   section).
       
   994 
   988   At the moment the command \isacommand{foobar} is not very useful. Let us
   995   At the moment the command \isacommand{foobar} is not very useful. Let us
   989   refine it a bit next by letting it take a proposition as argument and
   996   refine it a bit next by letting it take a proposition as argument and
   990   printing this proposition inside the tracing buffer. For this the crucial 
   997   printing this proposition inside the tracing buffer. We announce the
   991   part of a command is the function that determines the behaviour
   998   command as
   992   of the command. In the code above we used a ``do-nothing''-function, which
   999 
   993   because of @{ML_ind succeed in Scan} does not parse any argument, but immediately
  1000   \begin{graybox}
   994   returns the simple function @{ML "Local_Theory.background_theory I"}. We can
  1001   \isacommand{keywords} @{text [quotes] "foobar_trace"} @{text "::"} @{text "thy_decl"}
   995   replace this code by a function that first parses a proposition (using the
  1002   \end{graybox}
   996   parser @{ML Parse.prop}), then prints out some tracing
  1003 
   997   information (using the function @{text trace_prop}) and 
  1004   The crucial part of a command is the function that determines the
   998   finally does nothing. For this you can write:
  1005   behaviour of the command. In the code above we used a
       
  1006   ``do-nothing''-function, which because of the parser @{ML_ind succeed in Scan}
       
  1007   does not parse any argument, but immediately returns the simple
       
  1008   function @{ML "Local_Theory.background_theory I"}. We can replace
       
  1009   this code by a function that first parses a proposition (using the
       
  1010   parser @{ML Parse.prop}), then prints out some tracing information
       
  1011   (using the function @{text trace_prop}) and finally does
       
  1012   nothing. For this you can write:
   999 *}
  1013 *}
  1000 
  1014 
  1001 ML %grayML{*let
  1015 ML %grayML{*let
  1002   fun trace_prop str = 
  1016   fun trace_prop str = 
  1003     Local_Theory.background_theory (fn ctxt => (tracing str; ctxt))
  1017     Local_Theory.background_theory (fn ctxt => (tracing str; ctxt))
  1004 
  1018 in
  1005   val kind = Keyword.thy_decl
  1019   Outer_Syntax.local_theory @{command_spec "foobar_trace"} 
  1006 in
       
  1007   Outer_Syntax.local_theory ("foobar_trace", kind) 
       
  1008     "traces a proposition" 
  1020     "traces a proposition" 
  1009       (Parse.prop >> trace_prop)
  1021       (Parse.prop >> trace_prop)
  1010 end *}
  1022 end *}
  1011 
  1023 
  1012 text {*
  1024 text {*
  1013   The command is now \isacommand{foobar\_trace} and can be used to 
  1025   The command is now \isacommand{foobar\_trace} and can be used to 
  1014   see the proposition in the tracing buffer (remember you need to announce
  1026   see the proposition in the tracing buffer.
  1015   every new command in the header of the theory).  
       
  1016 *}
  1027 *}
  1017 
  1028 
  1018 foobar_trace "True \<and> False"
  1029 foobar_trace "True \<and> False"
  1019 
  1030 
  1020 text {*
  1031 text {*
  1028   \isacommand{function}). To achieve this kind of behaviour, you have to use
  1039   \isacommand{function}). To achieve this kind of behaviour, you have to use
  1029   the kind indicator @{ML_ind thy_goal in Keyword} and the function @{ML
  1040   the kind indicator @{ML_ind thy_goal in Keyword} and the function @{ML
  1030   "local_theory_to_proof" in Outer_Syntax} to set up the command. 
  1041   "local_theory_to_proof" in Outer_Syntax} to set up the command. 
  1031   Below we show the command \isacommand{foobar\_goal} which takes a
  1042   Below we show the command \isacommand{foobar\_goal} which takes a
  1032   proposition as argument and then starts a proof in order to prove
  1043   proposition as argument and then starts a proof in order to prove
  1033   it. Therefore in Line 9, we set the kind indicator to @{ML thy_goal in
  1044   it. Therefore, we need to announce this command in the header 
  1034   Keyword}.
  1045   as @{text "thy_goal"}
       
  1046 
       
  1047   \begin{graybox}
       
  1048   \isacommand{keywords} @{text [quotes] "foobar_goal"} @{text "::"} @{text "thy_goal"}
       
  1049   \end{graybox}
       
  1050 
       
  1051   Then we can write:
  1035 *}
  1052 *}
  1036 
  1053 
  1037 ML%linenosgray{*let
  1054 ML%linenosgray{*let
  1038   fun goal_prop str ctxt =
  1055   fun goal_prop str ctxt =
  1039     let
  1056     let
  1040       val prop = Syntax.read_prop ctxt str
  1057       val prop = Syntax.read_prop ctxt str
  1041     in
  1058     in
  1042       Proof.theorem NONE (K I) [[(prop, [])]] ctxt
  1059       Proof.theorem NONE (K I) [[(prop, [])]] ctxt
  1043     end
  1060     end
  1044   
  1061 in
  1045   val kind = Keyword.thy_goal
  1062   Outer_Syntax.local_theory_to_proof @{command_spec "foobar_goal"}
  1046 in
       
  1047   Outer_Syntax.local_theory_to_proof ("foobar_goal", kind) 
       
  1048     "proves a proposition" 
  1063     "proves a proposition" 
  1049       (Parse.prop >> goal_prop)
  1064       (Parse.prop >> goal_prop)
  1050 end *}
  1065 end *}
  1051 
  1066 
  1052 text {*
  1067 text {*
  1074 apply(rule conjI)
  1089 apply(rule conjI)
  1075 apply(rule TrueI)+
  1090 apply(rule TrueI)+
  1076 done
  1091 done
  1077 
  1092 
  1078 text {*
  1093 text {*
       
  1094   The final new command we describe is
       
  1095   \isacommand{foobar\_proof}. Like \isacommand{lemma}, its purpose is
       
  1096   to take a proposition and open a corresponding proof-state that
       
  1097   allows us to give a proof for it. However, unlike
       
  1098   \isacommand{lemma}, the proposition will be given as an
       
  1099   ML-value. Such a command is quite useful during development
       
  1100   of a tool that generates a goal on the ML-level and you want to see
       
  1101   whether it is provable. In addition you also want that the proved 
       
  1102   proposition can be given a lemma name that can be referenced later on.
       
  1103 
       
  1104   The first problem of this new command is to parse some text as 
       
  1105   ML-source and then interpret it as a term. For the parsing we can 
       
  1106   use the function @{ML_ind "ML_source" in Parse} in the structure 
       
  1107   @{ML_struct Parse}. For running the ML-interpreter we need the 
       
  1108   following scaffolding code.
       
  1109 *}
       
  1110 
       
  1111 ML %grayML{*
       
  1112 structure Result = Proof_Data (
       
  1113   type T = unit -> term
       
  1114   fun init thy () = error "Result")
       
  1115 
       
  1116 val result_cookie = (Result.get, Result.put, "Result.put") *}
       
  1117 
       
  1118 text {*
       
  1119   With this in place we can write the code for \isacommand{foobar\_prove}.
       
  1120 *}
       
  1121 
       
  1122 ML %linenosgray{*let
       
  1123    fun after_qed thm_name thms lthy =
       
  1124         Local_Theory.note (thm_name, (flat thms)) lthy |> snd
       
  1125 
       
  1126    fun setup_proof (thm_name, (txt, pos)) lthy =
       
  1127      let
       
  1128        val trm = Code_Runtime.value lthy result_cookie ("", txt)
       
  1129      in
       
  1130        Proof.theorem NONE (after_qed thm_name) [[(trm, [])]] lthy
       
  1131      end
       
  1132 
       
  1133    val parser = Parse_Spec.opt_thm_name ":" -- Parse.ML_source
       
  1134 in
       
  1135    Outer_Syntax.local_theory_to_proof @{command_spec "foobar_prove"}
       
  1136      "proving a proposition" 
       
  1137        (parser >> setup_proof)
       
  1138 end*}
       
  1139 
       
  1140 text {*
       
  1141   In Line 12, we implement a parser that first reads in an optional lemma name (terminated 
       
  1142   by ``:'') and then some ML-code. The function in Lines 5 to 10 takes the ML
       
  1143   text and lets the ML-runtime evaluate it using the function @{ML_ind value in Code_Runtime}
       
  1144   in the structure @{ML_struct Code_Runtime}.  Once the ML-text has been turned into a term, 
       
  1145   the function @{ML theorem in Proof} opens a corresponding proof-state. This function takes the
       
  1146   function @{ML_text "after_qed"} as argument, whose purpose is to store the theorem,
       
  1147   once it is proven, under the user given name @{text "thm_name"}.
       
  1148 
       
  1149   We can now give take a term, for example
       
  1150 *}
       
  1151 
       
  1152 ML %grayML{*val prop_true = @{prop "True"}*}
       
  1153 
       
  1154 text {*
       
  1155   and give a proove for it using \isacommand{foobar\_prove}:
       
  1156 *}
       
  1157 
       
  1158 foobar_prove test: prop_true
       
  1159 apply(rule TrueI)
       
  1160 done
       
  1161 
       
  1162 text {*
       
  1163   Finally we can test whether the lemma has been stored under the given name.
       
  1164   
       
  1165   \begin{isabelle}
       
  1166   \isacommand{thm}~@{text "test"}\\
       
  1167   @{text "> "}~@{thm TrueI}
       
  1168   \end{isabelle}
       
  1169 
  1079   While this is everything you have to do for a new command when using jEdit, 
  1170   While this is everything you have to do for a new command when using jEdit, 
  1080   things are not as simple when using Emacs and ProofGeneral. We explain the details 
  1171   things are not as simple when using Emacs and ProofGeneral. We explain the details 
  1081   next.
  1172   next.
  1082 *}
  1173 *}
  1083 
  1174 
  1111   \isacommand{begin}\\
  1202   \isacommand{begin}\\
  1112   \isacommand{ML}~@{text "\<verbopen>"}\\
  1203   \isacommand{ML}~@{text "\<verbopen>"}\\
  1113   @{ML
  1204   @{ML
  1114 "let
  1205 "let
  1115   val do_nothing = Scan.succeed (Local_Theory.background_theory I)
  1206   val do_nothing = Scan.succeed (Local_Theory.background_theory I)
  1116   val kind = Keyword.thy_decl
  1207 in
  1117 in
  1208   Outer_Syntax.local_theory @{command_spec \"foobar\"}
  1118   Outer_Syntax.local_theory (\"foobar\", kind) 
       
  1119     \"description of foobar\" 
  1209     \"description of foobar\" 
  1120        do_nothing
  1210        do_nothing
  1121 end"}\\
  1211 end"}\\
  1122   @{text "\<verbclose>"}\\
  1212   @{text "\<verbclose>"}\\
  1123   \isacommand{end}
  1213   \isacommand{end}
  1229 
  1319 
  1230   (FIXME: read a name and show how to store theorems; see @{ML_ind note in Local_Theory})
  1320   (FIXME: read a name and show how to store theorems; see @{ML_ind note in Local_Theory})
  1231   
  1321   
  1232 *}
  1322 *}
  1233 
  1323 
  1234 ML {*
  1324 
  1235 structure Result = Proof_Data (
  1325 
  1236   type T = unit -> term
  1326 
  1237   fun init thy () = error "Result")
       
  1238 
       
  1239 val result_cookie = (Result.get, Result.put, "Result.put")
       
  1240 *}
       
  1241 
       
  1242 ML %grayML{*let
       
  1243    fun after_qed thm_name thms lthy =
       
  1244         Local_Theory.note (thm_name, (flat thms)) lthy |> snd
       
  1245 
       
  1246    fun setup_proof (thm_name, (txt, pos)) lthy =
       
  1247    let
       
  1248      val trm = Code_Runtime.value lthy result_cookie ("", txt)
       
  1249    in
       
  1250      Proof.theorem NONE (after_qed thm_name) [[(trm,[])]] lthy
       
  1251    end
       
  1252 
       
  1253    val parser = Parse_Spec.opt_thm_name ":" -- Parse.ML_source
       
  1254  
       
  1255 in
       
  1256    Outer_Syntax.local_theory_to_proof ("foobar_prove", Keyword.thy_goal) 
       
  1257      "proving a proposition" 
       
  1258        (parser >> setup_proof)
       
  1259 end*}
       
  1260 
       
  1261 (*
       
  1262 foobar_prove test: {* @{prop "True"} *}
       
  1263 apply(rule TrueI)
       
  1264 done
       
  1265 *)
       
  1266 
  1327 
  1267 (*
  1328 (*
  1268 ML {*
  1329 ML {*
  1269 structure TacticData = ProofDataFun
  1330 structure TacticData = ProofDataFun
  1270 (
  1331 (