packages feed

symparsec-1.0.0: src/Symparsec/Parser/Take.hs

{-# LANGUAGE UndecidableInstances #-}

module Symparsec.Parser.Take where

import Symparsec.Parser.Common
import Singleraeh.Symbol ( RevCharsToSymbol, revCharsToSymbol )
import Singleraeh.List ( SList(..) )
import Singleraeh.Tuple ( STuple2(..) )
import Data.Type.Equality
import Singleraeh.Natural ( (%-) )
import Singleraeh.Either ( SEither(..) )
import TypeLevelShow.Natural
import GHC.TypeLits hiding ( ErrorMessage(..) )
import Unsafe.Coerce ( unsafeCoerce )
import DeFun.Core

-- | Return the next @n@ characters.
type Take n = 'PParser TakeChSym TakeEndSym '(n, '[])

type STakeS = STuple2 SNat (SList SChar)
type  TakeS = (Natural, [Char])

sTake :: SNat n -> SParser STakeS SSymbol (Take n)
sTake n = SParser takeChSym takeEndSym (STuple2 n SNil)

instance KnownNat n => SingParser (Take n) where
    type PS (Take n) = STakeS
    type PR (Take n) = SSymbol
    singParser' = sTake SNat

type TakeCh :: PParserCh TakeS Symbol
type family TakeCh ch s where
    TakeCh ch '(0, chs) = Done (RevCharsToSymbol chs)
    TakeCh ch '(n, chs) = Cont '(n-1, ch : chs)

type TakeChSym :: ParserChSym TakeS Symbol
data TakeChSym f
type instance App TakeChSym f = TakeChSym1 f

takeChSym :: SParserChSym STakeS SSymbol TakeChSym
takeChSym = Lam2 $ \ch (STuple2 n chs) ->
    case testEquality n (SNat @0) of
      Just Refl -> SDone $ revCharsToSymbol chs
      Nothing   ->
        unsafeCoerce $ SCont $ STuple2 (n %- (SNat @1)) (SCons ch chs)

type TakeChSym1 :: ParserChSym1 TakeS Symbol
data TakeChSym1 ch s
type instance App (TakeChSym1 ch) s = TakeCh ch s

type TakeEnd :: PParserEnd TakeS Symbol
type family TakeEnd s where
    TakeEnd '(0, chs) = Right (RevCharsToSymbol chs)
    TakeEnd '(n, _)   = Left (ETakeEnd n)

type ETakeEnd n = EBase "Take"
    (      Text "tried to take "
      :<>: Text (ShowNatDec n) :<>: Text " chars from empty string")

eTakeEnd :: SNat n -> SE (ETakeEnd n)
eTakeEnd sn = withKnownSymbol (sShowNatDec sn) singE

type TakeEndSym :: ParserEndSym TakeS Symbol
data TakeEndSym s
type instance App TakeEndSym s = TakeEnd s

takeEndSym :: SParserEndSym STakeS SSymbol TakeEndSym
takeEndSym = Lam $ \(STuple2 n chs) ->
    case testEquality n (SNat @0) of
      Just Refl -> SRight $ revCharsToSymbol chs
      Nothing   -> unsafeCoerce $ SLeft $ eTakeEnd n