packages feed

marxup-1.0.2: MarXup/Tex.hs

{-# LANGUAGE GeneralizedNewtypeDeriving #-}

module MarXup.Tex where

import Control.Monad.Fix
import Control.Monad.RWS.Lazy
import Control.Applicative
import Data.Either
import GHC.Exts( IsString(..) )
import System.FilePath
import System.Environment
import Data.List (intersperse)

---------------------------------
-- Marchup interface
textual = Tex  -- TODO: escape \, &, etc.
text = textual 


element :: Tex a -> Tex a
element = id

-----------------------------------
-- Basic datatype and semantics
type Label = Int

data Tex a where
  Return :: a -> Tex a
  Bind :: Tex a -> (a -> Tex b) -> Tex b

  Tex :: String -> Tex () -- ^ raw TeX code

  -- Reference management
  Label :: Tex Label
  Refer :: Label -> Tex Label
  MFix :: (a -> Tex a) -> Tex a 

  -- MetaPOST
  Metapost :: Tex a -> Tex a -- the output should go in the metapost file
  MPFigure :: Label -> Tex () -- inclusion of a metapost figure
  
tex = Tex
type TeX = Tex ()

newLabel = Label  
reference = Refer

instance MonadFix Tex where
  mfix = MFix

instance Monad Tex where
  (>>=) = Bind
  return = Return
  
instance Applicative Tex where
  (<*>) = ap
  pure = Return
  
instance Functor Tex where
  fmap = liftM
  
instance Monoid (Tex ()) where  
  mempty = Tex ""
  mappend = (>>)
  
instance IsString (Tex ()) where  
  fromString = Tex
  
type References = Int -- how many labels have been allocated
emptyRefs :: References
emptyRefs = 0
                  
select :: Bool -> a -> Either a a
select True x = Left x
select False x = Right x
            

type TexAndMPost = [Either String String]

newtype Displayer a = Displayer {fromDisplayer :: RWS () TexAndMPost References a }
  deriving (Monad, MonadWriter TexAndMPost, MonadState References, MonadFix)
-- | Produces a list of strings (either for LaTeX or MetaPOST).
display :: String -> Bool -> Tex a -> Displayer a 
display fname mp t = case t of
      (Tex s) -> tell' s
      (Return a) -> return a
      (Bind k f) -> rec k >>= (rec . f)
      Label -> do x <- get;  put $ x + 1; return x
      (Refer x) -> do tell [select mp $ show x]; return x
      (MFix f) -> mfix (rec . f)
      (Metapost x) -> display fname True x 
      (MPFigure l) -> tell' $ fname ++ "-" ++ show l ++ ".mps"
  where tell' :: String -> Displayer ()
        tell' s = tell [select mp s]   
        rec :: Tex a -> Displayer a
        rec = display fname mp
        
sho :: Show a => a -> Tex ()
sho = Tex . show
                             
render :: Tex a -> IO ()                   
render t = do 
  fname <- getProgName
  let (_,xs) = evalRWS (fromDisplayer $ display fname False t) () emptyRefs 
      (mp,tex) = partitionEithers xs
  writeFile (fname <.> "tex") (concat tex)
  writeFile (fname <.> "mp") (concat mp)
                    

texLn :: String -> Tex ()    
texLn s = Tex s >> Tex "\n"

texLines :: [String] -> Tex ()
texLines = mapM_ texLn

genParen :: String -> Tex a -> Tex a
genParen [l,r] x = Tex [l] *> x <* Tex [r]

braces :: Tex a -> Tex a
braces = genParen "{}"
brackets = genParen "[]"

backslash = Tex ['\\']

nil = braces (Tex "")

cmd0 c = cmdn' c [] [] >> return ()

cmd c = cmd' c []

cmd' cmd options arg = do
  [x] <- cmdn' cmd options [arg]
  return x

cmdn' :: String -> [String] -> [Tex a] -> Tex [a]
cmdn' cmd options args = do 
  backslash >> Tex cmd
  when (not $ null options) $ brackets $ sequence_ $ map tex $ intersperse "," options
  res <- sequence $ map braces args
  when (null args) $ Tex "{}" -- so that this does not get glued with the next thing.
  return res

cmdm :: String -> [Tex a] -> [Tex a] -> Tex [a]
cmdm cmd options args = do 
  backslash >> Tex cmd
  when (not $ null options) $ sequence_ $ map brackets $ options
  res <- sequence $ map braces args
  when (null args) $ Tex "{}" -- so that this does not get glued with the next thing.
  return res


cmdn'_ :: String -> [String] -> [Tex a] -> Tex ()
cmdn'_ cmd options args = cmdn' cmd options args >> return ()

cmdn cmd args = cmdn' cmd [] args
cmdn_ cmd args = cmdn'_ cmd [] args

env :: String -> Tex a -> Tex a
env e body = do
  cmd "begin" $ Tex e
  x <- body
  cmd "end" $ Tex e
  return x

label = do
  l <- Label
  cmd "label" (Refer l)
  
xref l = do
  cmd "ref" (Refer l)