lasercutter-0.1.0.0: src/Lasercutter/Types.hs
{-# LANGUAGE StrictData #-}
module Lasercutter.Types where
import Control.Applicative
import Control.Selective
import Data.Monoid
import Data.Profunctor
import Witherable
------------------------------------------------------------------------------
-- | Lasercutter supports any inductive tree types, as witnessed by
-- 'getChildren'.
--
-- @since 0.1.0.0
class IsTree t where
-- | Get all children of the current node.
--
-- @since 0.1.0.0
getChildren :: t -> [t]
------------------------------------------------------------------------------
-- | A tree parser which runs all queries in a single pass. This is
-- accomplished via a free encoding of the applicative structure, which can be
-- arbitrarily reassociated for better performance.
--
-- @since 0.1.0.0
data Parser bc t a where
-- | The free 'pure' constructor.
--
-- @since 0.1.0.0
Pure :: a -> Parser bc t a
-- | The free 'liftA2' constructor. This is an inlining of Day convolution.
--
-- @since 0.1.0.0
LiftA2 :: (b -> c -> a) -> Parser bc t b -> Parser bc t c -> Parser bc t a
-- | Get the breadcrumbs at the current part of the tree.
--
-- @since 0.1.0.0
GetCrumbs :: Parser bc t bc
-- | Run the given parser at every subtree which matches the given predicate.
-- This is not recursive --- that is, a given subtree only runs the given
-- parser once, not in all further matching subtrees.
--
-- @since 0.1.0.0
Target :: (t -> Bool) -> Parser bc t a -> Parser bc t [a]
-- | Run the given parser on each child of the current node.
--
-- @since 0.1.0.0
OnChildren :: Parser bc t a -> Parser bc t [a]
-- | Get the current node.
--
-- @since 0.1.0.0
Current :: Parser bc t t
-- | Swallow a parsed 'Maybe', failing the parser if it was 'Nothing'. Don't
-- use this constructor explicitly; prefer 'expect' which maintains some
-- invariants.
--
-- 'optional' is the inverse to this parser.
--
-- @since 0.1.0.0
Expect :: Parser bc t (Maybe a) -> Parser bc t a
-- | Immediately fail a parse. Equivalent to @'Expect' ('pure' 'Nothing')@.
--
-- @since 0.1.0.0
Fail :: Parser bc t a
deriving (Semigroup, Monoid) via (Ap (Parser bc t) a)
instance Show (Parser bc t a) where
show (Pure _) = "(Pure _)"
show (LiftA2 _ pa' pa_bctc) =
"(LiftA2 _ " <> show pa' <> " " <> show pa_bctc <> ")"
show GetCrumbs = "GetCrumbs"
show (Target _ pa') = "(Target _ " <> show pa' <> "')"
show (OnChildren pa') = "(OnChildren " <> show pa' <> ")"
show Current = "Current"
show (Expect pa') = "(Expect " <> show pa' <> ")"
show Fail = "Fail"
instance Functor (Parser bc t) where
fmap = liftA
instance Applicative (Parser bc t) where
pure = Pure
liftA2 f (Pure a) (Pure b) = Pure $ f a b
liftA2 _ Fail _ = Fail
liftA2 _ _ Fail = Fail
liftA2 f a b = LiftA2 f a b
instance Alternative (Parser bc t) where
empty = Fail
pa1 <|> pa2 =
expect $ maybe <$> try pa2 <*> pure Just <*> try pa1
instance Selective (Parser bc t) where
select = selectA
instance Filterable (Parser bc t) where
catMaybes = Expect
instance Profunctor (Parser bc) where
lmap = mapTree
rmap = fmap
------------------------------------------------------------------------------
-- | Transform the type of tree that a 'Parser' operates over.
--
-- @since 0.1.0.0
mapTree :: (t -> t') -> Parser bc t' a -> Parser bc t a
mapTree _ (Pure a) = Pure a
mapTree t (LiftA2 f pa pb) = LiftA2 f (mapTree t pa) (mapTree t pb)
mapTree _ GetCrumbs = GetCrumbs
mapTree t (Target p pa) = Target (p . t) $ mapTree t pa
mapTree t (OnChildren pa) = OnChildren $ mapTree t pa
mapTree t Current = fmap t Current
mapTree t (Expect pa) = Expect $ mapTree t pa
mapTree _ Fail = Fail
------------------------------------------------------------------------------
-- | A parser to run on children, and a subsequent continuation for how to
-- parse the parent.
--
-- @since 0.1.0.0
data Split bc t a where
Split
:: Parser bc t a
-- ^ The parser to run on children.
-> ([a] -> Parser bc t b)
-- ^ Continuation for how to subsequently parse the current node.
-> Split bc t b
------------------------------------------------------------------------------
-- | Swallow a parsed 'Maybe', failing the parser if it was 'Nothing'.
--
-- Use 'try' or 'optional' as the inverse to this parser.
--
-- @since 0.1.0.0
expect :: Parser bc t (Maybe a) -> Parser bc t a
expect (Pure Nothing) = Fail
expect (Pure (Just a)) = Pure a
expect p = Expect p
------------------------------------------------------------------------------
-- | Like 'optional', but slightly more efficient.
--
-- @since 0.1.0.0
try :: Parser bc t a -> Parser bc t (Maybe a)
try Fail = pure Nothing
try (Expect p) = p
try (LiftA2 f a b) = LiftA2 (liftA2 f) (try a) (try b)
try p = fmap Just p