forked from seL4/l4v
-
Notifications
You must be signed in to change notification settings - Fork 0
/
ML_Goal.thy
135 lines (111 loc) · 4.39 KB
/
ML_Goal.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
(*
* Copyright 2020, Data61, CSIRO (ABN 41 687 119 230)
*
* SPDX-License-Identifier: BSD-2-Clause
*)
theory ML_Goal
imports Main
keywords "ML_goal" :: thy_goal_stmt
begin
\<comment>\<open>
Defines a new command `ML_goal`. `ML_goal` is similar to @{command lemma}
and @{command theorem} in that it specifies a goal and enters a proof state
for you prove that goal.
However, instead of parsing goal terms from user input, `ML_goal` uses
an ML block to produce a list of ML terms, and then sets up the proof state
to prove those terms as goals.
Each goal term must be an instance of either @{typ bool} or @{typ prop}.
There are some concrete examples in @{file ML_Goal_Test.thy}.
\<close>
ML \<open>
structure ML_goal_data = Proof_Data(
type T = term list
val init = K []
);
local
\<comment>\<open>
Why are we stashing something into a `Proof_Data` and then immediately
taking it out again?
In @{ML "fn (pos, source, ctxt: Context.generic) =>
ML_Context.expression pos source ctxt"},
`source` is a fragment of ML code which gets evaluated in the ML context
associated with `ctxt`, which in this case should be the proof context
associated with the call to `ML_goal`. The result is a new generic context
with a new ML context, updated with the effects of evaluating `source`.
This means the only way to extract information out of `source` is via
its side-effects on the ML context. We have two main options:
- Declare an @{ML "Unsynchronized.ref"} in this file (ML_Goal), and stash
the value described by `source` into that.
- This is unlikely to play well with deferred or concurrent proofs.
- Use an instance of `Proof_Data` to declare some new state that's
associated with all proof contexts (in this case "the result of
the ML block passed to `ML_goal`"), and evaluate `source` in such a way
as to store the result there.
- This has more overhead compared to the `ref` solution, but it's
still negligible.
- This is the 'preferred' way to store 'local state' in contexts.
- This is safe for deferred or concurrent proofs.
\<close>
fun evaluate_goals source =
ML_Context.expression
(Input.pos_of source)
(ML_Lex.read "Theory.local_setup (ML_goal_data.put (("
@ ML_Lex.read_source source
@ ML_Lex.read "): term list))")
|> Context.proof_map
#> ML_goal_data.get;
fun err_msg_bad_type goal (typ: typ) ctxt =
"The goal " ^ (@{make_string} (Syntax.pretty_term ctxt goal)) ^ " " ^
"has unsupported type " ^ (@{make_string} typ) ^ ". " ^
"The ML_goal command expects either a " ^ (@{make_string} @{typ bool}) ^ " " ^
"or a " ^ (@{make_string} @{typ prop}) ^ ".";
fun begin_proof ((name, attrs): Attrib.binding, ml_block: Input.source) ctxt =
let
\<comment>\<open>
In the very common case that a goal is a @{typ bool}, we wrap
it in @{term Trueprop} to keep the Proof.theorem command happy.
\<close>
fun prop_wrap goal =
case Term.type_of goal of
@{typ bool} => goal |> HOLogic.mk_Trueprop
| @{typ prop} => goal
| other => error (err_msg_bad_type goal other ctxt);
val goals =
evaluate_goals ml_block ctxt
|> List.map prop_wrap
\<comment>\<open> Ensures free variables are type-consistent. \<close>
|> Syntax.check_props ctxt
|> List.map (rpair []);
\<comment>\<open>
Figures out that the `folded` in `[folded foo]` means
@{attribute folded}. Required for attributes to work.
\<close>
val parsed_attrs = map (Attrib.check_src ctxt) attrs;
val binding = (name, parsed_attrs);
val start_pos = Position.thread_data ();
fun after_qed (results: thm list list) ctxt =
let
val thms = results |> hd;
val ((res_name, res), ctxt') =
Local_Theory.note (binding, thms) ctxt;
val _ =
Proof_Display.print_results true start_pos ctxt'
(("theorem", res_name), [("", res)])
in ctxt' end
in
Proof.theorem NONE
after_qed
[goals] ctxt
end;
val ML_goal_cmd =
Scan.optional (Parse_Spec.opt_thm_name ":") Binding.empty_atts
-- Parse.ML_source
>> begin_proof
val _ =
Outer_Syntax.local_theory_to_proof
\<^command_keyword>\<open>ML_goal\<close>
"ML-provided goal"
ML_goal_cmd;
in end
\<close>
end