|
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; |