packages feed

semdoc-0.1.3: src/Text/Semdoc/Processor.hs

{-# LANGUAGE LambdaCase #-}
module Text.Semdoc.Processor where

import qualified Control.Monad.State.Strict as CMS
import qualified Data.Set                   as Set
import           Text.Groom                 (groom)
import           Text.Pandoc                (Block (..))
import qualified Text.Pandoc                as Pandoc
import           Text.Pandoc.Error          (PandocError)
import           Text.Pandoc.Walk           (walk, walkM)

import           Text.Semdoc.EvalExpr       (evalExpr)
import           Text.Semdoc.InlineHaskell  (Input (Input), extractExprs,
                                             replaceExprs)


readerOpts :: Pandoc.ReaderOptions
readerOpts = Pandoc.def
  { Pandoc.readerExtensions = Set.fromList [Pandoc.Ext_literate_haskell]
  , Pandoc.readerParseRaw = True
  }


semdocExtensions :: Set.Set Pandoc.Extension
semdocExtensions =
  Set.insert Pandoc.Ext_tex_math_dollars $
    foldl (flip Set.delete) Pandoc.githubMarkdownExtensions
      [ Pandoc.Ext_hard_line_breaks
      , Pandoc.Ext_native_spans
      , Pandoc.Ext_raw_html
      , Pandoc.Ext_tex_math_single_backslash
      ]


eraseCodeBlocks :: Block -> Block
eraseCodeBlocks (CodeBlock ("", ["sourceCode", "literate", "haskell"], []) _) = Null
eraseCodeBlocks x = x


processDoc :: Pandoc.Pandoc -> IO String
processDoc doc@(Pandoc.Pandoc _ parsed) = do
  putStrLn $ "[=] processing " ++ show (length parsed) ++ " toplevel blocks"
  --putStrLn $ groom parsed
  let Input exprsBlock exprsInline _ _ = snd . CMS.runState (walkM extractExprs doc) $ Pandoc.def
  putStrLn $ "[=] evaluating " ++ show (length exprsBlock) ++ " block expressions"
  resultsBlock <- evalExpr exprsBlock
  putStrLn $ "[=] evaluating " ++ show (length exprsInline) ++ " inline expressions"
  resultsInline <- evalExpr exprsInline
  putStrLn $ "[=] inserting " ++ show (length resultsBlock, length resultsInline) ++ " (block/inline) results"
  let (evaluated, remaining) = CMS.runState (walkM replaceExprs doc) (resultsBlock, resultsInline)
  case remaining of
    ([], []) ->
      let withoutCode = walk eraseCodeBlocks evaluated in
      return $ writeMarkdown withoutCode
    _ ->
      fail $ "extra results: " ++ groom remaining


phaseEval :: String -> IO String
phaseEval text = do
  putStrLn $ "[=] parsing " ++ show (length text) ++ " characters"
  case Pandoc.readLaTeX readerOpts text of
    Left parseError -> fail $ groom parseError
    Right doc       -> processDoc doc


readMarkdown :: String -> Either PandocError Pandoc.Pandoc
readMarkdown =
  Pandoc.readMarkdown Pandoc.def
    { Pandoc.readerExtensions    = semdocExtensions
    , Pandoc.readerColumns       = 79
    }


writeMarkdown :: Pandoc.Pandoc -> String
writeMarkdown =
  Pandoc.writeMarkdown Pandoc.def
    { Pandoc.writerExtensions    = semdocExtensions
    , Pandoc.writerColumns       = 79
    , Pandoc.writerSetextHeaders = False
    , Pandoc.writerTeXLigatures  = False
    }