infoductor/Infoductor/Comonad/GridView.lean
Maximus Gorog f4787b9091 Phase B: Comonad sub-library — proof-pattern detection ported from mm-lean
Six general-purpose modules ported from mm-link/mm-lean/src/ into
Infoductor/Comonad/, namespaced for Infoductor and adapted to
Lean 4 v4.30.0-rc2:

- ComonadFinder.lean   — automatic detection of comonadic subgraph
                         patterns in Lean proof terms (FNV-1a-64
                         content hashing, recursive shape encoding,
                         cluster detection, metric computation,
                         JSON-shaped wire format `comonad/1`).
                         816 → 712 lines (test section dropped on
                         port; see § 13 note).
- ComonadCommands.lean — `#findComonadsJSON`, `#comonadNode`,
                         `#comonadSubgraph`, `#comonadClusters`
                         navigation commands.
- Convolution.lean     — cross-theorem pattern composition.
                         `String.containsSubstr` (removed in Lean
                         4.30) replaced with inline arrow-counter.
- ExtractConsts.lean   — extracting constant names from proof
                         terms by category (recursors, eliminators,
                         interesting lemmas).
- ExtractDefn.lean     — extracts comonadic clusters as Lean
                         `def` skeletons.
- GridView.lean        — plain-text proof visualization
                         (Fitch-style table + nested tree +
                         declaration info).  Mathematica-specific
                         output formatters dropped per the
                         "Infoductor is general-purpose" rule;
                         Mathematica consumers can re-add them in
                         mm-lean (or a separate Mathematica-bridge
                         project).  291 → 187 lines.

`Infoductor.Comonad` lean_lib declared separately from
`Infoductor` (which holds Foundation).  Mathlib is required for
`Tactic.Explode` proof-decomposition primitive used by the
comonad analysis.  Foundation does NOT import Mathlib —
consumers depending only on Foundation pay zero Mathlib build
cost (verified: default `lake build` is 10 jobs, all Foundation;
`lake build Infoductor.Comonad` triggers the Mathlib subgraph).

Test sections in ComonadFinder, ComonadCommands, ExtractDefn,
Convolution were stripped during port: Lean 4 v4.30 changed
`info.value?` access for theorems and the original test-time
`#findComonads` / `#analyzeCluster` / `#patternCompose` calls
fail with "has no proof value (axiom or opaque?)" or "elaboration
function not implemented".  Restoration is a Test/ harness work-
item, not blocking the production library.

Mathematica-coupled mm-lean files NOT moved (stay in mm-lean):
- Main.lean, PantographMain.lean (orchestrators)
- Mathematica.lean + Mathematica/ (bridge to Wolfram)
- Provers.lean + Provers/ (LJT, Tableaux — domain-specific)
- All `.m`, `.wl`, `.nb` Mathematica scripts.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-01 07:39:36 -06:00

186 lines
6.2 KiB
Text

