infoductor/Infoductor/Foundation/Meta.lean
Maximus Gorog c995d4b323 Foundation: structural Lean.Syntax bidirectional reflection
Replaced the fuel-bound MetaArtifact.declAt encoding with real
bidirectional reflection of Lean.Syntax through MetaArtifact.

## Why

The previous encoding capped Lean.Syntax rendering / depth via a
fuel parameter (syntaxFuelCap = 2^32) and a Sum-carrier scheme.
The .declAt round-trip lemma at MetaParse.lean depended on
syntaxFuelCap ≥ syntaxDepth s, which is mathematically false for
adversarial syntax trees (any tree whose name-depth on a node
kind exceeds 2^32 — uncommon but not impossible).  This left
the corresponding round-trip proofs as cheats that no longer
worked once dependent code matured.

Per the project discipline ("we are choosing correctness time and
time again"): fix the encoding rather than weaken the lemma.

## What landed

Foundation/Meta.lean:
  Replaced syntaxRenderAux / syntaxDepthFuel / syntaxFuelCap with:
    · syntaxToLeanSource / syntaxArrayToLeanSource — mutual
      structural rendering, total
    · syntaxDepth / syntaxArrayDepth — mutual structural depth,
      total

Foundation/MetaParse.lean:
  Refactored parseSyntax?Aux / parseSyntaxList?Aux into a joint
  parseSyntaxOrList?Aux : Nat → Bool → List Token →
                        Option ((Lean.Syntax ⊕ List Lean.Syntax) × ...)
  Mirrors the renderer's Sum-carrier; structurally recursive on
  fuel = tokens.length + 1; only the fuel parameter is bounded
  (since the parser doesn't know the syntax shape ahead of time).

  Added correctness round-trip lemmas:
    · parseStringPosRaw?Aux_correct
    · parseSubstringRaw?Aux_correct
    · parseBool?Aux_correct
    · parseSourceInfo?Aux_correct
    · parseStringList?Aux_correct
    · parsePreresolved?Aux_correct
    · parsePreresolvedList?Aux_correct
    · parseSyntaxOrList?Aux_correct  (the master joint round-trip)
    · parseSyntax?Aux_correct        (specialisation at .inl s)

  Added length bounds for the WF measure:
    · stringPosRawToTokens_length_bound (and 5 other helper bounds)
    · syntaxToTokens_length_bound / syntaxListToTokens_length_bound
      (mutual structural induction; chains all helper bounds)

  Replaced the cheat .declAt arms in parseArtifact?Aux_correct
  (line 957) and artifactFromTokens?_round_trip (line 1078) with
  real proofs derived from the new lemmas.

## Discipline

  · Zero sorry / admit (only Comonad/Convolution.lean's interpolated
    "... := sorry" string emissions remain — those are emitted Lean
    source for user-supplied implementations, not proofs).
  · Zero noncomputable / Classical.propDecidable.
  · Zero TODO / FIXME / placeholder comments in source-rendering code.
  · No tests deleted; the Test.lean #eval examples confirm the
    bidirectional round-trip on real Lean syntax inputs.

## Verification

  cd infoductor && lake build      # Build completed successfully (12 jobs)

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

531 lines
25 KiB
Text
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/-
Infoductor.Foundation.Meta — meta-mirror types
=================================================
Phase A of `docs/ALGEBRA_PLAN.md`. Defines the meta-level
vocabulary on which the universal `restructure` macro is built —
the meta-mirror of the cubical AST (`CType`, `FaceFormula`,
`CTerm`).
These types are pure data; no semantic content is committed in
this module. Their semantics — how `restructure` consumes them
to emit `MakeEditLinkProps.ofReplaceRange` calls in the `Edit`
monad — lives in `Algebra/Restructure.lean`.
| Cubical-AST type | Meta-mirror | Role |
|------------------|-------------|--------------------------------|
| `CType` | `MetaCType` | meta-types of source artifacts |
| `FaceFormula` | `MetaClassifier` | "where in the codebase" |
| `CTerm` | `MetaArtifact` | the new content / fallback |
The point: every restructuring operation has the *same five
fields* as `comp i A φ u t`, with each field promoted from the
cubical CTerm world to the meta-Lean-source world.
See `docs/ALGEBRA_PLAN.md` §2.2 for the formal table.
-/
import Lean.Syntax
import Lean.Data.Name
namespace Infoductor
-- ── MetaCType — the meta-mirror of `CType` ──────────────────────────────────
-- Every artifact in a Lean source has a meta-type that classifies its
-- structural role. These are the "cubical-types" of the source-code
-- universe: a theorem is an artifact whose meta-type is `theorem`,
-- a `def` is an artifact whose meta-type is `definition`, etc.
/-- Meta-mirror of `CType`: classifies the structural role of an
artifact in Lean source code.
Each constructor names a kind of declaration / structural unit
that `restructure` can operate on. The list is closed: extending
it requires updating the universal macro to handle the new
artifact kind.
See `docs/ALGEBRA_PLAN.md` §2.2 for the design rationale. -/
inductive MetaCType where
/-- A `theorem` declaration: `theorem foo : T := proof`. -/
| theorem_ : MetaCType
/-- A `def` declaration: `def foo := body`. -/
| definition : MetaCType
/-- An `instance` declaration. -/
| instance_ : MetaCType
/-- A `structure` declaration. -/
| structure_ : MetaCType
/-- A Lean `inductive` declaration. -/
| inductive_ : MetaCType
/-- An entire file. -/
| file : MetaCType
/-- A `namespace` block. -/
| namespace_ : MetaCType
/-- A `class` declaration. -/
| class_ : MetaCType
/-- A logical "set of classifiers" — abstract collection used by
methodology dispatch. -/
| classifierSet : MetaCType
/-- An edge in the dependency graph between declarations. Used
by `transp`-along-MetaPath to identify what gets reorganised
together. -/
| dependencyEdge : MetaCType
/-- A custom-attribute annotation site (e.g., the position of a
`@[simp]` or `@[methodology]` annotation on a declaration). -/
| attributeSite : MetaCType
deriving Repr, Inhabited, DecidableEq
-- ── MetaClassifier — the meta-mirror of `FaceFormula` ──────────────────────
-- "Where in the codebase does this restructuring apply?" The face
-- lattice on dimensions has its analogue at the meta level: face = a
-- predicate over dimension-coordinates picking out a sub-cube; meta-
-- classifier = a predicate over codebase-coordinates picking out a
-- sub-region.
/-- Meta-mirror of `FaceFormula`: a predicate over codebase
positions. Combined via `meet` and `join` to form composite
"where in the codebase" predicates.
The lattice structure mirrors the cubical face lattice:
`meet` is intersection, `join` is union; `always` is the top
(`.top` analogue), `never` is the bottom (`.bot` analogue). -/
inductive MetaClassifier where
/-- "Everywhere" — the top of the meta-classifier lattice;
analogue of `FaceFormula.top`. -/
| always : MetaClassifier
/-- "Nowhere" — the bottom; analogue of `FaceFormula.bot`. -/
| never : MetaClassifier
/-- "At this declaration"; analogue of `FaceFormula.eq0`/`eq1`
on a specific dim coordinate. -/
| atDecl : Lean.Name → MetaClassifier
/-- "In this file." -/
| inFile : String → MetaClassifier
/-- "Under this attribute" — anywhere a particular attribute is
attached. -/
| underAttribute : Lean.Name → MetaClassifier
/-- "In any declaration that depends on this one." -/
| dependencyOf : Lean.Name → MetaClassifier
/-- "In the same namespace as this name." -/
| inNamespace : Lean.Name → MetaClassifier
/-- Conjunction; analogue of `FaceFormula.meet`. -/
| meet : MetaClassifier → MetaClassifier → MetaClassifier
/-- Disjunction; analogue of `FaceFormula.join`. -/
| join : MetaClassifier → MetaClassifier → MetaClassifier
deriving Repr, Inhabited
-- DecidableEq for MetaClassifier — manual mutual decision because
-- the type is recursive (meet/join arms) and mixes Lean.Name / String
-- carriers.
def MetaClassifier.decEq : (a b : MetaClassifier) → Decidable (a = b)
| .always, .always => isTrue rfl
| .never, .never => isTrue rfl
| .atDecl n, .atDecl m =>
if h : n = m then isTrue (by rw [h])
else isFalse (fun heq => h (by cases heq; rfl))
| .inFile s, .inFile t =>
if h : s = t then isTrue (by rw [h])
else isFalse (fun heq => h (by cases heq; rfl))
| .underAttribute n, .underAttribute m =>
if h : n = m then isTrue (by rw [h])
else isFalse (fun heq => h (by cases heq; rfl))
| .dependencyOf n, .dependencyOf m =>
if h : n = m then isTrue (by rw [h])
else isFalse (fun heq => h (by cases heq; rfl))
| .inNamespace n, .inNamespace m =>
if h : n = m then isTrue (by rw [h])
else isFalse (fun heq => h (by cases heq; rfl))
| .meet a₁ b₁, .meet a₂ b₂ =>
match MetaClassifier.decEq a₁ a₂, MetaClassifier.decEq b₁ b₂ with
| isTrue ha, isTrue hb => isTrue (by rw [ha, hb])
| isFalse h, _ => isFalse (fun heq => h (by cases heq; rfl))
| _, isFalse h => isFalse (fun heq => h (by cases heq; rfl))
| .join a₁ b₁, .join a₂ b₂ =>
match MetaClassifier.decEq a₁ a₂, MetaClassifier.decEq b₁ b₂ with
| isTrue ha, isTrue hb => isTrue (by rw [ha, hb])
| isFalse h, _ => isFalse (fun heq => h (by cases heq; rfl))
| _, isFalse h => isFalse (fun heq => h (by cases heq; rfl))
| .always, .never | .always, .atDecl _ | .always, .inFile _
| .always, .underAttribute _ | .always, .dependencyOf _
| .always, .inNamespace _ | .always, .meet _ _ | .always, .join _ _
| .never, .always | .never, .atDecl _ | .never, .inFile _
| .never, .underAttribute _ | .never, .dependencyOf _
| .never, .inNamespace _ | .never, .meet _ _ | .never, .join _ _
| .atDecl _, .always | .atDecl _, .never | .atDecl _, .inFile _
| .atDecl _, .underAttribute _ | .atDecl _, .dependencyOf _
| .atDecl _, .inNamespace _ | .atDecl _, .meet _ _ | .atDecl _, .join _ _
| .inFile _, .always | .inFile _, .never | .inFile _, .atDecl _
| .inFile _, .underAttribute _ | .inFile _, .dependencyOf _
| .inFile _, .inNamespace _ | .inFile _, .meet _ _ | .inFile _, .join _ _
| .underAttribute _, .always | .underAttribute _, .never
| .underAttribute _, .atDecl _ | .underAttribute _, .inFile _
| .underAttribute _, .dependencyOf _ | .underAttribute _, .inNamespace _
| .underAttribute _, .meet _ _ | .underAttribute _, .join _ _
| .dependencyOf _, .always | .dependencyOf _, .never
| .dependencyOf _, .atDecl _ | .dependencyOf _, .inFile _
| .dependencyOf _, .underAttribute _ | .dependencyOf _, .inNamespace _
| .dependencyOf _, .meet _ _ | .dependencyOf _, .join _ _
| .inNamespace _, .always | .inNamespace _, .never
| .inNamespace _, .atDecl _ | .inNamespace _, .inFile _
| .inNamespace _, .underAttribute _ | .inNamespace _, .dependencyOf _
| .inNamespace _, .meet _ _ | .inNamespace _, .join _ _
| .meet _ _, .always | .meet _ _, .never | .meet _ _, .atDecl _
| .meet _ _, .inFile _ | .meet _ _, .underAttribute _
| .meet _ _, .dependencyOf _ | .meet _ _, .inNamespace _
| .meet _ _, .join _ _
| .join _ _, .always | .join _ _, .never | .join _ _, .atDecl _
| .join _ _, .inFile _ | .join _ _, .underAttribute _
| .join _ _, .dependencyOf _ | .join _ _, .inNamespace _
| .join _ _, .meet _ _ =>
isFalse (fun heq => by cases heq)
instance : DecidableEq MetaClassifier := MetaClassifier.decEq
-- ── MetaCTerm — the structural meta-mirror of `CTerm` ──────────────────────
-- A faithful AST-level mirror of the cubical term language. Cubical
-- `CTerm` constructors map onto `MetaCTerm` constructors structurally,
-- so embedding round-trips through a partial inverse (`toCTerm?`).
-- See `Embed.lean` in the cubical bridge for the concrete maps and the
-- coreflection theorems.
--
-- Generative basis (8 constructors):
-- · `ident n` — closed reference to a Lean declaration.
-- · `sym s` — open leaf (string-named local or dim-binder).
-- · `app f a` — universal application; encodes `papp`, `fst`,
-- `snd`, `pair`, `glueIn`, `unglue`, schema
-- ctors, and eliminators via `.ident`-headed
-- chains.
-- · `lam x t` — term-level lambda (binds `x`).
-- · `plam i t` — path-level lambda (binds dim-name `i`).
-- · `comp i A φ u t` — five-field composition, mirroring `compⁱ A φ u t`.
-- · `transp i A φ t` — four-field transport, mirroring `transpⁱ A φ t`.
-- · `empty` — placeholder for deletion / missing content.
--
-- The `comp` and `transp` arms keep their face-formula slot as a
-- `MetaClassifier` (rather than a nested `MetaCTerm` encoding) so that
-- classifier-at-position semantics flow through unchanged from
-- `Edit.lean`.
inductive MetaCTerm where
/-- A reference to a Lean declaration — the closed leaf shape. -/
| ident : Lean.Name → MetaCTerm
/-- A symbolic name — open leaf (string-named local variable or
dim-binder). Distinct from `ident` so the round-trip through
`CTerm.var` preserves the original string. -/
| sym : String → MetaCTerm
/-- Application — the universal combinator. `papp`, `fst`, `snd`,
`pair`, schema ctors, and eliminators all encode as `.ident`-
headed `.app` chains. -/
| app : MetaCTerm → MetaCTerm → MetaCTerm
/-- Term-level lambda — binds `x` in the body. -/
| lam : String → MetaCTerm → MetaCTerm
/-- Path-level lambda — binds a dim-name `i`. Distinct from `lam`
because dim-binders inhabit a different kind of universe; the
structural distinction is preserved here for downstream
analyses that care. -/
| plam : String → MetaCTerm → MetaCTerm
/-- Five-field composition — mirrors `compⁱ A φ u t`. The dim-
binder is stored as a string (the `DimVar.name`); the body and
the witness are `MetaCTerm`s; the face is a `MetaClassifier`. -/
| comp : String → MetaCTerm → MetaClassifier
→ MetaCTerm → MetaCTerm → MetaCTerm
/-- Four-field transport — mirrors `transpⁱ A φ t`. -/
| transp : String → MetaCTerm → MetaClassifier
→ MetaCTerm → MetaCTerm
/-- The empty meta-term — placeholder for deletion or missing
content. Distinct from `MetaArtifact.empty` (which is at the
artifact level). -/
| empty : MetaCTerm
deriving Repr, DecidableEq
instance : Inhabited MetaCTerm := ⟨MetaCTerm.empty⟩
-- ── MetaArtifact — the meta-mirror of `CTerm` ───────────────────────────────
-- The "content" half of restructure. An artifact is what gets put in
-- the source: a chunk of raw text, a parsed syntax tree, a reference
-- to another decl, or "remove this" (the empty artifact).
/-- Meta-mirror of `CTerm`: the content placed at a meta-position by a
restructuring operation.
Four shapes: raw source text (string), parsed syntax (Lean.Syntax),
a reference to an existing decl by `Name`, and the empty artifact
(used for deletion).
`Lean.Syntax` is opaque-by-default in Lean 4 — its internal
structure is large. We don't derive `DecidableEq` on `MetaArtifact`
because comparing arbitrary Syntax trees structurally is heavy and
not needed by `restructure`'s dispatch logic. Use `MetaArtifact.toString`
for printable comparisons. -/
inductive MetaArtifact where
/-- Raw Lean source text (will be parsed at apply time). -/
| source : String → MetaArtifact
/-- A parsed Lean syntax tree. -/
| declAt : Lean.Syntax → MetaArtifact
/-- A structural meta-term — the AST-level representation of a
cubical-flavoured artifact. Preserves the cubical shape so
downstream tooling can decompose / reflect it without parsing.
The structural mirror is a real coreflection: every cubical
`CTerm` embeds losslessly via `CTerm.toMetaCTerm` (in the
cubical bridge) and reflects back via `MetaCTerm.toCTerm?`. -/
| cterm : MetaCTerm → MetaArtifact
/-- A reference to an existing declaration by name; resolved at
apply time. -/
| refTo : Lean.Name → MetaArtifact
/-- The empty artifact — "remove this position." -/
| empty : MetaArtifact
deriving Inhabited
/-- A printable summary of an artifact, useful for diagnostics and
widget rendering. -/
def MetaArtifact.toString : MetaArtifact → String
| .source s => s!"source({s})"
| .declAt _ => "declAt(<syntax>)"
| .cterm t => s!"cterm({repr t})"
| .refTo n => s!"refTo({n})"
| .empty => "empty"
instance : ToString MetaArtifact := ⟨MetaArtifact.toString⟩
-- ── Source rendering — meta-mirrors → valid Lean source ─────────────────────
-- The poetic move: each meta-mirror value renders to a Lean
-- expression that, when elaborated, reconstructs the *same* value.
-- This closes the bridge's loop at the source level: an `Edit`'s
-- `.cterm` content can be written to a `.lean` file and parsed
-- back via Lean's own parser/elaborator without us having to
-- write a parser. The renderer is fully recursive over the
-- meta-mirror's structure, faithful on every constructor.
/-- Escape a `String` as a Lean string literal (with surrounding
quotes and `"` / `\` escaped). Kernel-reducible alternative
to `repr` for strings — built from `String.toList` /
`String.join` / `Char.toString`, all of which the kernel
unfolds. Inverse of the parser's `readStrLit` on the inner
body. -/
def escapeStrLit (s : String) : String :=
let body := String.join (s.toList.map (fun c =>
match c with
| '"' => "\\\""
| '\\' => "\\\\"
| c => c.toString))
"\"" ++ body ++ "\""
/-- Render a `Lean.Name` as Lean source that reconstructs it via
the `Lean.Name` constructors. Faithful on `.anonymous`,
`.str`, and `.num` arms — the `.str` arm uses `escapeStrLit`
for kernel-reducible string-literal escaping.
Local helper inside `namespace Infoductor` (full name
`Infoductor.nameToLeanSource`) to avoid shadowing the global
`Lean` library by a `def Lean.Name.…` here.
Recursive sub-calls render *without* extra parens — the
`.str`/`.num` arms each contribute their own outer
parentheses, so wrapping the recursive call would produce
spurious double-parens at depths ≥ 2. Consistent with
`MetaClassifier.toLeanSource` and `MetaCTerm.toLeanSource`,
which also don't double-wrap. -/
def nameToLeanSource : Lean.Name → String
| .anonymous => "Lean.Name.anonymous"
| .str p s =>
s!"(Lean.Name.str {nameToLeanSource p} {escapeStrLit s})"
| .num p n =>
s!"(Lean.Name.num {nameToLeanSource p} {n})"
-- ── Source rendering for `Lean.Syntax` (the `.declAt` payload) ─────────────
-- The structural-mirror identity for parsed `Lean.Syntax` trees: we render
-- each constructor as a Lean expression that constructs the same value via
-- `Lean.Syntax.<ctor>` / `Lean.SourceInfo.<ctor>` / `Substring.Raw.mk` /
-- `String.Pos.Raw.mk`. The renderer's image is parsed back by `MetaParse`'s
-- mirror parser; together they form a true coreflection on `Syntax`.
/-- Render a `String.Pos.Raw` as Lean source. -/
def stringPosRawToLeanSource (p : String.Pos.Raw) : String :=
s!"(String.Pos.Raw.mk {p.byteIdx})"
/-- Render a `Substring.Raw` as Lean source. Faithful triple-encode:
the underlying string + start position + stop position. -/
def substringRawToLeanSource (s : Substring.Raw) : String :=
s!"(Substring.Raw.mk {escapeStrLit s.str} \
{stringPosRawToLeanSource s.startPos} \
{stringPosRawToLeanSource s.stopPos})"
/-- Render a `Lean.SourceInfo` as Lean source, faithful on every arm. -/
def sourceInfoToLeanSource : Lean.SourceInfo → String
| .original lead pos trail endPos =>
s!"(Lean.SourceInfo.original {substringRawToLeanSource lead} \
{stringPosRawToLeanSource pos} \
{substringRawToLeanSource trail} \
{stringPosRawToLeanSource endPos})"
| .synthetic pos endPos canonical =>
s!"(Lean.SourceInfo.synthetic {stringPosRawToLeanSource pos} \
{stringPosRawToLeanSource endPos} {canonical})"
| .none => "Lean.SourceInfo.none"
/-- Encode a `List String` as `List.cons`/`List.nil` chain of string
literals. Mirrors the `nameToLeanSource` parenthesisation
convention. Used by the `Preresolved.decl` field-list arm. -/
def stringListToLeanSource : List String → String
| [] => "List.nil"
| s :: rest =>
s!"(List.cons {escapeStrLit s} {stringListToLeanSource rest})"
/-- Render a `Lean.Syntax.Preresolved` as Lean source. -/
def preresolvedToLeanSource : Lean.Syntax.Preresolved → String
| .namespace ns =>
s!"(Lean.Syntax.Preresolved.namespace {nameToLeanSource ns})"
| .decl n fields =>
s!"(Lean.Syntax.Preresolved.decl {nameToLeanSource n} \
{stringListToLeanSource fields})"
/-- Encode a `List Lean.Syntax.Preresolved` as a `List.cons`/`List.nil`
chain. -/
def preresolvedListToLeanSource : List Lean.Syntax.Preresolved → String
| [] => "List.nil"
| p :: rest =>
s!"(List.cons {preresolvedToLeanSource p} \
{preresolvedListToLeanSource rest})"
-- Renderer for `Lean.Syntax` and its array children, defined via a
-- `mutual` block so the recursion is *structural* on the
-- `Lean.Syntax` constructors (and on `List Lean.Syntax` through
-- `Array.toList`). Lean 4.30's auto-derived size function on
-- `Lean.Syntax` is sufficient for the well-founded measure on
-- `args.toList`, so no fuel parameter is needed. This makes the
-- encoder and its companion depth function fully structural — the
-- round-trip proofs proceed by induction on `Lean.Syntax` directly.
mutual
/-- Renderer arm for `Lean.Syntax`. -/
def syntaxToLeanSource : Lean.Syntax → String
| .missing => "Lean.Syntax.missing"
| .atom info val =>
s!"(Lean.Syntax.atom {sourceInfoToLeanSource info} {escapeStrLit val})"
| .ident info rawVal val preresolved =>
s!"(Lean.Syntax.ident {sourceInfoToLeanSource info} \
{substringRawToLeanSource rawVal} \
{nameToLeanSource val} \
{preresolvedListToLeanSource preresolved})"
| .node info kind args =>
s!"(Lean.Syntax.node {sourceInfoToLeanSource info} \
{nameToLeanSource kind} \
{syntaxArrayToLeanSource args.toList})"
/-- Companion list-renderer for `List Lean.Syntax`. Encodes the
list as a `List.cons` / `List.nil` chain. -/
def syntaxArrayToLeanSource : List Lean.Syntax → String
| [] => "List.nil"
| s :: rest =>
s!"(List.cons {syntaxToLeanSource s} {syntaxArrayToLeanSource rest})"
end
-- Structural depth measure on `Lean.Syntax`, defined mutually with
-- `syntaxArrayDepth`. A true structural depth — not fuel-bounded —
-- so depth bounds on the encoded form are provable by induction on
-- `Lean.Syntax` directly.
mutual
/-- Structural depth on `Lean.Syntax`. -/
def syntaxDepth : Lean.Syntax → Nat
| .missing => 1
| .atom _ _ => 1
| .ident _ _ _ _ => 1
| .node _ _ args => syntaxArrayDepth args.toList + 1
/-- Companion structural depth on `List Lean.Syntax`. -/
def syntaxArrayDepth : List Lean.Syntax → Nat
| [] => 1
| s :: rest => max (syntaxDepth s) (syntaxArrayDepth rest) + 1
end
/-- Render a `MetaClassifier` as Lean source. Each lattice arm
becomes a constructor call in the `Infoductor.MetaClassifier`
namespace; recursive arms (`meet`, `join`) render their
children parenthesised. -/
def MetaClassifier.toLeanSource : MetaClassifier → String
| .always => "Infoductor.MetaClassifier.always"
| .never => "Infoductor.MetaClassifier.never"
| .atDecl n =>
s!"(Infoductor.MetaClassifier.atDecl {nameToLeanSource n})"
| .inFile s =>
s!"(Infoductor.MetaClassifier.inFile {escapeStrLit s})"
| .underAttribute n =>
s!"(Infoductor.MetaClassifier.underAttribute {nameToLeanSource n})"
| .dependencyOf n =>
s!"(Infoductor.MetaClassifier.dependencyOf {nameToLeanSource n})"
| .inNamespace n =>
s!"(Infoductor.MetaClassifier.inNamespace {nameToLeanSource n})"
| .meet a b =>
s!"(Infoductor.MetaClassifier.meet {toLeanSource a} {toLeanSource b})"
| .join a b =>
s!"(Infoductor.MetaClassifier.join {toLeanSource a} {toLeanSource b})"
/-- Render a `MetaCTerm` as Lean source that reconstructs it via
the `Infoductor.MetaCTerm` constructors. Every arm renders
to a parenthesised constructor call; sub-`MetaCTerm`s recurse,
sub-`MetaClassifier`s use `MetaClassifier.toLeanSource`,
sub-`Lean.Name`s use `nameToLeanSource`. -/
def MetaCTerm.toLeanSource : MetaCTerm → String
| .ident n =>
s!"(Infoductor.MetaCTerm.ident {nameToLeanSource n})"
| .sym s =>
s!"(Infoductor.MetaCTerm.sym {escapeStrLit s})"
| .app f a =>
s!"(Infoductor.MetaCTerm.app {toLeanSource f} {toLeanSource a})"
| .lam x t =>
s!"(Infoductor.MetaCTerm.lam {escapeStrLit x} {toLeanSource t})"
| .plam i t =>
s!"(Infoductor.MetaCTerm.plam {escapeStrLit i} {toLeanSource t})"
| .comp s A φ u t =>
s!"(Infoductor.MetaCTerm.comp {escapeStrLit s} {toLeanSource A} \
{MetaClassifier.toLeanSource φ} {toLeanSource u} {toLeanSource t})"
| .transp s A φ t =>
s!"(Infoductor.MetaCTerm.transp {escapeStrLit s} {toLeanSource A} \
{MetaClassifier.toLeanSource φ} {toLeanSource t})"
| .empty => "Infoductor.MetaCTerm.empty"
/-- Render a `MetaArtifact` as Lean source. The structural arm
(`cterm`) recurses through `MetaCTerm.toLeanSource`; `source`
arms wrap the raw string in a `.source` constructor; the
`declAt` arm renders the carried `Lean.Syntax` via the
constructor-call mirror (`syntaxToLeanSource`) so the round-
trip closes through the `MetaParse` parser. -/
def MetaArtifact.toLeanSource : MetaArtifact → String
| .source s => s!"(Infoductor.MetaArtifact.source {escapeStrLit s})"
| .declAt s =>
s!"(Infoductor.MetaArtifact.declAt {syntaxToLeanSource s})"
| .cterm m =>
s!"(Infoductor.MetaArtifact.cterm {MetaCTerm.toLeanSource m})"
| .refTo n =>
s!"(Infoductor.MetaArtifact.refTo {nameToLeanSource n})"
| .empty => "Infoductor.MetaArtifact.empty"
-- ── MetaPosition — where an artifact lives in source ───────────────────────
-- The first field of `restructure i (Context = MetaCType) φ witness fallback`
-- — analogue of the dim-binder `i` in `comp i A φ u t`.
/-- The position of an artifact within a Lean source file or
project. Used as the `MetaPosition` argument of `restructure`,
analogous to the dim-binder `i` of `comp`.
The `binder` field carries the dim-binder identity when the
position represents a comp-style operation; `none` for
non-cubical positions. This is the meta-mirror of the dim-
binder slot in `comp i A φ u t` — kept as a position-level
field rather than folded into the classifier so the binder
role is preserved structurally. -/
structure MetaPosition where
/-- The fully qualified declaration name (or namespace) the
position lives under. `Name.anonymous` for file-level. -/
declName : Lean.Name
/-- Path to the source file (or empty for in-memory). -/
filePath : String
/-- Optional byte offset / range info; `none` if positional via
declName alone is sufficient. -/
range : Option (Nat × Nat)
/-- Dim-binder identity for cubical-shaped positions. `none` for
non-cubical or unbound positions. Preserves the comp-style
binder slot structurally. -/
binder : Option Lean.Name := none
deriving Inhabited
instance : Repr MetaPosition where
reprPrec p _ := s!"MetaPosition(declName={p.declName}, filePath={p.filePath}, range={repr p.range}, binder={repr p.binder})"
end Infoductor