packages feed

semdoc-0.1.0: src/Text/Semdoc/InlineHaskell.hs

module Text.Semdoc.InlineHaskell where

import qualified Control.Monad.State.Strict as CMS
import           Data.Default.Class         (Default (..))
import           Text.Groom                 (groom)
import           Text.Pandoc                (Block (..), Format (..),
                                             Inline (..))
import           Text.Pandoc.Walk           (walkM)
import           Text.Regex.TDFA            ((=~))

import           Text.Semdoc.EvalExpr       (EvalInput (..))


data Input = Input
  { inputsBlock   :: [EvalInput Block]
  , inputsInline  :: [EvalInput Inline]
  , currentModule :: String
  , allModules    :: [String]
  }

instance Default Input where
  def = Input
    { inputsBlock   = []
    , inputsInline  = []
    , currentModule = "Prelude"
    , allModules    = []
    }


extractExprsInline :: Inline -> CMS.State Input Inline
extractExprsInline i =
  case i of
    RawInline (Format "latex") ('\\':'s':'h':'o':'w':'{':expr) -> doExtract expr
    Code ("", [], []) ('\\':'s':'h':'o':'w':'{':expr)          -> doExtract expr
    _                                                          -> return i

  where
    doExtract :: String -> CMS.State Input Inline
    doExtract expr = do
      let stmt = "it = " ++ init expr
      input <- CMS.get
      CMS.put input
        { inputsInline =
            EvalInput (currentModule input) stmt : inputsInline input
        }
      return i


extractExprs :: Block -> CMS.State Input Block
extractExprs i =
  case i of
    CodeBlock ("", [], [("semdoc", "")]) code ->
      doExtract code
    CodeBlock ("", ["sourceCode", "literate", "haskell"], []) code ->
      case code =~ "module ([A-Z][A-Za-z0-9_.]+)" of
        [[_, modName]] -> do
          input <- CMS.get
          CMS.put input
            { currentModule = modName
            , allModules    = modName : allModules input
            }
          return i
        _ ->
          return i
    CodeBlock   {} -> return i
    BulletList  {} -> return i
    OrderedList {} -> return i
    Table       {} -> return i
    Plain       {} -> walkM extractExprsInline i
    Para        {} -> walkM extractExprsInline i
    Header      {} -> walkM extractExprsInline i
    _              -> error $ groom i

  where
    doExtract :: String -> CMS.State Input Block
    doExtract stmt = do
      input <- CMS.get
      CMS.put input
        { inputsBlock =
            EvalInput (currentModule input) stmt : inputsBlock input
        }
      return i


replaceExprsInline :: Inline -> CMS.State ([Block], [Inline]) Inline
replaceExprsInline i =
  case i of
    RawInline (Format "latex") ('\\':'s':'h':'o':'w':'{':_) -> doReplace
    Code ("", [], []) ('\\':'s':'h':'o':'w':'{':_)          -> doReplace
    _                                                       -> return i

  where
    doReplace :: CMS.State ([Block], [Inline]) Inline
    doReplace = do
      (resultsBlock, resultsInline) <- CMS.get
      case resultsInline of
        [] -> fail "insufficient results"
        r:rs -> do
          CMS.put (resultsBlock, rs)
          return r


replaceExprs :: Block -> CMS.State ([Block], [Inline]) Block
replaceExprs i =
  case i of
    CodeBlock ("", [], [("semdoc", "")]) _ -> doReplace
    CodeBlock   {}                         -> return i
    BulletList  {}                         -> return i
    OrderedList {}                         -> return i
    Table       {}                         -> return i
    Plain       {}                         -> walkM replaceExprsInline i
    Para        {}                         -> walkM replaceExprsInline i
    Header      {}                         -> walkM replaceExprsInline i
    _                                      -> error $ groom i

  where
    doReplace :: CMS.State ([Block], [Inline]) Block
    doReplace = do
      (resultsBlock, resultsInline) <- CMS.get
      case resultsBlock of
        [] -> fail "insufficient results"
        r:rs -> do
          CMS.put (rs, resultsInline)
          return r