Download forth/Forth/ParseProps.lean from Snapkitty/ai-free: direct link, hf CLI and curl.
- Browser
- Download file 56.6 kB
-
https://huggingface.co/Snapkitty/ai-free/resolve/main/forth/Forth/ParseProps.lean
- Command line
-
hf download hf://Snapkitty/ai-free/forth/Forth/ParseProps.lean
-
curl -L -o ParseProps.lean https://huggingface.co/Snapkitty/ai-free/resolve/main/forth/Forth/ParseProps.lean
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 | |