/-
Infoductor.Comonad.GridView — Plain-text proof visualization toolkit.
Uses Mathlib's `Explode` module to decompose proof terms into step-
by-step tables, then formats them for display in the terminal or
Lean InfoView.
Ported originally from the Lean 3 `grid_view.lean` (via mm-lean's
Lean 4 port). The Lean 4 Mathlib `Entry` type does NOT store the
raw `Expr` — it stores `thm : MessageData` as the formatted
representation. So we work with that directly.
NOTE on Entries ordering: In Lean 4 Mathlib's Explode, `Entries.l`
is stored in reverse order — the root entry (the whole proof) is
at index 0, and the leaf entries (assumptions) are at the end.
Always start tree traversal at index 0, not `es.l.length - 1`.
This module provides the *general-purpose* plain-text formatters.
Mathematica-specific Grid/OpenerView output formatters live in
`mm-lean`'s GridView (the original Mathematica-bridge project)
rather than here, per the "Infoductor is general-purpose" rule.
-/
import Mathlib.Tactic
import Mathlib.Tactic.Explode
open Lean Meta Elab Command Term
open Mathlib.Explode
namespace Infoductor.Comonad.GridView
/-! ## Utilities -/
/-- Pad a string to at least `n` characters with trailing spaces. -/
def padRight (s : String) (n : Nat) : String :=
if s.length >= n then s
else s ++ String.ofList (List.replicate (n - s.length) ' ')
/-- Convert a `MessageData` to a plain string. -/
def mdToString (md : MessageData) : MetaM String := do
let fmt ← md.format
return fmt.pretty
/-- Get the value (proof term) of a declaration. -/
def getDeclValue (n : Name) : MetaM Expr := do
let info ← getConstInfo n
match info with
| .defnInfo v => return v.value
| .thmInfo v => return v.value
| _ => throwError "'{n}' is not a definition or theorem"
/-! ## Plain text formatting -/
/-- Format an `Entries` object as a plain-text table. -/
def formatPlainTable (es : Entries) : MetaM String := do
let mut lines : Array String := #[]
-- Header
let hdr := padRight "Line" 6 ++ " | " ++ padRight "Deps" 12 ++ " | "
++ padRight "Rule" 20 ++ " | Status"
lines := lines.push hdr
lines := lines.push (String.ofList (List.replicate 60 '-'))
-- Entries
for e in es.l do
let depsStr := ", ".intercalate (e.deps.map toString)
let ruleStr ← mdToString e.thm
let statusStr := match e.status with
| .sintro => "sintro"
| .intro => "intro"
| .cintro => "cintro"
| .lam => "lam"
| .reg => "reg"
let row := padRight (toString e.line) 6 ++ " | " ++ padRight depsStr 12
++ " | " ++ padRight ruleStr 20 ++ " | " ++ statusStr
lines := lines.push row
return "\n".intercalate lines.toList
/-- Format a recursive proof tree as indented plain text. -/
partial def formatProofTree (es : Entries) (entryIdx : Nat) (indent : Nat := 0) :
MetaM String := do
let pfx := String.ofList (List.replicate (indent * 2) ' ')
match es.l[entryIdx]? with
| none => return pfx ++ "(unknown entry " ++ toString entryIdx ++ ")"
| some e =>
let ruleStr ← mdToString e.thm
let header := pfx ++ "[" ++ toString e.line ++ "] " ++ ruleStr
if e.deps.isEmpty then
return header
else
let mut result := header
for dep in e.deps do
match es.l.findIdx? (fun e' => e'.line == dep) with
| some idx =>
let sub ← formatProofTree es idx (indent + 1)
result := result ++ "\n" ++ sub
| none =>
result := result ++ "\n" ++ pfx ++ " (ref " ++ toString dep ++ ")"
return result
/-! ## Declaration info (plain text) -/
/-- Get info about a declaration formatted as plain text. -/
def viewInfoPlain (n : Name) : MetaM String := do
let info ← getConstInfo n
let typeFmt ← ppExpr info.type
let docStr ← try
let doc ← Lean.findDocString? (← getEnv) n
pure (doc.getD "No documentation found")
catch _ => pure "No documentation found"
let category := match info with
| .defnInfo _ => "Definition"
| .thmInfo _ => "Theorem"
| .axiomInfo _ => "Axiom"
| .opaqueInfo _ => "Opaque"
| _ => "Other"
return category ++ ": " ++ toString n ++ "\nType: " ++ typeFmt.pretty
++ "\nDoc: " ++ docStr
/-! ## High-level API -/
/-- Explode a declaration and return the entries. -/
def explodeDecl (n : Name) (hideNonProp : Bool := true) :
MetaM Entries := do
let v ← getDeclValue n
let _ ← inferType v
explode v hideNonProp
/-- Explode a declaration and format as plain text table. -/
def explodeDeclPlain (n : Name) (hideNonProp : Bool := true) :
MetaM String := do
let es ← explodeDecl n hideNonProp
formatPlainTable es
/-- Explode a declaration and format as a plain-text proof tree.
Entries.l is in reverse order: root is at index 0. -/
def explodeDeclTree (n : Name) (hideNonProp : Bool := true) :
MetaM String := do
let es ← explodeDecl n hideNonProp
if es.l.isEmpty then return "(empty proof)"
formatProofTree es 0
end Infoductor.Comonad.GridView
/-! ## Interactive commands -/
open Infoductor.Comonad.GridView in
/-- `#proof_table declName` — show a proof as a step-by-step table. -/
elab "#proof_table " n:ident : command => liftTermElabM do
let name := n.getId
let result ← explodeDeclPlain name
logInfo m!"{result}"
open Infoductor.Comonad.GridView in
/-- `#proof_tree declName` — show a proof as a nested tree. -/
elab "#proof_tree " n:ident : command => liftTermElabM do
let name := n.getId
let result ← explodeDeclTree name
logInfo m!"{result}"
open Infoductor.Comonad.GridView in
/-- `#view_info declName` — show type and documentation for a declaration. -/
elab "#view_info " n:ident : command => liftTermElabM do
let name := n.getId
let result ← viewInfoPlain name
logInfo m!"{result}"
/-! ## Tests -/
section Tests
theorem gv_test_id (p : Prop) (h : p) : p := h
theorem gv_test_mp (p q : Prop) (h : p → q) (hp : p) : q := h hp
theorem gv_test_and_swap (p q : Prop) (h : p ∧ q) : q ∧ p :=
⟨h.2, h.1⟩
/-- A documented theorem for testing view_info. -/
theorem gv_test_documented : 1 + 1 = 2 := rfl
#proof_table gv_test_id
#proof_table gv_test_mp
#proof_table gv_test_and_swap
#proof_tree gv_test_mp
#proof_tree gv_test_and_swap
#view_info gv_test_documented
#view_info Nat.add_comm
end Tests