packages feed

concrete-haskell-0.1.0.13: src/Data/Concrete/Parsers/Types.hs

{-# LANGUAGE DeriveGeneric, OverloadedStrings #-}
module Data.Concrete.Parsers.Types ( Bookkeeper(..)
                                   , CommunicationParser
                                   , CommunicationAction
                                   , PathComponent(..)
                                   , Path
                                   ) where

import Data.Text.Lazy (Text)
import Data.Concrete.Autogen.Communication_Types (Communication)
import Data.Concrete.Autogen.Structure_Types (Section, Sentence)
import Text.Megaparsec (ParsecT)
import Text.Megaparsec.Error (Dec)
import Data.Map (Map)
import Control.Monad.State (StateT)

-- | A 'CommunicationAction' gets called on each Communication
--   as parsing proceeds
type CommunicationAction = Communication -> IO ()

-- | A 'PathComponent' represents one step in navigating a parse
--   tree.  The meaning will be format-specific: a "step" could
--   correspond to any number of parse rules.  See the definition
--   of 'Path' for an example.
data PathComponent = Index Int -- ^ An index into an anonymous sequence of successors of a path
                   | Name String -- ^ The name of an identifiable successor of a path

-- | A 'Path' is a sequence of 'PathComponent's that identifies
--   a particular location in the document being parsed, usually
--   the *current* location, as a sequence of indices and strings.
--   Think of it as the sequence of values you would use to
--   index a Python-style object of nested dictionaries and lists.
--   The meaning of the indices and strings depends on the format
--   being parsed: for example, in parsing this HTML:
--
-- >    <list id="abc">
-- >      <li>one</li>
-- >      <li style="final">two</li>
-- >    </list>
--
--   If we treat the "list" element as the top-level object, we
--   might, as we parse the document, generate the paths:
--
-- >    [Name "id"]
-- >    [Index 1]
-- >    [Index 2]
-- >    [Index 2, Name "style"]
--
--   The trick is to take a parser for a data format, and augment
--   the rules so that the current 'Path' is always correct and
--   meaningful.
type Path = [PathComponent]

-- | A 'Bookkeeper' tracks information about an ongoing attempt
--   to parse a Text stream into Communication objects.
data Bookkeeper = Bookkeeper { communication :: Communication
                             , valueMap :: Map String String -- | An arbitrary string-to-string map
                             , path :: [String]
                             , sections :: [Section] -- | List of Sections accumulated for the Communication currently being parsed
                             , action :: CommunicationAction
                             , contentSections :: [String]
                             , commId :: Text
                             , commType :: String
                             , commNum :: Int
                             }

-- | A StatefulParser is just a Megaparsec Parser that carries
--   a State, and has access to the IO monad.
type StatefulParser s a = ParsecT Dec Text (StateT s IO) a

-- | A 'CommunicationParser' is a stateful Megaparsec parser that, as it
--   processes a Text stream, builds a list of Concrete Communications.
type CommunicationParser a = StatefulParser Bookkeeper a