ai-free / forth /Forth /ParseProps.lean
SNAPKITTYWEST's picture
October 2026 main drop: sync from GitHub at 8d0f04c
9ad95e5 verified
Raw History Blame Contribute Delete
56.6 kB
import Forth.Print
/-!
# Forth.ParseProps
Properties of the text parser `Forth.parse`, the converse direction of `parse_print`:
* `parse_wf`: every successful parse yields a well-formed program (`Program.WF`, including that
every `LEAVE` is inside a loop), and hence
(`parse_print_of_parse`) `print P` is a canonical text for every parseable source: it parses
back to the same program.
* Case: `tokenize_upper` (the tokens of the upper-cased text are the upper-cased tokens) and
`parse_toUpper` / `parse_toUpper_ok` / `parse_case_insensitive` (upper-casing the source does
not change the parsed program, or whether parsing fails; only the spelling quoted in an error
message can change).
* Comments: `tokenize_paren_comment`, `tokenize_line_comment`, `tokenize_line_comment_end`
(comments tokenize like whitespace), with the token-level `stripParens_close` /
`stripParens_append`.
* Dictionary words: `classify_dict`, `parseTop_constant`, `parseTop_constant_lit` and
`classify_constant` (`n CONSTANT X` drops the literal and makes `X`, in any case, the literal
`n`), `parseTop_colon` and `parseTop_defs_prefix` (the body of the `k`-th definition is parsed
as word `k`), `classify_recurse` and `parseSeq_recurse_name` (`RECURSE` is a call of the word
being defined: replacing it by the word's own name changes nothing).
-/
namespace WordDialect
namespace Forth
open Parse
namespace ParseProps
theorem bind_ok {α β : Type} {x : Except String α} {f : α → Except String β} {y : β}
(h : (x >>= f) = .ok y) : ∃ a, x = .ok a ∧ f a = .ok y := by
cases x with
| error e => cases h
| ok a => exact ⟨a, rfl, h⟩
theorem iteT {c : Prop} [Decidable c] {α : Sort _} {a b : α} (h : c) : ite c a b = a := by
simp [h]
theorem iteF {c : Prop} [Decidable c] {α : Sort _} {a b : α} (h : ¬c) : ite c a b = b := by
simp [h]
/-- Two token lists related elementwise by `R`. -/
inductive Rel2 (R : Tok → Tok → Prop) : List Tok → List Tok → Prop
| nil : Rel2 R [] []
| cons {t t' : Tok} {ts ts' : List Tok} :
R t t' → Rel2 R ts ts' → Rel2 R (t :: ts) (t' :: ts')
theorem Rel2.length_eq {R : Tok → Tok → Prop} {ts ts' : List Tok} (h : Rel2 R ts ts') :
ts.length = ts'.length := by
induction h with
| nil => rfl
| cons _ _ ih => simp [ih]
/-! ## Well-formedness -/
theorem Block.WF_mono {k k' : Nat} {d : Bool} (hk : k ≤ k') :
∀ b : Block, b.WF k d → b.WF k' d
| .nil, _ => trivial
| .op _ r, h => Block.WF_mono hk r h
| .ite t e r, h =>
⟨Block.WF_mono hk t h.1, Block.WF_mono hk e h.2.1, Block.WF_mono hk r h.2.2⟩
| .untilL x r, h => ⟨Block.WF_mono hk x h.1, Block.WF_mono hk r h.2⟩
| .call _ r, h => ⟨by have := h.1; omega, Block.WF_mono hk r h.2⟩
| .exit r, h => ⟨h.1, Block.WF_mono hk r h.2⟩
| .doLoop x r, h => ⟨Block.WF_mono hk x h.1, Block.WF_mono hk r h.2⟩
| .plusLoop x r, h => ⟨Block.WF_mono hk x h.1, Block.WF_mono hk r h.2⟩
| .leave r, h => Block.WF_mono hk r h
| .whileL c x r, h =>
⟨Block.WF_mono hk c h.1, Block.WF_mono hk x h.2.1, Block.WF_mono hk r h.2.2⟩
theorem Block.WF_append {k : Nat} {d : Bool} :
∀ a b : Block, a.WF k d → b.WF k d → (a.append b).WF k d
| .nil, _, _, hb => hb
| .op _ r, b, ha, hb => Block.WF_append r b ha hb
| .ite _ _ r, b, ha, hb => ⟨ha.1, ha.2.1, Block.WF_append r b ha.2.2 hb⟩
| .untilL _ r, b, ha, hb => ⟨ha.1, Block.WF_append r b ha.2 hb⟩
| .call _ r, b, ha, hb => ⟨ha.1, Block.WF_append r b ha.2 hb⟩
| .exit r, b, ha, hb => ⟨ha.1, Block.WF_append r b ha.2 hb⟩
| .doLoop _ r, b, ha, hb => ⟨ha.1, Block.WF_append r b ha.2 hb⟩
| .plusLoop _ r, b, ha, hb => ⟨ha.1, Block.WF_append r b ha.2 hb⟩
| .leave r, b, ha, hb => Block.WF_append r b ha hb
| .whileL _ _ r, b, ha, hb => ⟨ha.1, ha.2.1, Block.WF_append r b ha.2.2 hb⟩
theorem Block.WF_dropLastLit {k : Nat} {d : Bool} :
∀ (b b' : Block) (z : Int), b.dropLastLit = some (b', z) → b.WF k d → b'.WF k d := by
intro b
induction b with
| nil => intro b' z h; cases h
| op o r ih =>
intro b' z h hw
rw [Block.dropLastLit.eq_def] at h
split at h
· cases h
· cases h; trivial
· rename_i heq
injection heq with h1 h2
subst h1 h2
simp only [Option.map_eq_some_iff] at h
obtain ⟨⟨p1, p2⟩, hp, he⟩ := h
cases he
exact ih p1 p2 hp hw
all_goals (rename_i heq; cases heq)
| ite t e r _ _ ih =>
intro b' z h hw
simp only [Block.dropLastLit, Option.map_eq_some_iff] at h
obtain ⟨⟨p1, p2⟩, hp, he⟩ := h
cases he
exact ⟨hw.1, hw.2.1, ih _ _ hp hw.2.2⟩
| untilL x r _ ih =>
intro b' z h hw
simp only [Block.dropLastLit, Option.map_eq_some_iff] at h
obtain ⟨⟨p1, p2⟩, hp, he⟩ := h
cases he
exact ⟨hw.1, ih _ _ hp hw.2⟩
| call i r ih =>
intro b' z h hw
simp only [Block.dropLastLit, Option.map_eq_some_iff] at h
obtain ⟨⟨p1, p2⟩, hp, he⟩ := h
cases he
exact ⟨hw.1, ih _ _ hp hw.2⟩
| exit r ih =>
intro b' z h hw
simp only [Block.dropLastLit, Option.map_eq_some_iff] at h
obtain ⟨⟨p1, p2⟩, hp, he⟩ := h
cases he
exact ⟨hw.1, ih _ _ hp hw.2⟩
| doLoop x r _ ih =>
intro b' z h hw
simp only [Block.dropLastLit, Option.map_eq_some_iff] at h
obtain ⟨⟨p1, p2⟩, hp, he⟩ := h
cases he
exact ⟨hw.1, ih _ _ hp hw.2⟩
| plusLoop x r _ ih =>
intro b' z h hw
simp only [Block.dropLastLit, Option.map_eq_some_iff] at h
obtain ⟨⟨p1, p2⟩, hp, he⟩ := h
cases he
exact ⟨hw.1, ih _ _ hp hw.2⟩
| leave r ih =>
intro b' z h hw
simp only [Block.dropLastLit, Option.map_eq_some_iff] at h
obtain ⟨⟨p1, p2⟩, hp, he⟩ := h
cases he
exact ih p1 p2 hp hw
| whileL c x r _ _ ih =>
intro b' z h hw
simp only [Block.dropLastLit, Option.map_eq_some_iff] at h
obtain ⟨⟨p1, p2⟩, hp, he⟩ := h
cases he
exact ⟨hw.1, hw.2.1, ih _ _ hp hw.2.2⟩
theorem Block.WF_loopOf {k : Nat} {d : Bool} (q : Bool) {mk : Block → Block → Block}
(hmk : ∀ b r : Block, b.WF k d → r.WF k d → (mk b r).WF k d) {body rest : Block}
(hb : body.WF k d) (hr : rest.WF k d) : (Block.loopOf q mk body rest).WF k d := by
cases q
· exact hmk _ _ hb hr
· exact ⟨trivial, hmk _ _ hb trivial, hr⟩
/-- Every dictionary entry compiles to a call of a word `< k` or to a literal. -/
def DictInv (k : Nat) (dict : Dict) : Prop :=
∀ p ∈ dict, (∃ i, i < k ∧ p.2 = Block.call i) ∨ ∃ z, p.2 = Block.op (.lit z)
theorem DictInv.mono {k k' : Nat} {dict : Dict} (h : DictInv k dict) (hk : k ≤ k') :
DictInv k' dict := by
intro p hp
rcases h p hp with ⟨i, hi, he⟩ | hz
· exact .inl ⟨i, by omega, he⟩
· exact .inr hz
theorem DictInv.cons {k : Nat} {dict : Dict} (h : DictInv k dict) (u : Tok) (w : Block → Block)
(hw : (∃ i, i < k ∧ w = Block.call i) ∨ ∃ z, w = Block.op (.lit z)) :
DictInv k ((u, w) :: dict) := by
intro p hp
simp only [List.mem_cons] at hp
rcases hp with rfl | hp
· exact hw
· exact h p hp
theorem classify_prim_wf {k : Nat} {inDef : Bool} {dict : Dict} {self : Option Nat} {t : Tok}
{w : Block → Block} (hd : DictInv k dict)
(hs : ∀ j, self = some j → j < k ∧ inDef = true)
(h : classify dict self t = .prim w) : ∀ r : Block, r.WF k inDef → (w r).WF k inDef := by
intro r hr
simp only [classify, lookupWord] at h
by_cases h1 : upper t ∈ stops
· rw [iteT h1] at h; cases h
rw [iteF h1] at h
by_cases h2 : upper t = kIF
· rw [iteT h2] at h; cases h
rw [iteF h2] at h
by_cases h3 : upper t = kBEGIN
· rw [iteT h3] at h; cases h
rw [iteF h3] at h
by_cases h4 : upper t = kRECURSE
· rw [iteT h4] at h
cases self with
| none => cases h
| some j => cases h; exact ⟨(hs j rfl).1, hr⟩
rw [iteF h4] at h
by_cases h5 : upper t = kEXIT
· rw [iteT h5] at h
cases self with
| none => cases h
| some j => cases h; exact ⟨(hs j rfl).2, hr⟩
rw [iteF h5] at h
by_cases h6 : upper t = kDO
· rw [iteT h6] at h; cases h
rw [iteF h6] at h
by_cases h7 : upper t = kQDO
· rw [iteT h7] at h; cases h
rw [iteF h7] at h
by_cases h8 : upper t = kLEAVE
· rw [iteT h8] at h; cases h; exact hr
rw [iteF h8] at h
by_cases h9 : upper t = kQDUP
· rw [iteT h9] at h; cases h; exact ⟨trivial, trivial, hr⟩
rw [iteF h9] at h
by_cases h10 : upper t = kCASE
· rw [iteT h10] at h; cases h
rw [iteF h10] at h
by_cases h11 : upper t = kOF
· rw [iteT h11] at h; cases h
rw [iteF h11] at h
split at h
· rename_i w' hl
cases h
obtain ⟨u, hm, -⟩ := Print.lookup_some_mem hl
rcases hd _ hm with ⟨i, hi, he⟩ | ⟨z, he⟩
· simp only at he; subst he; exact ⟨hi, hr⟩
· simp only at he; subst he; exact hr
split at h
· cases h; exact hr
split at h
· cases h; exact hr
· cases h
theorem parseSeq_wf {k : Nat} {inDef : Bool} :
∀ (fuel : Nat) (dict : Dict) (self : Option Nat) (ts : List Tok) (b : Block)
(st : Option Tok) (r : List Tok), DictInv k dict →
(∀ j, self = some j → j < k ∧ inDef = true) →
parseSeq fuel dict self ts = .ok (b, st, r) → b.WF k inDef
| 0, _, _, _, _, _, _, _, _, h => by simp [parseSeq] at h
| _ + 1, _, _, [], _, _, _, _, _, h => by
simp only [parseSeq, Except.ok.injEq, Prod.mk.injEq] at h
obtain ⟨rfl, -⟩ := h; trivial
| fuel + 1, dict, self, t :: ts, b, st, r, hd, hs, h => by
simp only [parseSeq] at h
split at h
· cases h; trivial
· cases h
· rename_i w hc
obtain ⟨⟨b1, s1, r1⟩, h1, h2⟩ := bind_ok h
cases h2
exact classify_prim_wf hd hs hc _ (parseSeq_wf fuel dict self ts b1 s1 r1 hd hs h1)
· obtain ⟨⟨b1, s1, r1⟩, h1, h2⟩ := bind_ok h
have w1 := parseSeq_wf fuel dict self ts b1 s1 r1 hd hs h1
simp only at h2
split at h2
· obtain ⟨⟨b2, s2, r2⟩, h3, h4⟩ := bind_ok h2
have w2 := parseSeq_wf fuel dict self _ b2 s2 r2 hd hs h3
simp only at h4
split at h4
· obtain ⟨⟨b3, s3, r3⟩, h5, h6⟩ := bind_ok h4
have w3 := parseSeq_wf fuel dict self _ b3 s3 r3 hd hs h5
cases h6
exact ⟨w1, w2, w3⟩
· cases h4
· split at h2
· obtain ⟨⟨b3, s3, r3⟩, h5, h6⟩ := bind_ok h2
have w3 := parseSeq_wf fuel dict self _ b3 s3 r3 hd hs h5
cases h6
exact ⟨w1, trivial, w3⟩
· cases h2
· obtain ⟨⟨b1, s1, r1⟩, h1, h2⟩ := bind_ok h
have w1 := parseSeq_wf fuel dict self ts b1 s1 r1 hd hs h1
simp only at h2
split at h2
· obtain ⟨⟨b3, s3, r3⟩, h5, h6⟩ := bind_ok h2
have w3 := parseSeq_wf fuel dict self _ b3 s3 r3 hd hs h5
cases h6
exact ⟨w1, w3⟩
· split at h2
· obtain ⟨⟨b2, s2, r2⟩, h3, h4⟩ := bind_ok h2
have w2 := parseSeq_wf fuel dict self _ b2 s2 r2 hd hs h3
simp only at h4
split at h4
· obtain ⟨⟨b3, s3, r3⟩, h5, h6⟩ := bind_ok h4
have w3 := parseSeq_wf fuel dict self _ b3 s3 r3 hd hs h5
cases h6
exact ⟨w1, w2, w3⟩
· cases h4
· cases h2
· rename_i q _
obtain ⟨⟨b1, s1, r1⟩, h1, h2⟩ := bind_ok h
have w1 := parseSeq_wf fuel dict self ts b1 s1 r1 hd hs h1
simp only at h2
split at h2
· obtain ⟨⟨b3, s3, r3⟩, h5, h6⟩ := bind_ok h2
have w3 := parseSeq_wf fuel dict self _ b3 s3 r3 hd hs h5
cases h6
exact Block.WF_loopOf (mk := Block.doLoop) q (fun _ _ h1 h2 => ⟨h1, h2⟩) w1 w3
· split at h2
· obtain ⟨⟨b3, s3, r3⟩, h5, h6⟩ := bind_ok h2
have w3 := parseSeq_wf fuel dict self _ b3 s3 r3 hd hs h5
cases h6
exact Block.WF_loopOf (mk := Block.plusLoop) q (fun _ _ h1 h2 => ⟨h1, h2⟩) w1 w3
· cases h2
· obtain ⟨⟨b1, s1, r1⟩, h1, h2⟩ := bind_ok h
have w1 := parseSeq_wf fuel dict self ts b1 s1 r1 hd hs h1
simp only at h2
split at h2
· obtain ⟨⟨b3, s3, r3⟩, h5, h6⟩ := bind_ok h2
have w3 := parseSeq_wf fuel dict self _ b3 s3 r3 hd hs h5
cases h6
exact Block.WF_append _ _ w1 w3
· cases h2
· obtain ⟨⟨b1, s1, r1⟩, h1, h2⟩ := bind_ok h
have w1 := parseSeq_wf fuel dict self ts b1 s1 r1 hd hs h1
simp only at h2
split at h2
· obtain ⟨⟨b3, s3, r3⟩, h5, h6⟩ := bind_ok h2
have w3 := parseSeq_wf fuel dict self _ b3 s3 r3 hd hs h5
simp only at h6
split at h6
· cases h6
exact ⟨Block.WF_append _ _ w1 trivial, w3, trivial⟩
· cases h6
· cases h2
/-- The block conditions of `Program.WF` (all but the `LEAVE` check, which `parseTokens` makes
on the finished program). -/
def BlocksWF (P : Program) : Prop :=
(∀ i (body : Block), P.defs[i]? = some body → body.WF (i + 1) true) ∧ P.main.WF P.defs.length false
/-- The invariant of `parseTop`: the dictionary and definitions so far are well formed. -/
theorem parseTop_wf :
∀ (fuel : Nat) (dict : Dict) (defs : List Block) (vars : Nat) (main : Block) (toks : List Tok)
(P : Program), DictInv defs.length dict →
(∀ i (body : Block), defs[i]? = some body → body.WF (i + 1) true) →
main.WF defs.length false →
parseTop fuel dict defs vars main toks = .ok P → BlocksWF P
| 0, _, _, _, _, _, _, _, _, _, h => by simp [parseTop] at h
| fuel + 1, dict, defs, vars, main, toks, P, hd, hdefs, hmain, h => by
simp only [parseTop] at h
obtain ⟨⟨b, stop, rest⟩, h1, h2⟩ := bind_ok h
have wb : b.WF defs.length false :=
parseSeq_wf _ dict none toks b stop rest hd (fun j hj => by cases hj) h1
simp only at h2
split at h2
· cases h2
exact ⟨hdefs, Block.WF_append _ _ hmain wb⟩
· split at h2
· split at h2
· cases h2
· rename_i name body
obtain ⟨⟨bb, s2, r2⟩, h3, h4⟩ := bind_ok h2
have hd' : DictInv (defs.length + 1) ((upper name, Block.call defs.length) :: dict) :=
(hd.mono (by omega)).cons _ _ (.inl ⟨defs.length, by omega, rfl⟩)
have wbb : bb.WF (defs.length + 1) true :=
parseSeq_wf _ _ _ body bb s2 r2 hd'
(fun j hj => by cases hj; exact ⟨by omega, rfl⟩) h3
simp only at h4
split at h4
· refine parseTop_wf fuel _ _ vars _ r2 P (by simpa using hd') ?_ ?_ h4
· intro i body' hi
rw [List.getElem?_append] at hi
split at hi
· exact hdefs i body' hi
· rename_i hlt
cases hk : i - defs.length with
| succ n => rw [hk] at hi; simp at hi
| zero =>
rw [hk] at hi
simp only [List.getElem?_cons_zero, Option.some.injEq] at hi
subst hi
have : i = defs.length := by omega
subst this; exact wbb
· simpa using Block.WF_append _ _ (Block.WF_mono (by omega) _ hmain)
(Block.WF_mono (by omega) _ wb)
· split at h4
· cases h4
· split at h4 <;> cases h4
· split at h2
· split at h2
· cases h2
· exact parseTop_wf fuel _ defs (vars + 1) _ _ P
(hd.cons _ _ (.inr ⟨_, rfl⟩)) hdefs (Block.WF_append _ _ hmain wb) h2
· split at h2
· split at h2
· cases h2
· cases h2
· rename_i b' z name rest' hdl
exact parseTop_wf fuel _ defs vars _ _ P (hd.cons _ _ (.inr ⟨_, rfl⟩)) hdefs
(Block.WF_append _ _ hmain (Block.WF_dropLastLit _ _ _ hdl wb)) h2
· split at h2
· split at h2
· cases h2
· exact parseTop_wf fuel _ defs vars _ _ P
(hd.cons _ _ (.inr ⟨_, rfl⟩)) hdefs (Block.WF_append _ _ hmain wb) h2
· split at h2
· split at h2
· cases h2
· rename_i b' z hdl
exact parseTop_wf fuel _ defs _ _ _ P hd hdefs
(Block.WF_append _ _ hmain (Block.WF_dropLastLit _ _ _ hdl wb)) h2
· split at h2
· split at h2
· cases h2
· rename_i b' z hdl
exact parseTop_wf fuel _ defs _ _ _ P hd hdefs
(Block.WF_append _ (.op (.lit z) (.op (.lit (vars : Int)) (.op .store .nil)))
(Block.WF_append _ _ hmain (Block.WF_dropLastLit _ _ _ hdl wb)) trivial) h2
· cases h2
end ParseProps
/-- **Every successful parse yields a well-formed program.** -/
theorem parse_wf {src : String} {P : Program} (h : parse src = .ok P) : P.WF := by
unfold parse at h
obtain ⟨toks, -, h2⟩ := ParseProps.bind_ok h
unfold parseTokens at h2
obtain ⟨P', h3, h4⟩ := ParseProps.bind_ok h2
split at h4
· cases h4
obtain ⟨hd, hm⟩ := ParseProps.parseTop_wf _ [] [] 0 .nil toks _ (fun p hp => by cases hp)
(fun i b hi => by simp at hi) trivial h3
exact ⟨hd, hm, ‹_›⟩
· cases h4
/-- **`print` is a canonical form**: whatever text parses to `P`, so does `print P`. -/
theorem parse_print_of_parse {src : String} {P : Program} (h : parse src = .ok P) :
parse (print P) = .ok P :=
parse_print P (parse_wf h)
namespace ParseProps
/-! ## Case-insensitivity -/
theorem toUpper_cases (c : Char) :
c.toUpper = c ∨ (65 ≤ c.toUpper.toNat ∧ c.toUpper.toNat ≤ 90) := by
unfold Char.toUpper
split
· right
rename_i h
obtain ⟨h1, h2⟩ := h
simp only [Char.toNat] at *
simp [UInt32.le_iff_toNat_le] at h1 h2
simp [UInt32.toNat_add]
omega
· left; rfl
theorem toUpper_self {x : Char} (h : ¬(97 ≤ x.toNat ∧ x.toNat ≤ 122)) : x.toUpper = x := by
unfold Char.toUpper
split
· rename_i h'
exfalso; apply h
obtain ⟨h1, h2⟩ := h'
simp only [Char.toNat]
simp [UInt32.le_iff_toNat_le] at h1 h2
omega
· rfl
theorem toUpper_toUpper (c : Char) : c.toUpper.toUpper = c.toUpper := by
rcases toUpper_cases c with h | h
· rw [h]; exact h
· exact toUpper_self (by omega)
theorem upper_upper (t : Tok) : upper (upper t) = upper t := by
simp only [upper, List.map_map]
congr 1
funext c
exact toUpper_toUpper c
/-- A character that upper-casing fixes and no other character upper-cases to. -/
def Fixed (x : Char) : Prop := x.toUpper = x ∧ ¬(65 ≤ x.toNat ∧ x.toNat ≤ 90)
theorem toUpper_eq_fixed {c x : Char} (hx : Fixed x) : c.toUpper = x ↔ c = x := by
constructor
· intro h
rcases toUpper_cases c with h' | h'
· rw [h'] at h; exact h
· rw [h] at h'; exact absurd h' hx.2
· rintro rfl; exact hx.1
theorem fixed_space : Fixed ' ' := ⟨by decide, by decide⟩
theorem fixed_tab : Fixed '\t' := ⟨by decide, by decide⟩
theorem fixed_nl : Fixed '\n' := ⟨by decide, by decide⟩
theorem fixed_cr : Fixed '\r' := ⟨by decide, by decide⟩
theorem fixed_bs : Fixed '\\' := ⟨by decide, by decide⟩
theorem fixed_lp : Fixed '(' := ⟨by decide, by decide⟩
theorem fixed_rp : Fixed ')' := ⟨by decide, by decide⟩
theorem beq_toUpper {c x : Char} (hx : Fixed x) : (c.toUpper == x) = (c == x) := by
apply Bool.eq_iff_iff.mpr
simp only [beq_iff_eq]
exact toUpper_eq_fixed hx
theorem isWs_toUpper (c : Char) : isWs c.toUpper = isWs c := by
simp only [isWs, beq_toUpper fixed_space, beq_toUpper fixed_tab, beq_toUpper fixed_nl,
beq_toUpper fixed_cr]
theorem map_toUpper_single {cs : List Char} {x : Char} (hx : Fixed x) :
cs.map Char.toUpper = [x] ↔ cs = [x] := by
match cs with
| [] => simp
| [c] => simp [toUpper_eq_fixed hx]
| _ :: _ :: _ => simp
theorem flush_upper (cur : List Char) (rest : List Tok) :
flush (cur.map Char.toUpper) (rest.map upper) = (flush cur rest).map upper := by
unfold flush
by_cases h : cur = []
· subst h; simp
· rw [iteF (by simpa using h), iteF h]
simp [upper, List.map_reverse]
/-- The lexer commutes with upper-casing. -/
theorem lex_upper : ∀ (cs cur : List Char) (skip : Bool),
lex (cs.map Char.toUpper) (cur.map Char.toUpper) skip = (lex cs cur skip).map upper := by
intro cs
induction cs with
| nil =>
intro cur skip
simp only [List.map_nil, lex, map_toUpper_single fixed_bs]
split
· rfl
· have := flush_upper cur []
simpa using this
| cons c cs ih =>
have ih0 := fun b => ih [] b
simp only [List.map_nil] at ih0
intro cur skip
cases skip with
| true =>
simp only [List.map_cons, lex, beq_toUpper fixed_nl]
split <;> exact ih0 _
| false =>
simp only [List.map_cons, lex, isWs_toUpper, map_toUpper_single fixed_bs,
beq_toUpper fixed_nl]
split
· split
· split <;> exact ih0 _
· rw [ih0, flush_upper]
· have := ih (c :: cur) false
simpa using this
theorem getLast_upper {t : Tok} : (upper t).getLast? = some ')' ↔ t.getLast? = some ')' := by
simp only [upper, List.getLast?_map, Option.map_eq_some_iff]
constructor
· rintro ⟨a, ha, he⟩
rw [(toUpper_eq_fixed fixed_rp).mp he] at ha; exact ha
· intro h; exact ⟨_, h, fixed_rp.1⟩
theorem stripParens_upper : ∀ (inside : Bool) (ts : List Tok),
stripParens inside (ts.map upper) = (stripParens inside ts).map (·.map upper)
| false, [] => rfl
| true, [] => rfl
| false, t :: ts => by
simp only [List.map_cons, stripParens]
by_cases h : t = ['(']
· rw [iteT h, iteT (by rw [h]; rfl)]
exact stripParens_upper true ts
· rw [iteF h, iteF (show ¬upper t = ['('] from
fun e => h ((map_toUpper_single fixed_lp).mp e))]
rw [stripParens_upper false ts]
cases stripParens false ts <;> rfl
| true, t :: ts => by
simp only [List.map_cons, stripParens, getLast_upper]
split
· exact stripParens_upper false ts
· exact stripParens_upper true ts
/-- **The tokenizer commutes with upper-casing**: the tokens of the upper-cased text are the
upper-cased tokens (and the errors are the same). -/
theorem tokenize_upper (src : String) :
tokenize (src.map Char.toUpper) = (tokenize src).map (·.map upper) := by
unfold tokenize
have := lex_upper src.toList [] false
simp only [List.map_nil] at this
rw [String.toList_map, this, stripParens_upper]
/-- Two classifications agree, except possibly in an error message. -/
def ISim (i j : Item) : Prop := i = j ∨ ∃ m m', i = .bad m ∧ j = .bad m'
theorem classify_sim {dict : Dict} {self : Option Nat} {t t' : Tok} (h : upper t = upper t') :
ISim (classify dict self t) (classify dict self t') := by
simp only [classify, lookupWord, h]
by_cases h1 : upper t' ∈ stops
· rw [iteT h1, iteT h1]; exact .inl rfl
rw [iteF h1, iteF h1]
by_cases h2 : upper t' = kIF
· rw [iteT h2, iteT h2]; exact .inl rfl
rw [iteF h2, iteF h2]
by_cases h3 : upper t' = kBEGIN
· rw [iteT h3, iteT h3]; exact .inl rfl
rw [iteF h3, iteF h3]
by_cases h4 : upper t' = kRECURSE
· rw [iteT h4, iteT h4]; exact .inl rfl
rw [iteF h4, iteF h4]
by_cases h5 : upper t' = kEXIT
· rw [iteT h5, iteT h5]; exact .inl rfl
rw [iteF h5, iteF h5]
by_cases h6 : upper t' = kDO
· rw [iteT h6, iteT h6]; exact .inl rfl
rw [iteF h6, iteF h6]
by_cases h7 : upper t' = kQDO
· rw [iteT h7, iteT h7]; exact .inl rfl
rw [iteF h7, iteF h7]
by_cases h8 : upper t' = kLEAVE
· rw [iteT h8, iteT h8]; exact .inl rfl
rw [iteF h8, iteF h8]
by_cases h9 : upper t' = kQDUP
· rw [iteT h9, iteT h9]; exact .inl rfl
rw [iteF h9, iteF h9]
by_cases h10 : upper t' = kCASE
· rw [iteT h10, iteT h10]; exact .inl rfl
rw [iteF h10, iteF h10]
by_cases h11 : upper t' = kOF
· rw [iteT h11, iteT h11]; exact .inl rfl
rw [iteF h11, iteF h11]
cases dict.lookup (upper t') with
| some w => exact .inl rfl
| none =>
cases opTable.lookup (upper t') with
| some o => exact .inl rfl
| none =>
cases number (upper t') with
| some z => exact .inl rfl
| none => exact .inr ⟨_, _, rfl, rfl⟩
/-- Two `parseSeq` results agree up to error messages, with related remaining tokens. -/
def RSim (R : Tok → Tok → Prop) :
Except String (Block × Option Tok × List Tok) →
Except String (Block × Option Tok × List Tok) → Prop
| .ok (b, s, r), .ok (b', s', r') => b = b' ∧ s = s' ∧ Rel2 R r r'
| .error _, .error _ => True
| _, _ => False
/-- Two results agree up to error messages. -/
def PSim {α : Type} : Except String α → Except String α → Prop
| .ok a, .ok a' => a = a'
| .error _, .error _ => True
| _, _ => False
theorem psim_iff {α : Type} {x y : Except String α} : PSim x y ↔ x.toOption = y.toOption := by
cases x <;> cases y <;> simp [PSim, Except.toOption]
theorem rsim_bind {R : Tok → Tok → Prop} {α : Type}
{S : Except String α → Except String α → Prop}
(hS : ∀ e e', S (.error e) (.error e'))
{x x' : Except String (Block × Option Tok × List Tok)} (h : RSim R x x')
{k k' : Block × Option Tok × List Tok → Except String α}
(hk : ∀ b s r r', Rel2 R r r' → S (k (b, s, r)) (k' (b, s, r'))) :
S (x >>= k) (x' >>= k') := by
match x, x', h with
| .ok (b, s, r), .ok (b', s', r'), h =>
obtain ⟨rfl, rfl, hr⟩ := h
exact hk b s r r' hr
| .error e, .error e', _ => exact hS e e'
| .ok _, .error _, h => exact h.elim
| .error _, .ok _, h => exact h.elim
theorem parseSeq_congr {R : Tok → Tok → Prop} {dict : Dict} {self : Option Nat}
(hR : ∀ t t', R t t' → ISim (classify dict self t) (classify dict self t')) :
∀ (fuel : Nat) (ts ts' : List Tok), Rel2 R ts ts' →
RSim R (parseSeq fuel dict self ts) (parseSeq fuel dict self ts')
| 0, _, _, _ => by simp [parseSeq, RSim]
| _ + 1, [], [], _ => ⟨rfl, rfl, .nil⟩
| f + 1, t :: ts, t' :: ts', .cons ht hts => by
have ih := parseSeq_congr hR f
have hS : ∀ e e', RSim R (.error e) (.error e') := fun _ _ => trivial
simp only [parseSeq]
rcases hR t t' ht with he | ⟨m, m', h1, h2⟩
· rw [he]
cases classify dict self t' with
| stop u => exact ⟨rfl, rfl, hts⟩
| bad msg => trivial
| prim w =>
refine rsim_bind hS (ih ts ts' hts) ?_
intro b s r r' hr; exact ⟨rfl, rfl, hr⟩
| ifK =>
refine rsim_bind hS (ih ts ts' hts) ?_
intro b s r r' hr
simp only
by_cases hs1 : s = some kELSE
· rw [iteT hs1, iteT hs1]
refine rsim_bind hS (ih r r' hr) ?_
intro b2 s2 r2 r2' hr2
simp only
by_cases hs2 : s2 = some kTHEN
· rw [iteT hs2, iteT hs2]
refine rsim_bind hS (ih r2 r2' hr2) ?_
intro b3 s3 r3 r3' hr3; exact ⟨rfl, rfl, hr3⟩
· rw [iteF hs2, iteF hs2]; trivial
· rw [iteF hs1, iteF hs1]
by_cases hs2 : s = some kTHEN
· rw [iteT hs2, iteT hs2]
refine rsim_bind hS (ih r r' hr) ?_
intro b3 s3 r3 r3' hr3; exact ⟨rfl, rfl, hr3⟩
· rw [iteF hs2, iteF hs2]; trivial
| beginK =>
refine rsim_bind hS (ih ts ts' hts) ?_
intro b s r r' hr
simp only
by_cases hs1 : s = some kUNTIL
· rw [iteT hs1, iteT hs1]
refine rsim_bind hS (ih r r' hr) ?_
intro b3 s3 r3 r3' hr3; exact ⟨rfl, rfl, hr3⟩
· rw [iteF hs1, iteF hs1]
by_cases hs2 : s = some kWHILE
· rw [iteT hs2, iteT hs2]
refine rsim_bind hS (ih r r' hr) ?_
intro b2 s2 r2 r2' hr2
simp only
by_cases hs3 : s2 = some kREPEAT
· rw [iteT hs3, iteT hs3]
refine rsim_bind hS (ih r2 r2' hr2) ?_
intro b3 s3 r3 r3' hr3; exact ⟨rfl, rfl, hr3⟩
· rw [iteF hs3, iteF hs3]; trivial
· rw [iteF hs2, iteF hs2]; trivial
| doK q =>
refine rsim_bind hS (ih ts ts' hts) ?_
intro b s r r' hr
simp only
by_cases hs1 : s = some kLOOP
· rw [iteT hs1, iteT hs1]
refine rsim_bind hS (ih r r' hr) ?_
intro b3 s3 r3 r3' hr3; exact ⟨rfl, rfl, hr3⟩
· rw [iteF hs1, iteF hs1]
by_cases hs2 : s = some kPLOOP
· rw [iteT hs2, iteT hs2]
refine rsim_bind hS (ih r r' hr) ?_
intro b3 s3 r3 r3' hr3; exact ⟨rfl, rfl, hr3⟩
· rw [iteF hs2, iteF hs2]; trivial
| caseK =>
refine rsim_bind hS (ih ts ts' hts) ?_
intro b s r r' hr
simp only
by_cases hs1 : s = some kENDCASE
· rw [iteT hs1, iteT hs1]
refine rsim_bind hS (ih r r' hr) ?_
intro b3 s3 r3 r3' hr3; exact ⟨rfl, rfl, hr3⟩
· rw [iteF hs1, iteF hs1]; trivial
| ofK =>
refine rsim_bind hS (ih ts ts' hts) ?_
intro b s r r' hr
simp only
by_cases hs1 : s = some kENDOF
· rw [iteT hs1, iteT hs1]
refine rsim_bind hS (ih r r' hr) ?_
intro b3 s3 r3 r3' hr3
simp only
by_cases hs2 : s3 = some kENDCASE
· rw [iteT hs2, iteT hs2]; exact ⟨rfl, rfl, hr3⟩
· rw [iteF hs2, iteF hs2]; trivial
· rw [iteF hs1, iteF hs1]; trivial
· rw [h1, h2]; trivial
/-- Tokens equal up to case. -/
def UEq (t t' : Tok) : Prop := upper t = upper t'
theorem parseTop_congr :
∀ (fuel : Nat) (dict : Dict) (defs : List Block) (vars : Nat) (main : Block)
(toks toks' : List Tok), Rel2 UEq toks toks' →
PSim (parseTop fuel dict defs vars main toks) (parseTop fuel dict defs vars main toks')
| 0, _, _, _, _, _, _, _ => by simp [parseTop, PSim]
| f + 1, dict, defs, vars, main, toks, toks', h => by
have hS : ∀ e e', PSim (α := Program) (.error e) (.error e') := fun _ _ => trivial
simp only [parseTop]
rw [h.length_eq]
refine rsim_bind hS (parseSeq_congr (fun t t' ht => classify_sim ht) _ _ _ h) ?_
intro b s r r' hr
simp only
cases s with
| none => rfl
| some u =>
simp only
by_cases hc : u = kCOLON
· rw [iteT hc, iteT hc]
cases hr with
| nil => trivial
| @cons name name' body body' hn hb =>
simp only
rw [show upper name = upper name' from hn, hb.length_eq]
refine rsim_bind hS (parseSeq_congr (fun t t' ht => classify_sim ht) _ _ _ hb) ?_
intro bb s2 r2 r2' hr2
simp only
by_cases e1 : s2 = some kSEMI
· rw [iteT e1, iteT e1]
exact parseTop_congr f _ _ _ _ _ _ hr2
· rw [iteF e1, iteF e1]
by_cases e2 : s2 = some kCOLON
· rw [iteT e2, iteT e2]; trivial
· rw [iteF e2, iteF e2]
by_cases e3 : s2 = some kVARIABLE ∨ s2 = some kCONSTANT ∨ s2 = some kCREATE ∨
s2 = some kALLOT ∨ s2 = some kCOMMA
· rw [iteT e3, iteT e3]; trivial
· rw [iteF e3, iteF e3]; trivial
· rw [iteF hc, iteF hc]
by_cases hv : u = kVARIABLE
· rw [iteT hv, iteT hv]
cases hr with
| nil => trivial
| @cons name name' rest rest' hn hb =>
simp only
rw [show upper name = upper name' from hn]
exact parseTop_congr f _ _ _ _ _ _ hb
· rw [iteF hv, iteF hv]
by_cases hk : u = kCONSTANT
· rw [iteT hk, iteT hk]
cases b.dropLastLit with
| none => trivial
| some p =>
obtain ⟨b', z⟩ := p
cases hr with
| nil => trivial
| @cons name name' rest rest' hn hb =>
simp only
rw [show upper name = upper name' from hn]
exact parseTop_congr f _ _ _ _ _ _ hb
· rw [iteF hk, iteF hk]
by_cases hcr : u = kCREATE
· rw [iteT hcr, iteT hcr]
cases hr with
| nil => trivial
| @cons name name' rest rest' hn hb =>
simp only
rw [show upper name = upper name' from hn]
exact parseTop_congr f _ _ _ _ _ _ hb
· rw [iteF hcr, iteF hcr]
by_cases hal : u = kALLOT
· rw [iteT hal, iteT hal]
cases b.dropLastLit with
| none => trivial
| some p => exact parseTop_congr f _ _ _ _ _ _ hr
· rw [iteF hal, iteF hal]
by_cases hcm : u = kCOMMA
· rw [iteT hcm, iteT hcm]
cases b.dropLastLit with
| none => trivial
| some p => exact parseTop_congr f _ _ _ _ _ _ hr
· rw [iteF hcm, iteF hcm]; trivial
theorem forall₂_upper : ∀ toks : List Tok, Rel2 UEq toks (toks.map upper)
| [] => .nil
| t :: ts => .cons (upper_upper t).symm (forall₂_upper ts)
end ParseProps
open ParseProps in
/-- **Parsing is case-insensitive**: upper-casing the source gives the same result, except that
an error message may quote a token in its other spelling. (Upper-casing never changes a digit,
a sign, whitespace, `\`, `(` or `)`, and every word is looked up upper-cased.) -/
theorem parse_toUpper (src : String) :
(parse (src.map Char.toUpper)).toOption = (parse src).toOption := by
unfold parse
rw [tokenize_upper]
cases tokenize src with
| error e => rfl
| ok toks =>
show (parseTokens (toks.map upper)).toOption = (parseTokens toks).toOption
unfold parseTokens
rw [List.length_map]
have h := parseTop_congr (toks.length + 1) [] [] 0 .nil _ _ (forall₂_upper toks)
revert h
cases parseTop (toks.length + 1) [] [] 0 .nil toks <;>
cases parseTop (toks.length + 1) [] [] 0 .nil (toks.map upper) <;>
intro h <;> simp only [PSim] at h <;> (try subst h) <;> (try contradiction) <;> rfl
theorem parse_toUpper_ok {src : String} {P : Program} :
parse (src.map Char.toUpper) = .ok P ↔ parse src = .ok P := by
have := parse_toUpper src
constructor
· intro h; rw [h] at this
cases h' : parse src <;> rw [h'] at this <;> simp_all [Except.toOption]
· intro h; rw [h] at this
cases h' : parse (src.map Char.toUpper) <;> rw [h'] at this <;> simp_all [Except.toOption]
/-- Sources that agree up to the case of their letters parse to the same program (or both
fail). -/
theorem parse_case_insensitive {src src' : String}
(h : src.map Char.toUpper = src'.map Char.toUpper) :
(parse src).toOption = (parse src').toOption := by
rw [← parse_toUpper src, ← parse_toUpper src', h]
namespace ParseProps
/-! ## Comments -/
theorem flush_eq (cur : List Char) (l : List Tok) : flush cur l = flush cur [] ++ l := by
unfold flush; split <;> simp
/-- The lexer is a left-to-right machine: after any prefix `a` it has emitted some tokens and
is in some state (`cur`, `skip`), and it continues from that state. -/
theorem lex_append : ∀ (a cur : List Char) (skip : Bool), (skip = true → cur = []) →
∃ (toks : List Tok) (cur' : List Char) (skip' : Bool), (skip' = true → cur' = []) ∧
∀ x, lex (a ++ x) cur skip = toks ++ lex x cur' skip' := by
intro a
induction a with
| nil => intro cur skip h; exact ⟨[], cur, skip, h, fun _ => rfl⟩
| cons c a ih =>
intro cur skip hinv
cases skip with
| true =>
by_cases hc : (c == '\n') = true
· obtain ⟨t, c', s', hi, h⟩ := ih [] false (by simp)
exact ⟨t, c', s', hi, fun x => by
simp only [List.cons_append, lex, hc, ite_true]; exact h x⟩
· obtain ⟨t, c', s', hi, h⟩ := ih [] true (fun _ => rfl)
exact ⟨t, c', s', hi, fun x => by
simp only [List.cons_append, lex, hc]; exact h x⟩
| false =>
by_cases hw : isWs c = true
· by_cases hb : cur = ['\\']
· by_cases hc : (c == '\n') = true
· obtain ⟨t, c', s', hi, h⟩ := ih [] false (by simp)
exact ⟨t, c', s', hi, fun x => by
simp only [List.cons_append, lex, hw, hb, hc, ite_true]; exact h x⟩
· obtain ⟨t, c', s', hi, h⟩ := ih [] true (fun _ => rfl)
exact ⟨t, c', s', hi, fun x => by
simp only [List.cons_append, lex, hw, hb, hc, ite_true]; exact h x⟩
· obtain ⟨t, c', s', hi, h⟩ := ih [] false (by simp)
exact ⟨flush cur [] ++ t, c', s', hi, fun x => by
simp only [List.cons_append, lex, hw, hb, ite_true, ite_false]
rw [h x, flush_eq, List.append_assoc]⟩
· obtain ⟨t, c', s', hi, h⟩ := ih (c :: cur) false (by simp)
exact ⟨t, c', s', hi, fun x => by
simp only [List.cons_append, lex, hw]; exact h x⟩
/-- Inside a `\` comment, everything up to the next newline is dropped. -/
theorem lex_skip {x : List Char} : ∀ (s cur : List Char), (∀ ch ∈ s, ch ≠ '\n') →
(s ≠ [] ∨ cur = []) → lex (s ++ x) cur true = lex x [] true
| [], cur, _, h => by
rcases h with h | rfl
· exact absurd rfl h
· rfl
| ch :: s, cur, hs, _ => by
have hc : (ch == '\n') = false := by simpa using hs ch (by simp)
simp only [List.cons_append, lex, hc]
exact lex_skip s [] (fun c hc => hs c (by simp [hc])) (.inr rfl)
/-- The text of a `( … )` comment body lexes to tokens none of which closes the comment. -/
theorem lex_paren_body {b : List Char} : ∀ (c cur : List Char), '\\' ∉ cur → ')' ∉ cur →
(∀ ch ∈ c, ch ≠ ')' ∧ ch ≠ '\\') →
∃ ctoks : List Tok, (∀ t ∈ ctoks, t.getLast? ≠ some ')') ∧
lex (c ++ ' ' :: ')' :: ' ' :: b) cur false = ctoks ++ [')'] :: lex b [] false
| [], cur, h1, h2, _ => by
have hb : cur ≠ ['\\'] := fun e => h1 (by simp [e])
refine ⟨flush cur [], ?_, ?_⟩
· intro t ht
unfold flush at ht
split at ht
· simp at ht
· simp only [List.mem_singleton] at ht
subst ht
intro e
have := List.mem_of_getLast? e
exact h2 (by simpa using this)
· simp only [List.nil_append, lex, show isWs ' ' = true from rfl, hb, ite_true, ite_false,
show isWs ')' = false from rfl, Bool.false_eq_true]
rw [flush_eq]
simp [flush]
| ch :: c, cur, h1, h2, hc => by
have hch := hc ch (by simp)
have hc' := fun c' (h : c' ∈ c) => hc c' (by simp [h])
by_cases hw : isWs ch = true
· have hb : cur ≠ ['\\'] := fun e => h1 (by simp [e])
obtain ⟨ct, hct, he⟩ := lex_paren_body c [] (by simp) (by simp) hc'
refine ⟨flush cur [] ++ ct, ?_, ?_⟩
· intro t ht
simp only [List.mem_append] at ht
rcases ht with ht | ht
· unfold flush at ht
split at ht
· simp at ht
· simp only [List.mem_singleton] at ht
subst ht
intro e
have := List.mem_of_getLast? e
exact h2 (by simpa using this)
· exact hct t ht
· simp only [List.cons_append, lex, hw, hb, ite_true, ite_false]
rw [he, flush_eq]; simp
· obtain ⟨ct, hct, he⟩ := lex_paren_body c (ch :: cur)
(by simp [h1, Ne.symm hch.2]) (by simp [h2, Ne.symm hch.1]) hc'
refine ⟨ct, hct, ?_⟩
simp only [List.cons_append, lex, hw, Bool.false_eq_true, ite_false]
exact he
theorem stripParens_close {rest : List Tok} : ∀ cs : List Tok,
(∀ t ∈ cs, t.getLast? ≠ some ')') →
stripParens true (cs ++ [')'] :: rest) = stripParens false rest
| [], _ => by simp [stripParens]
| t :: cs, h => by
simp only [List.cons_append, stripParens, iteF (h t (by simp))]
exact stripParens_close cs (fun t' ht => h t' (by simp [ht]))
theorem stripParens_append {rest : List Tok} : ∀ (ts us : List Tok) (inside : Bool),
stripParens inside ts = .ok us →
stripParens inside (ts ++ rest) = (us ++ ·) <$> stripParens false rest
| [], us, false, h => by
cases h
simp only [List.nil_append]
cases stripParens false rest <;> rfl
| [], us, true, h => by cases h
| t :: ts, us, false, h => by
simp only [List.cons_append, stripParens] at h ⊢
split
· rename_i ht; rw [iteT ht] at h; exact stripParens_append ts us true h
· rename_i ht
rw [iteF ht] at h
cases hs : stripParens false ts with
| error e => rw [hs] at h; cases h
| ok us' =>
rw [hs] at h
cases h
rw [stripParens_append ts us' false hs]
cases stripParens false rest <;> rfl
| t :: ts, us, true, h => by
simp only [List.cons_append, stripParens] at h ⊢
split
· rename_i ht; rw [iteT ht] at h; exact stripParens_append ts us false h
· rename_i ht; rw [iteF ht] at h; exact stripParens_append ts us true h
theorem lex_skip_step {c : Char} {cs cur : List Char} (hc : c ≠ '\n') :
lex (c :: cs) cur true = lex cs [] true := by
have : (c == '\n') = false := by simpa using hc
simp only [lex, this, Bool.false_eq_true, ite_false]
theorem lex_nl_skip {cs cur : List Char} : lex ('\n' :: cs) cur true = lex cs [] false := by
simp only [lex]; rfl
theorem lex_ws_bs {c : Char} {cs : List Char} (hw : isWs c = true) (hc : c ≠ '\n') :
lex (c :: cs) ['\\'] false = lex cs [] true := by
have : (c == '\n') = false := by simpa using hc
simp only [lex, hw, this, ite_true, Bool.false_eq_true, ite_false]
theorem lex_nl_bs {cs : List Char} : lex ('\n' :: cs) ['\\'] false = lex cs [] false := by
simp only [lex]; rfl
theorem lex_ws {c : Char} {cs cur : List Char} (hw : isWs c = true) (hb : cur ≠ ['\\']) :
lex (c :: cs) cur false = flush cur (lex cs [] false) := by
simp only [lex, hw, hb, ite_true, ite_false]
theorem lex_char {c : Char} {cs cur : List Char} (hw : isWs c = false) :
lex (c :: cs) cur false = lex cs (c :: cur) false := by
simp only [lex, hw, Bool.false_eq_true, ite_false]
end ParseProps
open ParseProps in
/-- **`( … )` comments are erased.** A comment ` ( c ) ` between `a` and `b` tokenizes like a
single space, provided `a` does not end inside an open `(` comment (`tokenize a` succeeds) and
the body `c` contains no `)` (which would close it early), no `\` (which could start a line
comment hiding the `)`) and no newline (which would end a `\` comment open at the end of `a`). -/
theorem tokenize_paren_comment {a b c : String} (ha : ∃ ts, tokenize a = .ok ts)
(hc : ∀ ch ∈ c.toList, ch ≠ ')' ∧ ch ≠ '\\' ∧ ch ≠ '\n') :
tokenize (a ++ " ( " ++ c ++ " ) " ++ b) = tokenize (a ++ " " ++ b) := by
obtain ⟨ts, ha⟩ := ha
unfold tokenize at ha ⊢
have e1 : (a ++ " ( " ++ c ++ " ) " ++ b).toList =
a.toList ++ (' ' :: '(' :: ' ' :: (c.toList ++ ' ' :: ')' :: ' ' :: b.toList)) := by simp
have e2 : (a ++ " " ++ b).toList = a.toList ++ (' ' :: b.toList) := by simp
rw [e1, e2]
obtain ⟨toks, cur, skip, -, hsplit⟩ := lex_append a.toList [] false (by simp)
rw [hsplit, hsplit]
have hnl : ∀ ch ∈ ('(' :: ' ' :: (c.toList ++ [' ', ')', ' '])), ch ≠ '\n' := by
intro ch hch
simp only [List.mem_cons, List.mem_append, List.not_mem_nil, or_false] at hch
rcases hch with rfl | rfl | h | rfl | rfl | rfl
all_goals first | decide | exact (hc ch h).2.2
have hskip : lex ('(' :: ' ' :: (c.toList ++ ' ' :: ')' :: ' ' :: b.toList)) [] true =
lex b.toList [] true := by
have := lex_skip ('(' :: ' ' :: (c.toList ++ [' ', ')', ' '])) [] hnl (.inl (by simp))
(x := b.toList)
simpa using this
have hsp : isWs ' ' = true := rfl
have hsn : ' ' ≠ '\n' := by decide
cases skip with
| true => rw [lex_skip_step hsn, lex_skip_step hsn, hskip]
| false =>
by_cases hb : cur = ['\\']
· subst hb
rw [lex_ws_bs hsp hsn, lex_ws_bs hsp hsn, hskip]
· obtain ⟨ct, hct, hbody⟩ := lex_paren_body (b := b.toList) c.toList [] (by simp) (by simp)
(fun ch h => ⟨(hc ch h).1, (hc ch h).2.1⟩)
have hpre : stripParens false (toks ++ flush cur []) = .ok ts := by
have := hsplit []
simp only [List.append_nil, lex, hb, ite_false] at this
rw [← this]; exact ha
rw [lex_ws hsp hb, lex_ws hsp hb, lex_char (by decide), lex_ws hsp (by decide), hbody]
have : flush ['('] (ct ++ [')'] :: lex b.toList [] false) =
['('] :: (ct ++ [')'] :: lex b.toList [] false) := rfl
rw [this, flush_eq cur (['('] :: _), flush_eq cur (lex b.toList [] false)]
rw [← List.append_assoc, ← List.append_assoc, stripParens_append _ _ _ hpre,
stripParens_append _ _ _ hpre]
simp only [stripParens, iteT, stripParens_close ct hct]
open ParseProps in
/-- **`\` comments are erased**: ` \ c` up to the end of the line is dropped (for any `a`; the
newline stays a separator). -/
theorem tokenize_line_comment {a b c : String} (hc : ∀ ch ∈ c.toList, ch ≠ '\n') :
tokenize (a ++ " \\ " ++ c ++ "\n" ++ b) = tokenize (a ++ "\n" ++ b) := by
unfold tokenize
have e1 : (a ++ " \\ " ++ c ++ "\n" ++ b).toList =
a.toList ++ (' ' :: '\\' :: ' ' :: (c.toList ++ '\n' :: b.toList)) := by simp
have e2 : (a ++ "\n" ++ b).toList = a.toList ++ ('\n' :: b.toList) := by simp
rw [e1, e2]
obtain ⟨toks, cur, skip, -, hsplit⟩ := lex_append a.toList [] false (by simp)
rw [hsplit, hsplit]
have hsk : lex (c.toList ++ '\n' :: b.toList) [] true = lex b.toList [] false := by
rw [lex_skip c.toList [] hc (.inr rfl), lex_nl_skip]
have hsp : isWs ' ' = true := rfl
have hsn : ' ' ≠ '\n' := by decide
have hbn : '\\' ≠ '\n' := by decide
cases skip with
| true => rw [lex_skip_step hsn, lex_skip_step hbn, lex_skip_step hsn, hsk, lex_nl_skip]
| false =>
by_cases hb : cur = ['\\']
· subst hb
rw [lex_ws_bs hsp hsn, lex_skip_step hbn, lex_skip_step hsn, hsk, lex_nl_bs]
· rw [lex_ws hsp hb, lex_char (by decide), lex_ws_bs hsp hsn, hsk, lex_ws rfl hb]
/-- At the end of the text, ` \ c` (no newline in `c`) is dropped. -/
theorem tokenize_line_comment_end {a c : String} (hc : ∀ ch ∈ c.toList, ch ≠ '\n') :
tokenize (a ++ " \\ " ++ c) = tokenize a := by
unfold tokenize
have e1 : (a ++ " \\ " ++ c).toList = a.toList ++ (' ' :: '\\' :: ' ' :: c.toList) := by simp
rw [e1, ← List.append_nil a.toList]
obtain ⟨toks, cur, skip, hinv, hsplit⟩ := ParseProps.lex_append a.toList [] false (by simp)
rw [hsplit, List.append_nil, hsplit]
have hsk : lex c.toList [] true = [] := by
have := ParseProps.lex_skip c.toList [] hc (.inr rfl) (x := [])
simp only [List.append_nil] at this
rw [this]; rfl
have hsp : isWs ' ' = true := rfl
have hsn : ' ' ≠ '\n' := by decide
have hbn : '\\' ≠ '\n' := by decide
cases skip with
| true =>
rw [hinv rfl, ParseProps.lex_skip_step hsn, ParseProps.lex_skip_step hbn,
ParseProps.lex_skip_step hsn, hsk]
rfl
| false =>
by_cases hb : cur = ['\\']
· subst hb
rw [ParseProps.lex_ws_bs hsp hsn, ParseProps.lex_skip_step hbn,
ParseProps.lex_skip_step hsn, hsk]
rfl
· rw [ParseProps.lex_ws hsp hb, ParseProps.lex_char (by decide), ParseProps.lex_ws_bs hsp hsn,
hsk]
simp only [lex, hb, ite_false]
/-! ## `CONSTANT`, `RECURSE` and dictionary words -/
namespace ParseProps
theorem Block.append_nil : ∀ b : Block, b.append .nil = b
| .nil => rfl
| .op _ r => by simp [Block.append, Block.append_nil r]
| .ite _ _ r => by simp [Block.append, Block.append_nil r]
| .untilL _ r => by simp [Block.append, Block.append_nil r]
| .call _ r => by simp [Block.append, Block.append_nil r]
| .exit r => by simp [Block.append, Block.append_nil r]
| .doLoop _ r => by simp [Block.append, Block.append_nil r]
| .plusLoop _ r => by simp [Block.append, Block.append_nil r]
| .leave r => by simp [Block.append, Block.append_nil r]
| .whileL _ _ r => by simp [Block.append, Block.append_nil r]
end ParseProps
open ParseProps
/-- A token whose upper-cased spelling is in the dictionary and is not a keyword means what the
dictionary says (dictionary entries shadow built-in words and numbers). -/
theorem classify_dict {dict : Dict} {self : Option Nat} {t : Tok} {w : Block → Block}
(hk : upper t ∉ Print.keywords) (hl : dict.lookup (upper t) = some w) :
classify dict self t = .prim w := by
obtain ⟨h1, h2, h3, h4, h5, h6, h7, h8, h9, h10, h11⟩ :=
Print.not_keyword (t := upper t) (fun kw hkw e => hk (e ▸ hkw))
simp only [classify]
rw [iteF h1, iteF h2, iteF h3, iteF h4, iteF h5, iteF h6, iteF h7, iteF h8, iteF h9,
iteF h10, iteF h11]
simp only [lookupWord, hl]
/-- `RECURSE` (any case) inside the definition of word `i` is a call of word `i`. -/
theorem classify_recurse {dict : Dict} {i : Nat} {t : Tok} (h : upper t = kRECURSE) :
classify dict (some i) t = .prim (.call i) := by
simp only [classify, h]
rfl
/-- One step of `parseSeq` over a word that compiles to `w`. -/
theorem parseSeq_prim {fuel : Nat} {dict : Dict} {self : Option Nat} {t : Tok} {ts : List Tok}
{w : Block → Block} (h : classify dict self t = .prim w) :
parseSeq (fuel + 1) dict self (t :: ts) =
(parseSeq fuel dict self ts).map fun p => (w p.1, p.2.1, p.2.2) := by
simp only [parseSeq, h]
cases parseSeq fuel dict self ts <;> rfl
/-- **`CONSTANT`**: when the top-level code before `CONSTANT` ends in a literal `z`, the literal is
removed from the main block and `name` is entered in the dictionary as the literal `z`. -/
theorem parseTop_constant {fuel : Nat} {dict : Dict} {defs : List Block} {vars : Nat}
{main b b' : Block} {toks rest : List Tok} {name : Tok} {z : Int}
(h : parseSeq (toks.length + 1) dict none toks = .ok (b, some kCONSTANT, name :: rest))
(hz : b.dropLastLit = some (b', z)) :
parseTop (fuel + 1) dict defs vars main toks =
parseTop fuel ((upper name, Block.op (.lit z)) :: dict) defs vars (main.append b') rest := by
simp only [parseTop, h, bind, Except.bind, hz, ite_true]
rw [iteF (by decide), iteF (by decide)]
/-- `n CONSTANT name` at the start of top-level code, with `n` a number literal not shadowed
by the dictionary: `name` is entered as the literal `n`, and the main block is unchanged. -/
theorem parseTop_constant_lit {fuel : Nat} {dict : Dict} {defs : List Block} {vars : Nat}
{main : Block} {zt c name : Tok} {rest : List Tok} {z : Int}
(hz : number (upper zt) = some z) (hzd : dict.lookup (upper zt) = none)
(hc : upper c = kCONSTANT) :
parseTop (fuel + 1) dict defs vars main (zt :: c :: name :: rest) =
parseTop fuel ((upper name, Block.op (.lit z)) :: dict) defs vars main rest := by
have hk : upper zt ∉ Print.keywords := fun hm => by
have := Print.keyword_no_number _ hm; rw [hz] at this; cases this
obtain ⟨h1, h2, h3, h4, h5, h6, h7, h8, h9, h10, h11⟩ :=
Print.not_keyword (t := upper zt) (fun kw hkw e => hk (e ▸ hkw))
have hop : opTable.lookup (upper zt) = none := by
cases h : opTable.lookup (upper zt) with
| none => rfl
| some o =>
obtain ⟨k, hm, rfl⟩ := Print.lookup_some_mem h
have := Print.opTable_no_number _ hm; simp only at this; rw [hz] at this; cases this
have hcz : classify dict none zt = .prim (.op (.lit z)) := by
simp only [classify]
rw [iteF h1, iteF h2, iteF h3, iteF h4, iteF h5, iteF h6, iteF h7, iteF h8, iteF h9,
iteF h10, iteF h11]
simp only [lookupWord, hzd, hop, hz]
have hcc : classify dict none c = .stop kCONSTANT := by
rw [← hc]; simp only [classify, hc]; rfl
have hseq : parseSeq ((zt :: c :: name :: rest).length + 1) dict none (zt :: c :: name :: rest) =
.ok (.op (.lit z) .nil, some kCONSTANT, name :: rest) := by
simp only [List.length_cons]
rw [parseSeq_prim hcz]
simp only [parseSeq, hcc]
rfl
rw [parseTop_constant hseq rfl, ParseProps.Block.append_nil]
/-- After `… CONSTANT name`, a token spelled like `name` (any case) is the literal. -/
theorem classify_constant {dict : Dict} {self : Option Nat} {name t : Tok} {z : Int}
(hn : upper name ∉ Print.keywords) (ht : upper t = upper name) :
classify ((upper name, Block.op (.lit z)) :: dict) self t = .prim (.op (.lit z)) :=
classify_dict (ht ▸ hn) (by rw [ht]; exact Print.lookup_cons_self)
/-- **Colon definitions**: the body of a definition is parsed with the new word in the dictionary
(as a call of index `defs.length`, its index in the final program, see `parseTop_defs_prefix`)
and with `RECURSE` meaning that same call. -/
theorem parseTop_colon {fuel : Nat} {dict : Dict} {defs : List Block} {vars : Nat}
{main bb : Block} {c name : Tok} {body rest : List Tok} (hc : upper c = kCOLON)
(h : parseSeq (body.length + 1) ((upper name, Block.call defs.length) :: dict)
(some defs.length) body = .ok (bb, some kSEMI, rest)) :
parseTop (fuel + 1) dict defs vars main (c :: name :: body) =
parseTop fuel ((upper name, Block.call defs.length) :: dict) (defs ++ [bb]) vars main
rest := by
have hcc : classify dict none c = .stop kCOLON := by
rw [← hc]; simp only [classify, hc]; rfl
simp only [parseTop, parseSeq, hcc, bind, Except.bind, iteT, h]
rw [show main.append .nil = main from ParseProps.Block.append_nil main]
/-- Definitions only grow: the definitions present when `parseTop` reaches a point are the first
definitions of the final program. -/
theorem parseTop_defs_prefix :
∀ (fuel : Nat) (dict : Dict) (defs : List Block) (vars : Nat) (main : Block)
(toks : List Tok) (P : Program),
parseTop fuel dict defs vars main toks = .ok P → ∃ more, P.defs = defs ++ more
| 0, _, _, _, _, _, _, h => by simp [parseTop] at h
| fuel + 1, dict, defs, vars, main, toks, P, h => by
simp only [parseTop] at h
obtain ⟨⟨b, stop, rest⟩, -, h2⟩ := bind_ok h
simp only at h2
split at h2
· cases h2; exact ⟨[], by simp⟩
· split at h2
· split at h2
· cases h2
· obtain ⟨⟨bb, s2, r2⟩, -, h4⟩ := bind_ok h2
simp only at h4
split at h4
· obtain ⟨more, hm⟩ := parseTop_defs_prefix fuel _ _ _ _ _ P h4
exact ⟨bb :: more, by simp [hm]⟩
· split at h4
· cases h4
· split at h4 <;> cases h4
· split at h2
· split at h2
· cases h2
· exact parseTop_defs_prefix fuel _ _ _ _ _ P h2
· split at h2
· split at h2
· cases h2
· cases h2
· exact parseTop_defs_prefix fuel _ _ _ _ _ P h2
· split at h2
· split at h2
· cases h2
· exact parseTop_defs_prefix fuel _ _ _ _ _ P h2
· split at h2
· split at h2
· cases h2
· exact parseTop_defs_prefix fuel _ _ _ _ _ P h2
· split at h2
· split at h2
· cases h2
· exact parseTop_defs_prefix fuel _ _ _ _ _ P h2
· cases h2
/-- Inside the definition of word `k` named `name`, `RECURSE` and `name` mean the same. -/
theorem classify_recurse_eq_name {dict : Dict} {k : Nat} {name t : Tok}
(hn : upper name ∉ Print.keywords) (ht : upper t = kRECURSE) :
classify ((upper name, Block.call k) :: dict) (some k) t =
classify ((upper name, Block.call k) :: dict) (some k) name := by
rw [classify_recurse ht, classify_dict hn Print.lookup_cons_self]
/-- **`RECURSE` is the word's own name**: inside the definition of word `k` named `name`,
replacing any `RECURSE` tokens by `name` does not change the parsed block or where it stops
(nor whether it fails). -/
theorem parseSeq_recurse_name {dict : Dict} {k : Nat} {name : Tok}
(hn : upper name ∉ Print.keywords) (fuel : Nat) {ts ts' : List Tok}
(h : Rel2 (fun t t' => t = t' ∨ (upper t = kRECURSE ∧ t' = name)) ts ts') :
RSim (fun t t' => t = t' ∨ (upper t = kRECURSE ∧ t' = name))
(parseSeq fuel ((upper name, Block.call k) :: dict) (some k) ts)
(parseSeq fuel ((upper name, Block.call k) :: dict) (some k) ts') := by
refine parseSeq_congr (fun t t' ht => ?_) fuel ts ts' h
rcases ht with rfl | ⟨ht, rfl⟩
· exact .inl rfl
· exact .inl (classify_recurse_eq_name hn ht)
end Forth
end WordDialect