302 lines
8.9 KiB
Text
302 lines
8.9 KiB
Text
/-
|
|
Copyright (c) 2022 Mac Malone. All rights reserved.
|
|
Released under Apache 2.0 license as described in the file LICENSE.
|
|
Authors: Mac Malone
|
|
-/
|
|
import Lean.Data.Name
|
|
import Lake.Util.Compare
|
|
|
|
open Lean
|
|
|
|
namespace Lake
|
|
|
|
export Lean (Name NameMap)
|
|
|
|
-- # Name Helpers
|
|
|
|
namespace Name
|
|
|
|
@[simp] theorem beq_rfl (n : Name) : (n == n) = true := by
|
|
simp only [BEq.beq]; induction n <;> simp [Name.beq, *]
|
|
|
|
@[simp] theorem isPrefixOf_self {n : Name} : n.isPrefixOf n = true := by
|
|
cases n <;> simp [Name.isPrefixOf]
|
|
|
|
@[simp] theorem isPrefixOf_append {n m : Name} : n.isPrefixOf (n ++ m) = true := by
|
|
simp only [HAppend.hAppend, Append.append]
|
|
induction m <;> simp [Name.isPrefixOf, Name.append, *]
|
|
|
|
/--
|
|
The propositional condition of a `Name` being well-formed.
|
|
A well-formed name has its has hash computable in the standard manner
|
|
(i.e., via the internals of `mkStr` and `mkNum`).
|
|
-/
|
|
inductive WellFormed : Name → Prop
|
|
| anonymousWff : WellFormed Name.anonymous
|
|
| strWff {n p s} : WellFormed p → n = Name.mkStr p s → WellFormed n
|
|
| numWff {n p v} : WellFormed p → n = Name.mkNum p v → WellFormed n
|
|
|
|
def WellFormed.elimStr : WellFormed (Name.str p s h) → WellFormed p
|
|
| strWff w rfl => w
|
|
|
|
def WellFormed.elimNum : WellFormed (Name.num p v h) → WellFormed p
|
|
| numWff w rfl => w
|
|
|
|
theorem eq_of_wf_quickCmpAux
|
|
(n : Name) (w : WellFormed n) (n' : Name) (w' : WellFormed n')
|
|
: Name.quickCmpAux n n' = Ordering.eq → n = n' := by
|
|
revert n'
|
|
induction w with
|
|
| anonymousWff =>
|
|
intro n' w'; cases w' <;> simp [Name.quickCmpAux, *]
|
|
| @strWff n p s _ h ih =>
|
|
intro n' w'
|
|
cases w' with
|
|
| anonymousWff =>
|
|
simp [h, Name.quickCmpAux]
|
|
| @strWff n' p' s' w' h' =>
|
|
simp only [h, h', Name.quickCmpAux]
|
|
intro h_cmp
|
|
split at h_cmp
|
|
next h_cmp_s =>
|
|
let p_eq := ih p' w' h_cmp
|
|
let s_eq := String.eq_of_compare h_cmp_s
|
|
rw [s_eq, p_eq]
|
|
next =>
|
|
contradiction
|
|
| @numWff n' p' v' w' h' =>
|
|
simp [h, h', Name.quickCmpAux]
|
|
| @numWff m p v _ h ih =>
|
|
intro n' w'
|
|
cases w' with
|
|
| anonymousWff =>
|
|
simp [h, Name.quickCmpAux]
|
|
| @strWff n' p' s' w' h' =>
|
|
simp [h, h', Name.quickCmpAux]
|
|
| @numWff n' p' v' w' h' =>
|
|
simp only [h, h', Name.quickCmpAux]
|
|
intro h_cmp
|
|
split at h_cmp
|
|
next h_cmp_v =>
|
|
let p_eq := ih p' w' h_cmp
|
|
let v_eq := Nat.eq_of_compare h_cmp_v
|
|
rw [v_eq, p_eq]
|
|
next =>
|
|
contradiction
|
|
|
|
theorem wf_of_append {n n' : Name}
|
|
(w : WellFormed n) (w' : WellFormed n') : WellFormed (n.append n') := by
|
|
induction w' with
|
|
| anonymousWff =>
|
|
simp [w, Name.append]
|
|
| @strWff n' p s w' h' ih =>
|
|
unfold Name.mkStr at h'
|
|
simp only [Name.append, h']
|
|
exact WellFormed.strWff ih rfl
|
|
| @numWff n' p v w' h' ih =>
|
|
unfold Name.mkNum at h'
|
|
simp only [Name.append, h']
|
|
exact WellFormed.numWff ih rfl
|
|
|
|
end Name
|
|
|
|
-- # Subtype Helpers
|
|
|
|
namespace Subtype
|
|
|
|
theorem val_eq_of_eq {a b : Subtype p} (h : a = b) : a.val = b.val :=
|
|
h ▸ rfl
|
|
|
|
theorem eq_of_val_eq : ∀ {a b : Subtype p}, a.val = b.val → a = b
|
|
| ⟨_, _⟩, ⟨_, _⟩, rfl => rfl
|
|
|
|
theorem eq_iff_val_eq {a b : Subtype p} : a = b ↔ a.val = b.val :=
|
|
Iff.intro val_eq_of_eq eq_of_val_eq
|
|
|
|
theorem ne_of_val_ne {a b : Subtype p} (h : a.val ≠ b.val) : a ≠ b :=
|
|
fun h' => absurd (val_eq_of_eq h') h
|
|
|
|
theorem val_ne_of_ne {a b : Subtype p} (h : a ≠ b) : a.val ≠ b.val :=
|
|
fun h' => absurd (eq_of_val_eq h') h
|
|
|
|
theorem ne_iff_val_ne {a b : Subtype p} : a ≠ b ↔ a.val ≠ b.val :=
|
|
Iff.intro val_ne_of_ne ne_of_val_ne
|
|
|
|
end Subtype
|
|
|
|
-- # Well-formed Names
|
|
|
|
|
|
/--
|
|
A `WellFormed` `Name`.
|
|
Such a name has its hash provably computed in the standard manner.
|
|
This allows us to prove that the `beq` and `quickCmp` functions' equality
|
|
corresponds to propositional equality for this subtype.
|
|
-/
|
|
abbrev WfName :=
|
|
{n : Name // Name.WellFormed n}
|
|
|
|
namespace WfName
|
|
|
|
def anonymous : WfName :=
|
|
⟨Name.anonymous, Name.WellFormed.anonymousWff⟩
|
|
|
|
instance : Inhabited WfName := ⟨anonymous⟩
|
|
|
|
def isAnonymous : WfName → Bool
|
|
| ⟨.anonymous, _⟩ => true
|
|
| _ => false
|
|
|
|
@[inline] def mkStr : WfName → String → WfName
|
|
| ⟨p, h⟩, s => ⟨Name.mkStr p s, Name.WellFormed.strWff h rfl⟩
|
|
|
|
@[inline] def mkNum : WfName → Nat → WfName
|
|
| ⟨p, h⟩, v => ⟨Name.mkNum p v, Name.WellFormed.numWff h rfl⟩
|
|
|
|
def ofName : Name → WfName
|
|
| .anonymous => anonymous
|
|
| .str p s _ => mkStr (ofName p) s
|
|
| .num p v _ => mkNum (ofName p) v
|
|
|
|
def ofString (str : String) : WfName :=
|
|
str.splitOn "." |>.foldl (fun n p => mkStr n p.trim) anonymous
|
|
|
|
@[inline] protected def toString (escape := true) : WfName → String
|
|
| ⟨n, _⟩ => n.toString escape
|
|
|
|
@[inline] protected def toStringWithSep (sep : String) (escape := true) : WfName → String
|
|
| ⟨n, _⟩ => n.toStringWithSep sep escape
|
|
|
|
instance : ToString WfName := ⟨(·.toString)⟩
|
|
|
|
def isPrefixOf : WfName → WfName → Bool
|
|
| ⟨n, _⟩, ⟨n', _⟩ => n.isPrefixOf n'
|
|
|
|
protected def append : WfName → WfName → WfName
|
|
| ⟨n, w⟩, ⟨n', w'⟩ => ⟨n.append n', Name.wf_of_append w w'⟩
|
|
|
|
instance : Append WfName := ⟨WfName.append⟩
|
|
|
|
def appendName (n : WfName) : Name → WfName
|
|
| .anonymous => n
|
|
| .str p s _ => mkStr (appendName n p) s
|
|
| .num p v _ => mkNum (appendName n p) v
|
|
|
|
instance : HAppend WfName Name WfName := ⟨appendName⟩
|
|
|
|
@[inline] protected def hash : WfName → UInt64
|
|
| ⟨n, _⟩ => n.hash
|
|
|
|
instance : Hashable WfName := ⟨WfName.hash⟩
|
|
|
|
@[inline] protected def beq : WfName → WfName → Bool
|
|
| ⟨n, _⟩, ⟨n', _⟩ => n.beq n'
|
|
|
|
instance : BEq WfName := ⟨WfName.beq⟩
|
|
|
|
theorem eq_of_beq_true : {n n' : WfName} → (n == n') = true → n = n' := by
|
|
intro ⟨n, w⟩
|
|
simp only [BEq.beq, WfName.beq, Subtype.eq_iff_val_eq]
|
|
induction w with
|
|
| anonymousWff =>
|
|
intro ⟨n', w'⟩; cases w' <;> simp [Name.beq, *]
|
|
| @strWff n p s _ h ih =>
|
|
intro ⟨n', w'⟩
|
|
simp only [Subtype.eq_iff_val_eq]
|
|
cases w' with
|
|
| anonymousWff =>
|
|
simp [Name.mkStr, Name.beq, h]
|
|
| @strWff n' p' s' w' h' =>
|
|
simp only [Name.mkStr, Name.beq, h, h']
|
|
simp only [BEq.beq, Bool.and_eq_true, decide_eq_true_eq]
|
|
intro ⟨s_eq, p_beq⟩
|
|
simp [s_eq, @ih ⟨p', w'⟩ p_beq]
|
|
| @numWff n' p' v' w' h' =>
|
|
simp only [Name.mkNum, Name.beq, h, h']
|
|
exact False.elim
|
|
| @numWff n p v _ h ih =>
|
|
intro ⟨n', w'⟩
|
|
simp only [Subtype.eq_iff_val_eq]
|
|
cases w' with
|
|
| anonymousWff =>
|
|
simp [Name.mkNum, Name.beq, h]
|
|
| @strWff n' p' s' w' h' =>
|
|
simp only [Name.mkNum, Name.beq, h, h']
|
|
exact False.elim
|
|
| @numWff n' p' v' w' h' =>
|
|
simp only [Name.mkNum, Name.beq, h, h']
|
|
simp only [BEq.beq, Bool.and_eq_true, decide_eq_true_eq]
|
|
intro ⟨n_beq, p_beq⟩
|
|
simp [Nat.eq_of_beq_eq_true n_beq, @ih ⟨p', w'⟩ p_beq]
|
|
|
|
instance : LawfulBEq WfName where
|
|
eq_of_beq := WfName.eq_of_beq_true
|
|
rfl {n} := Name.beq_rfl n
|
|
|
|
theorem ne_of_beq_false {n n' : WfName} : (n == n') = false → n ≠ n' :=
|
|
_root_.ne_of_beq_false
|
|
|
|
instance : DecidableEq WfName :=
|
|
fun n n' => match h:WfName.beq n n' with
|
|
| true => isTrue (eq_of_beq_true h)
|
|
| false => isFalse (ne_of_beq_false h)
|
|
|
|
@[inline] def quickCmp : WfName → WfName → Ordering
|
|
| ⟨n, _⟩, ⟨n', _⟩ => n.quickCmp n'
|
|
|
|
theorem eq_of_quickCmp :
|
|
{n n' : WfName} → quickCmp n n' = Ordering.eq → n = n' := by
|
|
intro ⟨n, w⟩ ⟨n', w'⟩
|
|
simp only [quickCmp, Name.quickCmp, Subtype.eq_iff_val_eq]
|
|
intro h_cmp; split at h_cmp
|
|
next => exact Name.eq_of_wf_quickCmpAux n w n' w' h_cmp
|
|
next => contradiction
|
|
|
|
instance : EqOfCmp WfName WfName.quickCmp where
|
|
eq_of_cmp h := WfName.eq_of_quickCmp h
|
|
|
|
open Syntax
|
|
|
|
def quoteNameFrom (ref : Syntax) : Name → Term
|
|
| .anonymous => mkCIdentFrom ref ``anonymous
|
|
| .str p s _ => mkApp (mkCIdentFrom ref ``mkStr)
|
|
#[quoteNameFrom ref p, quote s]
|
|
| .num p v _ => mkApp (mkCIdentFrom ref ``mkNum)
|
|
#[quoteNameFrom ref p, quote v]
|
|
|
|
protected def quoteFrom (ref : Syntax) : WfName → Term
|
|
| ⟨n, _⟩ => quoteNameFrom ref n
|
|
|
|
instance : Quote WfName := ⟨WfName.quoteFrom Syntax.missing⟩
|
|
|
|
end WfName
|
|
|
|
-- ## Notation
|
|
|
|
/--
|
|
A proven well-formed, unchecked name literal.
|
|
|
|
Lake augments name literals to produce well-formed names (`WfName`)
|
|
instead of their plain counterparts. Well-formed names have additional
|
|
properties that help ensure certain features of Lake work as intended.
|
|
-/
|
|
scoped macro:max (name := wfNameLit) "&" noWs stx:name : term =>
|
|
if let some nm := stx.raw.isNameLit? then
|
|
return WfName.quoteNameFrom stx nm
|
|
else
|
|
Macro.throwErrorAt stx "ill-formed name literal"
|
|
|
|
scoped notation "&`✝" => WfName.anonymous
|
|
|
|
@[scoped appUnexpander WfName.mkStr]
|
|
def unexpandWfNameMkStr : PrettyPrinter.Unexpander
|
|
| `($(_) &`✝ $s) => do
|
|
let some s := s.raw.isStrLit? | throw ()
|
|
let qn := quote (k := `term) <| Name.mkStr Name.anonymous s
|
|
`(&$(⟨qn.raw[0]⟩):name)
|
|
| `($(_) &$n:name $s) => do
|
|
let some s := s.raw.isStrLit? | throw ()
|
|
let some n := n.raw.isNameLit? | throw ()
|
|
let qn := quote (k := `term) <| Name.mkStr n s
|
|
`(&$(⟨qn.raw[0]⟩):name)
|
|
| _ => throw ()
|