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
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
|
(* -------------------------------------------------------------------------- *)
(* *)
(* Initial ritual dance *)
(* *)
(* -------------------------------------------------------------------------- *)
DECLARE PLUGIN "tuto2_plugin"
(*
Use this macro before any of the other OCaml macros.
Each plugin has a unique name.
We have decided to name this plugin as "tuto2_plugin".
That means that:
(1) We write the following command in a file called Loader.v:
Declare ML Module "tuto2_plugin".
to load this command into the Coq top-level.
(2) Users can then load our plugin in other Coq files by writing:
From Tuto2 Require Import Loader.
where Loader is the name of the file that declares "tuto2_plugin",
and where Tuto2 is the name passed to the -R argument in our _CoqProject.
(3) The above commands will succeed only if there is "tuto2_plugin.cmxs"
in some of the directories where Coq is supposed to look
(i.e. the ones we specified via "-I ..." command line options in
_CoqProject). As long as this is listed in our _CoqProject, the
Makefile takes care of placing it in the right directory.
(4) The file "tuto2_plugin.mlpack" lists the OCaml modules to be linked in
"tuto2_plugin.cmxs".
(5) The file "tuto2_plugin.mlpack" as well as all .ml, .mli and .mlg files
are listed in the "_CoqProject" file.
*)
(* -------------------------------------------------------------------------- *)
(* *)
(* Importing OCaml dependencies *)
(* *)
(* -------------------------------------------------------------------------- *)
(*
* This .mlg file is parsed into a .ml file. You can put OCaml in this file
* inside of curly braces. It's best practice to use this only to import
* other modules, and include most of your functionality in those modules.
*
* Here we list all of the dependencies that these commands have, and explain
* why. We also refer to the first command that uses them, where further
* explanation can be found in context.
*)
{
(*** Dependencies from Coq ***)
(*
* This lets us take non-terminal arguments to a command (for example,
* the PassInt command that takes an integer argument needs this
* this dependency).
*
* First used by: PassInt
*)
open Stdarg
(*
* This is Coq's pretty-printing module. Here, we need it to use some
* useful syntax for pretty-printing.
*
* First use by: Count
*)
open Pp
}
(* -------------------------------------------------------------------------- *)
(* *)
(* How to define a new Vernacular command? *)
(* *)
(* -------------------------------------------------------------------------- *)
(*
This command does nothing:
*)
VERNAC COMMAND EXTEND NoOp CLASSIFIED AS QUERY
| [ "Nothing" ] -> { () }
END
(*
--- Defining a Command ---
These:
VERNAC COMMAND EXTEND
and
END
mark the beginning and the end of the definition of a new Vernacular command.
--- Assigning a Command a Unique Identifier ---
NoOp is a unique identifier (which must start with an upper-case letter)
associated with the new Vernacular command we are defining. It is good
to make this identifier descriptive.
--- Classifying a Command ---
CLASSIFIED AS QUERY tells Coq that the new Vernacular command neither:
- changes the global environment, nor
- modifies the plugin's state.
If the new command could:
- change the global environment
- or modify a plugin's state
then one would have to use CLASSIFIED AS SIDEFF instead.
--- Defining Parsing and Interpretation Rules ---
This:
[ "Nothing" ] -> { () }
defines:
- the parsing rule (left)
- the interpretation rule (right)
The parsing rule and the interpretation rule are separated by -> token.
The parsing rule, in this case, is:
[ "Nothing" ]
By convention, all vernacular command start with an upper-case letter.
The '[' and ']' characters mark the beginning and the end of the parsing
rule, respectively. The parsing rule itself says that the syntax of the
newly defined command is composed from a single terminal Nothing.
The interpretation rule, in this case, is:
{ () }
Similarly to the case of the parsing rule, the
'{' and '}' characters mark the beginning and the end of the interpretation
rule. In this case, the following Ocaml expression:
()
defines the effect of the Vernacular command we have just defined.
That is, it behaves is no-op.
--- Calling a Command ---
In Demo.v, we call this command by writing:
Nothing.
since our parsing rule is "Nothing". This does nothing, since our
interpretation rule is ().
*)
(* -------------------------------------------------------------------------- *)
(* *)
(* How to define a new Vernacular command with some terminal parameters? *)
(* *)
(* -------------------------------------------------------------------------- *)
(*
This command takes some terminal parameters and does nothing.
*)
VERNAC COMMAND EXTEND NoOpTerminal CLASSIFIED AS QUERY
| [ "Command" "With" "Some" "Terminal" "Parameters" ] -> { () }
END
(*
--- Defining a Command with Terminal Parameters ---
As shown above, the Vernacular command can be composed from
any number of terminals.
By convention, each of these terminals starts with an upper-case letter.
--- Calling a Command with Terminal Parameters ---
In Demo.v, we call this command by writing:
Command With Some Terminal Parameters.
to match our parsing rule. As expected, this does nothing.
--- Recognizing Syntax Errors ---
Note that if we were to omit any of these terminals, for example by writing:
Command.
it would fail to parse (as expected), showing this error to the user:
Syntax error: illegal begin of vernac.
*)
(* -------------------------------------------------------------------------- *)
(* *)
(* How to define a new Vernacular command with some non-terminal parameter? *)
(* *)
(* -------------------------------------------------------------------------- *)
(*
This command takes an integer argument and does nothing.
*)
VERNAC COMMAND EXTEND PassInt CLASSIFIED AS QUERY
| [ "Pass" int(i) ] -> { () }
END
(*
--- Dependencies ---
Since this command takes a non-terminal argument, it is the first
to depend on Stdarg (opened at the top of this file).
--- Defining a Command with Non-Terminal Arguments ---
This:
int(i)
means that the new command is expected to be followed by an integer.
The integer is bound in the parsing rule to variable i.
This variable i then can be used in the interpretation rule.
To see value of which Ocaml types can be bound this way,
look at the wit_* function declared in interp/stdarg.mli
(in the Coq's codebase). There are more examples in tuto1.
If we drop the wit_ prefix, we will get the token
that we can use in the parsing rule.
That is, since there exists wit_int, we know that
we can write:
int(i)
By looking at the signature of the wit_int function:
val wit_int : int uniform_genarg_type
we also know that variable i will have the type int.
--- Recognizing Build Errors ---
The mapping from int(i) to wit_int is automatic.
This is why, if we forget to open Stdarg, we will get this error:
Unbound value wit_int
when we try to build our plugin. It is good to recognize this error,
since this is a common mistake in plugin development, and understand
that the fix is to open the file (Stdarg) where wit_int is defined.
--- Calling a Command with Terminal Arguments ---
We call this command in Demo.v by writing:
Pass 42.
We could just as well pass any other integer. As expected, this command
does nothing.
--- Recognizing Syntax Errors ---
As in our previous command, if we were to omit the arguments to the command,
for example by writing:
Pass.
it would fail to parse (as expected), showing this error to the user:
Syntax error: [prim:integer] expected after 'Pass' (in [vernac:command]).
The same thing would happen if we passed the wrong argument type:
Pass True.
If we pass too many arguments:
Pass 15 20.
we will get a different syntax error:
Syntax error: '.' expected after [vernac:command] (in [vernac_aux]).
It is good to recognize these errors, since doing so can help you
catch mistakes you make defining your parser rules during plugin
development.
*)
(* -------------------------------------------------------------------------- *)
(* *)
(* How to define a new Vernacular command with variable number of arguments? *)
(* *)
(* -------------------------------------------------------------------------- *)
(*
This command takes a list of integers and does nothing:
*)
VERNAC COMMAND EXTEND AcceptIntList CLASSIFIED AS QUERY
| [ "Accept" int_list(l) ] -> { () }
END
(*
--- Dependencies ---
Much like PassInt, this command depends on Stdarg.
--- Defining a Command that Takes a Variable Number of Arguments ---
This:
int_list(l)
means that the new Vernacular command is expected to be followed
by a (whitespace separated) list of integers.
This list of integers is bound to the indicated l.
In this case, as well as in the cases we point out below, instead of int
in int_list we could use any other supported type, e.g. ident, bool, ...
--- Other Ways to Take a Variable Number of Arguments ---
To see which other Ocaml type constructors (in addition to list)
are supported, have a look at the parse_user_entry function defined
in the coqpp/coqpp_parse.mly file.
E.g.:
- ne_int_list(x) would represent a non-empty list of integers,
- int_list(x) would represent a list of integers,
- int_opt(x) would represent a value of type int option,
- ···
Much like with int_list, we could use any other supported type here.
There are some more examples of this in tuto1.
--- Calling a Command with a Variable Number of Arguments ---
We call this command in Demo.v by writing:
Accept 100 200 300 400.
As expected, this does nothing.
Since our parser rule uses int_list, the arguments to Accept can be a
list of integers of any length. For example, we can pass the empty list:
Accept.
or just one argument:
Accept 2.
and so on.
*)
(* -------------------------------------------------------------------------- *)
(* *)
(* How to define a new Vernacular command that takes values of a custom type? *)
(* *)
(* -------------------------------------------------------------------------- *)
(*
--- Defining Custom Types ---
Vernacular commands can take custom types in addition to the built-in
ones. The first step to taking these custom types as arguments is
to define them.
We define a type of values that we want to pass to our Vernacular command
in custom.ml/custom.mli. The type is very simple:
type custom_type : Foo | Bar.
--- Using our New Module ---
Now that we have a new OCaml module Custom, in order to use it, we must
do the following:
1. Add src/custom.ml and src/custom.mli to our _CoqProject
2. Add Custom to our tuto2_plugin.mlpack
This workflow will become very familiar to you when you add new modules
to your plugins, so it is worth getting used to.
--- Depending on our New Module ---
Now that our new module is listed in both _CoqProject and tuto2_plugin.mlpack,
we can use fully qualified names Custom.Foo and Custom.Bar.
Alternatively, we could add the dependency on our module:
open Custom.
to the top of the file, and then refer to Foo and Bar directly.
--- Telling Coq About our New Argument Type ---
By default, we are able to define new Vernacular commands that can take
parameters of some of the supported types. Which types are supported,
that was discussed earlier.
If we want to be able to define Vernacular command that takes parameters
of a type that is not supported by default, we must use the following macro:
*)
VERNAC ARGUMENT EXTEND custom
| [ "Foo" ] -> { Custom.Foo }
| [ "Bar" ] -> { Custom.Bar }
END
(*
where:
custom
indicates that, from now on, in our parsing rules we can write:
custom(some_variable)
in those places where we expect user to provide an input
that can be parsed by the parsing rules above
(and interpreted by the interpretations rules above).
*)
(*
--- Defining a Command that Takes an Argument of a Custom Type ---
Now that Coq is aware of our new argument type, we can define a command
that uses it. This command takes an argument Foo or Bar and does nothing:
*)
VERNAC COMMAND EXTEND PassCustom CLASSIFIED AS QUERY
| [ "Foobar" custom(x) ] -> { () }
END
(*
--- Calling a Command that Takes an Argument of a Custom Type ---
We call this command in Demo.v by writing:
Foobar Foo.
Foobar Bar.
As expected, both of these do nothing. In the first case, x gets
the value Custom.Foo : Custom.custom_type, since our custom parsing
and interpretation rules (VERNAC ARGUMENT EXTEND custom ...) map
the input Foo to Custom.Foo. Similarly, in the second case, x gets
the value Custom.Bar : Custom.custom_type.
*)
(* -------------------------------------------------------------------------- *)
(* *)
(* How to give a feedback to the user? *)
(* *)
(* -------------------------------------------------------------------------- *)
(*
So far we have defined commands that do nothing.
We can also signal feedback to the user.
This command tells the user that everything is awesome:
*)
VERNAC COMMAND EXTEND Awesome CLASSIFIED AS QUERY
| [ "Is" "Everything" "Awesome" ] ->
{
Feedback.msg_notice (Pp.str "Everything is awesome!")
}
END
(*
--- Pretty Printing ---
User feedback functions like Feedback.msg_notice take a Pp.t as an argument.
Check the Pp module to see which functions are available to construct
a Pp.t.
The Pp module enable us to represent and construct pretty-printing
instructions. The concepts defined and the services provided by the
Pp module are in various respects related to the concepts and services
provided by the Format module that is part of the Ocaml standard library.
--- Giving Feedback ---
Once we have a Pp.t, we can use the following functions:
- Feedback.msg_info : Pp.t -> unit
- Feedback.msg_notice : Pp.t -> unit
- Feedback.msg_warning : Pp.t -> unit
- Feedback.msg_debug : Pp.t -> unit
to give user a textual feedback. Examples of some of these can be
found in tuto0.
--- Signaling Errors ---
While there is a Feedback.msg_error, when signaling an error,
it is currently better practice to use user_err. There is an example of
this in tuto0.
*)
(* -------------------------------------------------------------------------- *)
(* *)
(* How to implement a Vernacular command with (undoable) side-effects? *)
(* *)
(* -------------------------------------------------------------------------- *)
(*
This command counts how many times it has been called since importing
our plugin, and signals that information to the user:
*)
VERNAC COMMAND EXTEND Count CLASSIFIED AS SIDEFF
| [ "Count" ] ->
{
Counter.increment ();
let v = Counter.value () in
Feedback.msg_notice (Pp.str "Times Count has been called: " ++ Pp.int v)
}
END
(*
--- Dependencies ---
If we want to use the ++ syntax, then we need to depend on Pp explicitly.
This is why, at the top, we write:
open Pp.
--- Defining the Counter ---
We define our counter in the Counter module. Please see counter.ml and
counter.mli for details.
As with Custom, we must modify our _CoqProject and tuto2_plugin.mlpack
so that we can use Counter in our code.
--- Classifying the Command ---
This command has undoable side-effects: When the plugin is first loaded,
the counter is instantiated to 0. After each time we call Count, the value of
the counter increases by 1.
Thus, we must write CLASSIFIED AS SIDEEFF for this command, rather than
CLASSIFIED AS QUERY. See the explanation from the NoOp command earlier if
you do not remember the distinction.
--- Calling the Command ---
We call our command three times in Demo.v by writing:
Count.
Count.
Count.
This gives us the following output:
Times Count has been called: 1
Times Count has been called: 2
Times Count has been called: 3
Note that when the plugin is first loaded, the counter is 0. It increases
each time Count is called.
--- Behavior with Imports ---
Count.v shows the behavior with imports. Note that if we import Demo.v,
the counter is set to 0 from the beginning, even though Demo.v calls
Count three times.
In other words, this is not persistent!
*)
(* -------------------------------------------------------------------------- *)
(* *)
(* How to implement a Vernacular command that uses persistent storage? *)
(* *)
(* -------------------------------------------------------------------------- *)
(*
* This command is like Count, but it is persistent across modules:
*)
VERNAC COMMAND EXTEND CountPersistent CLASSIFIED AS SIDEFF
| [ "Count" "Persistent" ] ->
{
Persistent_counter.increment ();
let v = Persistent_counter.value () in
Feedback.msg_notice (Pp.str "Times Count Persistent has been called: " ++ Pp.int v)
}
END
(*
--- Persistent Storage ---
Everything is similar to the Count command, except that we use a counter
that is persistent. See persistent_counter.ml for details.
The key trick is that we must create a persistent object for our counter
to persist across modules. Coq has some useful APIs for this in Libobject.
We demonstrate these in persistent_counter.ml.
This is really, really useful if you want, for example, to cache some
results that your plugin computes across modules. A persistent object
can be a hashtable, for example, that maps inputs to outputs your command
has already computed, if you know the result will not change.
--- Calling the Command ---
We call the command in Demo.v and in Count.v, just like we did with Count.
Note that this time, the value of the counter from Demo.v persists in Count.v.
*)
|