This file is indexed.

/usr/share/acl2-6.3/books/paco/output-module.lisp is in acl2-books-source 6.3-5.

This file is owned by root:root, with mode 0o644.

The actual contents of the file can be viewed below.

  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
(in-package "PACO")

; I chose to code Paco without considering how it would track and
; report which rules it was using.  That decision allows the term
; manipulation functionality to be the primary consideration in the
; code.  As for how Paco would report what it was doing, my vision has
; always been to superimpose some kind of dependency-tracking ``after
; the fact,'' ideally with a meta-facility like tracing or compiling
; that secretly changes behavior without changing functionality.

; This is a first cut at such a module for Paco.  It is not quite
; post-facto, as this is an early file in the source sequence.  But I
; have managed to hide virtually all of Paco's dependency tracking and
; output features here.

; In this file I define fourteen macros, each with a name starting with
; "<" and ending with ">", e.g., <rewrite-id>.  Each macro is an
; an ``identity macro'' in the sense that (<rewrite-id> term) is equal
; to term.  These macros expand in such a way as to evaluate term -- which
; is often an expression returning a multiple value -- and keep track of
; which rules it is using.  To do this, the rules are squirrelled away in
; a wormhole.

; On a first reading of the Paco sources, I strongly recommend not to
; read any further through this file!  Just understand that
; (<rewrite-id> term) is the same as term when you read the rest of
; the sources.  Then, when you understand how Paco works, you might
; want to revisit this file and see how I make that happen.

; The rest of this rather long comment attempts to explain WHY I do
; dependency tracking this way and then HOW it actually works.  But you
; do not need to know this on an initial study of Paco.

; But I would like to obtain a rudimentary rule tracking/reporting
; mechanism via a ``meta'' facility akin to tracing.  Consider the
; factorial function.  We define it without regard for any kind of
; tracking/recording of the intermediate results.  (Fact 3) returns 6
; and we do not know ``how'' it computed 6.  But we can install a
; trace on fact -- after the fact, so to speak -- and then see that
; (fact 3) gave rise to (fact 2) which computed 2, etc.  

; My vision -- not yet fully realized and arguably impossible in light
; of the full exploration of this note -- is that I can define the
; Paco rewriter or type-set mechanism or prover and get it to do the
; necessary term manipulation to make it a ``theorem prover'' and
; then, after the fact, install some kind of ``rule trace'' that
; allows me to see what rules it is using.

; My attempt to understand this took the form of two earlier files, called
; io-hack1 and io-hack2, in which I doctored the pristine Paco sources with
; tracking code written in raw Lisp and sprinkled around the Paco code
; where ever necessary.  I just grabbed any Paco code I wanted to modify,
; copied it into io-hack1, modified it with raw Lisp, and loaded the io-hack
; into raw Lisp after Paco had been loaded, thus redefining the ``official''
; Paco code.  Then I studied the raw Lisp to look for patterns
; and abstractions that would make it more regular.  After doing this twice
; (io-hack1 and followed by io-hack2) I realized I could do it using wormholes
; and macros without actually redefining the Paco code -- if I defined the
; macros first.  The macros admittedly anticipate the term manipulation code
; I will write, right down to the variable names my code will use!  So it is
; not very automated.  But at least I've partitioned the entire dependency
; tracking code into this one place.

; Now, let me illustrate the basic problem and discuss some possible
; approaches.  The following is a pretty standard Paco idiom for using
; a rule: check if it is enabled, apply the rule to obtain some new
; term, test some heuristic condition, and return either the new term
; or the original one.

; (cond ((enabled-numep (access rule x :nume) ens)    ;;; [1]
;        (let ((new-term (apply-rule x term)))
;          (cond ((heuristicp new-term term)
;                 new-term)
;                (t term))))
;       (t term))

