lean-migrate / lean /PathSpec.lean
Hrushi's picture
Upload folder using huggingface_hub
bf9c466 verified
-- File: lean/PathSpec.lean
-- Path Canonicalization Specification
-- Agents migrate a C path library to Rust (Vec<String> segment lists).
-- Path = List String where each element is a non-empty segment (no "/" markers).
namespace PathSpec
abbrev Path := List String
-- Internal: reversed accumulator, head = most recently added segment
def normalizeAux (acc : Path) : PathPath
| [] => acc.reverse
| seg :: rest =>
if seg == "." then normalizeAux acc rest
else if seg == ".." then
match acc with
| [] => normalizeAux [] rest -- clamp at root
| _ :: acc' => normalizeAux acc' rest
else normalizeAux (seg :: acc) rest
-- Remove "." segments; resolve ".." clamped at root
def normalize (p : Path) : Path := normalizeAux [] p
-- Join two segment lists: normalize the concatenation
def join (base rel : Path) : Path := normalize (base ++ rel)
-- Path depth = segment count
def depth (p : Path) : Nat := p.length
-- A path is in normal form if it contains no "." or ".." segments
def isNormal (p : Path) : Bool :=
p.all (fun s => s != "." && s != "..")
-- Helper: elements of normalizeAux output are never "." or ".."
theorem normalizeAux_no_special (acc p : Path)
(hacc : ∀ s ∈ acc, s ≠ "." ∧ s ≠ "..") :
∀ s ∈ normalizeAux acc p, s ≠ "." ∧ s ≠ ".." := by
induction p generalizing acc with
| nil =>
intro s hs
simp only [normalizeAux] at hs
exact hacc s (List.mem_reverse.mp hs)
| cons seg rest ih =>
intro s hs
cases h1 : (seg == ".")
· -- (seg == ".") = false
cases h2 : (seg == "..")
· -- (seg == "..") = false — seg is normal, pushed onto acc
simp only [normalizeAux, h1, h2] at hs
apply ih (seg :: acc) _ s hs
intro t ht
rcases List.mem_cons.mp ht with rfl | ht'
· exactfun heq => by simp [heq] at h1, fun heq => by simp [heq] at h2⟩
· exact hacc t ht'
· -- (seg == "..") = true — pop acc
cases acc with
| nil =>
simp only [normalizeAux, h1, h2, ↓reduceIte] at hs
exact ih [] (fun _ hm => by simp at hm) s hs
| cons x acc' =>
simp only [normalizeAux, h1, h2, ↓reduceIte] at hs
exact ih acc' (fun t ht => hacc t (List.mem_cons.mpr (Or.inr ht))) s hs
· -- (seg == ".") = true — skip
simp only [normalizeAux, h1, ↓reduceIte] at hs
exact ih acc hacc s hs
-- Helper: normalizeAux on a path with no special segments equals acc.reverse ++ p
theorem normalizeAux_on_normal (acc p : Path)
(hp : ∀ s ∈ p, s ≠ "." ∧ s ≠ "..") :
normalizeAux acc p = acc.reverse ++ p := by
induction p generalizing acc with
| nil => simp [normalizeAux]
| cons seg rest ih =>
obtain ⟨hne1, hne2⟩ := hp seg (List.mem_cons.mpr (Or.inl rfl))
have h1 : (seg == ".") = false := beq_eq_false_iff_ne.mpr hne1
have h2 : (seg == "..") = false := beq_eq_false_iff_ne.mpr hne2
simp only [normalizeAux, h1, h2]
rw [ih (seg :: acc) (fun s hs => hp s (List.mem_cons.mpr (Or.inr hs)))]
simp [List.reverse_cons, List.append_assoc]
-- Normalization is idempotent
theorem normalize_idempotent (p : Path) : normalize (normalize p) = normalize p := by
unfold normalize
rw [normalizeAux_on_normal []
(normalizeAux [] p)
(normalizeAux_no_special [] p (fun _ h => by simp at h))]
simp
-- DRT checks (native_decide: ground equalities)
example : normalize ["usr", "local", "..", "bin"] = ["usr", "bin"] := by native_decide
example : normalize [".", "a", ".", "b"] = ["a", "b"] := by native_decide
example : normalize ["..", "a"] = ["a"] := by native_decide
example : normalize ["a", "b", "..", ".."] = [] := by native_decide
example : normalize ["a", ".", "b", "..", "c"] = ["a", "c"] := by native_decide
example : join ["usr", "local"] ["bin"] = ["usr", "local", "bin"] := by native_decide
example : join ["a", "b"] [".."] = ["a"] := by native_decide
example : depth ["a", "b", "c"] = 3 := by native_decide
example : depth [] = 0 := by native_decide
end PathSpec