packages feed

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