packages feed

lasercutter-0.1.0.0: src/Lasercutter.hs

module Lasercutter
  ( -- * Core types
    Parser
  , runParser
  , IsTree (..)

  -- * Building parsers
  -- ** Primitives
  , self
  , proj

  -- ** Controlling failure
  , expect
  , one
  , try
  , empty
  , (<|>)

  -- ** Traversing trees
  , onChildren
  , onSingleChild
  , target
  , targetMap

  -- ** Conditional parsing
  , when
  , whenNode
  , ifS
  , ifNode

  -- ** Breadcrumbs
  -- | All parsers support a notion of *breadcrumbs* --- a monoid that gets
  -- accumulated along subtrees. Callers to 'runParser' can choose
  -- a *summarization* function which describes how to generate the breadcrumb
  -- monoid from the current node.
  --
  -- Breadcrumbs are often used to refine the results of 'target', which has no
  -- notion of history, and thus can be too coarse for many position-depending
  -- parsing tasks.
  , breadcrumbs
  , onBreadcrumbs
  , mapBreadcrumbs

  -- * Re-exports
  -- | The 'Parser' is an instance of all of the following classes, and thus
  -- all of these methods are available on 'Parser's.

  -- ** 'Profunctor'
  -- | Even though 'Parser's are contravariant in their breadcrumbs and
  -- tree type, this instance targets only the tree. Use 'mapBreadcrumbs' to
  -- modify the breadcrumbs.
  , dimap
  , rmap
  , lmap

  -- ** 'Applicative'
  , liftA2

  -- ** 'Alternative'
  , optional
  , guard
  , asum

  -- ** 'Filterable'
  , mapMaybe
  , catMaybes

  -- ** 'Selective'
  -- | 'Parser's are boring 'Selective' functors that are unfortunately unable
  -- to elide any effects. Nevertheless, the 'Selective' API is often quite
  -- useful for everyday parsing tasks.
  , select
  , (<*?)
  , branch
  , fromMaybeS
  , orElse
  , andAlso
  , (<||>)
  , (<&&>)
  , foldS
  , anyS
  , allS
  , bindS
  ) where

import Control.Applicative
import Control.Monad (guard)
import Control.Selective
import Data.Foldable (asum)
import Data.Maybe (listToMaybe, isJust)
import Lasercutter.Internal
import Lasercutter.Types
import Prelude hiding (filter)
import Witherable (Filterable (..))
import Data.Profunctor


------------------------------------------------------------------------------
-- | Project a value out of the current node. This is the main way to build
-- primitive parsers.
--
-- * @'proj' f = 'fmap' f 'self'@
--
-- @since 0.1.0.0
proj :: (t -> a) -> Parser bc t a
proj f = fmap f self


------------------------------------------------------------------------------
-- | Run the given parser on every immediate child of the current node.
--
-- @since 0.1.0.0
onChildren :: Parser bc t a -> Parser bc t [a]
onChildren = OnChildren


------------------------------------------------------------------------------
-- | Run the given parser on every predicate-satisfying subtree of the current
-- node. This combinator is not recursive --- that is, if the predicate
-- is satisfied by both a node and its descendent, the descendent *will not*
-- receive the parser.
--
-- @since 0.1.0.0
target :: (t -> Bool) -> Parser bc t a -> Parser bc t [a]
target = Target


------------------------------------------------------------------------------
-- | Get the breadcrumbs at the current node. This is useful for refining the
-- coarse-grained matches of 'target' by restricting matches to certain
-- subtrees.
--
-- @since 0.1.0.0
breadcrumbs :: Parser bc t bc
breadcrumbs = GetCrumbs


------------------------------------------------------------------------------
-- | Get a value computed on the current breadcrumbs.
--
-- * @'onBreadcrumbs' f = 'fmap' f 'breadcrumbs'@
--
-- @since 0.1.0.0
onBreadcrumbs :: (bc -> a) -> Parser bc t a
onBreadcrumbs f = fmap f breadcrumbs


------------------------------------------------------------------------------
-- | Run a parser on the immediate children of the current node, returning the
-- first success.
--
-- * @'onSingleChild' = 'fmap' 'listToMaybe' . 'onChildren'@
--
-- @since 0.1.0.0
onSingleChild :: Parser bc t a -> Parser bc t (Maybe a)
onSingleChild = fmap listToMaybe . onChildren


------------------------------------------------------------------------------
-- | Get the current node.
--
-- @since 0.1.0.0
self :: Parser bc t t
self = Current


------------------------------------------------------------------------------
-- | Get the first result of a list of results, failing if there are none.
--
-- * @'one' = 'expect' . 'fmap' 'listToMaybe'@
--
-- @since 0.1.0.0
one :: Parser bc t [a] -> Parser bc t a
one = expect . fmap listToMaybe


------------------------------------------------------------------------------
-- | @'when' pc pa@ returns @pa@ when @pc@ evaluates to 'True', failing
-- otherwise.
--
-- @since 0.1.0.0
when
    :: Parser bc t Bool
    -> Parser bc t a
    -> Parser bc t a
when b tr = expect $ ifS b (fmap Just tr) $ pure Nothing


------------------------------------------------------------------------------
-- | @'ifNode' f pt pf@ runs @pt@ when @f@ evaluates to 'True' on the
-- current node, running @pf@ otherwise.
--
-- * @'ifNode' f tr fl = 'ifS' ('proj' f) tr fl@
--
-- @since 0.1.0.0
ifNode
    :: (t -> Bool)
    -> Parser bc t a
    -> Parser bc t a
    -> Parser bc t a
ifNode f tr fl = ifS (proj f) tr fl


------------------------------------------------------------------------------
-- | @'whenNode' f pa@ returns @pa@ when @pc@ evaluates to 'True' on the
-- current node, failing otherwise.
--
-- * @'whenNode' = 'when' . 'proj'@
--
-- @since 0.1.0.0
whenNode :: (t -> Bool) -> Parser bc t a -> Parser bc t a
whenNode = when . proj


------------------------------------------------------------------------------
-- | Run the given function on every subtree, accumulating those which return
-- 'Just'.
--
-- @since 0.1.0.0
targetMap :: (t -> Maybe a) -> Parser bc t [a]
targetMap f = fmap catMaybes $ target (isJust  . f) $ proj f