Skip to content

Commit e499c52

Browse files
authored
Use sentinel value instead of Option.none (#145)
* Use sentinel value instead of Option.none * Fix test setup
1 parent e2e33d6 commit e499c52

11 files changed

Lines changed: 187 additions & 72 deletions

File tree

correctness/RegexCorrectness/Backtracker/Refinement.lean

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ set_option autoImplicit false
66
open Regex (NFA)
77
open Regex.Data (BitMatrix BVPos)
88
open Regex.Strategy (materializeUpdates)
9-
open String (ValidPos)
9+
open String (ValidPos ValidPosPlusOne)
1010
namespace Regex.Backtracker
1111

1212
variable {s : String} {nfa : NFA} {wf : nfa.WellFormed} {startPos : ValidPos s} {bufferSize : Nat}

correctness/RegexCorrectness/Regex/Basic.lean

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -94,8 +94,8 @@ theorem captureNextBuf_soundness (h : re.captureNextBuf bufferSize pos = .some m
9494
pos ≤ pos' ∧
9595
s.expr.Captures pos' pos'' groups ∧
9696
EquivMaterializedUpdate (materializeRegexGroups groups) matched ∧
97-
matched[0] = .some pos' ∧
98-
matched[1] = .some pos'' := by
97+
matched[0] = .validPos pos' ∧
98+
matched[1] = .validPos pos'' := by
9999
have ⟨pos', pos'', groups, le, c, eqv⟩ := captureNextBuf_soundness' h s
100100
refine ⟨pos', pos'', groups, le, c, eqv, ?_⟩
101101

correctness/RegexCorrectness/Regex/Captures.lean

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ theorem captures_of_next?_some (h : self.next? = .some (captured, self')) (isr :
6868
. intro index p₁ p₂ le
6969
have hbufsize : captured.buffer.size = self.regex.maxTag + 1 := by grind
7070
if lt : 2 * index + 1 < self.regex.maxTag + 1 then
71-
simp [eqv.eq index lt p₁ p₂, ←h.1, CapturedGroups.get, Option.bind_eq_some_iff, le]
71+
simp [eqv.eq index lt p₁ p₂, CapturedGroups.get, ←h.1, Option.bind_eq_some_iff]
7272
grind
7373
else
7474
have : self.regex.maxTag ≤ 2 * index := by grind

correctness/RegexCorrectness/Strategy/Materialize/Basic.lean

Lines changed: 42 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import RegexCorrectness.Data.Expr.Semantics.CaptureGroups
33
set_option autoImplicit false
44

55
open Regex.Data (CaptureGroups)
6-
open String (ValidPos)
6+
open String (ValidPos ValidPosPlusOne)
77

88
namespace Regex.Strategy
99

@@ -24,27 +24,59 @@ def materializeRegexGroups : CaptureGroups s → Nat → Option (ValidPos s × V
2424
-- The later one will be preferred
2525
materializeRegexGroups g₂ tag <|> materializeRegexGroups g₁ tag
2626

27-
def materializeUpdatesAux (n : Nat) (accum : Vector (Option (ValidPos s)) n) : List (Nat × ValidPos s) → Vector (Option (ValidPos s)) n
27+
def materializeUpdatesAux (n : Nat) (accum : Vector (ValidPosPlusOne s) n) : List (Nat × ValidPos s) → Vector (ValidPosPlusOne s) n
2828
| [] => accum
2929
| (offset, pos) :: rest =>
30-
materializeUpdatesAux n (accum.setIfInBounds offset (some pos)) rest
30+
materializeUpdatesAux n (accum.setIfInBounds offset (.validPos pos)) rest
3131

3232
/--
3333
`materializeUpdates n updates` constructs a buffer of size `n` which interprets the updates list
3434
as the writes to the buffer. When the same offset appears multiple times in the list, the last
3535
one wins.
3636
-/
37-
def materializeUpdates (n : Nat) (updates : List (Nat × ValidPos s)) : Vector (Option (ValidPos s)) n :=
38-
materializeUpdatesAux n (Vector.replicate n .none) updates
37+
def materializeUpdates (n : Nat) (updates : List (Nat × ValidPos s)) : Vector (ValidPosPlusOne s) n :=
38+
materializeUpdatesAux n (Vector.replicate n (.sentinel s)) updates
3939

40-
def EquivMaterializedUpdate {n} (groups : Nat → Option (ValidPos s × ValidPos s)) (updates : Vector (Option (ValidPos s)) n) : Prop :=
40+
def equivPos {s} (p : Option (ValidPos s)) (p' : ValidPosPlusOne s) : Prop :=
41+
match p with
42+
| .some p => p' = .validPos p
43+
| .none => p' = .sentinel s
44+
45+
@[simp, grind =]
46+
theorem equivPos.some_iff {p : ValidPos s} {p' : ValidPosPlusOne s} : equivPos (.some p) p' ↔ p' = .validPos p := by
47+
grind [equivPos]
48+
49+
@[simp, grind =]
50+
theorem equivPos.none_iff {p' : ValidPosPlusOne s} : equivPos .none p' ↔ p' = .sentinel s := by
51+
grind [equivPos]
52+
53+
@[simp, grind =]
54+
theorem equivPos.validPos_iff {p p' : ValidPos s} : equivPos p (.validPos p') ↔ p = p' := by
55+
grind [equivPos]
56+
57+
@[simp, grind =]
58+
theorem equivPos.sentinel_iff {p} : equivPos p (.sentinel s) ↔ p = .none := by
59+
match p with
60+
| .some p => simpa using ValidPosPlusOne.validPos_ne_sentinel.symm
61+
| .none => simp
62+
63+
def EquivMaterializedUpdate {n} (groups : Nat → Option (ValidPos s × ValidPos s)) (updates : Vector (ValidPosPlusOne s) n) : Prop :=
4164
∀ tag,
42-
((h₁ : 2 * tag < n) → ((groups tag).map (·.1) = updates[2 * tag])) ∧
43-
((h₂ : 2 * tag + 1 < n) → ((groups tag).map (·.2) = updates[2 * tag + 1]))
65+
((h₁ : 2 * tag < n) → (equivPos ((groups tag).map (·.1)) updates[2 * tag])) ∧
66+
((h₂ : 2 * tag + 1 < n) → (equivPos ((groups tag).map (·.2)) updates[2 * tag + 1]))
67+
68+
theorem EquivMaterializedUpdate.eq {n} {groups : Nat → Option (ValidPos s × ValidPos s)} {updates : Vector (ValidPosPlusOne s) n}
69+
(eqv : EquivMaterializedUpdate groups updates) (tag : Nat) (lt : 2 * tag + 1 < n) (p₁ p₂ : ValidPos s) :
70+
groups tag = .some (p₁, p₂) ↔ updates[2 * tag] = .validPos p₁ ∧ updates[2 * tag + 1] = .validPos p₂ := by
71+
have eq₁ := (eqv tag).1 (by grind)
72+
have eq₂ := (eqv tag).2 lt
73+
match h : groups tag with
74+
| .some (p₁', p₂') => grind
75+
| .none => grind
4476

45-
theorem EquivMaterializedUpdate.eq {n} {groups : Nat → Option (ValidPos s × ValidPos s)} {updates : Vector (Option (ValidPos s)) n}
77+
theorem EquivMaterializedUpdate.eq_none {n} {groups : Nat → Option (ValidPos s × ValidPos s)} {updates : Vector (ValidPosPlusOne s) n}
4678
(eqv : EquivMaterializedUpdate groups updates) (tag : Nat) (lt : 2 * tag + 1 < n) (p₁ p₂ : ValidPos s) :
47-
groups tag = .some (p₁, p₂) ↔ updates[2 * tag] = .some p₁ ∧ updates[2 * tag + 1] = .some p₂ := by
79+
groups tag = .none ↔ updates[2 * tag] = .sentinel s ∧ updates[2 * tag + 1] = .sentinel s := by
4880
have eq₁ := (eqv tag).1 (by grind)
4981
have eq₂ := (eqv tag).2 lt
5082
match h : groups tag with

correctness/RegexCorrectness/Strategy/Materialize/Lemmas.lean

Lines changed: 11 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import RegexCorrectness.Data.Expr.Semantics
44

55
set_option autoImplicit false
66

7-
open String (ValidPos)
7+
open String (ValidPos ValidPosPlusOne)
88
open Regex.Data (CaptureGroups)
99

1010
namespace Regex.Strategy
@@ -61,12 +61,12 @@ theorem mem_tags_of_materializeRegexGroups_some {e : Expr} {pos pos' : ValidPos
6161
@[simp]
6262
theorem materializeUpdatesAux_snoc {n accum updates offset} {pos : ValidPos s} :
6363
materializeUpdatesAux n accum (updates ++ [(offset, pos)]) =
64-
(materializeUpdatesAux n accum updates).setIfInBounds offset (some pos) := by
64+
(materializeUpdatesAux n accum updates).setIfInBounds offset (.validPos pos) := by
6565
induction updates generalizing accum with
6666
| nil => simp [materializeUpdatesAux]
6767
| cons _ _ ih => simp [materializeUpdatesAux, ih]
6868

69-
theorem materializeUpdatesAux_swap {n : Nat} {accum : Vector (Option (ValidPos s)) n} {updates : List (Nat × ValidPos s)} {offset₁ pos₁ offset₂ pos₂}
69+
theorem materializeUpdatesAux_swap {n : Nat} {accum : Vector (ValidPosPlusOne s) n} {updates : List (Nat × ValidPos s)} {offset₁ pos₁ offset₂ pos₂}
7070
(ne : offset₁ ≠ offset₂) :
7171
materializeUpdatesAux n accum ((offset₁, pos₁) :: (offset₂, pos₂) :: updates) =
7272
materializeUpdatesAux n accum ((offset₂, pos₂) :: (offset₁, pos₁) :: updates) := by
@@ -86,7 +86,7 @@ theorem materializeUpdatesAux_swap {n : Nat} {accum : Vector (Option (ValidPos s
8686
theorem materializeUpdatesAux_cons_of_not_in {n accum updates offset} {pos : ValidPos s}
8787
(h : ∀ offset' pos', (offset', pos') ∈ updates → offset ≠ offset') :
8888
materializeUpdatesAux n accum ((offset, pos) :: updates) =
89-
(materializeUpdatesAux n accum updates).setIfInBounds offset (some pos) := by
89+
(materializeUpdatesAux n accum updates).setIfInBounds offset (.validPos pos) := by
9090
induction updates generalizing accum with
9191
| nil => simp [materializeUpdatesAux]
9292
| cons head updates ih =>
@@ -102,44 +102,29 @@ theorem materializeUpdatesAux_cons_of_not_in {n accum updates offset} {pos : Val
102102
rfl
103103

104104
@[simp]
105-
theorem materializeUpdatesAux_nil {n : Nat} {accum : Vector (Option (ValidPos s)) n} :
105+
theorem materializeUpdatesAux_nil {n : Nat} {accum : Vector (ValidPosPlusOne s) n} :
106106
materializeUpdatesAux n accum [] = accum := rfl
107107

108-
theorem materializeUpdatesAux_append {n : Nat} {accum : Vector (Option (ValidPos s)) n} {updates₁ updates₂ : List (Nat × ValidPos s)} :
108+
theorem materializeUpdatesAux_append {n : Nat} {accum : Vector (ValidPosPlusOne s) n} {updates₁ updates₂ : List (Nat × ValidPos s)} :
109109
materializeUpdatesAux n accum (updates₁ ++ updates₂) = materializeUpdatesAux n (materializeUpdatesAux n accum updates₁) updates₂ := by
110110
induction updates₁ generalizing accum with
111111
| nil => simp
112112
| cons head tail ih => simp [materializeUpdatesAux, ih]
113113

114-
theorem materializeUpdatesAux_getElem {n : Nat} {accum : Vector (Option (ValidPos s)) n} {updates : List (Nat × ValidPos s)} {offset : Nat} (h : offset < n) :
114+
theorem materializeUpdatesAux_getElem {n : Nat} {accum : Vector (ValidPosPlusOne s) n} {updates : List (Nat × ValidPos s)} {offset : Nat} (h : offset < n) :
115115
(materializeUpdatesAux n accum updates)[offset] =
116-
((materializeUpdatesAux n (Vector.replicate n .none) updates)[offset] <|> accum[offset]) := by
116+
((materializeUpdatesAux n (Vector.replicate n (.sentinel s)) updates)[offset] <|> accum[offset]) := by
117117
induction updates generalizing accum with
118118
| nil => simp
119-
| cons head updates ih =>
120-
simp [materializeUpdatesAux]
121-
conv =>
122-
lhs
123-
rw [ih]
124-
conv =>
125-
rhs
126-
rw [ih]
127-
cases (materializeUpdatesAux n (Vector.replicate n .none) updates)[offset] with
128-
| some _ => simp
129-
| none =>
130-
simp
131-
if h' : head.1 = offset then
132-
simp [h']
133-
else
134-
simp [h']
119+
| cons head updates ih => grind [materializeUpdatesAux]
135120

136121
@[simp]
137-
theorem materializeUpdates_empty {n} : @materializeUpdates s n [] = Vector.replicate n .none := rfl
122+
theorem materializeUpdates_empty {n} : @materializeUpdates s n [] = Vector.replicate n (.sentinel s) := rfl
138123

139124
@[simp]
140125
theorem materializeUpdates_snoc {n : Nat} {updates : List (Nat × ValidPos s)} {offset : Nat} {pos : ValidPos s} :
141126
materializeUpdates n (updates ++ [(offset, pos)]) =
142-
(materializeUpdates n updates).setIfInBounds offset (some pos) := by
127+
(materializeUpdates n updates).setIfInBounds offset (.validPos pos) := by
143128
simp [materializeUpdates]
144129

145130
theorem materializeUpdates_append_getElem {n : Nat} {updates₁ updates₂ : List (Nat × ValidPos s)} {offset : Nat} (h : offset < n) :

correctness/RegexCorrectness/Strategy/Materialize/Naturality.lean

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ set_option autoImplicit false
66

77
open Regex.Data (Expr CaptureGroups)
88
open Regex.Strategy
9-
open String (ValidPos)
9+
open String (ValidPos ValidPosPlusOne)
1010

1111
namespace Regex.NFA
1212

@@ -97,8 +97,7 @@ where
9797
match h : materializeRegexGroups g₂ tag with
9898
| .some (first, last) =>
9999
have := (eqv₂ tag).1 h₁
100-
simp [h] at this
101-
simp [←this]
100+
grind
102101
| .none =>
103102
have := (eqv₂ tag).1 h₁
104103
simp [h] at this
@@ -108,8 +107,7 @@ where
108107
match h : materializeRegexGroups g₂ tag with
109108
| .some (first, last) =>
110109
have := (eqv₂ tag).2 h₂
111-
simp [h] at this
112-
simp [←this]
110+
grind
113111
| .none =>
114112
have := (eqv₂ tag).2 h₂
115113
simp [h] at this

regex/Regex/Data/String.lean

Lines changed: 101 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -209,7 +209,7 @@ def validPos (p : ValidPos s) : ValidPosPlusOne s :=
209209
def sentinel (s : String) : ValidPosPlusOne s :=
210210
⟨s.rawEndPos.offsetBy ⟨1⟩, .inr rfl⟩
211211

212-
@[elab_as_elim]
212+
@[elab_as_elim, cases_eliminator]
213213
def rec'.{u} {motive : ValidPosPlusOne s → Sort u}
214214
(validPos : (p : ValidPos s) → motive (validPos p))
215215
(sentinel : motive (sentinel s))
@@ -231,6 +231,7 @@ instance : Inhabited (ValidPosPlusOne s) := ⟨.validPos s.startValidPos⟩
231231
def isValid (p : ValidPosPlusOne s) : Bool :=
232232
p.offset ≠ s.rawEndPos.offsetBy ⟨1
233233

234+
@[grind _=_]
234235
theorem isValid_iff_isValid (p : ValidPosPlusOne s) : p.isValid ↔ p.offset.IsValid s := by
235236
cases p.isValidOrPlusOne with
236237
| inl h =>
@@ -295,6 +296,105 @@ instance : WellFoundedRelation (ValidPosPlusOne s) where
295296
rel p q := q < p
296297
wf := wellFounded_gt
297298

299+
def le (p₁ p₂ : ValidPosPlusOne s) : Prop :=
300+
p₁.offset ≤ p₂.offset
301+
302+
instance : LE (ValidPosPlusOne s) := ⟨le⟩
303+
304+
@[grind =]
305+
theorem le_iff {p₁ p₂ : ValidPosPlusOne s} : p₁ ≤ p₂ ↔ p₁.offset ≤ p₂.offset :=
306+
Iff.rfl
307+
308+
@[simp, grind =]
309+
theorem validPos_le_validPos_iff {p₁ p₂ : ValidPos s} : ValidPosPlusOne.validPos p₁ ≤ ValidPosPlusOne.validPos p₂ ↔ p₁ ≤ p₂ :=
310+
Iff.rfl
311+
312+
instance {s : String} (p₁ p₂ : ValidPosPlusOne s) : Decidable (p₁ ≤ p₂) :=
313+
decidable_of_iff' _ le_iff
314+
315+
theorem isValid_of_isValid_of_le {p₁ p₂ : ValidPosPlusOne s} (h : p₂.isValid) (le : p₁ ≤ p₂) : p₁.isValid := by
316+
cases p₁.isValidOrPlusOne with
317+
| inl h₁ => simpa [p₁.isValid_iff_isValid] using h₁
318+
| inr h₁ =>
319+
rw [p₂.isValid_iff_isValid] at h
320+
have le' : p₂.offset ≤ s.rawEndPos := h.le_rawEndPos
321+
have le'' : s.rawEndPos.offsetBy ⟨1⟩ ≤ s.rawEndPos := h₁ ▸ Pos.Raw.le_trans le le'
322+
simp [Pos.Raw.le_iff] at le''
323+
grind
324+
325+
@[grind .]
326+
theorem validPos_inj {p₁ p₂ : ValidPos s} (h : ValidPosPlusOne.validPos p₁ = ValidPosPlusOne.validPos p₂) : p₁ = p₂ := by
327+
simp only [validPos, ValidPosPlusOne.mk.injEq] at h
328+
exact ValidPos.ext h
329+
330+
def or (p₁ p₂ : ValidPosPlusOne s) : ValidPosPlusOne s :=
331+
if p₁.isValid then
332+
p₁
333+
else
334+
p₂
335+
336+
def orElse (p₁ : ValidPosPlusOne s) (p₂ : Unit → ValidPosPlusOne s) : ValidPosPlusOne s :=
337+
if p₁.isValid then
338+
p₁
339+
else
340+
p₂ ()
341+
342+
instance : OrElse (ValidPosPlusOne s) := ⟨orElse⟩
343+
344+
@[simp, grind =]
345+
theorem orElse_eq_or {p₁ : ValidPosPlusOne s} {p₂} : p₁.orElse p₂ = p₁.or (p₂ ()) := by
346+
grind [orElse, or]
347+
348+
@[simp, grind =]
349+
theorem hOrElse_eq_orElse {p₁ : ValidPosPlusOne s} {p₂} : HOrElse.hOrElse p₁ p₂ = p₁.orElse p₂ := rfl
350+
351+
@[simp, grind =]
352+
theorem or_valid {p₁ p₂ : ValidPosPlusOne s} (h : p₁.isValid) : p₁.or p₂ = p₁ := by
353+
simp [or, h]
354+
355+
@[simp, grind =]
356+
theorem or_not_valid {p₁ p₂ : ValidPosPlusOne s} (h : ¬p₁.isValid) : p₁.or p₂ = p₂ := by
357+
simp [or, h]
358+
359+
@[simp, grind =]
360+
theorem isValid_validPos {p : ValidPos s} : (ValidPosPlusOne.validPos p).isValid = true :=
361+
(isValid_iff_isValid (.validPos p)).mpr p.isValid
362+
363+
@[simp, grind =]
364+
theorem not_isValid_sentinel {s : String} : (ValidPosPlusOne.sentinel s).isValid = false := by
365+
simp [sentinel, isValid]
366+
@[simp, grind =]
367+
theorem sentinel_or {p₁ p₂ : ValidPosPlusOne s} (h : p₁ = .sentinel s) : p₁.or p₂ = p₂ := by
368+
grind
369+
370+
@[simp, grind =>]
371+
theorem validPos_or {p₁ p₂ : ValidPosPlusOne s} (h : p₁ = .validPos p) : p₁.or p₂ = p₁ := by
372+
grind
373+
374+
@[simp, grind =]
375+
theorem or_sentinel {p₁ p₂ : ValidPosPlusOne s} (h : p₂ = .sentinel s) : p₁.or p₂ = p₁ := by
376+
cases p₁ with
377+
| validPos p => simp
378+
| sentinel => simp [h]
379+
380+
@[grind .]
381+
theorem validPos_ne_sentinel {p : ValidPos s} : ValidPosPlusOne.validPos p ≠ ValidPosPlusOne.sentinel s := by
382+
intro eq
383+
have : isValid (.validPos p) = isValid (.sentinel s) := by grind
384+
simp at this
385+
386+
@[simp, grind =]
387+
theorem or_self {p : ValidPosPlusOne s} : p.or p = p := by
388+
cases p with
389+
| validPos p => simp
390+
| sentinel => simp
391+
392+
@[simp, grind =]
393+
theorem asValidPos_validPos {p : ValidPos s} : (ValidPosPlusOne.validPos p).asValidPos (by grind) = p := rfl
394+
395+
@[simp, grind =]
396+
theorem validPos_asValidPos {p : ValidPosPlusOne s} {h : p.isValid} : (ValidPosPlusOne.validPos (p.asValidPos h)) = p := rfl
397+
298398
end ValidPosPlusOne
299399

300400
def startValidPosPlusOne (s : String) : ValidPosPlusOne s :=

regex/Regex/Regex/Basic.lean

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import Regex.Regex.OptimizationInfo
66

77
set_option autoImplicit false
88

9-
open String (ValidPos Slice)
9+
open String (ValidPos ValidPosPlusOne Slice)
1010
open Regex.Data (Expr)
1111

1212
/--
@@ -51,10 +51,12 @@ Searches for the next match in the input string.
5151
-/
5252
def searchNext {s : String} (self : Regex) (p : ValidPos s) : Option Slice := do
5353
let slots ← captureNextBuf self 2 p
54-
let startPos ← slots[0]
55-
let stopPos ← slots[1]
56-
if h : startPos ≤ stopPos then
57-
pure ⟨s, startPos, stopPos, h⟩
54+
let startPos := slots[0]
55+
let stopPos := slots[1]
56+
if h : stopPos.isValid && startPos ≤ stopPos then
57+
have isStopPosValid : stopPos.isValid := by grind
58+
have h' : startPos.isValid := ValidPosPlusOne.isValid_of_isValid_of_le isStopPosValid (by grind)
59+
pure ⟨s, startPos.asValidPos h', stopPos.asValidPos isStopPosValid, ValidPosPlusOne.le_iff.mp (by grind)⟩
5860
else
5961
.none
6062

@@ -65,8 +67,7 @@ theorem searchNext_str_eq_some {s : String} {self : Regex} {p : ValidPos s} {s'
6567
(h : searchNext self p = some s') :
6668
s'.str = s := by
6769
simp [searchNext, Option.bind_eq_some_iff] at h
68-
obtain ⟨_, _, _, _, _, _, _, eq⟩ := h
69-
simp [←eq]
70+
grind
7071

7172
/--
7273
Constructs a `Regex` from a regular expression.

0 commit comments

Comments
 (0)