This PR adds the basic infrastructure to perform termination proofs about `String.ValidPos` and `String.Slice.Pos`. We choose approach where the intended way to do termination arguments is to argue about the position itself rather than some projection of it like `remainingBytes`. The types `String.ValidPos` and `String.Slice.Pos` are equipped with a `WellFoundedRelation` instance given by the greater-than relation. This means that if a function takes a position `p` and performs a recursive call on `q`, then the decreasing obligation will be `p < q`. This works well in the common case where `q` is `p.next h`, in which case the goal `p < p.next h` is solved by the simplifier. For stepping through a string backwards, we introduce a type synonym with a `WellFoundedRelation` instance given by the less-than relation. This means that if a function takes a position `p` and performs a recursive call on `q` and specifies `termination_by p.down`, then the decreasing obligation will be `q < p`. This works well in the case where `q` is `p.prev h`, in which case the goal `p.prev h < p` is solved by the simplifier. For termination arguments invoving multiple strings, the lower-level primitive `p.remainingBytes` (landing in `Nat`) is also available. In a future PR, we will additionally provide the necessary typeclasses instances to register `String.ValidPos` and `String.Slice.Pos` with `grind` to make complex termination arguments more convenient in user code.
42 lines
1.1 KiB
Text
42 lines
1.1 KiB
Text
module
|
|
|
|
-- Test that termination proofs for stepping through a string with `next` and `prev` works.
|
|
|
|
def isConsonant (i : String.ValidPos str) : Bool :=
|
|
match i.get! with
|
|
| 'a' | 'e' | 'i' | 'o' | 'u' => false
|
|
| 'y' =>
|
|
if h : i = str.startValidPos then true
|
|
else !isConsonant (i.prev h)
|
|
| _ => true
|
|
termination_by i.down
|
|
|
|
def measure₁ (word : String) : Nat :=
|
|
let rec aux (pos : String.ValidPos word) (inVowel : Bool) (count : Nat) : Nat :=
|
|
match h : pos.next? with
|
|
| some next =>
|
|
if !isConsonant pos then
|
|
aux next true count
|
|
else if inVowel then
|
|
aux next false (count + 1)
|
|
else
|
|
aux next false count
|
|
| none => count
|
|
termination_by pos
|
|
|
|
aux word.startValidPos false 0
|
|
|
|
def measure₂ (word : String) : Nat :=
|
|
let rec aux (pos : String.ValidPos word) (inVowel : Bool) (count : Nat) : Nat :=
|
|
if h : pos = word.endValidPos then count
|
|
else
|
|
let next := pos.next h
|
|
if !isConsonant pos then
|
|
aux next true count
|
|
else if inVowel then
|
|
aux next false (count + 1)
|
|
else
|
|
aux next false count
|
|
termination_by pos
|
|
|
|
aux word.startValidPos false 0
|