diff --git a/src/Util/String.agda b/src/Util/String.agda new file mode 100644 index 00000000..2fd99a6c --- /dev/null +++ b/src/Util/String.agda @@ -0,0 +1,159 @@ +module Util.String where + +open import Data.Bool using (true; false) +open import Data.Char as Char using (Char) +open import Data.Digit using (toDigits; fromDigits; Decimal; Expansion) +open import Data.Empty using (⊥-elim) +open import Data.Fin as Fin using (Fin) +import Data.Fin.Properties as Fin +open import Data.List as List using (List; []; _∷_; _++_) +import Data.List.Properties as List +open import Data.Maybe using (Maybe; nothing; just; maybe′) +open import Data.Nat using (ℕ; zero; suc; _+_; _∸_; _/_; _%_; _≤_; _<_; _≤?_; s≤s) +import Data.Nat.Properties as ℕ +import Data.Nat.Show as ℕ +open import Data.Product using (_×_; _,_; proj₁; proj₂; map₁; map₂; swap) +open import Data.String as String using (String) +open import Function using (id; _∘_) +open import Relation.Binary.PropositionalEquality as Eq using (_≡_; _≗_; refl) +open import Relation.Nullary.Decidable using (yes; no) + +open Eq.≡-Reasoning using (step-≡; step-≡˘; _≡⟨⟩_; _∎) + +import Agda.Builtin.Equality.Erase + +-- The following axioms are needed to proof properties about string +-- computations but are not yet provided by Agda. See +-- https://github.com/agda/agda/issues/6119 for details. +fromList-toList : ∀ (s : String) → String.fromList (String.toList s) ≡ s +fromList-toList s = Agda.Builtin.Equality.Erase.primEraseEquality trustMe + where postulate trustMe : _ + +toList-fromList : ∀ (cs : List Char) → String.toList (String.fromList cs) ≡ cs +toList-fromList cs = Agda.Builtin.Equality.Erase.primEraseEquality trustMe + where postulate trustMe : _ + +fromℕ-toℕ : ∀ (c : Char) → Char.fromℕ (Char.toℕ c) ≡ c +fromℕ-toℕ c = Agda.Builtin.Equality.Erase.primEraseEquality trustMe + where postulate trustMe : _ + +-- `Char.fromℕ` is only well behaved for unicode code points from the basic +-- plane. Code points in the basic plane are U+0000 - U+D7FF and U+E000 - +-- U+FFFF. However, we only need the first range so we use a simplified +-- precondition. +toℕ-fromℕ : ∀ (c : ℕ) → c < 0xD7FF → Char.toℕ (Char.fromℕ c) ≡ c +toℕ-fromℕ c p = Agda.Builtin.Equality.Erase.primEraseEquality trustMe + where postulate trustMe : _ + + +-- Converts the characters '0' to '9' to their equivalent number. Returns +-- `nothing` for any other character. +Char→Decimal : Char → Maybe Decimal +Char→Decimal c with Char.toℕ '0' ≤? Char.toℕ c with Char.toℕ c ≤? Char.toℕ '9' +... | no _ | _ = nothing +... | yes _ | no _ = nothing +... | yes 0≤c | yes c≤9 = just (Fin.fromℕ< {Char.toℕ c ∸ Char.toℕ '0'} (ℕ.+-monoʳ-≤ 1 (ℕ.∸-monoˡ-≤ (Char.toℕ '0') c≤9))) + +-- Inverse of `Char→Decimal` +Decimal→Char : Decimal → Char +Decimal→Char d = Char.fromℕ (Fin.toℕ d + Char.toℕ '0') + +-- Proof that `Char→Decimal` reverses `Decimal→Char`. +Char→Decimal-Decimal→Char : (d : Decimal) → Char→Decimal (Decimal→Char d) ≡ just d +Char→Decimal-Decimal→Char d with Char.toℕ '0' ≤? Char.toℕ (Decimal→Char d) with Char.toℕ (Decimal→Char d) ≤? Char.toℕ '9' +... | no p | _ = ⊥-elim (p (ℕ.≤-trans (ℕ.m≤n+m (Char.toℕ '0') (Fin.toℕ d)) (ℕ.≤-reflexive (Eq.sym (toℕ-fromℕ (Fin.toℕ d + (Char.toℕ '0')) (ℕ.m≤n⇒m≤n+o 55236 (s≤s (ℕ.+-monoˡ-≤ (Char.toℕ '0') (Fin.toℕ≤n d))))))))) +... | yes _ | no p = ⊥-elim (p (ℕ.≤-trans (ℕ.≤-reflexive (toℕ-fromℕ (Fin.toℕ d + (Char.toℕ '0')) (ℕ.m≤n⇒m≤n+o 55236 (s≤s (ℕ.+-monoˡ-≤ (Char.toℕ '0') (Fin.toℕ≤n d)))))) (ℕ.+-monoˡ-≤ (Char.toℕ '0') (ℕ.≤-pred (Fin.toℕ