{-# LANGUAGE OverloadedStrings #-}

-- | Provides a function to render a LaTeX structure into LaTeX code as text.
module Language.Ltml.ToLaTeX.Renderer
    ( renderLaTeX
    , renderLaTeXPretty
    ) where

import Data.Int (Int64)
import qualified Data.Text as T
import qualified Data.Text.Lazy as LT
import qualified Data.Text.Lazy.Builder as B
import Language.Ltml.ToLaTeX.LaTeXType (LaTeX (..))

-- | central function of the module. converts a given latex-structure into text
--   by using the builder module.
renderLaTeX :: LaTeX -> T.Text
renderLaTeX :: LaTeX -> Text
renderLaTeX = LazyText -> Text
LT.toStrict (LazyText -> Text) -> (LaTeX -> LazyText) -> LaTeX -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Builder -> LazyText
B.toLazyText (Builder -> LazyText) -> (LaTeX -> Builder) -> LaTeX -> LazyText
forall b c a. (b -> c) -> (a -> b) -> a -> c
. LaTeX -> Builder
build
  where
    build :: LaTeX -> B.Builder
    build :: LaTeX -> Builder
build (Text Text
t) = Text -> Builder
escape Text
t
    build (Raw Text
t) = Text -> Builder
B.fromText Text
t
    build (CommandS Text
name) =
        Builder
"\\" Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Text -> Builder
B.fromText Text
name
    build (Command Text
name [Text]
opts [LaTeX]
args) =
        Builder
"\\"
            Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Text -> Builder
B.fromText Text
name
            Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> [Text] -> Builder
renderOpts [Text]
opts
            Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> [Builder] -> Builder
forall a. Monoid a => [a] -> a
mconcat ((LaTeX -> Builder) -> [LaTeX] -> [Builder]
forall a b. (a -> b) -> [a] -> [b]
map (Builder -> Builder
wrapInBraces (Builder -> Builder) -> (LaTeX -> Builder) -> LaTeX -> Builder
forall b c a. (b -> c) -> (a -> b) -> a -> c
. LaTeX -> Builder
build) [LaTeX]
args)
    build (Environment Text
name [Text]
opts [LaTeX]
body) =
        Builder
"\\begin{"
            Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Text -> Builder
B.fromText Text
name
            Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Builder
"}"
            Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> [Text] -> Builder
renderOpts [Text]
opts
            Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> [Builder] -> Builder
forall a. Monoid a => [a] -> a
mconcat ((LaTeX -> Builder) -> [LaTeX] -> [Builder]
forall a b. (a -> b) -> [a] -> [b]
map LaTeX -> Builder
build [LaTeX]
body)
            Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Builder
"\\end{"
            Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Text -> Builder
B.fromText Text
name
            Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Builder
"}"
    build (Braced LaTeX
latex) = Builder -> Builder
wrapInBraces (LaTeX -> Builder
build LaTeX
latex)
    build (Sequence [LaTeX]
xs) = [Builder] -> Builder
forall a. Monoid a => [a] -> a
mconcat ((LaTeX -> Builder) -> [LaTeX] -> [Builder]
forall a b. (a -> b) -> [a] -> [b]
map LaTeX -> Builder
build [LaTeX]
xs)

-- | function to render latex code with linebreaks and indentation.
--   mainly for finding bugs during development
renderLaTeXPretty :: LaTeX -> T.Text
renderLaTeXPretty :: LaTeX -> Text
renderLaTeXPretty = LazyText -> Text
LT.toStrict (LazyText -> Text) -> (LaTeX -> LazyText) -> LaTeX -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Builder -> LazyText
B.toLazyText (Builder -> LazyText) -> (LaTeX -> Builder) -> LaTeX -> LazyText
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Int64 -> LaTeX -> Builder
build Int64
0
  where
    build :: Int64 -> LaTeX -> B.Builder
    build :: Int64 -> LaTeX -> Builder
build Int64
_ (Text Text
t) = Text -> Builder
escape Text
t
    build Int64
_ (Raw Text
t) = Text -> Builder
B.fromText Text
t
    build Int64
_ (CommandS Text
name) =
        Builder
"\\" Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Text -> Builder
B.fromText Text
name Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Builder
" "
    build Int64
n (Command Text
name [Text]
opts [LaTeX]
args) =
        Builder
"\\"
            Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Text -> Builder
B.fromText Text
name
            Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> [Text] -> Builder
renderOpts [Text]
opts
            Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> [Builder] -> Builder
forall a. Monoid a => [a] -> a
mconcat ((LaTeX -> Builder) -> [LaTeX] -> [Builder]
forall a b. (a -> b) -> [a] -> [b]
map (Builder -> Builder
wrapInBraces (Builder -> Builder) -> (LaTeX -> Builder) -> LaTeX -> Builder
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Int64 -> LaTeX -> Builder
build Int64
n) [LaTeX]
args)
    build Int64
n (Environment Text
name [Text]
opts [LaTeX]
body) =
        Builder
"\n"
            Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Text -> Builder
B.fromText (Int -> Text -> Text
T.replicate (Int64 -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int64
n) Text
"\t")
            Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Builder
"\\begin{"
            Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Text -> Builder
B.fromText Text
name
            Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Builder
"}"
            Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> [Text] -> Builder
renderOpts [Text]
opts
            Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Builder
"\n"
            Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> [Builder] -> Builder
forall a. Monoid a => [a] -> a
mconcat
                ( (LaTeX -> Builder) -> [LaTeX] -> [Builder]
forall a b. (a -> b) -> [a] -> [b]
map
                    ( (Text -> Builder
B.fromText (Int -> Text -> Text
T.replicate (Int64 -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral (Int64
n Int64 -> Int64 -> Int64
forall a. Num a => a -> a -> a
+ Int64
1)) Text
"\t") Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<>)
                        (Builder -> Builder) -> (LaTeX -> Builder) -> LaTeX -> Builder
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Builder
"\n")
                        (Builder -> Builder) -> (LaTeX -> Builder) -> LaTeX -> Builder
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Int64 -> LaTeX -> Builder
build (Int64
n Int64 -> Int64 -> Int64
forall a. Num a => a -> a -> a
+ Int64
1)
                    )
                    [LaTeX]
body
                )
            Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Text -> Builder
