\begin{code} {-# OPTIONS --experimental-lossy-unification --safe #-} open import Prelude renaming (tt to ⟨⟩) open import Signatures open import FinitenessConditions open import FreeMonad.PackagedTheory open import Data.String.Properties renaming (discreteChar to _≟_) module Effects.Parser (choiceString : SplitQuotientedChoiceω String) where open import Cubical.Data.List.Properties using (isOfHLevelList) open import Data.Tree import Effects.NonDet as ℕ𝔻 import Effects.State String choiceString isSetString as 𝕊 open import FreeMonad.Combinations.Tensor ℕ𝔻.ℕ𝔻-theory 𝕊.𝕊-theory renaming (combinedTheory to ℙ-theory) public open 𝕊 using (`get; `put) open ℕ𝔻.NonDet using (`<|>; `fail) module SyntaxExamples where open import FreeMonad.Syntax (ℕ𝔻.ℕ𝔻 ⊞ 𝕊.𝕊) open SyntaxBind \end{code} %<*put-synt> \begin{code} put : String → Syntax ⊤ put s = op (inr (`put s) , var) \end{code} %</put-synt> %<*get-synt> \begin{code} get : Syntax String get = op (inr `get , var) \end{code} %</get-synt> %<*fail-synt> \begin{code} fail : Syntax A fail = op (inl `fail , absurd) \end{code} %</fail-synt> \begin{code} infixl 6 _<|>_ _<|>_ : Syntax A → Syntax A → Syntax A x <|> y = op (inl `<|> , bool x y) guard : Bool → Syntax ⊤ guard = bool fail (var ⟨⟩) \end{code} %<*eof-synt> \begin{code} eof : Syntax ⊤ eof = do s ← get; guard (null s) \end{code} %</eof-synt> %<*any-char-synt-sig> \begin{code} any-char : Syntax Char \end{code} %</any-char-synt-sig> %<*any-char-synt> \begin{code} any-char = do c ∷ cs ← get where [] → fail put cs return c \end{code} %</any-char-synt> %<*char-synt-sig> \begin{code} char : Char → Syntax ⊤ \end{code} %</char-synt-sig> %<*char-synt> \begin{code} char c₁ = do c₂ ← any-char if (does (c₁ ≟ c₂)) then return ⟨⟩ else fail \end{code} %</char-synt> \begin{code} open import FreeMonad.Quotiented ℙ-theory public import FreeMonad.Syntax as Synt open Synt (ℕ𝔻.ℕ𝔻 ⊞ 𝕊.𝕊) using (Op⟦_⟧; op; var) open Signature (ℕ𝔻.ℕ𝔻 ⊞ 𝕊.𝕊) open ℕ𝔻.NonDet.Op private module DisplayPut where \end{code} %<*put-term> \begin{code} put : String → Term ⊤ put s = [ op (inr (`put s) , var) ] \end{code} %</put-term> \begin{code} put : String → Term ⊤ put s = [ inj₂ (𝕊.Synt.put s) ] get : Term String get = [ inj₂ 𝕊.Synt.get ] fail : Term A fail = [ op (inl `fail , absurd) ] infixl 6 _<|>_ \end{code} %<*alt-term> \begin{code} _<|>_ : Term A → Term A → Term A x <|> y = opₜ (inl `<|> , λ i → if i then y else x) \end{code} %</alt-term> \begin{code} guard : Bool → Term ⊤ guard = bool fail (return ⟨⟩) eof : Term ⊤ eof = do s ← get; guard (null s) any-char : Term Char any-char = get >>= maybe fail (λ { (c , cs) → put cs >> return c }) ∘ uncons char : Char → Term ⊤ char c₁ = do c₂ ← any-char guard (does (c₁ ≟ c₂)) modify : (String → String) → Term ⊤ modify f = do s ← get; put (f s) push : String → Term ⊤ push s = modify (s ++_) fail->>= : (k : A → Term B) → fail >>= k ≡ fail fail->>= k = (fail >>= k) ≡⟨ cong {x = k ∘ absurd} (opₜ ∘ _,_ (inl `fail )) (funExt λ () ) ⟩ opₜ (inl `fail , [_] ∘ absurd) ≡⟨ opₜ-com-[] absurd ⟩ [ op (inl `fail , absurd) ] ≡⟨ cong ([_] ∘ op ∘ _,_ (inl `fail)) (funExt λ ()) ⟩ fail ∎ \end{code} %<*assoc-term-sig> \begin{code} <|>-assoc : (x y z : Term A) → (x <|> y) <|> z ≡ x <|> (y <|> z) \end{code} %</assoc-term-sig> \begin{code} <|>-assoc = elimProp3/ (λ _ _ _ → squash/ _ _) λ x y z → let h = eq/ _ _ (eqₜ (inl (inl ℕ𝔻.Synt.assoc)) ⟨⟩ λ { zero → x ; (suc zero) → y ; _ → z }) in cong ([_] ∘ Synt.op ∘ _,_ (inl `<|>)) (funExt (bool (cong (op ∘ _,_ (inl `<|>)) (funExt (bool refl refl))) refl)) ; h ; cong ([_] ∘ Synt.op ∘ _,_ (inl `<|>)) (funExt (bool refl (cong (op ∘ _,_ (inl `<|>)) (funExt (bool refl refl))))) <|>-idem : (x : Term A) → x <|> x ≡ x <|>-idem = elimProp/ (λ _ → squash/ _ _) λ x → cong ([_] ∘ Synt.op ∘ _,_ (inl `<|>)) (funExt (bool refl refl)) ; eq/ _ _ (eqₜ (inl (inl ℕ𝔻.Synt.idem)) ⟨⟩ λ _ → x) open import Effects.NonDetState String choiceString isSetString as NDS using (sef-<|>; fail-absurd; <|>-conj) module _ {A : Type} where <|>-distrib : \end{code} %<*alt-distrib> \begin{code} ∀ (c : Char) (p q : Term A) → (do char c; p) <|> (do char c; q) ≡ (do char c; p <|> q) \end{code} %</alt-distrib> \begin{code} <|>-distrib c x y = sym (sef-<|> (char c) (const y) (const x)) open import Truth.MonoLevel ℓzero open import Truth using (_|≡|_) open import Hoare.Definition ℙ-theory open import Hoare.Calculus ℙ-theory open import Hoare.Lemmas ℙ-theory fail-anything : \end{code} %<*fail-anything> \begin{code}[number=fail-anything] (ψ : Term Ω) → ⟅⟆ fail ⟅ ψ ⟆ \end{code} %</fail-anything> \begin{code} fail-anything ψ = fail-absurd (const ψ) module _ {A : Type} {ϕ : Term Ω} {p q : Term A} {ψ : A → Term Ω} where infixl 6 _⟨<|>⟩_ _⟨<|>⟩_ : \end{code} %<*nondet-conj> \begin{code} (⟅ ϕ ⟆ x ← p ⟅ ψ x ⟆) → (⟅ ϕ ⟆ x ← q ⟅ ψ x ⟆) → (⟅ ϕ ⟆ x ← p <|> q ⟅ ψ x ⟆) \end{code} %</nondet-conj> \begin{code} lhs ⟨<|>⟩ rhs = <|>-conj ϕ ψ q p rhs lhs module NoInpAssertion where \end{code} %<*no-input> \begin{code} no-input : Term A → Term Ω no-input p = do s₁ ← get; p; s₂ ← get; return (s₁ |≡| s₂) \end{code} %</no-input> \begin{code} open DisplayGlobal using (𝒢) module NoInpG where no-input : Term A → Type _ \end{code} %<*no-input-g> \begin{code} no-input p = 𝒢 do s₁ ← get; x ← p; s₂ ← get; return (x , s₁ |≡| s₂) \end{code} %</no-input-g> %<*remaining> \begin{code} remaining : String → Term Ω remaining s₁ = do s₂ ← get return (s₁ |≡| s₂) \end{code} %</remaining> %<*no-input-h> \begin{code} no-input : Term A → Type- no-input p = ∀ s → ⟅ remaining s ⟆ _ ← p ⟅ remaining s ⟆ \end{code} %</no-input-h> \begin{code} module _ {A : Type} where _∈_↦_ : String → Term A → A → Type _ \end{code} %<*parses-to> \begin{code} s ∈ p ↦ v = ∀ r → ⟅ remaining (s ++ r) ⟆ v′ ← p ⟅ return (v |≡| v′) ⟨∧⟩ remaining r ⟆ \end{code} %</parses-to> \begin{code} open import Truth hiding (True; False; Ω) open import Effects.NonDet using (ℕ𝔻-theory; term-iso) open import Effects.State.Tensor String choiceString isSetString ℕ𝔻-theory as ST using (stateT-iso) open import Data.Set hiding (ψ) put-put : ∀ s₁ s₂ (k : Term A) → (do put s₁; put s₂; k) ≡ (do put s₂; k) put-put s₁ s₂ k = cong (λ p → do p ; k) (eq/ _ _ (lawₜ (inl (inr 𝕊.Synt.put-put)) (s₁ , s₂))) get-put : ∀ (k : Term A) → (do s ← get; put s; k) ≡ k get-put k = cong (_>> k) (eq/ _ _ (lawₜ (inl (inr 𝕊.Synt.get-put)) _)) put-get : ∀ s (k : String → Term A) → (do put s; s′ ← get; k s′) ≡ (do put s; k s) put-get s k = cong (λ p → do r ← p; k r) (eq/ _ _ (lawₜ (inl (inr 𝕊.Synt.put-get)) s)) get-put-get : (k : String → Term A) → (do s ← get; put s; k s) ≡ (do s ← get; k s) get-put-get k = sym (s ⇐ get ;/ put-get s k) ; get-put (get >>= k) get-nop : (k : Term A) → (get >> k) ≡ k get-nop k = sym (get-put-get (λ _ → k)) ; get-put k get-get : ∀ (k : String → String → Term B) → (do s₁ ← get s₂ ← get k s₁ s₂) ≡ (do s ← get k s s) get-get k = (do s₁ ← get s₂ ← get k s₁ s₂) ≡˘⟨ get-put _ ⟩ (do s ← get put s s₁ ← get s₂ ← get k s₁ s₂) ≡⟨ s ⇐ get ;/ put-get s _ ⟩ (do s₁ ← get put s₁ s₂ ← get k s₁ s₂) ≡⟨ s₁ ⇐ get ;/ put-get s₁ _ ⟩ (do s ← get put s k s s) ≡⟨ get-put-get _ ⟩ (do s ← get k s s) ∎ module _ {A : Type} where parser-iso : Term A ⇔ (String → 𝒦 (A × String)) parser-iso .fun = _∘_ (term-iso .fun) ∘ stateT-iso .fun parser-iso .inv = stateT-iso .inv ∘ (_∘_ (term-iso .inv)) parser-iso .rightInv x = cong (_∘_ (term-iso .fun) ) (stateT-iso .rightInv _) ; funExt (term-iso .rightInv ∘′ x) parser-iso .leftInv x = cong (stateT-iso .inv) (funExt λ s → term-iso .leftInv _) ; stateT-iso .leftInv x sef-rem : ∀ s → SEF a (remaining s) sef-rem s k = assoc get _ _ ; get-nop k det-rem : ∀ s → DET a (remaining s) det-rem s k = (do x ← remaining s ; y ← remaining s ; k x y) ≡⟨ assoc get _ _ ⟩ (do s₁ ← get; y ← remaining s ; k (s |≡| s₁) y) ≡⟨ s₁ ⇐ get ;/ assoc get _ _ ⟩ (do s₁ ← get; s₂ ← get; k (s |≡| s₁) (s |≡| s₂)) ≡⟨ get-get _ ⟩ (do s₁ ← get; k (s |≡| s₁) (s |≡| s₁)) ≡˘⟨ s₁ ⇐ get ;/ get-nop _ ⟩ (do s₁ ← get; s₂ ← get; k (s |≡| s₁) (s |≡| s₁)) ≡˘⟨ s₁ ⇐ get ;/ assoc get _ _ ⟩ (do s₁ ← get; remaining s; k (s |≡| s₁) (s |≡| s₁)) ≡˘⟨ assoc get _ _ ⟩ (do x ← remaining s ; remaining s ; k x x) ∎ ⟨remaining⟩ : String → Assertion ℓzero ⟨remaining⟩ s .fst = remaining s ⟨remaining⟩ s .snd .fst = sef-rem s ⟨remaining⟩ s .snd .snd = det-rem s put-remaining : ∀ s → ⟅⟆ _ ← put s ⟅ remaining s ⟆ put-remaining s .proof k = (do put s b ← remaining s k ⟨⟩ (True |→| b)) ≡⟨ cong (put s >>_) (assoc get _ _) ⟩ (do put s s′ ← get k ⟨⟩ (True |→| (s |≡| s′))) ≡⟨ put-get s _ ⟩ (do put s k ⟨⟩ (True |→| (s |≡| s))) ≡⟨ _ ⇐ put s ;/ cong (k ⟨⟩) (proves λ _ → ∣ refl ∣) ⟩ (do put s k ⟨⟩ True) ≡˘⟨ _ ⇐ put s ;/ sef-rem s (k ⟨⟩ True) ⟩ (do put s remaining s k ⟨⟩ True) ∎ head-def : A → List A → A head-def x [] = x head-def _ (x ∷ _) = x parse-char-lemma : ∀ (k : ⊤ → Ω → Term A) (c₁ c₂ c₃ : Char) (s₁ s₂ : String) (eq : Dec (c₂ ≡ c₃)) → (do put s₂ guard (does eq) s₃ ← get k ⟨⟩ ((c₁ List.∷ s₁) |≡| (c₃ ∷ s₂) |→| ((c₁ |≡| c₂) |∧| (s₁ |≡| s₃)))) ≡ (do put s₂ guard (does eq) s₃ ← get k ⟨⟩ True) parse-char-lemma k c₁ c₂ c₃ s₁ s₂ (no c₁≢c₂) = cong (put s₂ >>_) (fail->>= {A = ⊤} (λ _ → get >>= λ s₃ → k ⟨⟩ ((c₁ List.∷ s₁) |≡| (c₃ List.∷ s₂) |→| ((c₁ |≡| c₂) |∧| (s₁ |≡| s₃)))) ; sym (fail->>= {A = ⊤} (λ _ → get >>= λ s₃ → k ⟨⟩ True))) parse-char-lemma k c₁ c₂ c₃ s₁ s₂ (yes c₁≡c₂) = (do put s₂ s₃ ← get k ⟨⟩ ((c₁ List.∷ s₁) |≡| (c₃ ∷ s₂) |→| ((c₁ |≡| c₂) |∧| (s₁ |≡| s₃)))) ≡⟨ put-get s₂ _ ⟩ (do put s₂ k ⟨⟩ ((c₁ List.∷ s₁) |≡| (c₃ ∷ s₂) |→| ((c₁ |≡| c₂) |∧| (s₁ |≡| s₂)))) ≡⟨ cong (λ e → do put s₂; k ⟨⟩ e) ((λ _ → _) iff λ _ → ∥rec∥ (isProp× squash squash) λ p → ∣ cong (head-def c₁) p ; sym c₁≡c₂ ∣ , ∣ cong tail p ∣ ) ⟩ (do put s₂ k ⟨⟩ True) ≡˘⟨ put-get s₂ _ ⟩ (do put s₂ s₃ ← get k ⟨⟩ True) ∎ parse-char : \end{code} %<*parse-char> \begin{code}[number=parse-char] ∀ c₁ c₂ s → ⟅ remaining (c₁ ∷ s) ⟆ char c₂ ⟅ return (c₁ |≡| c₂) ⟨∧⟩ remaining s ⟆ \end{code} %</parse-char> \begin{code} parse-char c₁ c₂ s .proof k = (do a ← remaining (c₁ ∷ s) char c₂ b ← return (c₁ |≡| c₂) ⟨∧⟩ remaining s k ⟨⟩ (a |→| b)) ≡⟨ assoc get _ _ ⟩ (do s₁ ← get char c₂ b ← return (c₁ |≡| c₂) ⟨∧⟩ remaining s k ⟨⟩ ((c₁ ∷ s) |≡| s₁ |→| b)) ≡⟨ s₁ ⇐ get ;/ _ ⇐ char c₂ ;/ assoc (return (c₁ |≡| c₂)) (λ b′ → remaining s >>= λ b″ → return (b′ |∧| b″) ) _ ; assoc (remaining s) _ _ ; assoc get _ _ ⟩ (do s₁ ← get char c₂ s₂ ← get k ⟨⟩ ((c₁ ∷ s) |≡| s₁ |→| ((c₁ |≡| c₂) |∧| (s |≡| s₂)))) ≡⟨⟩ (do s₁ ← get (get >>= maybe fail (λ { (c , cs) → put cs >> return c }) ∘ uncons) >>= (guard ∘ does ∘′ (c₂ ≟_)) s₂ ← get k ⟨⟩ ((c₁ ∷ s) |≡| s₁ |→| ((c₁ |≡| c₂) |∧| (s |≡| s₂)))) ≡⟨ s₁ ⇐ get ;/ assoc (get >>= maybe fail (λ { (c , cs) → put cs >> return c }) ∘ uncons) _ _ ⟩ (do s₁ ← get c₃ ← (get >>= maybe fail (λ { (c , cs) → put cs >> return c }) ∘ uncons) guard (does (c₂ ≟ c₃)) s₂ ← get k ⟨⟩ ((c₁ ∷ s) |≡| s₁ |→| ((c₁ |≡| c₂) |∧| (s |≡| s₂)))) ≡⟨ s₁ ⇐ get ;/ assoc get _ _ ⟩ (do s₁ ← get s₃ ← get c₃ ← maybe fail (λ { (c , cs) → put cs >> return c }) (uncons s₃) guard (does (c₂ ≟ c₃)) s₂ ← get k ⟨⟩ ((c₁ ∷ s) |≡| s₁ |→| ((c₁ |≡| c₂) |∧| (s |≡| s₂)))) ≡⟨ get-get _ ⟩ (do s₁ ← get c₃ ← maybe fail (λ { (c , cs) → put cs >> return c }) (uncons s₁) guard (does (c₂ ≟ c₃)) s₂ ← get k ⟨⟩ ((c₁ ∷ s) |≡| s₁ |→| ((c₁ |≡| c₂) |∧| (s |≡| s₂)))) ≡⟨ cong (get >>=_) (funExt λ { [] → fail->>= (λ c₃ → do guard (does (c₂ ≟ c₃)); s₂ ← get; k ⟨⟩ ((c₁ ∷ s) |≡| [] |→| ((c₁ |≡| c₂) |∧| (s |≡| s₂) ))) ; sym (fail->>= (λ c₃ → do guard (does (c₂ ≟ c₃)); s₂ ← get; k ⟨⟩ True)) ; (c₃ ∷ cs) → parse-char-lemma k c₁ c₂ c₃ _ _ (c₂ ≟ c₃) }) ⟩ (do s₁ ← get c₃ ← maybe fail (λ { (c , cs) → put cs >> return c }) (uncons s₁) guard (does (c₂ ≟ c₃)) s₂ ← get k ⟨⟩ True) ≡˘⟨ assoc get _ _ ; (s₁ ⇐ get ;/ _ ⇐ char c₂ ;/ assoc (return (c₁ |≡| c₂)) (λ b′ → remaining s >>= λ b″ → return (b′ |∧| b″) ) _ ; assoc (remaining s) _ _ ; assoc get _ _) ; (s₁ ⇐ get ;/ assoc (get >>= maybe fail (λ { (c , cs) → put cs >> return c }) ∘ uncons) _ _) ; (s₁ ⇐ get ;/ assoc get _ _) ; get-get _ ⟩ (do a ← remaining (c₁ ∷ s) char c₂ b ← return (c₁ |≡| c₂) ⟨∧⟩ remaining s k ⟨⟩ True) ∎ module _ {A : Type} (Ψ : A → Assertion 0) where private ψ = fst ∘ Ψ char-ne : ∀ c₁ c₂ s (t : Term A) → c₁ ≢ c₂ → ⟅ remaining (c₁ ∷ s) ⟆ x ← char c₂ >> t ⟅ ψ x ⟆ char-ne c₁ c₂ s t c₁≢c₂ = seq′ (⟨remaining⟩ (c₁ ∷ s)) (λ _ → (return (c₁ |≡| c₂) ⟨∧⟩ remaining s) , sef-<$> _ _ (⟨remaining⟩ s .snd .fst) , det-<$> _ _ (⟨remaining⟩ s .snd .snd)) Ψ (parse-char c₁ c₂ s) (λ _ → ≡⟅∙⟆ (sym (cong (_⟨∧⟩ remaining s) (cong return (disproves (∥rec∥ (λ ()) c₁≢c₂))) ; False⟨∧⟩ (remaining s) (sef-rem s) )) (False⟅→⟆ t ψ)) from-dec-false : (d : Dec A) → T (! (does d)) → ¬ A from-dec-false (no ¬p) t = ¬p \end{code}