{-# LANGUAGE FlexibleContexts #-} module Language.LaTeX.Builder.Internal where import Prelude hiding (sqrt, min, max, lcm, gcd, log, mod, tanh, cosh, tan, sinh, sin, cos, succ, sum, pi, mapM) import qualified Data.List as L import Data.Maybe import Data.Traversable (sequenceA, mapM) import Control.Monad hiding (mapM) import Control.Monad.Error (throwError) import Language.LaTeX.Types import Language.LaTeX.Builder.MonoidUtils {- TODO: - robust/fragile/moving - tracking savebin in the monad? - generating a doc with examples: [...("sum", [| sum⊕sub(i⊕eq⊕0)⊕sup infty⊕i⊕sup 2 |])...] - pictures - choose between optFoo and optionalFoo -} noArg :: Arg a noArg = NoArg starArg :: Arg a starArg = StarArg starToArg :: Star -> Arg a starToArg Star = starArg starToArg NoStar = noArg mandatory, optional :: a -> Arg a mandatory = Mandatory . pure optional = Optional . pure mandatoryLatexItem, optionalLatexItem :: LatexItem -> Arg AnyItem mandatoryLatexItem = mandatory . latexItem optionalLatexItem = optional . latexItem coordinates :: a -> a -> Arg a coordinates = Coordinates mandatoryList :: [a] -> Arg a mandatoryList = Mandatory optionals :: [a] -> Arg a optionals [] = NoArg optionals xs = Optional xs named :: String -> a -> Named a named = Named namedOpts :: [Named a] -> Arg a namedOpts [] = NoArg namedOpts xs = NamedOpts xs namedArgs :: [Named a] -> Arg a namedArgs = NamedArgs optionalLatexItems :: [LatexItem] -> Arg AnyItem optionalLatexItems = optionals . map latexItem usepackage :: [AnyItem] -> PackageName -> PreambleItem usepackage opts pkg = preambleCmdArgs "usepackage" [providePackage pkg, optionals opts, mandatory (packageName pkg)] nilNote :: Note nilNote = stringNote "" stringNote :: String -> Note stringNote = TextNote intNote :: Int -> Note intNote = IntNote locNote :: Loc -> Note locNote = LocNote -- Note that @rawArg ø@ reduces to ø. rawArg :: String -> Arg m rawArg "" = NoArg rawArg x = RawArg x -- Note that @liftArg ø@ reduces to ø. liftArg :: (Eq m, Monoid m) => m -> Arg m liftArg x | x == ø = NoArg | otherwise = LiftArg x rawDecls :: [TexDecl] -> LatexItem rawDecls = mapNonEmpty $ fmap TexDecls . sequenceA texDecl :: String -> TexDecl texDecl s = pure $ TexDcl s [] texDecl' :: String -> [Arg AnyItem] -> TexDecl texDecl' s opts = TexDcl s <$> mapM (mapM anyItmM) opts texDeclOpt :: String -> AnyItem -> TexDecl texDeclOpt s opt = texDecl' s [optional opt] parNote :: Key -> Note -> ParItem -> ParItem parNote k = fmap . ParNote k parCmdArgs :: String -> [Arg AnyItem] -> ParItem parCmdArgs x ys = ParCmdArgs x <$> mapM (mapM anyItmM) ys parCmdArg :: String -> AnyItem -> ParItem parCmdArg x y = parCmdArgs x [mandatory y] latexNote :: Key -> Note -> LatexItem -> LatexItem latexNote k = fmap . LatexNote k latexCmdArgs :: String -> [Arg LatexItem] -> LatexItem latexCmdArgs x ys = LatexCmdArgs x <$> mapM sequenceA ys latexCmdAnyArgs :: String -> [Arg AnyItem] -> LatexItem latexCmdAnyArgs x ys = LatexCmdAnyArgs x <$> mapM (mapM anyItmM) ys latexCmdArg :: String -> LatexItem -> LatexItem latexCmdArg x y = latexCmdArgs x [mandatory y] latexCmdAnyArg :: String -> AnyItem -> LatexItem latexCmdAnyArg x y = latexCmdAnyArgs x [mandatory y] preambleNote :: Key -> Note -> PreambleItem -> PreambleItem preambleNote k = fmap . PreambleNote k preambleCmdArgs :: String -> [Arg AnyItem] -> PreambleItem preambleCmdArgs x ys = PreambleCmdArgs x <$> mapM (mapM anyItmM) ys preambleCmdArg :: String -> AnyItem -> PreambleItem preambleCmdArg x y = preambleCmdArgs x [mandatory y] preambleEnv :: String -> [Arg AnyItem] -> AnyItem -> PreambleItem preambleEnv x ys = liftM2 (PreambleEnv x) (mapM (mapM anyItmM) ys) . anyItmM rawPreamble :: String -> PreambleItem rawPreamble = mapNonEmpty $ pure . RawPreamble texLength :: LatexLength -> AnyItem texLength = AnyItem . pure . Length mandatoryTexLength :: LatexLength -> Arg AnyItem mandatoryTexLength = mandatory . texLength optTexLength :: LatexLength -> Arg AnyItem optTexLength = optional . texLength latexItem :: LatexItem -> AnyItem latexItem = AnyItem . fmap LatexItm mathItem :: MathItem -> AnyItem mathItem = AnyItem . fmap MathItm . mathItmM parItem :: ParItem -> AnyItem parItem = AnyItem . fmap ParItm preambleItem :: PreambleItem -> AnyItem preambleItem = AnyItem . fmap PreambleItm packageName :: PackageName -> AnyItem packageName = AnyItem . pure . PackageName locSpecs :: [LocSpec] -> AnyItem locSpecs = AnyItem . pure . LocSpecs rawEncoding :: String -> Encoding rawEncoding = Encoding pkgName :: String -> PackageName pkgName = PkgName packageDependency :: PackageName -> Arg a packageDependency = PackageAction . PackageDependency -- This phantom argument states that the given package -- is considered provided from now on. -- This especially make sense when building the usepackage -- command. providePackage :: PackageName -> Arg a providePackage = PackageAction . ProvidePackage showPaper :: LatexPaperSize -> String showPaper A4paper = "a4paper" showPaper (OtherPaperSize s) = s latexPaper :: LatexPaperSize -> AnyItem latexPaper = rawAnyTex . showPaper otherDocumentClassKind :: String -> DocumentClassKind otherDocumentClassKind = OtherDocumentClassKind bool :: Bool -> AnyItem bool True = rawAnyTex "true" bool False = rawAnyTex "false" coord :: Coord -> AnyItem coord = AnyItem . pure . Coord latexSaveBin :: SaveBin -> AnyItem latexSaveBin = AnyItem . pure . SaveBin latexCast :: AnyItem -> LatexItem latexCast = fmap cast . anyItmM where cast (LatexItm x) = x cast x = LatexCast x mathCast :: AnyItem -> MathItem mathCast = MathItem . fmap cast . anyItmM where cast (MathItm x) = x cast x = MathCast x parCast :: AnyItem -> ParItem parCast = fmap cast . anyItmM where cast (ParItm x) = x cast x = ParCast x preambleCast :: AnyItem -> PreambleItem preambleCast = fmap cast . anyItmM where cast (PreambleItm x) = x cast x = PreambleCast x latexEnvironmentAny :: String -> [Arg AnyItem] -> AnyItem -> LatexItem latexEnvironmentAny x ys = liftM2 (Environment x) (mapM (mapM anyItmM) ys) . anyItmM latexEnvironment :: String -> [Arg AnyItem] -> LatexItem -> LatexItem latexEnvironment x ys = latexEnvironmentAny x ys . latexItem latexEnvironmentPar :: String -> [Arg AnyItem] -> ParItem -> LatexItem latexEnvironmentPar x ys = latexEnvironmentAny x ys . parItem latexParModeArgs :: String -> [Arg AnyItem] -> ParItem -> LatexItem latexParModeArgs x ys z = latexCmdAnyArgs x (ys ++ [mandatory (parItem z)]) parEnv :: String -> [Arg AnyItem] -> AnyItem -> ParItem parEnv x ys = liftM2 (ParEnv x) (mapM (mapM anyItmM) ys) . anyItmM parEnvironmentPar :: String -> [Arg AnyItem] -> ParItem -> ParItem parEnvironmentPar x ys = parEnv x ys . parItem figureLike :: String -> Star -> [LocSpec] -> ParItem -> ParItem figureLike name star locs = parEnvironmentPar (starize name star) [optional . locSpecs $ locs] -- liftM $ FigureLike (starize x s) y listLikeEnv :: String -> [Arg LatexItem] -> [ListItem] -> ParItem listLikeEnv name opts items = parEnvironmentPar name ((map.fmap) latexItem opts) (mconcat <$> mapM (fmap mkItem) items) where mkItem (ListItm opts' contents) = ParCmdArgs "item" ((map.fmap) LatexItm opts') ⊕ contents rawTex :: String -> LatexItem rawTex = mapNonEmpty $ pure . RawTex rawAnyTex :: String -> AnyItem rawAnyTex = latexItem . rawTex rawMath :: String -> MathItem rawMath = MathItem . pure . RawMath rawMathChar :: Char -> MathItem rawMathChar = rawMath . ('{':) . (:"}") texCmdNoArg :: String -> LatexItem texCmdNoArg = pure . TexCmdNoArg latexKey :: Key -> AnyItem latexKey = AnyItem . pure . Key latexKeys :: [Key] -> [AnyItem] latexKeys = map latexKey latexKeysArg :: [Key] -> Arg AnyItem latexKeysArg = mandatoryList . latexKeys latexKeyArg :: Key -> Arg AnyItem latexKeyArg = mandatory . latexKey normSpaces :: String -> String normSpaces = unlines . map (L.unwords . words) . lines num :: Real a => a -> AnyItem num = texLength . fromRational . toRational rat :: Rational -> AnyItem rat = texLength . fromRational space :: LatexItem space = rawTex "{ }" starize :: String -> Star -> String starize s NoStar = s starize s Star = s ++ "*" -- TODO: make a safe version using a monad -- http://www.personal.ceu.hu/tex/spacebox.htm#newsavebox -- fragile unsafeNewsavebox :: Int -> (SaveBin, LatexItem) unsafeNewsavebox n = let bin = UnsafeMakeSaveBin n in (bin, latexCmdAnyArg "newsavebox" $ latexSaveBin bin) -- sectioning -- Sectioning commands arguments are 'moving'. sectioning :: String -> (LatexItem -> ParItem, Star -> Maybe LatexItem -> LatexItem -> ParItem) sectioning name = (sect, sect') where sect = sect' ø Nothing sect' s opt arg = parCmdArgs (starize name s) (maybeToList (fmap (optional . latexItem) opt) ++ [mandatory (latexItem arg)]) -- The array and tablular Environments tabularLike :: ([RowSpec a] -> [Row a] -> b) -> [RowSpec (LatexM a)] -> [Row (LatexM a)] -> LatexM b tabularLike f specs rows = do spcs <- mapM sequenceA specs f spcs <$> (checkRows spcs =<< mapM sequenceA rows) checkRows :: [RowSpec a] -> [Row a] -> LatexM [Row a] checkRows specs = mapM checkRow where checkRow (Cells cs) | cols /= length cs = err "wrong number of cells" cols "different from" (length cs) | otherwise = pure $ Cells cs checkRow Hline = pure Hline checkRow (Cline c1 c2) | c1 > cols = err "cline: start column too high" c1 ">" cols | c1 < 0 = throwError "tabular: cline: negative start column" | c2 > cols = err "cline: end column too high" c2 ">" cols | c2 < 0 = throwError "tabular: cline: negative end column" | otherwise = pure $ Cline c1 c2 cols = length $ filter isCol specs isCol Rc = True isCol Rl = True isCol Rr = True isCol Rvline = False isCol (Rtext _) = False err msg x op y = throwError $ L.unwords ["tabular:", msg, '(' : show x, op, show y ++ ")"] -- `{' `}' are like bgroup plus egroup except that `{' and `}' are -- syntactically forced to be balanced. -- begingroup and endgroup only save the scopes of definitions. -- bgroup and egroup save the scopes as well but also resolve the springs -- independently. bgroup, egroup, begingroup, endgroup :: TexDecl bgroup = texDecl "bgroup" egroup = texDecl "egroup" begingroup = texDecl "begingroup" endgroup = texDecl "endgroup"