symparsec-1.1.1: src/Symparsec/Run.hs
{-# LANGUAGE UndecidableInstances #-}
{-# LANGUAGE AllowAmbiguousTypes #-} -- for reifying/singled parsers
module Symparsec.Run where -- ( Run, ERun(..) ) where
import Symparsec.Parser
import GHC.TypeLits hiding ( ErrorMessage(..), fromSNat )
import GHC.TypeNats ( fromSNat )
import GHC.TypeLits qualified as TE
import DeFun.Core
import TypeLevelShow.Doc
import TypeLevelShow.Utils ( ShowChar )
import TypeLevelShow.Natural ( ShowNatDec )
import Singleraeh.Tuple ( STuple2(..) )
import Singleraeh.Maybe ( SMaybe(..) )
import Singleraeh.Either ( SEither(..) )
import Singleraeh.Symbol
( sConsSymbol, sUnconsSymbol, ReconsSymbol, sReconsSymbol )
import Singleraeh.Natural ( (%+) )
import Singleraeh.Demote
-- | Run the given parser on the given 'Symbol', returning an 'TE.ErrorMessage'
-- on failure.
type Run :: PParser s r -> Symbol -> Either TE.ErrorMessage (r, Symbol)
type Run p sym = MapLeftRender (Run' p sym)
type MapLeftRender :: Either PERun r -> Either TE.ErrorMessage r
type family MapLeftRender eer where
MapLeftRender (Right a) = Right a
MapLeftRender (Left e) = Left (RenderPDoc (PrettyERun e))
-- | Run the given parser on the given 'Symbol', returning a 'PERun' on failure.
type Run' :: PParser s r -> Symbol -> Either PERun (r, Symbol)
type family Run' p str where
Run' ('PParser pCh pEnd s0) str =
RunStart pCh pEnd s0 (UnconsSymbol str)
-- | Run the given parser on the given 'Symbol', emitting a type error on
-- failure.
--
-- This /would/ be useful for @:k!@ runs, but it doesn't work properly with
-- 'TE.TypeError's, printing @= (TypeError ...)@ instead of the error message.
-- Alas! Instead, do something like @> Proxy \@(RunTest ...)@.
type RunTest :: PParser s r -> Symbol -> (r, Symbol)
type RunTest p sym = MapLeftTypeError (Run p sym)
-- | Run the given parser on the given 'Symbol', returning a 'PERun' on failure,
-- and ignoring any remaining non-consumed characters.
type Run'_ :: PParser s r -> Symbol -> Either TE.ErrorMessage r
type Run'_ p str = Run'_Inner (Run' p str)
type Run'_Inner :: Either PERun (a, b) -> Either TE.ErrorMessage a
type family Run'_Inner eeab where
Run'_Inner (Right '(a, b)) = Right a
Run'_Inner (Left e) = Left (RenderPDoc (PrettyERun e))
-- | Run the singled version of type-level parser on the given 'String',
-- returning an 'ERun' on failure.
--
-- You must provide a function for demoting the singled return type.
-- ('Singleraeh.Demote.demote' can do this for you automatically.)
run'
:: forall {s} {r} (p :: PParser s r) r'. SingParser p
=> (forall a. PR p a -> r') -> String -> Either (ERun String) (r', String)
run' demotePR str = withSomeSSymbol str $ \sstr ->
case sRun' (singParser @p) sstr of
SRight (STuple2 pr sstr') -> Right (demotePR pr, fromSSymbol sstr')
SLeft e -> Left $ demoteSERun e
-- TODO prettify error. actually run' needs an error demoter :| such a pain
runTest
:: forall {s} {r} (p :: PParser s r). (SingParser p, Demotable (PR p))
=> String -> (Demote (PR p), String)
runTest str =
case run' @p demote str of
Right r -> r
Left e -> error $ show e
sRun'
:: SParser ss sr p
-> SSymbol str
-> SEither SERun (STuple2 sr SSymbol) (Run' p str)
sRun' (SParser pCh pEnd s0) str =
sRunStart pCh pEnd s0 (sUnconsSymbol str)
type family RunStart pCh pEnd s0 mstr where
RunStart pCh pEnd s0 (Just '(ch, str)) =
RunCh pCh pEnd 0 ch (UnconsSymbol str) (pCh @@ ch @@ s0)
RunStart pCh pEnd s0 Nothing =
RunEnd0 (pEnd @@ s0)
sRunStart
:: SParserChSym ss sr pCh
-> SParserEndSym ss sr pEnd
-> ss s0
-> SMaybe (STuple2 SChar SSymbol) mstr
-> SEither SERun (STuple2 sr SSymbol) (RunStart pCh pEnd s0 mstr)
sRunStart pCh pEnd s0 = \case
SJust (STuple2 ch str) ->
sRunCh pCh pEnd (SNat @0) ch (sUnconsSymbol str) (pCh @@ ch @@ s0)
SNothing -> sRunEnd0 (pEnd @@ s0)
type MapLeftTypeError :: Either TE.ErrorMessage a -> a
type family MapLeftTypeError eea where
MapLeftTypeError (Right a) = a
MapLeftTypeError (Left e) = TE.TypeError e
-- | Inspect character parser result.
--
-- This is purposely written so that the main case is at the top, and a single
-- equation (we parse, prepare next character and inspect character parser
-- result at the same time). My hope is that this keeps GHC fast.
type family RunCh pCh pEnd idx ch' mstr res where
-- | OK, and more to come: parse next character
RunCh pCh pEnd idx ch' (Just '(ch, sym)) (Cont s) =
RunCh pCh pEnd (idx+1) ch (UnconsSymbol sym) (pCh @@ ch @@ s)
-- | OK, and we're at the end of the string: run end parser
RunCh pCh pEnd idx ch' Nothing (Cont s) =
RunEnd idx ch' (pEnd @@ s)
-- | OK, and we're finished early: return value and remaining string
RunCh pCh pEnd idx ch' mstr (Done r) =
Right '(r, ConsSymbol ch' (ReconsSymbol mstr))
-- | Parse error: return error
RunCh pCh pEnd idx ch' mstr (Err e) =
Left ('ERun idx ch' e)
sRunCh
:: SParserChSym ss sr pCh
-> SParserEndSym ss sr pEnd
-> SNat idx
-> SChar chPrev
-> SMaybe (STuple2 SChar SSymbol) mstr
-> SResult ss sr res
-> SEither SERun (STuple2 sr SSymbol) (RunCh pCh pEnd idx chPrev mstr res)
sRunCh pCh pEnd idx chPrev mstr = \case
SCont s ->
case mstr of
SJust (STuple2 ch str) ->
sRunCh pCh pEnd (idx %+ (SNat @1)) ch (sUnconsSymbol str)
(pCh @@ ch @@ s)
SNothing ->
sRunEnd idx chPrev (pEnd @@ s)
SDone r -> SRight (STuple2 r (sConsSymbol chPrev (sReconsSymbol mstr)))
SErr e -> SLeft (SERun idx chPrev e)
-- | Inspect end parser result.
type RunEnd
:: Natural -> Char
-> Either PE r
-> Either PERun (r, Symbol)
type family RunEnd idx ch res where
RunEnd idx ch (Right r) = Right '(r, "")
RunEnd idx ch (Left e) = Left ('ERun idx ch e)
sRunEnd
:: SNat idx -> SChar ch
-> SEither SE sr res
-> SEither SERun (STuple2 sr SSymbol) (RunEnd idx ch res)
sRunEnd idx ch = \case
SRight r -> SRight (STuple2 r (SSymbol @""))
SLeft e -> SLeft (SERun idx ch e)
-- | Inspect end parser result for the empty string, where we have no previous
-- character or (meaningful) index.
type family RunEnd0 res where
RunEnd0 (Right r) = Right '(r, "")
RunEnd0 (Left e) = Left (ERun0 e)
sRunEnd0
:: SEither SE sr res
-> SEither SERun (STuple2 sr SSymbol) (RunEnd0 res)
sRunEnd0 = \case
SRight r -> SRight (STuple2 r (SSymbol @""))
SLeft e -> SLeft (SERun0 e)
type PrettyERun :: PERun -> PDoc
type family PrettyERun e where
PrettyERun (ERun0 e) = Text "parse error on empty string" :$$: PrettyE e
PrettyERun ('ERun idx ch e) =
Text "parse error at index " :<>: Text (ShowNatDec idx)
:<>: Text ", char '" :<>: Text (ShowChar ch) :<>: Text "'"
:$$: PrettyE e
type PrettyE :: PE -> PDoc
type family PrettyE e where
PrettyE (EBase name emsg) = Text name :<>: Text ": " :<>: emsg
PrettyE (EIn name e) = Text name :<>: Text ": " :<>: PrettyE e
-- | Error while running parser.
data ERun s
-- | Parser error at index X, character C.
= ERun Natural Char (E s)
-- | Parser error on the empty string.
| ERun0 (E s)
deriving stock Show
-- | Promoted 'ERun'.
type PERun = ERun Symbol
data SERun (erun :: PERun) where
SERun :: SNat idx -> SChar ch -> SE e -> SERun ('ERun idx ch e)
SERun0 :: SE e -> SERun (ERun0 e)
demoteSERun :: SERun erun -> ERun String
demoteSERun = \case
SERun idx ch e -> ERun (fromSNat idx) (fromSChar ch) (demoteSE e)
SERun0 e -> ERun0 (demoteSE e)
instance Demotable SERun where
type Demote SERun = ERun String
demote = demoteSERun