; The Nqthm approach Boyer and I adopted was analogous to the
; following.  We had a secret stack, here called the ``nume stack'',
; composed of frames.  Each frame contains the numes that have been
; used so far on behalf of the current subgoal.  The basic operations
; on the nume stack are to push a new frame, push a nume into the top
; frame, pop and discard a frame (in the event that the work is
; abandoned) or pop and union a frame into the one below it (in the
; event that the work contributes a new term).  The code above can be
; annotated with in raw Lisp using these concepts.  The annotations
; are shown in upper case.

; (cond ((enabled-numep (access rule x :nume) ens)    ;;; [2]
;        (PUSH-NUME-FRAME (ACCESS RULE X :NUME))
;        (let ((new-term (apply-rule x term)))
;          (cond ((heuristicp new-term term)
;                 (POP-NUME-FRAME T)
;                 new-term)
;                (t
;                 (POP-NUME-FRAME NIL)
;                 term))))
;       (t term))

; Upon entering the block of code in which the nume might be used, we
; push a new frame containing the nume.  Upon exiting, we pop the
; frame and either union it into its caller or discard it, depending
; on whether the rule application was successful.

; The ACL2 approach used by Kaufmann and me was to make all term
; manipulation functions return two results: the manipulated term and
; a trace of rules used.  These traces, actually ACL2's ``ttrees'',
; are explicitly passed around and combined or discarded
; appropriately.  Here is the above code fragment in the ACL2 style:

; (cond ((enabled-numep (access rule x :nume) ens)    ;;; [3]
;        (MV-let (new-term numes)
;                (apply-rule x term)
;                (cond ((heuristicp new-term term)
;                       (MV new-term (ADD-TO-SET (ACCESS RULE X :NUME) NUMES)))
;                      (t (MV term NIL)))))
;       (t (MV term NIL)))

; This snippet of code is deceptive because it doesn't really drive
; home the fact that the whole system has to be written to use
; multiple values.  But this is maximally flexible because everything
; is done explicitly.

; In my view, [1] is the clearest from the logical perspective.  It
; just says how we manipulate the term.  What I would like is to
; incant some trace-like magic with reference to [1] and obtain [2] or
; [3].  In principle, it doesn't matter to me which way we go.  But if
; the ``magic'' transforms [1] into the ACL2-style [3], then all
; routines which called the original snippet would have to be
; transformed to handle the multiple values, their callers would have
; to be transformed, etc.  For that reason, I think it is easier to
; shoot for [2], where the tracing does not change the functionality
; of the snippet (as long as one views it without looking at the
; nume-stack).  This can be implemented locally.

; Another question I have considered is what the ``magic'' looks like.
; I see no way, even with the simple idiom [1], to obtain [2] from [1]
; with no additional typing.  That is, (track-rules [1]) cannot be
; implemented because I do not think, in general, we can automatically
; distinguish the exits upon which the rule was used from the exits
; upon which it was not.

; I can imagine implementing the no-op macros WON and LOST so that I
; could rewrite [1] as

