packages feed

parsley-core-2.1.0.0: src/ghc/Parsley/Internal/Core/CombinatorAST.hs

{-# LANGUAGE OverloadedStrings #-}
module Parsley.Internal.Core.CombinatorAST (module Parsley.Internal.Core.CombinatorAST) where

import Data.Kind                         (Type)
import Parsley.Internal.Common           (IFunctor(..), Fix, Const1(..), cata, intercalateDiff, (:+:))
import Parsley.Internal.Core.Identifiers (MVar, ΣVar)
import Parsley.Internal.Core.CharPred    (CharPred)
import Parsley.Internal.Core.Defunc      (Defunc)

{-|
The opaque datatype that represents parsers.

@since 0.1.0.0
-}
newtype Parser a = Parser {unParser :: Fix (Combinator :+: ScopeRegister) a}

-- Core datatype
data Combinator (k :: Type -> Type) (a :: Type) where
  Pure           :: Defunc a -> Combinator k a
  Satisfy        :: CharPred -> Combinator k Char
  (:<*>:)        :: k (a -> b) -> k a -> Combinator k b
  (:*>:)         :: k a -> k b -> Combinator k b
  (:<*:)         :: k a -> k b -> Combinator k a
  (:<|>:)        :: k a -> k a -> Combinator k a
  Empty          :: Combinator k a
  Try            :: k a -> Combinator k a
  LookAhead      :: k a -> Combinator k a
  Let            :: Bool -> MVar a -> Combinator k a
  NotFollowedBy  :: k a -> Combinator k ()
  Branch         :: k (Either a b) -> k (a -> c) -> k (b -> c) -> Combinator k c
  Match          :: k a -> [Defunc (a -> Bool)] -> [k b] -> k b -> Combinator k b
  Loop           :: k () -> k a -> Combinator k a
  MakeRegister   :: ΣVar a -> k a -> k b -> Combinator k b
  GetRegister    :: ΣVar a -> Combinator k a
  PutRegister    :: ΣVar a -> k a -> Combinator k ()
  Position       :: PosSelector -> Combinator k Int
  Debug          :: String -> k a -> Combinator k a
  MetaCombinator :: MetaCombinator -> k a -> Combinator k a

data ScopeRegister (k :: Type -> Type) (a :: Type) where
  ScopeRegister :: k a -> (forall r. Reg r a -> k b) -> ScopeRegister k b

data PosSelector where
  Line :: PosSelector
  Col  :: PosSelector

{-|
This is an opaque representation of a parsing register. It cannot be manipulated as a user, and the
type parameter @r@ is used to ensure that it cannot leak out of the scope it has been created in.
It is the abstracted representation of a runtime storage location.

@since 0.1.0.0
-}
newtype Reg (r :: Type) a = Reg (ΣVar a)

data MetaCombinator where
  -- | After this combinator exits, a cut has happened
  Cut         :: MetaCombinator
  -- | This combinator requires a cut from below to respect parsec semantics
  RequiresCut :: MetaCombinator
  -- | This combinator denotes that within its scope, cut semantics are not enforced
  --
  -- @since 1.6.0.0
  CutImmune   :: MetaCombinator

-- Instances
instance IFunctor Combinator where
  imap _ (Pure x)             = Pure x
  imap _ (Satisfy p)          = Satisfy p
  imap f (p :<*>: q)          = f p :<*>: f q
  imap f (p :*>: q)           = f p :*>: f q
  imap f (p :<*: q)           = f p :<*: f q
  imap f (p :<|>: q)          = f p :<|>: f q
  imap _ Empty                = Empty
  imap f (Try p)              = Try (f p)
  imap f (LookAhead p)        = LookAhead (f p)
  imap _ (Let r v)            = Let r v
  imap f (NotFollowedBy p)    = NotFollowedBy (f p)
  imap f (Branch b p q)       = Branch (f b) (f p) (f q)
  imap f (Match p fs qs d)    = Match (f p) fs (map f qs) (f d)
  imap f (Loop body exit)     = Loop (f body) (f exit)
  imap f (MakeRegister σ p q) = MakeRegister σ (f p) (f q)
  imap _ (GetRegister σ)      = GetRegister σ
  imap f (PutRegister σ p)    = PutRegister σ (f p)
  imap _ (Position sel)       = Position sel
  imap f (Debug name p)       = Debug name (f p)
  imap f (MetaCombinator m p) = MetaCombinator m (f p)

instance Show (Fix Combinator a) where
  show = ($ "") . getConst1 . cata (Const1 . alg)
    where
      alg (Pure x)                                  = "pure " . shows x
      alg (Satisfy f)                               = "satisfy " . shows f
      alg (Const1 pf :<*>: Const1 px)               = "(" . pf . " <*> " .  px . ")"
      alg (Const1 p :*>: Const1 q)                  = "(" . p . " *> " . q . ")"
      alg (Const1 p :<*: Const1 q)                  = "(" . p . " <* " . q . ")"
      alg (Const1 p :<|>: Const1 q)                 = "(" . p . " <|> " . q . ")"
      alg Empty                                     = "empty"
      alg (Try (Const1 p))                          = "try (". p . ")"
      alg (LookAhead (Const1 p))                    = "lookAhead (" . p . ")"
      alg (Let False v)                             = "let-bound " . shows v
      alg (Let True v)                              = "rec " . shows v
      alg (NotFollowedBy (Const1 p))                = "notFollowedBy (" . p . ")"
      alg (Branch (Const1 b) (Const1 p) (Const1 q)) = "branch (" . b . ") (" . p . ") (" . q . ")"
      alg (Match (Const1 p) fs qs (Const1 def))     = "match (" . p . ") " . shows fs . " [" . intercalateDiff ", " (map getConst1 qs) . "] ("  . def . ")"
      alg (Loop (Const1 body) (Const1 exit))        = "loop (" . body . ") (" . exit . ")"
      alg (MakeRegister σ (Const1 p) (Const1 q))    = "make " . shows σ . " (" . p . ") (" . q . ")"
      alg (GetRegister σ)                           = "get " . shows σ
      alg (PutRegister σ (Const1 p))                = "put " . shows σ . " (" . p . ")"
      alg (Position Line)                           = "line"
      alg (Position Col)                            = "col"
      alg (Debug _ (Const1 p))                      = p
      alg (MetaCombinator m (Const1 p))             = p . " [" . shows m . "]"

instance IFunctor ScopeRegister where
  imap f (ScopeRegister p g) = ScopeRegister (f p) (f . g)

instance Show MetaCombinator where
  show Cut = "coins after"
  show RequiresCut = "requires cut"
  show CutImmune = "immune to cuts"

{-# INLINE traverseCombinator #-}
traverseCombinator :: Applicative m => (forall a. f a -> m (k a)) -> Combinator f a -> m (Combinator k a)
traverseCombinator expose (pf :<*>: px)        = (:<*>:) <$> expose pf <*> expose px
traverseCombinator expose (p :*>: q)           = (:*>:) <$> expose p <*> expose q
traverseCombinator expose (p :<*: q)           = (:<*:) <$> expose p <*> expose q
traverseCombinator expose (p :<|>: q)          = (:<|>:) <$> expose p <*> expose q
traverseCombinator _      Empty                = pure Empty
traverseCombinator expose (Try p)              = Try <$> expose p
traverseCombinator expose (LookAhead p)        = LookAhead <$> expose p
traverseCombinator expose (NotFollowedBy p)    = NotFollowedBy <$> expose p
traverseCombinator expose (Branch b p q)       = Branch <$> expose b <*> expose p <*> expose q
traverseCombinator expose (Match p fs qs d)    = Match <$> expose p <*> pure fs <*> traverse expose qs <*> expose d
traverseCombinator expose (Loop body exit)     = Loop <$> expose body <*> expose exit
traverseCombinator expose (MakeRegister σ p q) = MakeRegister σ <$> expose p <*> expose q
traverseCombinator _      (GetRegister σ)      = pure (GetRegister σ)
traverseCombinator expose (PutRegister σ p)    = PutRegister σ <$> expose p
traverseCombinator _      (Position sel)       = pure (Position sel)
traverseCombinator expose (Debug name p)       = Debug name <$> expose p
traverseCombinator _      (Pure x)             = pure (Pure x)
traverseCombinator _      (Satisfy f)          = pure (Satisfy f)
traverseCombinator _      (Let r v)            = pure (Let r v)
traverseCombinator expose (MetaCombinator m p) = MetaCombinator m <$> expose p