src/HOL/Eisbach/method_closure.ML
changeset 60119 54bea620e54f
child 60209 022ca2799c73
equal deleted inserted replaced
60116:5d90d301ad66 60119:54bea620e54f
       
     1 (*  Title:      method_closure.ML
       
     2     Author:     Daniel Matichuk, NICTA/UNSW
       
     3 
       
     4 Facilities for treating method syntax as a closure, with abstraction
       
     5 over terms, facts and other methods.
       
     6 
       
     7 The 'method' command allows to define new proof methods by combining
       
     8 existing ones with their usual syntax.
       
     9 *)
       
    10 
       
    11 signature METHOD_CLOSURE =
       
    12 sig
       
    13   val is_dummy: thm -> bool
       
    14   val tag_free_thm: thm -> thm
       
    15   val free_aware_rule_attribute: thm list -> (Context.generic -> thm -> thm) -> Thm.attribute
       
    16   val read_inner_method: Proof.context -> Token.src -> Method.text
       
    17   val read_text_closure: Proof.context -> Input.source -> Token.src * Method.text
       
    18   val method_evaluate: Method.text -> Proof.context -> Method.method
       
    19   val get_inner_method: Proof.context -> string * Position.T ->
       
    20     (term list * (string list * string list)) * Method.text
       
    21   val eval_inner_method: Proof.context -> (term list * string list) * Method.text ->
       
    22     term list -> (string * thm list) list -> Method.method list ->
       
    23     Proof.context -> Method.method
       
    24   val method_definition: binding -> (binding * typ option * mixfix) list ->
       
    25     binding list -> binding list -> binding list -> Input.source -> local_theory -> local_theory
       
    26   val method_definition_cmd: binding -> (binding * string option * mixfix) list ->
       
    27     binding list -> binding list -> binding list -> Input.source -> local_theory -> local_theory
       
    28 end;
       
    29 
       
    30 structure Method_Closure: METHOD_CLOSURE =
       
    31 struct
       
    32 
       
    33 (* context data *)
       
    34 
       
    35 structure Data = Generic_Data
       
    36 (
       
    37   type T =
       
    38     ((term list * (string list * string list)) * Method.text) Symtab.table;
       
    39   val empty: T = Symtab.empty;
       
    40   val extend = I;
       
    41   fun merge (methods1,methods2) : T =
       
    42     (Symtab.merge (K true) (methods1, methods2));
       
    43 );
       
    44 
       
    45 val get_methods = Data.get o Context.Proof;
       
    46 val map_methods = Data.map;
       
    47 
       
    48 
       
    49 structure Local_Data = Proof_Data
       
    50 (
       
    51   type T =
       
    52     Method.method Symtab.table *  (*dynamic methods*)
       
    53     (term list -> Proof.context -> Method.method)  (*recursive method*);
       
    54   fun init _ : T = (Symtab.empty, fn _ => fn _ => Method.fail);
       
    55 );
       
    56 
       
    57 fun lookup_dynamic_method full_name ctxt =
       
    58   (case Symtab.lookup (#1 (Local_Data.get ctxt)) full_name of
       
    59     SOME m => m
       
    60   | NONE => error ("Illegal use of internal Eisbach method: " ^ quote full_name));
       
    61 
       
    62 val update_dynamic_method = Local_Data.map o apfst o Symtab.update;
       
    63 
       
    64 val get_recursive_method = #2 o Local_Data.get;
       
    65 val put_recursive_method = Local_Data.map o apsnd o K;
       
    66 
       
    67 
       
    68 (* free thm *)
       
    69 
       
    70 fun is_dummy thm =
       
    71   (case try Logic.dest_term (Thm.concl_of (perhaps (try Goal.conclude) thm)) of
       
    72     NONE => false
       
    73   | SOME t => Term.is_dummy_pattern t);
       
    74 
       
    75 val free_thmN = "Method_Closure.free_thm";
       
    76 fun tag_free_thm thm = Thm.tag_rule (free_thmN, "") thm;
       
    77 
       
    78 val dummy_free_thm = tag_free_thm Drule.dummy_thm;
       
    79 
       
    80 fun is_free_thm thm = Properties.defined (Thm.get_tags thm) free_thmN;
       
    81 
       
    82 fun is_free_fact [thm] = is_free_thm thm
       
    83   | is_free_fact _ = false;
       
    84 
       
    85 fun free_aware_rule_attribute args f =
       
    86   Thm.rule_attribute (fn context => fn thm =>
       
    87     if exists is_free_thm (thm :: args) then dummy_free_thm
       
    88     else f context thm);
       
    89 
       
    90 
       
    91 (* thm semantics for combined methods with internal parser. Simulates outer syntax parsing. *)
       
    92 (* Creates closures for each combined method while parsing, based on the parse context *)
       
    93 
       
    94 fun read_inner_method ctxt src =
       
    95   let
       
    96     val toks = Token.args_of_src src;
       
    97     val parser = Parse.!!! (Method.parser' ctxt 0 --| Scan.ahead Parse.eof);
       
    98   in
       
    99     (case Scan.read Token.stopper parser toks of
       
   100       SOME (method_text, _) => method_text
       
   101     | NONE => error ("Failed to parse method" ^ Position.here (#2 (Token.name_of_src src))))
       
   102   end;
       
   103 
       
   104 fun read_text_closure ctxt input =
       
   105   let
       
   106     (*tokens*)
       
   107     val keywords = Thy_Header.get_keywords' ctxt;
       
   108     val toks =
       
   109       Input.source_explode input
       
   110       |> Token.read_no_commands keywords (Scan.one Token.not_eof);
       
   111     val _ =
       
   112       toks |> List.app (fn tok =>
       
   113         if Token.keyword_with Symbol.is_ascii_identifier tok then
       
   114           Context_Position.report ctxt (Token.pos_of tok) Markup.keyword1
       
   115         else ());
       
   116 
       
   117     (*source closure*)
       
   118     val src =
       
   119       Token.src ("", Input.pos_of input) toks
       
   120       |> Token.init_assignable_src;
       
   121     val method_text = read_inner_method ctxt src;
       
   122     val method_text' = Method.map_source (Method.method_closure ctxt) method_text;
       
   123     val src' = Token.closure_src src;
       
   124   in (src', method_text') end;
       
   125 
       
   126 val parse_method =
       
   127   Args.context -- Scan.lift (Parse.token Parse.cartouche) >> (fn (ctxt, tok) =>
       
   128     (case Token.get_value tok of
       
   129       NONE =>
       
   130         let
       
   131            val (src, text) = read_text_closure ctxt (Token.input_of tok);
       
   132            val _ = Token.assign (SOME (Token.Source src)) tok;
       
   133         in text end
       
   134     | SOME (Token.Source src) => read_inner_method ctxt src
       
   135     | SOME _ =>
       
   136         error ("Unexpected inner token value for method cartouche" ^
       
   137           Position.here (Token.pos_of tok))));
       
   138 
       
   139 fun method_evaluate text ctxt : Method.method = fn facts => fn st =>
       
   140   if is_dummy st then Seq.empty
       
   141   else Method.evaluate text (Config.put Method.closure false ctxt) facts st;
       
   142 
       
   143 
       
   144 fun parse_term_args args =
       
   145   Args.context :|-- (fn ctxt =>
       
   146     let
       
   147       fun parse T =
       
   148         (if T = propT then Syntax.parse_prop ctxt else Syntax.parse_term ctxt)
       
   149         #> Type.constraint (Type_Infer.paramify_vars T);
       
   150 
       
   151       fun do_parse' T =
       
   152         Parse_Tools.name_term >>
       
   153           (fn Parse_Tools.Parse_Val (s, f) => (parse T s, f)
       
   154             | Parse_Tools.Real_Val t' => (t', K ()));
       
   155 
       
   156       fun do_parse (Var (_, T)) = do_parse' T
       
   157         | do_parse (Free (_, T)) = do_parse' T
       
   158         | do_parse t = error ("Unexpected method parameter: " ^ Syntax.string_of_term ctxt t);
       
   159 
       
   160        fun rep [] x = Scan.succeed [] x
       
   161          | rep (t :: ts) x  = (do_parse t -- rep ts >> op ::) x;
       
   162 
       
   163       fun check ts =
       
   164         let
       
   165           val (ts, fs) = split_list ts;
       
   166           val ts' = Syntax.check_terms ctxt ts |> Variable.polymorphic ctxt;
       
   167           val _ = ListPair.app (fn (f, t) => f t) (fs, ts');
       
   168         in ts' end;
       
   169     in Scan.lift (rep args) >> check end);
       
   170 
       
   171 fun match_term_args ctxt args ts =
       
   172   let
       
   173     val match_types = Sign.typ_match (Proof_Context.theory_of ctxt) o apply2 Term.fastype_of;
       
   174     val tyenv = fold match_types (args ~~ ts) Vartab.empty;
       
   175     val tenv =
       
   176       fold (fn ((xi, T), t) => Vartab.update_new (xi, (T, t)))
       
   177         (map Term.dest_Var args ~~ ts) Vartab.empty;
       
   178   in Envir.Envir {maxidx = ~1, tenv = tenv, tyenv = tyenv} end;
       
   179 
       
   180 fun check_attrib ctxt attrib =
       
   181   let
       
   182     val name = Binding.name_of attrib;
       
   183     val pos = Binding.pos_of attrib;
       
   184     val named_thm = Named_Theorems.check ctxt (name, pos);
       
   185     val parser: Method.modifier parser =
       
   186       Args.$$$ name -- Args.colon >>
       
   187         K {init = I, attribute = Named_Theorems.add named_thm, pos = pos};
       
   188   in (named_thm, parser) end;
       
   189 
       
   190 
       
   191 fun instantiate_text env text =
       
   192   let val morphism = Morphism.term_morphism "instantiate_text" (Envir.norm_term env);
       
   193   in Method.map_source (Token.transform_src morphism) text end;
       
   194 
       
   195 fun evaluate_dynamic_thm ctxt name =
       
   196   (case (try (Named_Theorems.get ctxt) name) of
       
   197     SOME thms => thms
       
   198   | NONE => Proof_Context.get_thms ctxt name);
       
   199 
       
   200 
       
   201 fun evaluate_named_theorems ctxt =
       
   202   (Method.map_source o Token.map_values)
       
   203     (fn Token.Fact (SOME name, _) =>
       
   204           Token.Fact (SOME name, evaluate_dynamic_thm ctxt name)
       
   205       | x => x);
       
   206 
       
   207 fun evaluate_method_def fix_env raw_text ctxt =
       
   208   let
       
   209     val text = raw_text
       
   210       |> instantiate_text fix_env
       
   211       |> evaluate_named_theorems ctxt;
       
   212   in method_evaluate text ctxt end;
       
   213 
       
   214 fun setup_local_method binding lthy =
       
   215   let
       
   216     val full_name = Local_Theory.full_name lthy binding;
       
   217   in
       
   218     lthy
       
   219     |> update_dynamic_method (full_name, Method.fail)
       
   220     |> Method.local_setup binding (Scan.succeed (lookup_dynamic_method full_name)) "(internal)"
       
   221   end;
       
   222 
       
   223 fun setup_local_fact binding = Named_Theorems.declare binding "";
       
   224 
       
   225 fun parse_method_args method_names =
       
   226   let
       
   227     fun bind_method (name, text) ctxt =
       
   228       update_dynamic_method (name, method_evaluate text ctxt) ctxt;
       
   229 
       
   230     fun do_parse t = parse_method >> pair t;
       
   231     fun rep [] x = Scan.succeed [] x
       
   232       | rep (t :: ts) x  = (do_parse t -- rep ts >> op ::) x;
       
   233   in rep method_names >> fold bind_method end;
       
   234 
       
   235 
       
   236 (* FIXME redundant!? -- base name of binding is not changed by usual morphisms*)
       
   237 fun Morphism_name phi name =
       
   238   Morphism.binding phi (Binding.make (name, Position.none)) |> Binding.name_of;
       
   239 
       
   240 fun add_method binding ((fixes, declares, methods), text) lthy =
       
   241   lthy |>
       
   242   Local_Theory.declaration {syntax = false, pervasive = true} (fn phi =>
       
   243     map_methods
       
   244       (Symtab.update (Local_Theory.full_name lthy binding,
       
   245         (((map (Morphism.term phi) fixes),
       
   246           (map (Morphism_name phi) declares, map (Morphism_name phi) methods)),
       
   247           Method.map_source (Token.transform_src phi) text))));
       
   248 
       
   249 fun get_inner_method ctxt (xname, pos) =
       
   250   let
       
   251     val name = Method.check_name ctxt (xname, pos);
       
   252   in
       
   253     (case Symtab.lookup (get_methods ctxt) name of
       
   254       SOME entry => entry
       
   255     | NONE => error ("Unknown Eisbach method: " ^ quote name ^ Position.here pos))
       
   256   end;
       
   257 
       
   258 fun eval_inner_method ctxt0 header fixes attribs methods =
       
   259   let
       
   260     val ((term_args, hmethods), text) = header;
       
   261 
       
   262     fun match fixes = (* FIXME proper context!? *)
       
   263       (case Seq.pull (Unify.matchers (Context.Proof ctxt0) (term_args ~~ fixes)) of
       
   264         SOME (env, _) => env
       
   265       | NONE => error "Couldn't match term arguments");
       
   266 
       
   267     fun add_thms (name, thms) =
       
   268       fold (Context.proof_map o Named_Theorems.add_thm name) thms;
       
   269 
       
   270     val setup_ctxt = fold add_thms attribs
       
   271       #> fold update_dynamic_method (hmethods ~~ methods)
       
   272       #> put_recursive_method (fn fixes => evaluate_method_def (match fixes) text);
       
   273   in
       
   274     fn ctxt => evaluate_method_def (match fixes) text (setup_ctxt ctxt)
       
   275   end;
       
   276 
       
   277 fun gen_method_definition prep_vars name vars uses attribs methods body lthy =
       
   278   let
       
   279     val (uses_nms, lthy1) = lthy
       
   280       |> Proof_Context.concealed
       
   281       |> Local_Theory.open_target |-> Proof_Context.private_scope
       
   282       |> Local_Theory.map_background_naming (Name_Space.add_path (Binding.name_of name))
       
   283       |> Config.put Method.old_section_parser true
       
   284       |> fold setup_local_method methods
       
   285       |> fold_map setup_local_fact uses;
       
   286 
       
   287     val ((xs, Ts), lthy2) = lthy1
       
   288       |> prep_vars vars |-> Proof_Context.add_fixes
       
   289       |-> (fn xs => fold_map Proof_Context.inferred_param xs #>> pair xs);
       
   290 
       
   291     val term_args = map Free (xs ~~ Ts);
       
   292     val (named_thms, modifiers) = map (check_attrib lthy2) (attribs @ uses) |> split_list;
       
   293     val self_name :: method_names = map (Local_Theory.full_name lthy2) (name :: methods);
       
   294 
       
   295     fun parser args eval =
       
   296       apfst (Config.put_generic Method.old_section_parser true) #>
       
   297       (parse_term_args args --|
       
   298         Method.sections modifiers --
       
   299         (*Scan.depend (fn context => Scan.succeed () >> (K (fold XNamed_Theorems.empty uses_nms context, ()))) --*)  (* FIXME *)
       
   300         parse_method_args method_names >> eval);
       
   301 
       
   302     val lthy3 = lthy2
       
   303       |> Method.local_setup (Binding.make (Binding.name_of name, Position.none))
       
   304         (parser term_args
       
   305           (fn (fixes, decl) => fn ctxt => get_recursive_method ctxt fixes (decl ctxt))) "(internal)";
       
   306 
       
   307     val (src, text) = read_text_closure lthy3 body;
       
   308 
       
   309     val morphism =
       
   310       Variable.export_morphism lthy3
       
   311         (lthy
       
   312           |> Proof_Context.transfer (Proof_Context.theory_of lthy3)
       
   313           |> Token.declare_maxidx_src src
       
   314           |> Variable.declare_maxidx (Variable.maxidx_of lthy3));
       
   315 
       
   316     val text' = Method.map_source (Token.transform_src morphism) text;
       
   317     val term_args' = map (Morphism.term morphism) term_args;
       
   318 
       
   319     fun real_exec ts ctxt =
       
   320       evaluate_method_def (match_term_args ctxt term_args' ts) text' ctxt;
       
   321     val real_parser =
       
   322       parser term_args' (fn (fixes, decl) => fn ctxt =>
       
   323         real_exec fixes (put_recursive_method real_exec (decl ctxt)));
       
   324   in
       
   325     lthy3
       
   326     |> Local_Theory.close_target
       
   327     |> Proof_Context.restore_naming lthy
       
   328     |> Method.local_setup name real_parser "(defined in Eisbach)"
       
   329     |> add_method name ((term_args', named_thms, method_names), text')
       
   330   end;
       
   331 
       
   332 val method_definition = gen_method_definition Proof_Context.cert_vars;
       
   333 val method_definition_cmd = gen_method_definition Proof_Context.read_vars;
       
   334 
       
   335 val _ =
       
   336   Outer_Syntax.local_theory @{command_keyword method} "Eisbach method definition"
       
   337     (Parse.binding -- Parse.for_fixes --
       
   338       ((Scan.optional (@{keyword "uses"} |-- Parse.!!! (Scan.repeat1 Parse.binding)) []) --
       
   339         (Scan.optional (@{keyword "declares"} |-- Parse.!!! (Scan.repeat1 Parse.binding)) [])) --
       
   340       (Scan.optional (@{keyword "methods"} |-- Parse.!!! (Scan.repeat1 Parse.binding)) []) --
       
   341       Parse.!!! (@{keyword "="} |-- Parse.token Parse.cartouche)
       
   342       >> (fn ((((name, vars), (uses, attribs)), methods), cartouche) =>
       
   343         method_definition_cmd name vars uses attribs methods (Token.input_of cartouche)));
       
   344 end;