content: {-# LANGUAGE RankNTypes #-}
  module Commonmark.Syntax
    ( SyntaxSpec(..)
    , defaultSyntaxSpec
    )
  where
  
  import Text.Parsec (ParsecT)
  import Commonmark.Tokens (Tok)
  import Commonmark.Types
  import Commonmark.Blocks
  import Commonmark.Inlines
  
  -- | A 'SyntaxSpec' defines a basic collection of syntax
  -- elements or an extension.  'SyntaxSpec's can be composed
  -- using monoidal 'mappend'.
  data SyntaxSpec m il bl = SyntaxSpec
       { syntaxBlockSpecs      :: [BlockSpec m il bl]
          -- ^ Defines block structure
       , syntaxBracketedSpecs  :: [BracketedSpec il]
          -- ^ Defines bracketed inline containers (inli, image)
       , syntaxFormattingSpecs :: [FormattingSpec il]
          -- ^ Defines formatted inline containers (strong, emph)
       , syntaxInlineParsers   :: [InlineParser m il]
          -- ^ Defines inline elements that don't contain inlines
       , syntaxFinalParsers    :: [BlockParser m il bl bl]
          -- ^ Run at the end of document, e.g. to collect footnotes
       , syntaxAttributeParsers
               :: forall u m1 . Monad m1 => [ParsecT [Tok] u m1 Attributes]
         -- ^ Parse attributes
       }
  
  instance Semigroup (SyntaxSpec m il bl) where
    SyntaxSpec bl1 br1 fo1 il1 fp1 ap1 <> SyntaxSpec bl2 br2 fo2 il2 fp2 ap2
      = SyntaxSpec (removeDuplicateBlockSpecs $ bl1 <> bl2)
                   (br1 <> br2) (fo1 <> fo2) (il1 <> il2)
                   (fp1 <> fp2) (ap1 <> ap2)
  instance Monoid (SyntaxSpec m il bl) where
    mempty = SyntaxSpec mempty mempty mempty mempty mempty mempty
    mappend = (<>)
  
  removeDuplicateBlockSpecs :: [BlockSpec m il bl] -> [BlockSpec m il bl]
  removeDuplicateBlockSpecs []     = []
  removeDuplicateBlockSpecs (b:bs) =
    b : removeDuplicateBlockSpecs (filter ((/= blockType b) . blockType) bs)
  
  -- | Standard commonmark syntax.
  defaultSyntaxSpec :: (Monad m, IsBlock il bl, IsInline il)
                    => SyntaxSpec m il bl
  defaultSyntaxSpec = SyntaxSpec
    { syntaxBlockSpecs          = defaultBlockSpecs
    , syntaxBracketedSpecs      = defaultBracketedSpecs
    , syntaxFormattingSpecs     = defaultFormattingSpecs
    , syntaxInlineParsers       = [defaultInlineParser]
    , syntaxFinalParsers        = []
    , syntaxAttributeParsers    = []
    }
  