\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}