-
Notifications
You must be signed in to change notification settings - Fork 108
/
Copy pathMatch_Abbreviation.thy
278 lines (232 loc) · 10.6 KB
/
Match_Abbreviation.thy
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
(*
* Copyright 2020, Data61, CSIRO (ABN 41 687 119 230)
*
* SPDX-License-Identifier: BSD-2-Clause
*)
theory Match_Abbreviation
imports Main
keywords "match_abbreviation" :: thy_decl
and "reassoc_thm" :: thy_decl
begin
text \<open>Splicing components of terms and saving as abbreviations.
See the example at the bottom for explanation/documentation.
\<close>
ML \<open>
structure Match_Abbreviation = struct
fun app_cons_dummy cons x y
= Const (cons, dummyT) $ x $ y
fun lazy_lam x t = if Term.exists_subterm (fn t' => t' aconv x) t
then lambda x t else t
fun abs_dig_f ctxt lazy f (Abs (nm, T, t))
= let
val (nms, ctxt) = Variable.variant_fixes [nm] ctxt
val x = Free (hd nms, T)
val t = betapply (Abs (nm, T, t), x)
val t' = f ctxt t
in if lazy then lazy_lam x t' else lambda x t' end
| abs_dig_f _ _ _ t = raise TERM ("abs_dig_f: not abs", [t])
fun find_term1 ctxt get (f $ x)
= (get ctxt (f $ x) handle Option => (find_term1 ctxt get f
handle Option => find_term1 ctxt get x))
| find_term1 ctxt get (a as Abs _)
= abs_dig_f ctxt true (fn ctxt => find_term1 ctxt get) a
| find_term1 ctxt get t = get ctxt t
fun not_found pat t = raise TERM ("pattern not found", [pat, t])
fun find_term ctxt get pat t = find_term1 ctxt get t
handle Option => not_found pat t
fun lambda_frees_vars ctxt ord_t t = let
fun is_free t = is_Free t andalso not (Variable.is_fixed ctxt (Term.term_name t))
fun is_it t = is_free t orelse is_Var t
val get = fold_aterms (fn t => if is_it t then insert (op =) t else I)
val all_vars = get ord_t []
val vars = get t []
val ord_vars = filter (member (op =) vars) all_vars
in fold lambda ord_vars t end
fun parse_pat_fixes ctxt fixes pats = let
val (_, ctxt') = Variable.add_fixes
(map (fn (b, _, _) => Binding.name_of b) fixes) ctxt
val read_pats = Syntax.read_terms ctxt' pats
in Variable.export_terms ctxt' ctxt read_pats end
fun add_reassoc name rhs fixes thms_info ctxt = let
val thms = Attrib.eval_thms ctxt thms_info
val rhs_pat = singleton (parse_pat_fixes ctxt fixes) rhs
|> Thm.cterm_of ctxt
val rew = Simplifier.rewrite (clear_simpset ctxt addsimps thms) rhs_pat
|> Thm.symmetric
val (_, ctxt) = Local_Theory.note ((name, []), [rew]) ctxt
val pretty_decl = Pretty.block [Pretty.str (Binding.name_of name ^ ":\n"),
Thm.pretty_thm ctxt rew]
in Pretty.writeln pretty_decl; ctxt end
fun dig_f ctxt repeat adj (f $ x) = (adj ctxt (f $ x)
handle Option => (dig_f ctxt repeat adj f
$ (if repeat then (dig_f ctxt repeat adj x
handle Option => x) else x)
handle Option => f $ dig_f ctxt repeat adj x))
| dig_f ctxt repeat adj (a as Abs _)
= abs_dig_f ctxt false (fn ctxt => dig_f ctxt repeat adj) a
| dig_f ctxt _ adj t = adj ctxt t
fun do_rewrite ctxt repeat rew_pair t = let
val thy = Proof_Context.theory_of ctxt
fun adj _ t = case Pattern.match_rew thy t rew_pair
of NONE => raise Option | SOME (t', _) => t'
in dig_f ctxt repeat adj t
handle Option => not_found (fst rew_pair) t end
fun select_dig ctxt [] f t = f ctxt t
| select_dig ctxt (p :: ps) f t = let
val thy = Proof_Context.theory_of ctxt
fun do_rec ctxt t = if Pattern.matches thy (p, t)
then select_dig ctxt ps f t else raise Option
in dig_f ctxt false do_rec t handle Option => not_found p t end
fun ext_dig_lazy ctxt f (a as Abs _)
= abs_dig_f ctxt true (fn ctxt => ext_dig_lazy ctxt f) a
| ext_dig_lazy ctxt f t = f ctxt t
fun report_adjust ctxt nm t = let
val pretty_decl = Pretty.block [Pretty.str (nm ^ ", have:\n"),
Syntax.pretty_term ctxt t]
in Pretty.writeln pretty_decl; t end
fun do_adjust ctxt ((("select", []), [p]), fixes) t = let
val p = singleton (parse_pat_fixes ctxt fixes) p
val thy = Proof_Context.theory_of ctxt
fun get _ t = if Pattern.matches thy (p, t) then t else raise Option
val t = find_term ctxt get p t
in report_adjust ctxt "Selected" t end
| do_adjust ctxt ((("retype_consts", []), consts), []) t = let
fun get_constname (Const (s, _)) = s
| get_constname (Abs (_, _, t)) = get_constname t
| get_constname (f $ _) = get_constname f
| get_constname _ = raise Option
fun get_constname2 t = get_constname t
handle Option => raise TERM ("do_adjust: no constant", [t])
val cnames = map (get_constname2 o Syntax.read_term ctxt) consts
|> Symtab.make_set
fun adj (Const (cn, T)) = if Symtab.defined cnames cn
then Const (cn, dummyT) else Const (cn, T)
| adj t = t
val t = Syntax.check_term ctxt (Term.map_aterms adj t)
in report_adjust ctxt "Adjusted types" t end
| do_adjust ctxt (((r, in_selects), [from, to]), fixes) t = if
r = "rewrite1" orelse r = "rewrite" then let
val repeat = r <> "rewrite1"
val sel_pats = map (fn (p, fixes) => singleton (parse_pat_fixes ctxt fixes) p)
in_selects
val rewrite_pair = case parse_pat_fixes ctxt fixes [from, to]
of [f, t] => (f, t) | _ => error ("do_adjust: unexpected length")
val t = ext_dig_lazy ctxt (fn ctxt => select_dig ctxt sel_pats
(fn ctxt => do_rewrite ctxt repeat rewrite_pair)) t
in report_adjust ctxt (if repeat then "Rewrote" else "Rewrote (repeated)") t end
else error ("do_adjust: unexpected: " ^ r)
| do_adjust _ args _ = error ("do_adjust: unexpected: " ^ @{make_string} args)
fun unvarify_types_same ty = ty
|> Term.map_atyps_same
(fn TVar ((a, i), S) => TFree (a ^ "_var_" ^ string_of_int i, S)
| _ => raise Same.SAME)
fun unvarify_types tm = tm
|> Same.commit (Term.map_types_same unvarify_types_same)
fun match_abbreviation mode name init adjusts int ctxt = let
val init_term = init ctxt
val init_lambda = lambda_frees_vars ctxt init_term init_term
|> unvarify_types
|> Syntax.check_term ctxt
val decl = (name, NONE, Mixfix.NoSyn)
val result = fold (do_adjust ctxt) adjusts init_lambda
val lhs = Free (Binding.name_of name, fastype_of result)
val eq = Logic.mk_equals (lhs, result)
val ctxt = Specification.abbreviation mode (SOME decl) [] eq int ctxt
val pretty_eq = Syntax.pretty_term ctxt eq
in Pretty.writeln pretty_eq; ctxt end
fun from_thm f thm_info ctxt = let
val thm = singleton (Attrib.eval_thms ctxt) thm_info
in f thm end
fun from_term term_str ctxt = Syntax.parse_term ctxt term_str
val init_term_parse = Parse.$$$ "in" |--
((Parse.reserved "concl" |-- Parse.thm >> from_thm Thm.concl_of)
|| (Parse.reserved "thm_prop" |-- Parse.thm >> from_thm Thm.prop_of)
|| (Parse.term >> from_term)
)
val term_to_term = (Parse.term -- (Parse.reserved "to" |-- Parse.term))
>> (fn (a, b) => [a, b])
val p_for_fixes = Scan.optional
(Parse.$$$ "(" |-- Parse.for_fixes --| Parse.$$$ ")") []
val adjust_parser = Parse.and_list1
((Parse.reserved "select" -- Scan.succeed [] -- (Parse.term >> single) -- p_for_fixes)
|| (Parse.reserved "retype_consts" -- Scan.succeed []
-- Scan.repeat Parse.term -- Scan.succeed [])
|| ((Parse.reserved "rewrite1" || Parse.reserved "rewrite")
-- Scan.repeat (Parse.$$$ "in" |-- Parse.term -- p_for_fixes)
-- term_to_term -- p_for_fixes)
)
(* install match_abbreviation. see below for examples/docs *)
val _ =
Outer_Syntax.local_theory' @{command_keyword match_abbreviation}
"setup abbreviation for subterm of theorem"
(Parse.syntax_mode -- Parse.binding
-- init_term_parse -- adjust_parser
>> (fn (((mode, name), init), adjusts)
=> match_abbreviation mode name init adjusts));
val _ =
Outer_Syntax.local_theory @{command_keyword reassoc_thm}
"store a reassociate-theorem"
(Parse.binding -- Parse.term -- p_for_fixes -- Scan.repeat Parse.thm
>> (fn (((name, rhs), fixes), thms)
=> add_reassoc name rhs fixes thms));
end
\<close>
text \<open>
The match/abbreviate command. There are examples of all elements below,
and an example involving monadic syntax in the theory Match-Abbreviation-Test.
Each invocation is match abbreviation, a syntax mode (e.g. (input)), an
abbreviation name, a term specifier, and a list of adjustment specifiers.
A term specifier can be term syntax or the conclusion or proposition of
some theorem. Examples below.
Each adjustment is a select, a rewrite, or a constant retype.
The select adjustment picks out the part of the term matching the
pattern (examples below). It picks the first match point, ordered in
term order with compound terms before their subterms and functions
before their arguments.
The rewrite adjustment uses a pattern pair, and rewrites instances
of the first pattern into the second. The match points are found in
the same order as select. The "in" specifiers (examples below)
limit the rewriting to within some matching subterm, specified with
pattern in the same way as select. The rewrite1 variant only
rewrites once, at the first matching site.
The rewrite mechanism can be used to replace terms with terms
of different types. The retype adjustment can then be used
to repair the term by resetting the types of all instances of
the named constants. This is used below with list constructors,
to assemble a new list with a different element type.
\<close>
experiment begin
text \<open>Fetching part of the statement of a theorem.\<close>
match_abbreviation (input) fixp_thm_bit
in thm_prop fixp_induct_tailrec
select "X \<equiv> Y" (for X Y)
text \<open>Ditto conclusion.\<close>
match_abbreviation (input) rev_simps_bit
in concl rev.simps(2)
select "X" (for X)
text \<open>Selecting some conjuncts and reorienting an equality.\<close>
match_abbreviation (input) conjunct_test
in "(P \<and> Q \<and> P \<and> P \<and> P \<and> ((1 :: nat) = 2) \<and> Q \<and> Q, [Suc 0, 0])"
select "Q \<and> Z" (for Z)
and rewrite "x = y" to "y = x" (for x y)
and rewrite in "x = y & Z" (for x y Z)
"A \<and> B" to "A" (for A B)
text \<open>The relevant reassociate theorem, that rearranges a
conjunction like the above to group the elements selected.\<close>
reassoc_thm conjunct_test_reassoc
"conjunct_test P Q \<and> Z" (for P Q Z)
conj_assoc
text \<open>Selecting some elements of a list, and then replacing
tuples with equalities, and adjusting the type of the list constructors
so the new term is type correct.\<close>
match_abbreviation (input) list_test
in "[(Suc 1, Suc 2), (4, 5), (6, 7), (8, 9), (10, 11), (x, y), (6, 7),
(18, 19), a, a, a, a, a, a, a]"
select "(4, V) # xs" (for V xs)
and rewrite "(x, y)" to "(y, x)" (for x y)
and rewrite1 in "(9, V) # xs" (for V xs) in "(7, V) # xs" (for V xs)
"x # xs" to "[x]" (for x xs)
and rewrite "(x, y)" to "x = y" (for x y)
and retype_consts Cons Nil
end
end