forked from tlringer/plugin-tutorial
-
Notifications
You must be signed in to change notification settings - Fork 0
/
g_tuto1.mlg
154 lines (141 loc) · 5.89 KB
/
g_tuto1.mlg
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
DECLARE PLUGIN "tuto1_plugin"
{
(*
* In this exercise, we will implement a Coq plugin!
* Our plugin will manipulate terms from Coq and define new terms.
* As always, this will be discussion-based, with the usual format.
*)
open Pp
open Stdarg
open Termutils
open Exercise
}
(*** Defining terms ***)
(*
* The cool thing about this exercise is showing you how you can
* define terms inside of a Coq plugin. Coq plugins are implemented
* inside of OCaml, with a special syntax to interface with Coq.
* We can take Coq terms as input, and we can also define new Coq terms.
* All Coq terms that we define are checked by Coq's type checker in the end,
* so we don't have to worry about accidentally breaking Coq.
*
* Below is a custom version of the Definition command in Coq,
* called MyDefine. MyDefine works like Definition---you can check out Demo.v
* to see it in action. It calls some functions from Termutils, which I've
* written for you to simplify your job a tiny bit.
*
* EXERCISE 1: Look at the functions I've defined for you in termutils.mli,
* and the comments I've writen explaining them. From those functions,
* add a comment explaining each function call in MyDefine below.
*)
VERNAC COMMAND EXTEND MyDefine CLASSIFIED AS SIDEFF
| [ "MyDefine" ident(i) ":=" constr(e) ] ->
{
let sigma, env = global_env () in
let sigma, trm = internalize env e sigma in
define i trm sigma
}
END
(*** Reasoning about terms in a plugin ***)
(*
* It isn't just that we can define our own terms in plugins.
* We can also reason about existing terms!
*
* Below I've implemented a command that counts all occurrences of terms
* that are definitionally equal to src_e inside of e, with a few assumptions.
* You can again check out Demo.v to see this command in action.
*
* EXERCISE 2: Look once more the functions I've defined for you in
* termutils.mli, and the comments I've writen explaining them.
* Look also at the signature of the count function I've written for you
* in exercise.mli. From those functions, add a comment explaining each
* function call in Count below.
*
* EXERCISE 3: Look at the implementation of count in exercise.ml.
* Look also at the definition of the AST in the Coq kernel:
* https://github.com/coq/coq/blob/v8.14/kernel/constr.mli#L237
* What is each case of the implementation of count in exercise.ml doing?
* Add comments explaining each case.
*
* EXERCISE 4: Look again at the implementation of count in exercise.ml.
* Look again also at the definition of the AST in the Coq kernel:
* https://github.com/coq/coq/blob/v8.14/kernel/constr.mli#L237
* Are there any cases that are not handled well right now?
* (It's OK if you do not know what some of the cases are---though feel free
* to ask. This is to get you thinking about the Coq AST, mostly.)
*)
VERNAC COMMAND EXTEND Count CLASSIFIED AS SIDEFF
| [ "Count" constr(src_e) "in" constr(e) ] ->
{
let sigma, env = global_env () in
let sigma, src = internalize env src_e sigma in
let sigma, trm = internalize env e sigma in
let sigma, count = count env src trm sigma in
Feedback.msg_notice (strbrk (string_of_int count))
}
END
(*** Both together ***)
(*
* OK, now finally time to have some fun. You're going to implement
* the Sub command, which substitutes all subterms of e that are
* definitionally equal to the terms in the source list src_es
* with the corresponding terms in the destination list dst_es.
* In other words, this behaves like count, but instead of counting,
* it substitutes, and it does this over an ordered list of substitutions.
*
* Please check out Demo.v before starting this so that you can get a better
* idea of what this is supposed to do.
*
* EXERCISE 5: Implement sub in exercise.ml. When you are done, the Sub
* command should behave as expected in Demo.v.
*)
VERNAC COMMAND EXTEND Sub CLASSIFIED AS SIDEFF
| [ "Sub" constr_list(src_es) "with" constr_list(dst_es) "in"
constr(e) "as" ident(i) ] ->
{
let sigma, env = global_env () in
let sigma, srcs = map_state (internalize env) src_es sigma in
let sigma, dsts = map_state (internalize env) dst_es sigma in
let sigma, trm = internalize env e sigma in
let sigma, subbed =
fold_left_state
(fun subbed (src, dst) -> sub env (src, dst) subbed)
trm
(List.combine srcs dsts)
sigma
in Termutils.define i subbed sigma
}
END
(*** DISCUSSION ***)
(*
* Same format as usual---please post in the forum as a group!
* And you need to answer _just one_ of these questions to get credit
* (though you can answer both if you are feeling adventurous).
*
* QUESTION 1: Based on your experiences so far, what do you think the
* tradeoffs are of writing automation inside of a Coq plugin versus inside
* of Ltac? Can you think of any use cases for which you'd prefer Ltac
* over a plugin? And can you think of any use cases for which you'd prefer
* a plugin over Ltac? In each case, why?
*
* QUESTION 2: Think back to all of the Coq tactics you've written or used
* so far, like "rewrite" or "induction" or "autoinduct". Are there any you
* think you could improve by implementing them inside of a plugin? How so?
*)
(*** BONUS ***)
(*
* There are so, so many ways you can extend this. Some ideas below.
*
* BONUS 1: Extend your command to handle let expressions.
* The function push_let_in from here may help:
* https://github.com/uwplse/coq-plugin-lib/blob/master/src/coq/logicutils/contexts/envutils.ml
*
* BONUS 2: Extend your command to unfold constants.
* The function lookup_definition from the same file linked to above may
* help, though it is a bit out of date.
*
* BONUS 3: Notice how both "count" and "sub" have similar structure.
* Factor that structure out into a fold over the structure of the AST,
* and use it inside of both "count" and "sub". This will be useful for
* other commands and tactics you might want to implement in the future!
*)