{-# LANGUAGE FlexibleInstances, UndecidableInstances, MultiParamTypeClasses, AllowAmbiguousTypes, OverloadedStrings, BangPatterns #-}
module SMCDEL.Internal.TexDisplay where
import Control.Monad
import Data.List
import Control.Concurrent (threadDelay)
import qualified Data.Text.Lazy as T
import System.Directory (findExecutable)
import System.IO (hGetContents)
import System.IO.Temp
import System.IO.Unsafe (unsafePerformIO)
import System.Process
import Data.GraphViz
import Data.GraphViz.Types.Generalised
import Data.GraphViz.Types.Monadic
import Data.Time.Clock.POSIX
begintab, endtab, newline :: String
begintab :: String
begintab = String
"\\\\begin{tabular}{c}"
endtab :: String
endtab = String
"\\\\end{tabular}"
newline :: String
newline = String
" \\\\\\\\[0pt] "
removeDoubleSpaces :: String -> String
removeDoubleSpaces :: String -> String
removeDoubleSpaces (Char
' ':Char
' ':String
rest) = String -> String
removeDoubleSpaces (Char
' 'Char -> String -> String
forall a. a -> [a] -> [a]
:String
rest)
removeDoubleSpaces (Char
x : String
xs ) = Char
x Char -> String -> String
forall a. a -> [a] -> [a]
: String -> String
removeDoubleSpaces String
xs
removeDoubleSpaces [ ] = [ ]
class TexAble a where
tex :: a -> String
texTo :: a -> String -> IO ()
texTo !a
x String
filename = String -> String -> IO ()
writeFile (String
filenameString -> String -> String
forall a. [a] -> [a] -> [a]
++String
".tex") (a -> String
forall a. TexAble a => a -> String
tex a
x)
texDocumentTo :: a -> String -> IO ()
texDocumentTo !a
x String
filename =
String -> String -> IO ()
writeFile (String
filenameString -> String -> String
forall a. [a] -> [a] -> [a]
++String
".tex") (String
pre String -> String -> String
forall a. [a] -> [a] -> [a]
++ a -> String
forall a. TexAble a => a -> String
tex a
x String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
post) where
pre :: String
pre = [String] -> String
forall (t :: * -> *) a. Foldable t => t [a] -> [a]
concat [ String
"\\documentclass{standalone}"
, String
"\\usepackage[utf8]{inputenc}"
, String
"\\usepackage{tikz,fontenc,graphicx}"
, String
"\\usepackage[pdftex]{hyperref}"
, String
"\\hypersetup{pdfborder={0 0 0},breaklinks=true}"
, String
"\\begin{document}"
]
post :: String
post= String
"\\end{document}"
pdfTo :: a -> String -> IO ()
pdfTo !a
x String
filename = do
a -> String -> IO ()
forall a. TexAble a => a -> String -> IO ()
texDocumentTo a
x String
filename
String -> IO ()
runAndWait (String -> IO ()) -> String -> IO ()
forall a b. (a -> b) -> a -> b
$ String
"cd " String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
filename String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
"/../; /usr/bin/pdflatex -interaction=nonstopmode "String -> String -> String
forall a. [a] -> [a] -> [a]
++String
filenameString -> String -> String
forall a. [a] -> [a] -> [a]
++String
".tex"
disp :: a -> IO ()
disp !a
x = String -> (String -> IO ()) -> IO ()
forall (m :: * -> *) a.
(MonadIO m, MonadMask m) =>
String -> (String -> m a) -> m a
withSystemTempDirectory String
"smcdel" ((String -> IO ()) -> IO ()) -> (String -> IO ()) -> IO ()
forall a b. (a -> b) -> a -> b
$ \String
tmpdir -> do
Int
ts <- POSIXTime -> Int
forall b. Integral b => POSIXTime -> b
forall a b. (RealFrac a, Integral b) => a -> b
round (POSIXTime -> Int) -> IO POSIXTime -> IO Int
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> IO POSIXTime
getPOSIXTime
let filename :: String
filename = String
tmpdir String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
"/disp-" String -> String -> String
forall a. [a] -> [a] -> [a]
++ Int -> String
forall a. Show a => a -> String
show (Int
ts :: Int)
a -> String -> IO ()
forall a. TexAble a => a -> String -> IO ()
pdfTo a
x String
filename
String -> IO ()
runIgnoreAndWait (String -> IO ()) -> String -> IO ()
forall a b. (a -> b) -> a -> b
$ String
"open " String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
filename String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
".pdf"
Int -> IO ()
threadDelay Int
5000000
svgViaTex :: a -> String
svgViaTex !a
x = IO String -> String
forall a. IO a -> a
unsafePerformIO (IO String -> String) -> IO String -> String
forall a b. (a -> b) -> a -> b
$ String -> (String -> IO String) -> IO String
forall (m :: * -> *) a.
(MonadIO m, MonadMask m) =>
String -> (String -> m a) -> m a
withSystemTempDirectory String
"smcdel" ((String -> IO String) -> IO String)
-> (String -> IO String) -> IO String
forall a b. (a -> b) -> a -> b
$ \String
tmpdir -> do
Int
ts <- POSIXTime -> Int
forall b. Integral b => POSIXTime -> b
forall a b. (RealFrac a, Integral b) => a -> b
round (POSIXTime -> Int) -> IO POSIXTime -> IO Int
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> IO POSIXTime
getPOSIXTime
let filename :: String
filename = String
tmpdir String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
"/svgViaTex-" String -> String -> String
forall a. [a] -> [a] -> [a]
++ Int -> String
forall a. Show a => a -> String
show (Int
ts :: Int)
a -> String -> IO ()
forall a. TexAble a => a -> String -> IO ()
pdfTo a
x String
filename
String -> IO ()
runAndWait (String -> IO ()) -> String -> IO ()
forall a b. (a -> b) -> a -> b
$ String
"pdftocairo -nocrop -svg "String -> String -> String
forall a. [a] -> [a] -> [a]
++String
filenameString -> String -> String
forall a. [a] -> [a] -> [a]
++String
".pdf "String -> String -> String
forall a. [a] -> [a] -> [a]
++String
filenameString -> String -> String
forall a. [a] -> [a] -> [a]
++String
".svg"
String -> IO String
readFile (String
filename String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
".svg")
instance TexAble String where
tex :: String -> String
tex String
i = String
" \\text{" String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
i String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
"} "
instance TexAble Int where
tex :: Int -> String
tex = Int -> String
forall a. Show a => a -> String
show
instance TexAble a => TexAble [(a,Bool)] where
tex :: [(a, Bool)] -> String
tex [(a, Bool)]
ass = case ((a, Bool) -> Bool) -> [(a, Bool)] -> [(a, Bool)]
forall a. (a -> Bool) -> [a] -> [a]
filter (a, Bool) -> Bool
forall a b. (a, b) -> b
snd [(a, Bool)]
ass of
[] -> String
""
[(a, Bool)]
ps -> String
"$" String -> String -> String
forall a. [a] -> [a] -> [a]
++ String -> [String] -> String
forall a. [a] -> [[a]] -> [a]
intercalate String
"," (((a, Bool) -> String) -> [(a, Bool)] -> [String]
forall a b. (a -> b) -> [a] -> [b]
map (a -> String
forall a. TexAble a => a -> String
tex(a -> String) -> ((a, Bool) -> a) -> (a, Bool) -> String
forall b c a. (b -> c) -> (a -> b) -> a -> c
.(a, Bool) -> a
forall a b. (a, b) -> a
fst) [(a, Bool)]
ps) String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
"$"
runAndWait :: String -> IO ()
runAndWait :: String -> IO ()
runAndWait String
command = do
(Handle
_inp,Handle
_out,Handle
err,ProcessHandle
pid) <- String -> IO (Handle, Handle, Handle, ProcessHandle)
runInteractiveCommand String
command
ExitCode
_ <- ProcessHandle -> IO ExitCode
waitForProcess ProcessHandle
pid
Handle -> IO String
hGetContents Handle
err IO String -> (String -> IO ()) -> IO ()
forall a b. IO a -> (a -> IO b) -> IO b
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= (\String
x -> Bool -> IO () -> IO ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
unless (String -> Bool
forall a. [a] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null String
x) (String -> IO ()
putStrLn String
x))
() -> IO ()
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return ()
runIgnoreAndWait :: String -> IO ()
runIgnoreAndWait :: String -> IO ()
runIgnoreAndWait String
command = do
(Handle
_inp,Handle
_out,Handle
_err,ProcessHandle
pid) <- String -> IO (Handle, Handle, Handle, ProcessHandle)
runInteractiveCommand String
command
ExitCode
_ <- ProcessHandle -> IO ExitCode
waitForProcess ProcessHandle
pid
() -> IO ()
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return ()
class KripkeLike a where
getNodes :: a -> [(String,String)]
getEdges :: a -> [(String,String,String)]
getActuals :: a -> [String]
getActuals = [String] -> a -> [String]
forall a b. a -> b -> a
const []
directed :: a -> Bool
directed = Bool -> a -> Bool
forall a b. a -> b -> a
const Bool
True
nodeAts :: a -> Bool -> Attributes
nodeAts a
_ Bool
True = [Shape -> Attribute
shape Shape
DoubleCircle]
nodeAts a
_ Bool
False = [Shape -> Attribute
shape Shape
Circle]
toGraph :: a -> Data.GraphViz.Types.Generalised.DotGraph String
toGraph a
x = (if a -> Bool
forall a. KripkeLike a => a -> Bool
directed a
x then DotM String () -> DotGraph String
forall n a. DotM n a -> DotGraph n
digraph' else DotM String () -> DotGraph String
forall n a. DotM n a -> DotGraph n
graph') (DotM String () -> DotGraph String)
-> DotM String () -> DotGraph String
forall a b. (a -> b) -> a -> b
$ do
let nodes :: [(String, String)]
nodes = a -> [(String, String)]
forall a. KripkeLike a => a -> [(String, String)]
getNodes a
x
let actuals :: [(String, String)]
actuals = ((String, String) -> Bool)
-> [(String, String)] -> [(String, String)]
forall a. (a -> Bool) -> [a] -> [a]
filter (\(String, String)
n -> (String, String) -> String
forall a b. (a, b) -> a
fst (String, String)
n String -> [String] -> Bool
forall a. Eq a => a -> [a] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` a -> [String]
forall a. KripkeLike a => a -> [String]
getActuals a
x) [(String, String)]
nodes
((String, String) -> DotM String ())
-> [(String, String)] -> DotM String ()
forall (t :: * -> *) (m :: * -> *) a b.
(Foldable t, Monad m) =>
(a -> m b) -> t a -> m ()
mapM_
(\(String
nid,String
nlabel) -> String -> Attributes -> DotM String ()
forall n. n -> Attributes -> Dot n
node String
nid (String -> Attribute
forall a. Labellable a => a -> Attribute
toLabel String
nlabel Attribute -> Attributes -> Attributes
forall a. a -> [a] -> [a]
: a -> Bool -> Attributes
forall a. KripkeLike a => a -> Bool -> Attributes
nodeAts a
x Bool
True))
[(String, String)]
actuals
((String, String) -> DotM String ())
-> [(String, String)] -> DotM String ()
forall (t :: * -> *) (m :: * -> *) a b.
(Foldable t, Monad m) =>
(a -> m b) -> t a -> m ()
mapM_
(\(String
nid,String
nlabel) -> String -> Attributes -> DotM String ()
forall n. n -> Attributes -> Dot n
node String
nid (String -> Attribute
forall a. Labellable a => a -> Attribute
toLabel String
nlabel Attribute -> Attributes -> Attributes
forall a. a -> [a] -> [a]
: a -> Bool -> Attributes
forall a. KripkeLike a => a -> Bool -> Attributes
nodeAts a
x Bool
False))
([(String, String)]
nodes [(String, String)] -> [(String, String)] -> [(String, String)]
forall a. Eq a => [a] -> [a] -> [a]
\\ [(String, String)]
actuals)
((String, String, String) -> DotM String ())
-> [(String, String, String)] -> DotM String ()
forall (t :: * -> *) (m :: * -> *) a b.
(Foldable t, Monad m) =>
(a -> m b) -> t a -> m ()
mapM_
(\(String
elabel,String
from,String
to) -> String -> String -> Attributes -> DotM String ()
forall n. n -> n -> Attributes -> Dot n
edge String
from String
to [String -> Attribute
forall a. Labellable a => a -> Attribute
toLabel String
elabel])
(a -> [(String, String, String)]
forall a. KripkeLike a => a -> [(String, String, String)]
getEdges a
x)
dispDot :: a -> IO ()
dispDot a
x = GraphvizCommand -> DotGraph String -> GraphvizCanvas -> IO ()
forall (dg :: * -> *) n.
PrintDotRepr dg n =>
GraphvizCommand -> dg n -> GraphvizCanvas -> IO ()
runGraphvizCanvas GraphvizCommand
Dot (a -> DotGraph String
forall a. KripkeLike a => a -> DotGraph String
toGraph a
x) GraphvizCanvas
Xlib
textDot :: a -> T.Text
textDot = Text -> [Text] -> Text
T.intercalate Text
" " ([Text] -> Text) -> (a -> [Text]) -> a -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> [Text]
T.lines (Text -> [Text]) -> (a -> Text) -> a -> [Text]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. DotGraph String -> Text
forall (dg :: * -> *) n. PrintDotRepr dg n => dg n -> Text
printDotGraph (DotGraph String -> Text) -> (a -> DotGraph String) -> a -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. a -> DotGraph String
forall a. KripkeLike a => a -> DotGraph String
toGraph
svg :: a -> String
svg a
x = IO String -> String
forall a. IO a -> a
unsafePerformIO (IO String -> String) -> IO String -> String
forall a b. (a -> b) -> a -> b
$
String -> (String -> IO String) -> IO String
forall (m :: * -> *) a.
(MonadIO m, MonadMask m) =>
String -> (String -> m a) -> m a
withSystemTempDirectory String
"smcdel" ((String -> IO String) -> IO String)
-> (String -> IO String) -> IO String
forall a b. (a -> b) -> a -> b
$ \String
tmpdir -> do
String
_ <- GraphvizCommand
-> DotGraph String -> GraphvizOutput -> String -> IO String
forall (dg :: * -> *) n.
PrintDotRepr dg n =>
GraphvizCommand -> dg n -> GraphvizOutput -> String -> IO String
runGraphvizCommand GraphvizCommand
Dot (a -> DotGraph String
forall a. KripkeLike a => a -> DotGraph String
toGraph a
x) GraphvizOutput
Svg (String
tmpdir String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
"/temp.svg")
String -> IO String
readFile (String
tmpdir String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
"/temp.svg")
newtype ViaDot a = ViaDot a
deriving (ViaDot a -> ViaDot a -> Bool
(ViaDot a -> ViaDot a -> Bool)
-> (ViaDot a -> ViaDot a -> Bool) -> Eq (ViaDot a)
forall a. Eq a => ViaDot a -> ViaDot a -> Bool
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: forall a. Eq a => ViaDot a -> ViaDot a -> Bool
== :: ViaDot a -> ViaDot a -> Bool
$c/= :: forall a. Eq a => ViaDot a -> ViaDot a -> Bool
/= :: ViaDot a -> ViaDot a -> Bool
Eq,Eq (ViaDot a)
Eq (ViaDot a) =>
(ViaDot a -> ViaDot a -> Ordering)
-> (ViaDot a -> ViaDot a -> Bool)
-> (ViaDot a -> ViaDot a -> Bool)
-> (ViaDot a -> ViaDot a -> Bool)
-> (ViaDot a -> ViaDot a -> Bool)
-> (ViaDot a -> ViaDot a -> ViaDot a)
-> (ViaDot a -> ViaDot a -> ViaDot a)
-> Ord (ViaDot a)
ViaDot a -> ViaDot a -> Bool
ViaDot a -> ViaDot a -> Ordering
ViaDot a -> ViaDot a -> ViaDot a
forall a.
Eq a =>
(a -> a -> Ordering)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> a)
-> (a -> a -> a)
-> Ord a
forall a. Ord a => Eq (ViaDot a)
forall a. Ord a => ViaDot a -> ViaDot a -> Bool
forall a. Ord a => ViaDot a -> ViaDot a -> Ordering
forall a. Ord a => ViaDot a -> ViaDot a -> ViaDot a
$ccompare :: forall a. Ord a => ViaDot a -> ViaDot a -> Ordering
compare :: ViaDot a -> ViaDot a -> Ordering
$c< :: forall a. Ord a => ViaDot a -> ViaDot a -> Bool
< :: ViaDot a -> ViaDot a -> Bool
$c<= :: forall a. Ord a => ViaDot a -> ViaDot a -> Bool
<= :: ViaDot a -> ViaDot a -> Bool
$c> :: forall a. Ord a => ViaDot a -> ViaDot a -> Bool
> :: ViaDot a -> ViaDot a -> Bool
$c>= :: forall a. Ord a => ViaDot a -> ViaDot a -> Bool
>= :: ViaDot a -> ViaDot a -> Bool
$cmax :: forall a. Ord a => ViaDot a -> ViaDot a -> ViaDot a
max :: ViaDot a -> ViaDot a -> ViaDot a
$cmin :: forall a. Ord a => ViaDot a -> ViaDot a -> ViaDot a
min :: ViaDot a -> ViaDot a -> ViaDot a
Ord,Int -> ViaDot a -> String -> String
[ViaDot a] -> String -> String
ViaDot a -> String
(Int -> ViaDot a -> String -> String)
-> (ViaDot a -> String)
-> ([ViaDot a] -> String -> String)
-> Show (ViaDot a)
forall a. Show a => Int -> ViaDot a -> String -> String
forall a. Show a => [ViaDot a] -> String -> String
forall a. Show a => ViaDot a -> String
forall a.
(Int -> a -> String -> String)
-> (a -> String) -> ([a] -> String -> String) -> Show a
$cshowsPrec :: forall a. Show a => Int -> ViaDot a -> String -> String
showsPrec :: Int -> ViaDot a -> String -> String
$cshow :: forall a. Show a => ViaDot a -> String
show :: ViaDot a -> String
$cshowList :: forall a. Show a => [ViaDot a] -> String -> String
showList :: [ViaDot a] -> String -> String
Show)
dot2tex :: String -> IO ()
dot2tex :: String -> IO ()
dot2tex String
args = do
Maybe String
haveDot2tex <- String -> IO (Maybe String)
findExecutable String
"dot2tex"
case Maybe String
haveDot2tex of
Maybe String
Nothing -> String -> IO ()
forall a. HasCallStack => String -> a
error String
"Please install dot2tex which is needed to show this."
Just String
d2t -> String -> IO ()
runAndWait (String -> IO ()) -> String -> IO ()
forall a b. (a -> b) -> a -> b
$ String
d2t String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
args
dot2texDefaultArgs :: String
dot2texDefaultArgs :: String
dot2texDefaultArgs = String
" -ftikz -traw -p --autosize -w --usepdflatex "
instance (Ord a, Show a, KripkeLike a) => TexAble (ViaDot a) where
tex :: ViaDot a -> String
tex (ViaDot a
x) = IO String -> String
forall a. IO a -> a
unsafePerformIO (IO String -> String) -> IO String -> String
forall a b. (a -> b) -> a -> b
$
String -> (String -> IO String) -> IO String
forall (m :: * -> *) a.
(MonadIO m, MonadMask m) =>
String -> (String -> m a) -> m a
withSystemTempDirectory String
"smcdel" ((String -> IO String) -> IO String)
-> (String -> IO String) -> IO String
forall a b. (a -> b) -> a -> b
$ \String
tmpdir -> do
String
_ <- DotGraph String -> GraphvizOutput -> String -> IO String
forall (dg :: * -> *) n.
PrintDotRepr dg n =>
dg n -> GraphvizOutput -> String -> IO String
runGraphviz (a -> DotGraph String
forall a. KripkeLike a => a -> DotGraph String
toGraph a
x) GraphvizOutput
DotOutput (String
tmpdir String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
"/temp.dot")
String -> IO ()
dot2tex (String -> IO ()) -> String -> IO ()
forall a b. (a -> b) -> a -> b
$ String
dot2texDefaultArgs String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
" --figonly " String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
tmpdir String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
"/temp.dot | sed '/^$/d' > " String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
tmpdir String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
"/temp.tex;"
String -> IO String
readFile (String
tmpdir String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
"/temp.tex")
texTo :: ViaDot a -> String -> IO ()
texTo (ViaDot a
x) String
filename = do
String
_ <- DotGraph String -> GraphvizOutput -> String -> IO String
forall (dg :: * -> *) n.
PrintDotRepr dg n =>
dg n -> GraphvizOutput -> String -> IO String
runGraphviz (a -> DotGraph String
forall a. KripkeLike a => a -> DotGraph String
toGraph a
x) GraphvizOutput
DotOutput (String
filename String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
".dot")
String -> IO ()
dot2tex (String -> IO ()) -> String -> IO ()
forall a b. (a -> b) -> a -> b
$ String
dot2texDefaultArgs String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
" --figonly " String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
filename String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
".dot | sed '/^$/d' > " String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
filename String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
".tex;"
texDocumentTo :: ViaDot a -> String -> IO ()
texDocumentTo (ViaDot a
x) String
filename = do
String
_ <- DotGraph String -> GraphvizOutput -> String -> IO String
forall (dg :: * -> *) n.
PrintDotRepr dg n =>
dg n -> GraphvizOutput -> String -> IO String
runGraphviz (a -> DotGraph String
forall a. KripkeLike a => a -> DotGraph String
toGraph a
x) GraphvizOutput
DotOutput (String
filename String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
".dot")
String -> IO ()
dot2tex (String -> IO ()) -> String -> IO ()
forall a b. (a -> b) -> a -> b
$ String
dot2texDefaultArgs String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
filename String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
".dot -o " String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
filename String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
".tex;"