; (cond ((enabled-numep (access rule x :nume) ens)    ;;; [1']
;        (let ((new-term (apply-rule x term)))
;          (cond ((heuristicp new-term term)
;                 (WON new-term))
;                (t (LOST term)))))
;       (t term))

; Here the idea is that enabled-numep secretly pushes a nume frame if
; it succeeds.  Thereafter, we are obliged to pop it with either WON
; or LOST which do the obvious secret pops.

; I don't like the asymmetry between implicit pushes and explicit pops.

; But there are more serious problem.  The simple idiom of [1] is too simple.
; It is not uncommon to see
; (and (enabled-numep ...)
;      (some-other-conditionp ...))
; governing the use of the rule.  In this case, if the enabled-numep
; secretly pushes a frame, we have to secretly pop it without ever entering
; the block where the rule is used.

; Another complication is that we sometimes do things like call
; enabled-numep inside a function that ``collects candidates'' but
; doesn't actually use the rules.  We pass the candidates to some
; other function, which weighs their merits and decides which one to
; choose, knowing the corresponding rule is enabled.  This is where
; the ACL2 style [3] shines.  By explicitly collecting the
; dependencies with the candidates, we can pass those dependencies
; back up whenever we select the candidate to use.  Put another way,
; idiom [1] is misleading because it suggests the tracking can be done
; with lexical scope but it is dynamic scope that is important.

; Finally, I believe it is inherently impossible to recognize whether
; a given rule was actually used.  For example, a type-prescription
; rule might fire but actually contribute nothing to the already known
; type-set.  That is, we might know the term is an positive integer
; and the rule might tell us that it is an integer.  Or, the first
; rule might tell us a term is a true-listp and the second rule might
; tell us it is NIL.  Was the first rule used?  The proof would have
; proceeded exactly the same had it been disabled, so evidently not.
; It is certainly the case in ACL2 that we sometimes do not report the
; use of a rule we ``used'' because we realize we could have reached
; the same conclusion without it, e.g., when the final type-set is
; *ts-unknown*.  When looked at this way, the whole issue is so
; ill-specified that it is difficult to pin down even what I mean when
; I say I'm looking for an automatic way to track dependencies.

; To be more specific, what do we want to track in induction?  Recall
; how induction in ACL2 works.  We typically do an induction that is
; suggested by some function symbol appearing in the goal clause.
; That induction is justified by the argument used to admit the
; suggesting function's definition.  Call that function symbol suggfn.
; But recall that we also have induction rules, that cause other,
; arbitrary, function symbols to suggest inductions justified by other
; symbols.  So g might occur in the goal clause and an induction rule,
; rule, might link g to suggfn.  Furthermore, rule might be applicable
; only if we can establish certain hypotheses (with type-set).

; So WHY do we want dependencies?  Is it to allow us to construct or
; describe a formal proof?  Or is it so that we can inform the user of
; every fact in the database that has led us to the proof we allege
; can be constructed?  If we only care about the former, it is
; sufficient to collect only suggfn.  Regardless of how we came to it,
; its suggestion is the induction we did and that induction is
; logically justified by suggfn's admission.  But if we want to inform
; the user of everything in the database that is steering us toward
; this proof, we are obliged not only to report suggfn and rule but
; the rules used to relieve the hypotheses of rule.

; The latter can only be done with the explicit involvement of the
; theorem proving code.  I see no way to recover the heuristic
; information leading to the suggestion of suggfn without modifying
; the basic sources.  Such a modification would consist of collecting
; the dependencies used to reach the suggfn and putting them in the
; induction candidate.  Such dependencies would be collected for all
; the suggested candidates and then when the winning candidate is
; finally chosen, we could extract from it the rules used to lead us
; to it.

; But note the fundamental violation of the tracing premise I'm pursuing,
; i.e., that dependency information can be obtained by some meta-level
; tracking device: I just proposed to collect the dependencies used to
; reach suggfn and PUT THEM INTO A DATA STRUCTURE being manipulated by
; the theorem prover.  This is what ACL2 does and I see no way around
; it!  It's as avoidable as it is only because most of the time ACL2
; does not do too much search.  But here we see the prover collecting
; all possible suggestions, using type-set reasoning to help it, and
; then choosing among them long after any kind of dynamic tracking
; information will have been lost.

; Punchline:  I've reached two conclusions.

; First, Paco will collect only enough information to justify the
; logical soundness of its proof, e.g., suggfn, not rule and its
; type-set supporters.  That is because the code already puts the
; suggesting term into the candidate.  (That is probably unnecessary
; and is a vestige of the ACL2 origins of Paco; but unlike rule and
; the type information, at least we know locally (lexically) what
; suggfn is so it is no big deal.  Tracking the type rules used in an
; arbitrary call of type-set is non-local.)

; Second, we made the right decision in ACL2, to make dependency
; tracking an explicit part of the code.  While the ubiquitous
; presence of ttrees and multiple values is ugly, there are times when
; it is necessary.  If I were to re-do ACL2, I would consider more
; transparent idioms for tracking dependencies (sort of like the
; nearly-transparent idioms ACL2 uses to track ``errors'' with er-let*
; and value).  But I would accept upfront that I dependency tracking
; is part of my responsibility.

;-----------------------------------------------------------------
; Section:  Mapping Numes to Runes

; The map below is an ACL2-style enabled structure and is accessed by
; ACL2's enabled-numep not Paco's.  The map is defined in Paco's
; database.lisp file, where I transfer ACL2 rules to Paco's world.
; Paco rules only contain numes (to minimize the traffic when
; different processors are communicating state) and this code converts
; them to runes.

(defun nume-to-rune (nume map)

  (declare (xargs :mode :program))
; Look up nume in the map and return the corresponding rune.  If nume is
; not a nume, return nil.

  (let ((temp (if (and (integerp nume)
                       (<= 0 nume))
                  (acl2::enabled-numep nume map)
                nil)))
    (if (consp temp) temp nil)))

(defun numes-to-runes (numes map)
  (declare (xargs :mode :program))
  (cond ((endp numes) nil)
        (t (let ((rune (nume-to-rune (car numes) map)))
             (cond (rune (cons rune (numes-to-runes (cdr numes) map)))
                   (t (numes-to-runes (cdr numes) map)))))))

;-----------------------------------------------------------------
; Section:  Turning Clauses into Formulas

; This is a rudimentary ``printer'' for clauses, turning ((not p) (not q) r)
; into the more attractive (implies (and p q) r).

(defun make-not (term)
  (cond ((and (consp term)
              (eq (car term) 'NOT))
         (cadr term))
        (t (list 'NOT term))))

(defun make-not-lst (lst)
  (cond ((endp lst) nil)
        (t (cons (make-not (car lst))
                 (make-not-lst (cdr lst))))))

(defun prettyify-clause (cl)
  (cond ((endp cl) nil)
        ((endp (cdr cl)) (car cl))
        ((endp (cddr cl))
         `(implies ,(make-not (car cl)) ,(cadr cl)))
        (t `(implies (and ,@(make-not-lst (all-but-last cl)))
                     ,(car (last cl))))))

;-----------------------------------------------------------------
; Section:  The Nume Tracker

; This section should be ignored for a long time.  It uses one of the
; most obscure and strange aspects of ACL2, namely wormholes, to
; implement a macro that allows me to track the use of rules without
; cluttering my code.

; I create a wormhole named nume-stack into which I squirrel away
; the nume stack.  I then implement the basic operations on that
; stack, as described above.

; The wormhole status of the nume-stack will be (:ENTER . stack).

(defun reset-nume-stack nil
  (wormhole-eval 'nume-stack
                 '(lambda ()
                    (make-wormhole-status nil :ENTER nil))
                 nil))

(defun push-nume-frame (nume)
  (wormhole-eval 'nume-stack
                 '(lambda (whs)
                    (set-wormhole-data whs
                                       (cons (if nume
                                                 (list nume)
                                                 nil)
                                             (wormhole-data whs))))
                 nil))
  
(defun push-nume (nume)
  (if nume
      (wormhole-eval 'nume-stack
                     '(lambda (whs)
                        (set-wormhole-data whs
                                           (cons (cons nume (car (wormhole-data whs)))
                                                 (cdr (wormhole-data whs)))))
                     nil)
      nil))

(defun pop-nume-frame-nil ()
  (wormhole-eval 'nume-stack
                 '(lambda (whs)
                    (set-wormhole-data whs
                                       (cdr (wormhole-data whs))))
                 nil))

(defun pop-nume-frame-t ()
  (wormhole-eval 'nume-stack
                 '(lambda (whs)
                    (set-wormhole-data whs
                                       (cons (union-equal (car (wormhole-data whs))
                                                          (cadr (wormhole-data whs)))
                                             (cddr (wormhole-data whs)))))
                 nil))

; And here is the basic macro that annotates a piece of code with
; nume stack handling.  See the example after the macro.

(defmacro with-nume-tracking (vars body nume test)
  (let* ((vars (if (symbolp vars)
                   (list vars)
                 vars))
         (binder
          (cond ((and (consp vars)
                      (endp (cdr vars)))
                 `(let ((,(car vars) ,body))))
                (t `(mv-let ,vars ,body))))
         (ans (cond ((and (consp vars)
                          (endp (cdr vars)))
                     (car vars))
                    (t `(mv ,@vars)))))
    (cond
     ((eq nume :RESET) ; ignore the test
      `(prog2$ (reset-nume-stack) ,body))
     ((eq nume :NO-FRAME) ; push nothing but eval the test
      `(,@binder (prog2$ ,test ,ans)))
     ((eq test t) ; unconditional push nume
      `(prog2$ (push-nume ,nume) ,body))
     (t `(prog2$ (push-nume-frame ,nume)
                 (,@binder
                  (prog2$ (if ,test
                              (pop-nume-frame-t)
                            (pop-nume-frame-nil))
                          ,ans)))))))

; Given all of the foregoing, the form

; (with-nume-tracking (x y z)                      ; vars
;                     <body>                       ; body
;                     (access rule r :nume)        ; nume
;                     (not (equal y *ts-unknown))) ; test


; where <body> is a form that returns 3 values, i.e., (mv x y z), is
; logically equivalent to <body>.

; However, before <body> is evaluated, a new frame is pushed onto a
; secret stack, called the ``nume stack'' and this frame contains the
; nume produced by (access rule r :nume).  Then <body> is evaluated
; and returns (mv x y z).  Then the test, in this case (not (equal y
; *ts-unknown*)), is evaluated and if it is non-nil, the top-most
; frame of the nume stack is popped and unioned into the frame below
; it.  If, on the other hand, the test evaluates to nil, the frame is
; popped and discarded.

; There are a handful of special cases signaled by special values of
; nume and test and implemented above in the defmacro.

; If the nume is :RESET, we reset the nume stack to nil.

; If the nume is :NO-FRAME, we do not push a frame and we do not pop
; one afterwards.

; If the test is t, we do not bother to create a new frame and just
; push the given nume into the current top frame.

; To avoid cluttering the code with such forms, I tend to define
; macros with names like <foo-id> and then write (<foo-id> <body>).
; These expressions are logically equivalent to <body> but track
; the numes as described.  

(defmacro <scons-term-id> (body)
  `(with-nume-tracking (hitp val) ,body
                       (fn-nume fn :executable-counterpart wrld)
                       t))

(defmacro <type-set-id> (body)
  `(with-nume-tracking (ans-ts) ,body
                       (access recognizer-tuple recog-tuple :nume)
                       (not (ts= ans-ts *ts-unknown*))))

(defmacro <assume-true-false-id> (body)
  `(with-nume-tracking (mbt mbf tta fta) ,body
                       (access recognizer-tuple recog-tuple :nume)
                       t))

(defmacro <type-set-with-rule-id> (body)
  `(with-nume-tracking (ans-ts ans-type-alist) ,body
                       (access type-prescription tp :nume)
                       (not (ts= ans-ts *ts-unknown*))))

(defmacro <reduce-id> (body)
  `(with-nume-tracking (tuple) ,body
                       :NO-FRAME
                       (and tuple
                            (not (push-nume
                                  (access recognizer-tuple tuple :nume))))))

(defmacro <rewrite-id> (body)
  `(with-nume-tracking (new-term) ,body
                       (fn-nume :executable-counterpart fn wrld)
                       t))

(defmacro <rewrite-with-lemmas1-id> (body)
  `(with-nume-tracking (hitp new-term) ,body
                       (access rewrite-rule (car lemmas) :nume)
                       hitp))


(defmacro <built-in-clausep2-id> (body)
  `(with-nume-tracking (ans) ,body
                       (access built-in-clause (car bic-lst) :nume)
                       t))

(defmacro <convert-type-set-to-term-lst-id> (body)
  `(with-nume-tracking (ans) ,body
                       (access type-set-inverter-rule (car rules) :nume)
                       t))

(defmacro <apply-generalize-rule-id> (body)
  `(with-nume-tracking (flg term) ,body
                       (access generalize-rule gen-rule :nume)
                       t))

(defmacro <apply-instantiated-elim-rule-id> (body)
  `(with-nume-tracking (new-cl elim-vars) ,body
                       (access elim-rule rule :nume)
                       t))

(defmacro <induct-clause-id> (body)
  `(with-nume-tracking (signal clauses hist-obj pspv) ,body
                       (fn-nume :induction
                                (ffn-symb
                                 (access candidate
                                         winning-candidate
                                         :induction-term))
                                wrld)
                       t))

(defun apply-waterfall-process-msg-and-pop-nume-frame-t (acl2::state)
  (declare (xargs :mode :program
                  :stobjs (acl2::state)))
  (prog2$
   (LET ((process  (nth 0 (@ wormhole-input)))
         (id       (nth 1 (@ wormhole-input)))
         (cl       (nth 2 (@ wormhole-input)))
         (clauses  (nth 3 (@ wormhole-input)))
         (hist-obj (nth 4 (@ wormhole-input)))
         (NAMES 
          (REMOVE-DUPLICATES-EQUAL
           (STRIP-CADRS
            (NUMES-TO-RUNES
             (car (wormhole-data (@ wormhole-status)))
             (@ ACL2::NUME-TO-RUNE-MAP))))))
     (CW
      "Subgoal ~x0~%~
            ~q1~
            ~@2 ~x3 goal~#4~[~/s~]~#5~[~/, using ~&6~].~%~%"
      ID
      (PRETTYIFY-CLAUSE CL)
      (CASE PROCESS
        (APPLY-HINTS-CLAUSE "Apply hints to produce")
        (SIMPLIFY-CLAUSE "Simplify to produce")
        (SETTLED-DOWN-CLAUSE "This clause has settled down to")
        (ELIMINATE-DESTRUCTORS-CLAUSE
         "Eliminate destructors to produce")
        (INDUCT-CLAUSE
         (CONS "Induct according to ~xa to produce"
               (LIST
                (CONS #\a
                      (CAR (CDR (ASSOC :SUGGESTORS
                                       hist-obj
                                       )))))))
        (OTHERWISE "Apply unknown process to produce"))
      (LEN clauses)
      (IF (AND (NOT (ENDP clauses))
               (ENDP (CDR clauses)))
          0 1)
      (IF (ENDP NAMES) 0 1)
      NAMES))
   (er-progn
    (assign wormhole-status
            (set-wormhole-data (@ wormhole-status)
                               (cons (union-equal (car (wormhole-data (@ wormhole-status)))
                                                  (cadr (wormhole-data (@ wormhole-status))))
                                     (cddr (wormhole-data (@ wormhole-status))))))
    (value :q))))

(defmacro <apply-waterfall-process-id> (body)
  `(prog2$
    (push-nume-frame nil)
    (mv-let
     (signal clauses hist-obj pspv)
     ,body
     (cond
      ((eq signal 'hit)
       (prog2$
        (wormhole
         'nume-stack
         '(lambda (whs) whs)
         (list process id cl clauses hist-obj)
         '(apply-waterfall-process-msg-and-pop-nume-frame-t acl2::state)
         :ld-prompt nil
         :ld-verbose nil
         :ld-pre-eval-print nil)
        (mv signal clauses hist-obj pspv)))
      (t (prog2$
          (pop-nume-frame-nil)
          (mv signal clauses hist-obj pspv)))))))

(defun prove-summary-msg (acl2::state)
  (declare (xargs :mode :program
                  :stobjs (acl2::state)))
  (prog2$
   (cw "~x0~%"
       (CONS 'SUMMARY
             (NUMES-TO-RUNES
              (CAR (wormhole-data (@ wormhole-status)))
              (@ ACL2::NUME-TO-RUNE-MAP))))
   (value :q)))

(defmacro <prove-id> (body)
  `(let ((proof-attempt
          (with-nume-tracking (proof-attempt) ,body :RESET t)))
     (prog2$
      (wormhole 'nume-stack
                '(lambda (whs) whs)
                nil
                '(prove-summary-msg acl2::state)
                :ld-prompt nil
                :ld-verbose nil
                :ld-pre-eval-print nil)
      proof-attempt)))