module ADP.Fusion.Base.Classes where import Data.Strict.Tuple import Data.Vector.Fusion.Stream.Size import qualified Data.Vector.Fusion.Stream.Monadic as S import Data.PrimitiveArray data OutsideContext s = OStatic s | ORightOf s | OFirstLeft s | OLeftOf s data InsideContext s = IStatic s | IVariable s data ComplementContext = Complemented class RuleContext i where type Context i :: * initialContext :: i -> Context i -- | During construction of the stream, we need to extract individual elements -- from symbols in production rules. An element in a stream is fixed by both, -- the type @x@ of the actual argument we want to grab (say individual -- characters we parse from an input) and the type of indices @i@ we use. -- -- @Elm@ data constructors are all eradicated during fusion and should never -- show up in CORE. class Element x i where data Elm x i :: * type RecElm x i :: * type Arg x :: * getArg :: Elm x i -> Arg x getIdx :: Elm x i -> i getOmx :: Elm x i -> i getElm :: Elm x i -> RecElm x i -- | @mkStream@ creates the actual stream of elements (@Elm@) that will be fed -- to functions on the left of the @(<<<)@ operator. Streams work over all -- monads and are specialized for each combination of arguments @x@ and indices -- @i@. class (Monad m) => MkStream m x i where mkStream :: x -> Context i -> i -> i -> S.Stream m (Elm x i) -- | Finally, we need to be able to correctly build together symbols on the -- right-hand side of the @(<<<)@ operator. -- -- The default makes sure that the last (or only) argument left over is -- correctly assigned a @Z@ to terminate the symbol stack. class Build x where type Stack x :: * type Stack x = S :!: x build :: x -> Stack x default build :: (Stack x ~ (S :!: x)) => x -> Stack x build x = S :!: x {-# Inline build #-} instance Build x => Build (x:!:y) where type Stack (x:!:y) = Stack x :!: y build (x:!:y) = build x :!: y {-# Inline build #-} -- | Similar to 'Z', but terminates an argument stack. data S = S deriving (Eq,Show) instance ( ) => Element S i where data Elm S i = ElmS !i !i type Arg S = Z getArg (ElmS _ _) = Z getIdx (ElmS i _) = i getOmx (ElmS _ o) = o {-# Inline getArg #-} {-# Inline getIdx #-} {-# Inline getOmx #-} deriving instance Show ix => Show (Elm S ix) -- | 'staticCheck' acts as a static filter. If 'b' is true, we keep all stream -- elements. If 'b' is false, we discard all stream elements. staticCheck :: Monad m => Bool -> S.Stream m a -> S.Stream m a staticCheck b (S.Stream step t n) = b `seq` S.Stream snew (CheckLeft (b:.t)) (toMax n) where {-# Inline [0] snew #-} snew (CheckLeft (False:._)) = return $ S.Done snew (CheckLeft (True :.s)) = return $ S.Skip (CheckRight s) snew (CheckRight s ) = do r <- step s case r of S.Yield x s' -> return $ S.Yield x (CheckRight s') S.Skip s' -> return $ S.Skip (CheckRight s') S.Done -> return $ S.Done {-# INLINE staticCheck #-} data StaticCheck a b = CheckLeft a | CheckRight b -- | Constrains the behaviour of the memoizing tables. They may be 'EmptyOk' if -- @i==j@ is allowed (empty subwords or similar); or they may need 'NonEmpty' -- indices, or finally they can be 'OnlyZero' (only @i==j@ allowed) which is -- useful in multi-dimensional casese. data TableConstraint = EmptyOk | NonEmpty | OnlyZero deriving (Eq,Show) minSize :: TableConstraint -> Int minSize NonEmpty = 1 minSize _ = 0 {-# INLINE minSize #-} class ModifyConstraint t where toNonEmpty :: t -> t toEmpty :: t -> t -- | type family TblConstraint x :: * type instance TblConstraint (is:.i) = TblConstraint is :. TblConstraint i type instance TblConstraint Z = Z type instance TblConstraint (Outside o) = TblConstraint o type instance TblConstraint (Complement o) = TblConstraint o -- TODO move into the sub-modules type instance TblConstraint PointL = TableConstraint type instance TblConstraint PointR = TableConstraint type instance TblConstraint Subword = TableConstraint