Foundation.Meta gains an 8-constructor MetaCTerm inductive that mirrors the cubical CTerm's generic shape (ident/sym/app/lam/plam plus dedicated comp/transp arms; cubical-specific operators encode via .ident-headed .app chains). MetaArtifact picks up a .cterm arm for structure-preserving artifact content. MetaPosition gets an Option Lean.Name binder field so the dim-binder of a comp/transp can be threaded structurally instead of folded into the classifier. These additions back the cubical-bridge's Embed.lean overhaul: a real coreflection between the cubical universe (CType/CTerm/FaceFormula/ DimExpr) and the meta-mirror, with partial inverses and per-constructor round-trip theorems. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
188 lines
8.2 KiB
Text
188 lines
8.2 KiB
Text
/-
|
||
Infoductor.Foundation.Restructure — the universal macro
|
||
==========================================================
|
||
Phase B of `docs/ALGEBRA_PLAN.md`. Defines the `restructure`
|
||
operation — a function with the same five-field shape as
|
||
`comp i A φ u t`, lifted to the meta-source-code level.
|
||
|
||
Per ALGEBRA_PLAN §0:
|
||
> One macro. Built from `comp`. Aliases accrue by usage; tactics
|
||
> are search over a library that grows under structural-Path
|
||
> declarations alone.
|
||
|
||
All 32 enumerated macros from the original sketch are *frozen
|
||
partial applications* of this single `restructure`; the alias
|
||
layer in `Algebra/MacroAlias.lean` lets users (or the system)
|
||
name recurring patterns.
|
||
-/
|
||
|
||
import Infoductor.Foundation.Edit
|
||
|
||
namespace Infoductor
|
||
|
||
-- ── The universal `restructure` operation ───────────────────────────────────
|
||
|
||
/-- `restructure i ctx φ witness fallback` — the universal source-
|
||
mutation operation. Five fields, each mirroring a field of
|
||
`comp i A φ u t`:
|
||
|
||
· `i : MetaPosition` — where in source.
|
||
· `ctx : MetaCType` — meta-type of the artifact.
|
||
· `φ : MetaClassifier` — when this restructuring applies.
|
||
· `witness : MetaArtifact` — new content valid on φ.
|
||
· `fallback : MetaArtifact` — existing content off-φ.
|
||
|
||
The result is an `Edit Unit`: a single `EditOp` whose content is
|
||
`witness` (when `φ` matches at `i`) or `fallback` (otherwise).
|
||
|
||
The `ctx` argument is currently used only for documentation /
|
||
diagnostics — Phase C's `@[macroAlias]` attribute consumes it
|
||
when binding aliases. Future Phase D' may use it to dispatch
|
||
different rendering / preview modes in the widget. -/
|
||
def restructure (i : MetaPosition) (_ctx : MetaCType)
|
||
(φ : MetaClassifier) (witness fallback : MetaArtifact) : Edit Unit :=
|
||
let active := φ.atPosition i
|
||
let chosen := if active then witness else fallback
|
||
let label := if active then "hit" else "miss"
|
||
Edit.emit
|
||
{ position := i
|
||
, newContent := chosen
|
||
, oldContent := none
|
||
, description := s!"restructure φ={label} at {repr i}" }
|
||
|
||
-- ── The 32 macros as frozen aliases ─────────────────────────────────────────
|
||
-- Per ALGEBRA_PLAN §2.3 every "macro" in the original 32-item list
|
||
-- is a frozen partial application. These are the foundational
|
||
-- aliases; a downstream user / agent can register more via
|
||
-- `@[macroAlias]` (Phase C).
|
||
|
||
/-- Frozen alias: `transport_artifact i ctx w` — `restructure` with
|
||
`φ = .always`, `witness = fallback = w`. Always applies; just
|
||
relocates `w` to `i`. -/
|
||
@[reducible]
|
||
def transport_artifact (i : MetaPosition) (ctx : MetaCType) (w : MetaArtifact) :
|
||
Edit Unit :=
|
||
restructure i ctx .always w w
|
||
|
||
/-- Frozen alias: `relocate_invariant i src dst` — moves a file's
|
||
content from `src` to `dst`, classifier set to "in source file." -/
|
||
@[reducible]
|
||
def relocate_invariant (decl : Lean.Name) (src dst : String) : Edit Unit :=
|
||
restructure { declName := decl, filePath := dst, range := none }
|
||
.file (.inFile src) (.refTo decl) .empty
|
||
|
||
/-- Frozen alias: `rename_throughout x y` — emit a rename edit for
|
||
declaration `x` (witness on `.atDecl x`, fallback `.empty`). -/
|
||
@[reducible]
|
||
def rename_throughout (oldName newName : Lean.Name) : Edit Unit :=
|
||
restructure { declName := oldName, filePath := "", range := none }
|
||
.definition (.atDecl oldName) (.refTo newName) .empty
|
||
|
||
/-- Frozen alias: `define_question_shape S` — register a new question
|
||
schema as an inductive declaration. -/
|
||
@[reducible]
|
||
def define_question_shape (declName : Lean.Name) (witness : MetaArtifact) :
|
||
Edit Unit :=
|
||
restructure { declName := declName, filePath := "", range := none }
|
||
.inductive_ .always witness .empty
|
||
|
||
/-- Frozen alias: `compose_proof_fragments` — pure `restructure` with
|
||
no freezing. This is the "raw" `restructure` exposed under a
|
||
documentary name; identical in behaviour. -/
|
||
@[reducible]
|
||
def compose_proof_fragments
|
||
(i : MetaPosition) (ctx : MetaCType) (φ : MetaClassifier)
|
||
(w f : MetaArtifact) : Edit Unit :=
|
||
restructure i ctx φ w f
|
||
|
||
/-- Frozen alias: `materialize` — emit a piece of raw Lean source at
|
||
a position. The leaf operation; everything else composes from
|
||
here. -/
|
||
@[reducible]
|
||
def materialize (i : MetaPosition) (ctx : MetaCType) (src : String) :
|
||
Edit Unit :=
|
||
restructure i ctx .always (.source src) .empty
|
||
|
||
-- ── Headless interpreter: apply edits to an in-memory buffer ────────────────
|
||
-- Phase 5.3 No-LSP fallback: same operations as `lake exe
|
||
-- algebra-restructure` subcommands. Useful for batch CI runs.
|
||
|
||
/-- A simple in-memory source buffer. Maps file paths to current
|
||
contents. `applyOp` mutates the buffer in place. -/
|
||
abbrev SourceBuffer := Std.HashMap String String
|
||
|
||
/-- Apply a single `EditOp` to a source buffer. For now this is a
|
||
crude full-file overwrite when `position.range` is `none`; range-
|
||
based partial overwrites are scheduled for the LSP-backed
|
||
integration in `Algebra/EditLSP.lean`.
|
||
|
||
Returns the updated buffer. Diagnostic lines printed to
|
||
`IO.stderr` describe what changed. -/
|
||
def EditOp.apply (op : EditOp) (buf : SourceBuffer) : IO SourceBuffer := do
|
||
let path := op.position.filePath
|
||
match op.newContent with
|
||
| .source s =>
|
||
IO.eprintln s!"[restructure] writing {path} (decl {op.position.declName})"
|
||
return buf.insert path s
|
||
| .empty =>
|
||
IO.eprintln s!"[restructure] removing {path} (decl {op.position.declName})"
|
||
return buf.erase path
|
||
| .refTo n =>
|
||
IO.eprintln s!"[restructure] {path}: rename / link → {n}"
|
||
return buf
|
||
| .declAt _ =>
|
||
IO.eprintln s!"[restructure] {path}: emit syntax (LSP-bound; headless skipped)"
|
||
return buf
|
||
| .cterm t =>
|
||
IO.eprintln s!"[restructure] writing {path} (decl {op.position.declName}) — meta-term"
|
||
return buf.insert path (toString (repr t))
|
||
|
||
/-- Run an `Edit` headless: apply every emitted op in order to an
|
||
initial source buffer. Returns the final buffer plus the
|
||
computation's value. -/
|
||
def Edit.runHeadless {α : Type} (e : Edit α) (initial : SourceBuffer) :
|
||
IO (α × SourceBuffer) := do
|
||
let mut buf := initial
|
||
for op in e.ops do
|
||
buf ← op.apply buf
|
||
return (e.result, buf)
|
||
|
||
-- ── Soundness guard scaffold ────────────────────────────────────────────────
|
||
-- ALGEBRA_PLAN §3.3: every Edit passes through `preserve_typing`.
|
||
-- The full integration requires invoking `lean` on a fresh source
|
||
-- buffer; the data-level guard here checks that no edit deletes
|
||
-- a declaration that other edits in the same batch reference.
|
||
|
||
/-- The names referenced by an `EditOp` (as a `MetaArtifact.refTo`). -/
|
||
def EditOp.referenced : EditOp → List Lean.Name
|
||
| { newContent := .refTo n, .. } => [n]
|
||
| _ => []
|
||
|
||
/-- The decl-name *removed* by an `EditOp` (when content is `.empty`). -/
|
||
def EditOp.removed : EditOp → List Lean.Name
|
||
| { newContent := .empty, position := p, .. } => [p.declName]
|
||
| _ => []
|
||
|
||
/-- A "broken reference" predicate: does this batch reference any
|
||
name that the same batch removes? The structural check that
|
||
runs in any environment, headless or not. -/
|
||
def Edit.brokenRefs {α : Type} (e : Edit α) : List Lean.Name :=
|
||
let removed := e.ops.flatMap EditOp.removed
|
||
let referenced := e.ops.flatMap EditOp.referenced
|
||
referenced.filter (· ∈ removed)
|
||
|
||
/-- The structural soundness check: an `Edit` is *self-consistent*
|
||
if no op references a name another op in the batch removes.
|
||
Full type-checking of the post-edit buffer is the LSP-integrated
|
||
layer's job (`Algebra/EditLSP.lean`). -/
|
||
def Edit.selfConsistent {α : Type} (e : Edit α) : Bool :=
|
||
e.brokenRefs.isEmpty
|
||
|
||
/-- Guarded wrapper: aborts the edit if its self-consistency check
|
||
fails. Mirrors the `Edit.guarded` shape in ALGEBRA_PLAN §3.3. -/
|
||
def Edit.guarded {α : Type} [Inhabited α] (e : Edit α) :
|
||
Except String (Edit α) :=
|
||
if e.selfConsistent then .ok e
|
||
else .error s!"restructure batch breaks references: {repr e.brokenRefs}"
|
||
|
||
end Infoductor
|