".format(repr((self.state_label_before, self.state_label_after, self.evaluated, self.rolled_back, self.execution_id, self.parent_header)))
32 |
33 |
34 | class CellJournal:
35 |
36 | def __init__(self, kernel):
37 | self.log = kernel.log # TODO
38 | self.history = []
39 |
40 | def add(self, state_label_before, state_label_after, evaluated, rolled_back, execution_id, parent_header):
41 | record = CellRecord(state_label_before, state_label_after, evaluated, rolled_back, execution_id, parent_header)
42 | self.history.append(record)
43 | return record
44 |
45 | def find_by_execution_id(self, execution_id):
46 | result = list(filter(lambda r: r.execution_id == execution_id, self.history))
47 | if len(result) == 1:
48 | return result[0]
49 | else:
50 | return None
51 |
52 | def find_rolled_back_transitively(self, state_label_before):
53 | return list(filter(lambda r: int(r.state_label_before) > int(state_label_before) and not r.rolled_back, self.history))
54 |
55 |
56 | def shutdown_on_coqtop_error(function):
57 | def wrapper(self, *args, **kwargs):
58 | try:
59 | return function(self, *args, **kwargs)
60 | except CoqtopError:
61 | self.log.exception("CoqtopError has occured. Scheduling shutdown.")
62 | from tornado import ioloop
63 | loop = ioloop.IOLoop.current()
64 | loop.add_timeout(time.time() + 0.1, loop.stop)
65 | raise
66 |
67 | return wrapper
68 |
69 |
70 | class CoqKernel(Kernel):
71 | implementation = 'coq'
72 | implementation_version = __version__
73 | language = 'coq'
74 |
75 | @property
76 | def language_info(self):
77 | return {
78 | 'name': 'coq',
79 | 'mimetype': 'text/x-coq',
80 | 'file_extension': '.v',
81 | 'version': self.language_version
82 | }
83 |
84 | @property
85 | def banner(self):
86 | return self._coqtop.banner
87 |
88 | @property
89 | def language_version(self):
90 | return ".".join(map(str, self._coqtop.version))
91 |
92 |
93 | coqtop_executable = Unicode().tag(config=True)
94 | coqtop_args = Unicode().tag(config=True)
95 |
96 |
97 | @shutdown_on_coqtop_error
98 | def __init__(self, **kwargs):
99 | Kernel.__init__(self, **kwargs)
100 | self._coqtop = Coqtop(self, self.coqtop_executable, self.coqtop_args)
101 | self._journal = CellJournal(self)
102 | self._renderer = Renderer()
103 | self._kernel_comms = []
104 | for msg_type in ['comm_open', 'comm_msg', 'comm_close']:
105 | self.shell_handlers[msg_type] = getattr(self, msg_type)
106 |
107 | def do_execute(self, code, silent, store_history=True, user_expressions=None, allow_stdin=False):
108 | try:
109 | self.log.info("Processing 'execute_request', code: \n{}\n".format(repr(code)))
110 |
111 | if "coq_kernel_roll_back_cell" in self._parent_header["content"]:
112 | self._roll_back(self._parent_header["content"]["coq_kernel_roll_back_cell"])
113 |
114 | if code.strip("\n\r\t ") != "":
115 | state_label_before = self._coqtop.tip
116 | (evaluated, outputs) = shutdown_on_coqtop_error(lambda self: self._coqtop.eval(code))(self)
117 | state_label_after = self._coqtop.tip
118 | execution_id = self._parent_header["msg_id"]
119 |
120 | record = self._journal.add(state_label_before, state_label_after, evaluated, False, execution_id, self._parent_header)
121 |
122 | if not silent:
123 | self.log.info("Sending 'execute_result', cell record:\n{}\n".format(repr(record)))
124 | self._send_execute_result(outputs, execution_id, evaluated, False, state_label_after)
125 |
126 | return self._build_ok_content(state_label_before)
127 | else:
128 | self.log.info("code is empty - skipping evaluation and sending results.")
129 | return self._build_ok_content(self._coqtop.tip)
130 |
131 | except Exception as e:
132 | self.log.exception("Error during evaluating code: \n'{}'\n".format(repr(code)))
133 | return self._build_error_content(*sys.exc_info())
134 |
135 | def comm_open(self, stream, ident, msg):
136 | content = msg["content"]
137 | if content["target_name"] == CELL_COMM_TARGET_NAME:
138 | self._init_kernel_comm(content["comm_id"])
139 | else:
140 | self.log.error("Unexpected comm_open, msg: {}".format(repr(msg)))
141 |
142 | def comm_close(self, stream, ident, msg):
143 | self._kernel_comms.remove(msg["content"]["comm_id"])
144 | self.log.info("Kernel comm closed, msg: {}".format(repr(msg)))
145 |
146 | @shutdown_on_coqtop_error
147 | def comm_msg(self, stream, ident, msg):
148 | content = msg["content"]
149 | if content["comm_id"] in self._kernel_comms:
150 | if content["data"]["comm_msg_type"] == "roll_back":
151 | self._roll_back(msg["content"]["data"]["execution_id"])
152 | else:
153 | self.log.error("Unexpected comm_msg, msg: {}".format(repr(msg)))
154 | else:
155 | self.log.info("Unexpected (possibly leftover) comm_msg, msg: {}, opened comms: {}".format(repr(msg)), repr(self._kernel_comms))
156 |
157 | def _init_kernel_comm(self, comm_id):
158 | self._send_kernel_comm_opened_comm_msg(comm_id, self._journal.history)
159 | self._kernel_comms.append(comm_id)
160 | self.log.info("Kernel comm opened, comm_id: {}".format(comm_id))
161 |
162 | def _roll_back(self, execution_id):
163 | self.log.info("roll back, execution_id: {}".format(execution_id))
164 | cell_record = self._journal.find_by_execution_id(execution_id)
165 |
166 | if cell_record is not None and cell_record.evaluated and not cell_record.rolled_back:
167 | self._coqtop.roll_back_to(cell_record.state_label_before)
168 |
169 | for record in [cell_record] + self._journal.find_rolled_back_transitively(cell_record.state_label_before):
170 | # mark cell as rolled back
171 | record.rolled_back= True
172 |
173 | # update content of rolled back cell
174 | self._send_roll_back_update_display_data(record.parent_header, record.execution_id, record.evaluated, record.rolled_back)
175 |
176 | # update cell state via kernel comms
177 | for comm_id in self._kernel_comms:
178 | self._send_cell_state_comm_msg(comm_id, record.execution_id, record.evaluated, record.rolled_back)
179 |
180 | else:
181 | self.log.info("Unexpected (possibly leftover) roll back request for execution_id: {}".format(execution_id))
182 |
183 | def _build_ok_content(self, state_label_before):
184 | return {
185 | 'status': 'ok',
186 | 'execution_count': int(state_label_before),
187 | 'payload': [],
188 | 'user_expressions': {},
189 | }
190 |
191 | def _build_error_content(self, ex_type, ex, tb):
192 | return {
193 | 'status': 'error',
194 | 'ename' : ex_type.__name__,
195 | 'evalue' : repr(ex),
196 | 'traceback' : traceback.format_list(traceback.extract_tb(tb))
197 | }
198 |
199 | def _build_display_data_content(self, text, html, execution_id, evaluated, rolled_back):
200 | return {
201 | 'data': {
202 | 'text/plain': text,
203 | 'text/html': html
204 | },
205 | 'metadata': {
206 | 'coq_kernel_execution_id': execution_id,
207 | 'coq_kernel_evaluated': evaluated,
208 | 'coq_kernel_rolled_back': rolled_back
209 | },
210 | 'transient': { 'display_id': execution_id }
211 | }
212 |
213 | def _send_kernel_comm_opened_comm_msg(self, comm_id, history):
214 | content = {
215 | "comm_id": comm_id,
216 | "data": {
217 | "comm_msg_type": "kernel_comm_opened",
218 | "history": [
219 | {
220 | "execution_id": record.execution_id,
221 | "evaluated": record.evaluated,
222 | "rolled_back": record.rolled_back
223 | }
224 | for record in history
225 | ]
226 | }
227 | }
228 | self.session.send(self.iopub_socket, "comm_msg", content, None, None, None, None, None, None)
229 |
230 | def _send_cell_state_comm_msg(self, comm_id, execution_id, evaluated, rolled_back):
231 | content = {
232 | "comm_id": comm_id,
233 | "data": {
234 | "comm_msg_type": "cell_state",
235 | "execution_id": execution_id,
236 | "evaluated": evaluated,
237 | "rolled_back": rolled_back
238 | }
239 | }
240 | self.session.send(self.iopub_socket, "comm_msg", content, None, None, None, None, None, None)
241 |
242 | def _send_roll_back_update_display_data(self, parent_header, execution_id, evaluated, rolled_back):
243 | content = self._build_display_data_content(TEXT_ROLLED_BACK_STATUS_MESSAGE, HTML_ROLLED_BACK_STATUS_MESSAGE, execution_id, evaluated, rolled_back)
244 | self.session.send(self.iopub_socket, "update_display_data", content, parent_header, None, None, None, None, None)
245 |
246 | def _send_execute_result(self, outputs, execution_id, evaluated, rolled_back, state_label_after):
247 | text = self._renderer.render_text_result(outputs)
248 | html = self._renderer.render_html_result(outputs, execution_id, evaluated)
249 | content = self._build_display_data_content(text, html, execution_id, evaluated, rolled_back)
250 | content['execution_count'] = int(state_label_after)
251 | self.send_response(self.iopub_socket, 'execute_result', content)
252 |
--------------------------------------------------------------------------------
/coq_jupyter/renderer.py:
--------------------------------------------------------------------------------
1 | from __future__ import unicode_literals
2 |
3 | import os
4 |
5 |
6 | HTML_SUCCESS_STATUS_MESSAGE = """
7 |
8 |
9 | Cell evaluated.
10 |
11 | """
12 |
13 | HTML_ERROR_STATUS_MESSAGE = """
14 |
15 |
16 | Error while evaluating cell. Cell rolled back.
17 |
18 | """
19 |
20 | HTML_ROLLED_BACK_STATUS_MESSAGE = """
21 |
22 |
23 | Cell rolled back.
24 |
25 | """
26 |
27 | HTML_ROLLED_BACK_STATUS_MESSAGE_HIDDEN = """
28 |
29 |
30 | Cell rolled back.
31 |
32 | """
33 |
34 | TEXT_ROLLED_BACK_STATUS_MESSAGE = "Cell rolled back."
35 |
36 | HTML_OUTPUT_TEMPLATE = """
37 |
40 | """
41 |
42 | HTML_ROLL_BACK_CONTROLS = """
43 |
44 |
48 |
49 |
50 |
51 |
52 |
53 |
54 | """
55 |
56 |
57 | class Renderer:
58 |
59 | def render_text_result(self, outputs):
60 | cell_output = "\n\n".join(outputs)
61 | return cell_output
62 |
63 | def render_html_result(self, outputs, execution_id, success_output):
64 | html = HTML_OUTPUT_TEMPLATE.format(self.render_text_result(outputs))
65 | if success_output:
66 | html += HTML_SUCCESS_STATUS_MESSAGE
67 | html += HTML_ROLLED_BACK_STATUS_MESSAGE_HIDDEN
68 | html += HTML_ROLL_BACK_CONTROLS
69 | else:
70 | html += HTML_ERROR_STATUS_MESSAGE
71 |
72 | return html
73 |
--------------------------------------------------------------------------------
/logo.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/main.py:
--------------------------------------------------------------------------------
1 |
2 | # This entry point is used for debug only:
3 | if __name__ == '__main__':
4 | from sys import argv
5 | from ipykernel.kernelapp import IPKernelApp
6 | from coq_jupyter.kernel import CoqKernel
7 | IPKernelApp.launch_instance(kernel_class=CoqKernel, args=argv)
8 |
--------------------------------------------------------------------------------
/process_indexes.py:
--------------------------------------------------------------------------------
1 |
2 | # TODO find a better way to do this
3 | # This script parses coq tactics\commands index pages and outputs relevant
4 | # keywords to be used in CodeMirror coq mode
5 | # X_data variables are taken from Coq 8.18.0 documentation (Command/Tactic index)
6 |
7 | commands_data = """
8 | a
9 | Abort
10 | About
11 | Add
12 | Add Field
13 | Add Morphism
14 | Add Parametric Morphism
15 | Add Parametric Relation
16 | Add Parametric Setoid
17 | Add Relation
18 | Add Ring
19 | Add Setoid
20 | Add Zify
21 | Admit Obligations
22 | Admitted
23 | Arguments
24 | Axiom
25 | Axioms
26 |
27 | b
28 | Back
29 | BackTo
30 | Bind Scope
31 |
32 | c
33 | Canonical Structure
34 | Cd
35 | Check
36 | Class
37 | Close Scope
38 | Coercion
39 | CoFixpoint
40 | CoInductive
41 | Collection
42 | Combined Scheme
43 | Comments
44 | Compute
45 | Conjecture
46 | Conjectures
47 | Constraint
48 | Context
49 | Corollary
50 | Create HintDb
51 |
52 | d
53 | Debug
54 | Declare Custom Entry
55 | Declare Equivalent Keys
56 | Declare Instance
57 | Declare Left Step
58 | Declare ML Module
59 | Declare Module
60 | Declare Morphism
61 | Declare Reduction
62 | Declare Right Step
63 | Declare Scope
64 | Defined
65 | Definition
66 | Delimit Scope
67 | Derive
68 | Derive Dependent Inversion
69 | Derive Dependent Inversion_clear
70 | Derive Inversion
71 | Derive Inversion_clear
72 | Disable Notation
73 | Drop
74 |
75 | e
76 | Enable Notation
77 | End
78 | Eval
79 | Example
80 | Existing Class
81 | Existing Instance
82 | Existing Instances
83 | Export
84 | Extract Constant
85 | Extract Inductive
86 | Extract Inlined Constant
87 | Extraction
88 | Extraction Blacklist
89 | Extraction Implicit
90 | Extraction Inline
91 | Extraction Language
92 | Extraction Library
93 | Extraction NoInline
94 | Extraction TestCompile
95 |
96 | f
97 | Fact
98 | Fail
99 | Final Obligation
100 | Fixpoint
101 | Focus
102 | From … Dependency
103 | From … Require
104 | Function
105 | Functional Case
106 | Functional Scheme
107 |
108 | g
109 | Generalizable
110 | Generate graph for
111 | Goal
112 | Guarded
113 |
114 | h
115 | Hint Constants
116 | Hint Constructors
117 | Hint Cut
118 | Hint Extern
119 | Hint Immediate
120 | Hint Mode
121 | Hint Opaque
122 | Hint Resolve
123 | Hint Rewrite
124 | Hint Transparent
125 | Hint Unfold
126 | Hint Variables
127 | Hint View for
128 | Hint View for apply
129 | Hint View for move
130 | Hypotheses
131 | Hypothesis
132 |
133 | i
134 | Identity Coercion
135 | Implicit Type
136 | Implicit Types
137 | Import
138 | Include
139 | Include Type
140 | Inductive
141 | Infix
142 | Info
143 | infoH
144 | Inspect
145 | Instance
146 |
147 | l
148 | Lemma
149 | Let
150 | Let CoFixpoint
151 | Let Fixpoint
152 | Load
153 | Locate
154 | Locate File
155 | Locate Library
156 | Locate Ltac
157 | Locate Ltac2
158 | Locate Module
159 | Locate Term
160 | Ltac
161 | Ltac2
162 | Ltac2 Eval
163 | Ltac2 external
164 | Ltac2 Notation
165 | Ltac2 Notation (abbreviation)
166 | Ltac2 Set
167 | Ltac2 Type
168 |
169 | m
170 | Module
171 | Module Type
172 |
173 | n
174 | Next Obligation
175 | Notation
176 | Notation (abbreviation)
177 | Number Notation
178 |
179 | o
180 | Obligation
181 | Obligation Tactic
182 | Obligations
183 | Opaque
184 | Open Scope
185 | Optimize Heap
186 | Optimize Proof
187 |
188 | p
189 | Parameter
190 | Parameters
191 | Prenex Implicits
192 | Preterm
193 | Primitive
194 | Print
195 | Print All
196 | Print All Dependencies
197 | Print Assumptions
198 | Print Canonical Projections
199 | Print Classes
200 | Print Coercion Paths
201 | Print Coercions
202 | Print Custom Grammar
203 | Print Debug GC
204 | Print Equivalent Keys
205 | Print Extraction Blacklist
206 | Print Extraction Inline
207 | Print Fields
208 | Print Firstorder Solver
209 | Print Grammar
210 | Print Graph
211 | Print Hint
212 | Print HintDb
213 | Print Implicit
214 | Print Instances
215 | Print Keywords
216 | Print Libraries
217 | Print LoadPath
218 | Print Ltac
219 | Print Ltac Signatures
220 | Print Ltac2
221 | Print Ltac2 Signatures
222 | Print ML Modules
223 | Print ML Path
224 | Print Module
225 | Print Module Type
226 | Print Namespace
227 | Print Notation
228 | Print Opaque Dependencies
229 | Print Options
230 | Print Registered
231 | Print Rewrite HintDb
232 | Print Rings
233 | Print Scope
234 | Print Scopes
235 | Print Section
236 | Print Strategies
237 | Print Strategy
238 | Print Table
239 | Print Tables
240 | Print Transparent Dependencies
241 | Print Typeclasses
242 | Print Typing Flags
243 | Print Universes
244 | Print Visibility
245 | Proof
246 | Proof `term`
247 | Proof Mode
248 | Proof using
249 | Proof with
250 | Property
251 | Proposition
252 | Pwd
253 |
254 | q
255 | Qed
256 | Quit
257 |
258 | r
259 | Record
260 | Recursive Extraction
261 | Recursive Extraction Library
262 | Redirect
263 | Register
264 | Register Inline
265 | Remark
266 | Remove
267 | Remove Hints
268 | Require
269 | Require Export
270 | Require Import
271 | Reserved Infix
272 | Reserved Notation
273 | Reset
274 | Reset Extraction Blacklist
275 | Reset Extraction Inline
276 | Reset Initial
277 | Reset Ltac Profile
278 | Restart
279 |
280 | s
281 | Save
282 | Scheme
283 | Scheme Boolean Equality
284 | Scheme Equality
285 | Search
286 | SearchPattern
287 | SearchRewrite
288 | Section
289 | Separate Extraction
290 | Set
291 | Show
292 | Show Conjectures
293 | Show Existentials
294 | Show Extraction
295 | Show Goal
296 | Show Intro
297 | Show Intros
298 | Show Lia Profile
299 | Show Ltac Profile
300 | Show Match
301 | Show Obligation Tactic
302 | Show Proof
303 | Show Universes
304 | Show Zify
305 | Solve All Obligations
306 | Solve Obligations
307 | Strategy
308 | String Notation
309 | Structure
310 | SubClass
311 | Succeed
312 |
313 | t
314 | Tactic Notation
315 | Test
316 | Theorem
317 | Time
318 | Timeout
319 | Transparent
320 | Type
321 | Typeclasses eauto
322 | Typeclasses Opaque
323 | Typeclasses Transparent
324 |
325 | u
326 | Undelimit Scope
327 | Undo
328 | Unfocus
329 | Unfocused
330 | Universe
331 | Universes
332 | Unset
333 | Unshelve
334 |
335 | v
336 | Validate Proof
337 | Variable
338 | Variables
339 | Variant
340 | """
341 |
342 | tactics_data = """
343 | +
344 | + (backtracking branching)
345 |
346 | =
347 | =>
348 |
349 | [
350 | [ … | … | … ] (dispatch)
351 | [> … | … | … ] (dispatch)
352 |
353 | a
354 | abstract
355 | abstract (ssreflect)
356 | absurd
357 | admit
358 | apply
359 | apply (ssreflect)
360 | assert
361 | assert_fails
362 | assert_succeeds
363 | assumption
364 | auto
365 | autoapply
366 | autorewrite
367 | autounfold
368 | autounfold_one
369 |
370 | b
371 | btauto
372 | bullet (- + *)
373 | by
374 |
375 | c
376 | case
377 | case (ssreflect)
378 | case_eq
379 | casetype
380 | cbn
381 | cbv
382 | change
383 | change_no_check
384 | classical_left
385 | classical_right
386 | clear
387 | clear dependent
388 | clearbody
389 | cofix
390 | compare
391 | compute
392 | congr
393 | congruence
394 | constr_eq
395 | constr_eq_nounivs
396 | constr_eq_strict
397 | constructor
398 | context
399 | contradict
400 | contradiction
401 | cut
402 | cutrewrite
403 | cycle
404 |
405 | d
406 | debug auto
407 | debug eauto
408 | debug trivial
409 | decide
410 | decide equality
411 | decompose
412 | decompose record
413 | decompose sum
414 | dependent destruction
415 | dependent generalize_eqs
416 | dependent generalize_eqs_vars
417 | dependent induction
418 | dependent inversion
419 | dependent inversion_clear
420 | dependent rewrite
421 | dependent simple inversion
422 | destauto
423 | destruct
424 | dfs eauto
425 | dintuition
426 | discriminate
427 | discrR
428 | do
429 | do (ssreflect)
430 | done
431 | dtauto
432 |
433 | e
434 | eapply
435 | eassert
436 | eassumption
437 | easy
438 | eauto
439 | ecase
440 | econstructor
441 | edestruct
442 | ediscriminate
443 | eelim
444 | eenough
445 | eexact
446 | eexists
447 | einduction
448 | einjection
449 | eintros
450 | eleft
451 | elim
452 | elim (ssreflect)
453 | elimtype
454 | enough
455 | epose
456 | epose proof
457 | eremember
458 | erewrite
459 | eright
460 | eset
461 | esimplify_eq
462 | esplit
463 | etransitivity
464 | eval
465 | evar
466 | exact
467 | exact (ssreflect)
468 | exact_no_check
469 | exactly_once
470 | exfalso
471 | exists
472 |
473 | f
474 | f_equal
475 | fail
476 | field
477 | field_lookup
478 | field_simplify
479 | field_simplify_eq
480 | finish_timing
481 | first
482 | first (ssreflect)
483 | first last
484 | firstorder
485 | fix
486 | fold
487 | fresh
488 | fun
489 | functional induction
490 | functional inversion
491 |
492 | g
493 | generalize
494 | generalize dependent
495 | generalize_eqs
496 | generalize_eqs_vars
497 | generally have
498 | gfail
499 | gintuition
500 | give_up
501 | guard
502 |
503 | h
504 | has_evar
505 | have
506 | head_of_constr
507 | hnf
508 |
509 | i
510 | idtac
511 | if-then-else (Ltac2)
512 | in
513 | induction
514 | info_auto
515 | info_eauto
516 | info_trivial
517 | injection
518 | instantiate
519 | intro
520 | intros
521 | intros until
522 | intuition
523 | inversion
524 | inversion_clear
525 | inversion_sigma
526 | is_cofix
527 | is_const
528 | is_constructor
529 | is_evar
530 | is_fix
531 | is_ground
532 | is_ind
533 | is_proj
534 | is_var
535 |
536 | l
537 | lapply
538 | last
539 | last first
540 | lazy
541 | lazy_match!
542 | lazy_match! goal
543 | lazymatch
544 | lazymatch goal
545 | left
546 | let
547 | lia
548 | lra
549 | ltac-seq
550 |
551 | m
552 | match
553 | match (Ltac2)
554 | match goal
555 | match!
556 | match! goal
557 | move
558 | move (ssreflect)
559 | multi_match!
560 | multi_match! goal
561 | multimatch
562 | multimatch goal
563 |
564 | n
565 | native_cast_no_check
566 | native_compute
567 | nia
568 | not_evar
569 | now
570 | now_show
571 | nra
572 | nsatz
573 | nsatz_compute
574 | numgoals
575 |
576 | o
577 | once
578 | only
579 | optimize_heap
580 | over
581 |
582 | p
583 | pattern
584 | pose
585 | pose (ssreflect)
586 | pose proof
587 | progress
588 | protect_fv
589 | psatz
590 |
591 | r
592 | rapply
593 | red
594 | refine
595 | reflexivity
596 | remember
597 | rename
598 | repeat
599 | replace
600 | reset ltac profile
601 | restart_timer
602 | revert
603 | revert dependent
604 | revgoals
605 | rewrite
606 | rewrite (ssreflect)
607 | rewrite *
608 | rewrite_db
609 | rewrite_strat
610 | right
611 | ring
612 | ring_lookup
613 | ring_simplify
614 | rtauto
615 |
616 | s
617 | set
618 | set (ssreflect)
619 | setoid_etransitivity
620 | setoid_reflexivity
621 | setoid_replace
622 | setoid_rewrite
623 | setoid_symmetry
624 | setoid_transitivity
625 | shelve
626 | shelve_unifiable
627 | show ltac profile
628 | simpl
629 | simple apply
630 | simple congruence
631 | simple destruct
632 | simple eapply
633 | simple induction
634 | simple injection
635 | simple inversion
636 | simple subst
637 | simplify_eq
638 | soft functional induction
639 | solve
640 | solve_constraints
641 | specialize
642 | specialize_eqs
643 | split
644 | split_Rabs
645 | split_Rmult
646 | start ltac profiling
647 | stepl
648 | stepr
649 | stop ltac profiling
650 | subst
651 | substitute
652 | suff
653 | suffices
654 | swap
655 | symmetry
656 |
657 | t
658 | tauto
659 | time
660 | time_constr
661 | timeout
662 | transitivity
663 | transparent_abstract
664 | trivial
665 | try
666 | tryif
667 | type of
668 | type_term
669 | typeclasses eauto
670 |
671 | u
672 | under
673 | unfold
674 | unify
675 | unlock
676 | unshelve
677 |
678 | v
679 | vm_cast_no_check
680 | vm_compute
681 |
682 | w
683 | with_strategy
684 | without loss
685 | wlia
686 | wlog
687 | wlra_Q
688 | wnia
689 | wnra_Q
690 | wpsatz_Q
691 | wpsatz_Z
692 | wsos_Q
693 | wsos_Z
694 |
695 | x
696 | xlia
697 | xlra_Q
698 | xlra_R
699 | xnia
700 | xnra_Q
701 | xnra_R
702 | xpsatz_Q
703 | xpsatz_R
704 | xpsatz_Z
705 | xsos_Q
706 | xsos_R
707 | xsos_Z
708 |
709 | z
710 | zify
711 | zify_elim_let
712 | zify_iter_let
713 | zify_iter_specs
714 | zify_op
715 | zify_saturate
716 | """
717 |
718 | def extract(data):
719 | result = map(lambda l: l.strip("\t "), data.splitlines())
720 | result = filter(lambda l: len(l) > 1, result)
721 | result = map(lambda l: l.split(" ")[0], result)
722 | result = filter(lambda l: l[0].isalpha(), result)
723 | result = map(lambda l: l.rstrip(":"), result)
724 | result = sorted(set(result))
725 | return result
726 |
727 | print("Commands:")
728 | for command in extract(commands_data):
729 | print('"{}",'.format(command))
730 |
731 | print("Tactics:")
732 | for tactic in extract(tactics_data):
733 | print('"{}",'.format(tactic))
734 |
--------------------------------------------------------------------------------
/screenshot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EugeneLoy/coq_jupyter/ca58efef3fc315c16b339a6b6e609e49efa2a636/screenshot.png
--------------------------------------------------------------------------------
/setup.py:
--------------------------------------------------------------------------------
1 | from setuptools import setup
2 |
3 | readme = """Jupyter kernel for Coq.
4 | See: https://github.com/EugeneLoy/coq_jupyter
5 | """
6 |
7 | setup(
8 | name='coq_jupyter',
9 | version='1.6.2',
10 | packages=['coq_jupyter'],
11 | description='Coq kernel for Jupyter',
12 | long_description=readme,
13 | author='Eugene Loy',
14 | author_email='eugeny.loy@gmail.com',
15 | url='https://github.com/EugeneLoy/coq_jupyter',
16 | include_package_data=True,
17 | install_requires=[
18 | 'jupyter_client',
19 | 'IPython',
20 | 'ipykernel',
21 | 'future',
22 | 'pexpect>=4.0'
23 | ],
24 | classifiers=[
25 | 'Development Status :: 4 - Beta',
26 | 'License :: OSI Approved :: Apache Software License',
27 | 'Programming Language :: Python :: 3',
28 | 'Operating System :: POSIX :: Linux',
29 | 'Framework :: Jupyter',
30 | 'Intended Audience :: Education',
31 | 'Intended Audience :: Developers',
32 | 'Intended Audience :: Science/Research',
33 | 'Topic :: Software Development'
34 | ],
35 | )
36 |
--------------------------------------------------------------------------------
/test/container_test_entrypoint.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | export OPAMROOT=/home/coq/.opam
4 | eval $(opam env)
5 |
6 | sudo apt-get update
7 | sudo apt-get install -y python3-pip
8 |
9 | python3 --version
10 | coqtop --version
11 |
12 | sudo pip3 install --upgrade --force-reinstall /github/workspace/dist/coq_jupyter-*.tar.gz 'jupyter_client<=6.1.12' 'jupyter_kernel_test<=0.3'
13 | sudo python3 -m coq_jupyter.install
14 |
15 | python3 /github/workspace/test/kernel_test.py -v
16 |
--------------------------------------------------------------------------------
/test/kernel_test.py:
--------------------------------------------------------------------------------
1 | import unittest
2 | import jupyter_kernel_test
3 | import os
4 |
5 | class KernelTests(jupyter_kernel_test.KernelTests):
6 |
7 | # Required by jupyter_kernel_test.KernelTests:
8 | kernel_name = "coq"
9 | language_name = "coq"
10 |
11 | _coq_version = tuple([int(i) for i in os.environ.get("COQ_VERSION", "1000.0.0").split(".")])
12 | _counter = iter(range(1, 100))
13 |
14 | def _build_sum_command(self):
15 | lhs = 100
16 | rhs = next(self._counter)
17 | result = str(lhs + rhs)
18 | command = "Compute {} + {}.".format(lhs, rhs)
19 | return (result, command)
20 |
21 | def _execute_cell(self, code):
22 | self.flush_channels()
23 | reply, output_msgs = self.execute_helper(code=code)
24 |
25 | self.assertEqual(reply['content']['status'], 'ok')
26 | self.assertEqual(len(output_msgs), 1)
27 | self.assertEqual(output_msgs[0]['msg_type'], 'execute_result')
28 | self.assertIn('text/plain', output_msgs[0]['content']['data'])
29 |
30 | return output_msgs[0]['content']['data']['text/plain']
31 |
32 | def setUp(self):
33 | self._execute_cell("Reset Initial.")
34 |
35 | def test_coq_jupyter____executing_empty_code____should_not_print_anything(self):
36 | self.flush_channels()
37 | reply, output_msgs = self.execute_helper(code="")
38 |
39 | self.assertEqual(reply['content']['status'], 'ok')
40 | self.assertEqual(len(output_msgs), 0)
41 |
42 | def test_coq_jupyter____executing_code_without_coq_content____should_not_print_anything(self):
43 | self.flush_channels()
44 | reply, output_msgs = self.execute_helper(code="\n\r\t \n\r\t ")
45 |
46 | self.assertEqual(reply['content']['status'], 'ok')
47 | self.assertEqual(len(output_msgs), 0)
48 |
49 | def test_coq_jupyter____executing_one_command____prints_computed_command_result(self):
50 | (expected_result, command) = self._build_sum_command()
51 | result = self._execute_cell(command)
52 | self.assertIn(expected_result, result)
53 |
54 | def test_coq_jupyter____executing_one_command____does_not_print_command(self):
55 | (expected_result, command) = self._build_sum_command()
56 | result = self._execute_cell(command)
57 | self.assertNotIn(command, result)
58 |
59 | def test_coq_jupyter____executing_compute_command_when_not_proving____does_not_print_proving_context(self):
60 | (expected_result, command) = self._build_sum_command()
61 | result = self._execute_cell(command)
62 | self.assertNotIn("proving:", result)
63 | self.assertNotIn("subgoal", result)
64 |
65 | def test_coq_jupyter____executing_multiple_commands____prints_computed_command_results(self):
66 | (expected_result1, command1) = self._build_sum_command()
67 | (expected_result2, command2) = self._build_sum_command()
68 | result = self._execute_cell(command1 + " " + command2)
69 | self.assertIn(expected_result1, result)
70 | self.assertIn(expected_result2, result)
71 |
72 | def test_coq_jupyter____executing_commands_when_proving____prints_proving_context(self):
73 | result = self._execute_cell("Theorem t1 : True.")
74 | self.assertIn("1 subgoal", result)
75 | self.assertIn("Proving: t1", result)
76 |
77 | result = self._execute_cell(self._build_sum_command()[1])
78 | self.assertIn("1 subgoal", result)
79 | self.assertIn("Proving: t1", result)
80 |
81 | result = self._execute_cell("Proof. pose (i1 := I).")
82 | self.assertIn("1 subgoal", result)
83 | self.assertIn("i1 := I : True", result)
84 | self.assertIn("Proving: t1", result)
85 |
86 | result = self._execute_cell(self._build_sum_command()[1])
87 | self.assertIn("1 subgoal", result)
88 | self.assertIn("i1 := I : True", result)
89 | self.assertIn("Proving: t1", result)
90 |
91 | result = self._execute_cell("exact i1. Qed.")
92 | self.assertNotIn("1 subgoal", result)
93 | self.assertNotIn("No more subgoals", result)
94 | self.assertNotIn("i1 := I : True", result)
95 | self.assertNotIn("Proving:", result)
96 |
97 | result = self._execute_cell(self._build_sum_command()[1])
98 | self.assertNotIn("1 subgoal", result)
99 | self.assertNotIn("No more subgoals", result)
100 | self.assertNotIn("i1 := I : True", result)
101 | self.assertNotIn("proving:", result)
102 |
103 | def test_coq_jupyter____when_proving____prints_most_recent_proving_context_once(self):
104 | result = self._execute_cell("Theorem t3 : bool. Proof. pose (b1 := true). pose (b2 := false).")
105 | self.assertEqual(result.count("Proving: t3"), 1, "result: " + repr(result))
106 | self.assertEqual(result.count("1 subgoal"), 1, "result: " + repr(result))
107 | self.assertEqual(result.count("No more subgoals"), 0, "result: " + repr(result))
108 | self.assertEqual(result.count("b1 := true : bool"), 1, "result: " + repr(result))
109 | self.assertEqual(result.count("b2 := false : bool"), 1, "result: " + repr(result))
110 |
111 | result = self._execute_cell("exact b2.")
112 | self.assertEqual(result.count("Proving: t3"), 1, "result: " + repr(result))
113 | self.assertEqual(result.count("1 subgoal"), 0, "result: " + repr(result))
114 | self.assertEqual(result.count("No more subgoals"), 1, "result: " + repr(result))
115 | self.assertEqual(result.count("b1 := true : bool"), 0, "result: " + repr(result))
116 | self.assertEqual(result.count("b2 := false : bool"), 0, "result: " + repr(result))
117 |
118 | def _build_commands_with_error_fixture(self, t_base, t, valid_command_template, invalid_command_template, expected_error_message):
119 | return (
120 | "t{}_{}".format(t_base + t, t),
121 | ["t{}_{}".format(t_base + t, i) for i in (1, 2, 3) if i != t],
122 | ("t{}_0".format(t_base + t), valid_command_template.format(t_base + t, 0)),
123 | "\n".join([
124 | valid_command_template.format(t_base + t, i) if i != t else invalid_command_template.format(t_base + t, i)
125 | for i in (1, 2, 3)
126 | ]),
127 | expected_error_message
128 | )
129 |
130 | def test_coq_jupyter____executing_commands_with_error____prints_error_and_rolls_back(self):
131 | cases_with_reference_error = [
132 | self._build_commands_with_error_fixture(
133 | 3,
134 | t,
135 | "Definition t{}_{} := I.",
136 | "Definition t{}_{} := INVALID_REFERENCE.",
137 | "Error: The reference INVALID_REFERENCE was not found"
138 | )
139 | for t in (1,2,3)
140 | ]
141 | cases_with_syntax_error = [
142 | self._build_commands_with_error_fixture(
143 | 6,
144 | t,
145 | "Definition t{}_{} := I.",
146 | "Definition t{}_{} := (I.",
147 | "Syntax error: ',' or ')' expected after"
148 | )
149 | for t in (1,2,3)
150 | ]
151 | cases_with_incomplete_command_error = [
152 | self._build_commands_with_error_fixture(
153 | 9,
154 | t,
155 | "Definition t{}_{} := I.",
156 | "Definition t{}_{} := I",
157 | "Syntax error: '.' expected after"
158 | )
159 | for t in (1,2,3)
160 | ]
161 | fixture = cases_with_reference_error + cases_with_syntax_error + cases_with_incomplete_command_error
162 |
163 | for f in range(len(fixture)):
164 | (invalid_definition, valid_definitions, commited_definition, code, expected_error_message) = fixture[f]
165 |
166 | self._execute_cell(commited_definition[1])
167 |
168 | result = self._execute_cell(code)
169 | self.assertIn(expected_error_message, result, msg="fixture: {}".format(repr(fixture[f])))
170 |
171 | # verify roll back
172 | result = self._execute_cell("Print All.")
173 | self.assertIn(commited_definition[0], result, msg="fixture: {}".format(repr(fixture[f])))
174 | self.assertNotIn(invalid_definition, result, msg="fixture: {}".format(repr(fixture[f])))
175 | self.assertNotIn(valid_definitions[0], result, msg="fixture: {}".format(repr(fixture[f])))
176 | self.assertNotIn(valid_definitions[1], result, msg="fixture: {}".format(repr(fixture[f])))
177 |
178 | def test_coq_jupyter____when_executing_command_that_results_in_warning____prints_warning(self):
179 | # this test ensures fix of the following:
180 | # https://github.com/EugeneLoy/coq_jupyter/issues/21
181 | # https://github.com/EugeneLoy/coq_jupyter/issues/23
182 |
183 | result = self._execute_cell("Compute 5001.")
184 |
185 | self.assertIn("Warning: ", result)
186 | self.assertNotIn("", result)
187 |
188 | def test_coq_jupyter____when_executing_command_that_results_in_error____prints_error_once(self):
189 | result = self._execute_cell("Compute INVALID_REFERENCE.")
190 |
191 | self.assertEqual(result.count("Error: The reference INVALID_REFERENCE was not found"), 1, "result: " + repr(result))
192 | self.assertNotIn("", result)
193 |
194 | def test_coq_jupyter____when_executing_command_that_results_in_notice_message____does_not_print_notice_message_level(self):
195 | (_, command) = self._build_sum_command()
196 | result = self._execute_cell(command)
197 |
198 | self.assertNotIn("notice", result.lower())
199 |
200 | def test_coq_jupyter____executing_code_with_unclosed_comment____prints_error(self):
201 | (_, command) = self._build_sum_command()
202 | result = self._execute_cell(command + " (* ")
203 |
204 | self.assertIn("Unterminated comment", result)
205 |
206 | def test_coq_jupyter____executing_code_surrounded_by_unclosed_comments____prints_evaluation_result(self):
207 | if self._coq_version >= (8,10,0):
208 | self.skipTest("skipping due to coq version: {}".format(self._coq_version)) # TODO skipping might not be the best solution here
209 |
210 | (expected_result, command) = self._build_sum_command()
211 | result = self._execute_cell("(* comment *)" + command + "(* comment *)")
212 |
213 | self.assertIn(expected_result, result)
214 | self.assertNotIn("error", result.lower())
215 |
216 | def test_coq_jupyter____executing_code_with_comments_woven_in____prints_evaluation_result(self):
217 | result = self._execute_cell("Check (* some comment with '.' in the middle *) I.")
218 |
219 | self.assertIn("True", result)
220 | self.assertNotIn("error", result.lower())
221 |
222 | def test_coq_jupyter____executing_code_comments_only____does_not_result_in_error(self):
223 | if self._coq_version >= (8,10,0):
224 | self.skipTest("skipping due to coq version: {}".format(self._coq_version)) # TODO skipping might not be the best solution here
225 |
226 | result = self._execute_cell("(* comment *)")
227 |
228 | self.assertNotIn("error", result.lower())
229 |
230 | def test_coq_jupyter____executing_code_with_non_xml_symbols____prints_evaluation_result(self):
231 | code = """
232 | Compute
233 | match 0 with
234 | | 0 => 1 + 1
235 | | S n' => n'
236 | end.
237 | """
238 | result = self._execute_cell(code)
239 |
240 | self.assertIn("2", result, msg="Code:\n{}".format(code))
241 | self.assertNotIn("error", result.lower(), msg="Code:\n{}".format(code))
242 |
243 | def test_coq_jupyter____executing_long_running_code_____prints_evaluation_result(self):
244 | code = "Goal True. timeout 10 (repeat eapply proj1)."
245 | result = self._execute_cell(code)
246 |
247 | self.assertIn("Tactic timeout", result)
248 |
249 | def test_coq_jupyter____executing_code_with_undotted_separators____prints_evaluation_result_for_every_statement(self):
250 | fixture = [
251 | ("-", "-", ""),
252 | ("*", "*", ""),
253 | ("+", "+", ""),
254 | ("---", "---", ""),
255 | ]
256 | if self._coq_version >= (8,9,0):
257 | fixture += [
258 | ("{", "{", "}"),
259 | ("1:{", "1 : {", "}"),
260 | ("[G1]:{", "[ g2_' ] : {", "}")
261 | ]
262 |
263 | for (opening_separator1, opening_separator2, closing_separator) in fixture:
264 | (expected_results, commands) = zip(*[self._build_sum_command() for _ in range(4)])
265 | code = """
266 | Goal True /\ True.
267 | split ; [ refine ?[G1] | refine ?[g2_'] ].
268 | {0} {3}
269 | {4}
270 | exact I.
271 | {2}
272 | {1} {5}
273 | {6}
274 | exact I.
275 | {2}
276 | Qed.
277 | """.format(opening_separator1, opening_separator2, closing_separator, *commands)
278 |
279 | result = self._execute_cell(code)
280 |
281 | for expected_result in expected_results:
282 | self.assertIn(expected_result, result, msg="Code:\n{}".format(code))
283 | self.assertNotIn("error", result.lower(), msg="Code:\n{}".format(code))
284 |
285 | def test_coq_jupyter____executing_code_that_completes_subproof_while_having_unfocused_goals____prints_info_about_unfocused_goals(self):
286 | marker1 = next(self._counter)
287 | marker2 = next(self._counter)
288 | marker3 = next(self._counter)
289 | code = """
290 | Goal {0}={0} /\ {1}={1} /\ {2}={2}.
291 | split ; [ | split].
292 | - easy.
293 | """.format(marker1, marker2, marker3)
294 |
295 | result = self._execute_cell(code)
296 |
297 | self.assertIn("This subproof is complete, but there are some unfocused goals:", result, msg="Code:\n{}".format(code))
298 | self.assertIn("{0} = {0}".format(marker2), result, msg="Code:\n{}".format(code))
299 | self.assertIn("{0} = {0}".format(marker3), result, msg="Code:\n{}".format(code))
300 | self.assertNotIn("error", result.lower(), msg="Code:\n{}".format(code))
301 |
302 | if __name__ == '__main__':
303 | unittest.main()
304 |
--------------------------------------------------------------------------------