B.fromText (Int -> Text -> Text
T.replicate (Int64 -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int64
n) Text
"\t")
            Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Builder
"\\end{"
            Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Text -> Builder
B.fromText Text
name
            Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Builder
"}\n"
    build Int64
n (Braced LaTeX
latex) = Builder -> Builder
wrapInBraces (Int64 -> LaTeX -> Builder
build Int64
n LaTeX
latex)
    build Int64
n (Sequence [LaTeX]
xs) = [Builder] -> Builder
forall a. Monoid a => [a] -> a
mconcat ((LaTeX -> Builder) -> [LaTeX] -> [Builder]
forall a b. (a -> b) -> [a] -> [b]
map (Int64 -> LaTeX -> Builder
build Int64
n) [LaTeX]
xs)

-- | seperates options with commata
renderOpts :: [T.Text] -> B.Builder
renderOpts :: [Text] -> Builder
renderOpts [] = Builder
forall a. Monoid a => a
mempty
renderOpts [Text]
os = Builder
"[" Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Text -> Builder
B.fromText (Text -> [Text] -> Text
T.intercalate Text
"," [Text]
os) Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Builder
"]"

-- | puts braces around an argument
wrapInBraces :: B.Builder -> B.Builder
wrapInBraces :: Builder -> Builder
wrapInBraces Builder
b = Builder
"{" Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Builder
b Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Builder
"}"

-- | function to be able to represent Text correctly in latex
escape :: T.Text -> B.Builder
escape :: Text -> Builder
escape = (Char -> Builder -> Builder) -> Builder -> Text -> Builder
forall a. (Char -> a -> a) -> a -> Text -> a
T.foldr Char -> Builder -> Builder
escapeChar Builder
forall a. Monoid a => a
mempty

-- | function to convert certain chars that would not be represented correctly in latex
escapeChar :: Char -> B.Builder -> B.Builder
escapeChar :: Char -> Builder -> Builder
escapeChar Char
'#' Builder
acc = Builder
"\\#" Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Builder
acc
escapeChar Char
'$' Builder
acc = Builder
"\\$" Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Builder
acc
escapeChar Char
'%' Builder
acc = Builder
"\\%" Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Builder
acc
escapeChar Char
'&' Builder
acc = Builder
"\\&" Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Builder
acc
escapeChar Char
'~' Builder
acc = Builder
"\\~{}" Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Builder
acc
escapeChar Char
'_' Builder
acc = Builder
"\\verb|_|" Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Builder
acc
escapeChar Char
'^' Builder
acc = Builder
"\\^{}" Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Builder
acc
escapeChar Char
'\\' Builder
acc = Builder
"\\textbackslash{}" Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Builder
acc
escapeChar Char
'{' Builder
acc = Builder
"\\{" Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Builder
acc
escapeChar Char
'}' Builder
acc = Builder
"\\}" Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Builder
acc
escapeChar Char
'\'' Builder
acc = Builder
"\\string'|" Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Builder
acc
escapeChar Char
'`' Builder
acc = Builder
"\\string`" Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Builder
acc
escapeChar Char
'"' Builder
acc = Builder
"\\string\"" Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Builder
acc
escapeChar Char
'-' Builder
acc = Builder
"\\string-" Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Builder
acc
escapeChar Char
c Builder
acc = Char -> Builder
B.singleton Char
c Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Builder
acc