aboutsummaryrefslogtreecommitdiff
path: root/latexfmt.hs
blob: 6c070fc94f5899711d7b6ffa3aaf39c35ee023f3 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE OverloadedStrings #-}

import Control.Applicative
import Control.Monad
import Control.Monad.State
import Data.Attoparsec.Text hiding (take)
import Data.Char
  ( isAlpha,
    isDigit,
  )
import qualified Data.Text as T
import qualified Data.Text.IO as T
import Prelude hiding
  ( print,
    takeWhile,
  )

data Tokens = Wrd {unwrd :: T.Text} | Cmd T.Text | BSq [Tokens] | BCr [Tokens] | NL | Cmt T.Text | Nobs | IMath T.Text | Math T.Text | Verbatim Bool T.Text
  deriving (Show)

isCtrl x = not $ isAlpha x || isDigit x

-- tokens to strings
toStr (Verbatim star str) = do
  print' "\n\\begin{verbatim*}"
  print' str
  print' "\\end{verbatim*}"
toStr (Wrd w) = do
  if T.length w >= 1 && T.head w `elem` (".,;:!?)" :: [Char])
    then print' w
    else print w
  when (w == "(") suppress
  when (T.last w == '.') printnl
toStr (Cmd c) =
  if T.length c > 0 && isCtrl (T.head c) || T.length c == 1
    then print' $ "\\" `T.append` c
    else do
      printnl'
      when (c == "end") unindent
      print $ "\\" `T.append` c
      when (c == "begin") indent
toStr (BSq ts) = do
  print' "["
  suppress
  indent
  mapM_ toStr ts
  unindent
  print' "]"
toStr (BCr ts) = do
  print' "{"
  suppress
  indent
  mapM_ toStr ts
  unindent
  print' "}"
toStr NL = printnl' >> printnl
toStr (Cmt c) = do
  print "%"
  print' c
toStr Nobs = do
  print' "~"
  suppress
toStr (Math m) = do
  printnl
  print "\\["
  print' m
  println "\\]"
toStr (IMath m) = do
  print "\\("
  print' m
  print' "\\)"

-- Tokeniser
ws = inClass " \t\n"

pad = takeWhile ws

token = nl <|> pad *> (nobs <|> verbatim <|> verbatimStar <|> math <|> cmd <|> bsq <|> bcr <|> cmt <|> wrd)

nobs = string "~" *> pure Nobs

nl = string "\n" *> takeWhile (inClass " \t") *> string "\n" *> pure NL

cmd :: Parser Tokens
cmd = Cmd <$> (string "\\" *> ((unwrd <$> wrd) <|> pure ""))

bsq = BSq <$> (string "[" *> many' token) <* pad <* string "]"

bcr = BCr <$> (string "{" *> many' token) <* pad <* string "}"

wrd = Wrd <$> takeWhile1 (notInClass " \t\n[]{}\\~")

cmt = Cmt <$> (takeWhile1 (== '%') *> takeWhile (/= '\n'))

math = oldmath <|> newmath <|> oldimath <|> newimath

oldmath = Math <$> (string "$$" *> takeWhile (/= '$')) <* string "$$"

newmath = Math . T.pack <$> (string "\\[" *> manyTill' anyChar (string "\\]"))

oldimath = IMath <$> (string "$" *> takeWhile (/= '$')) <* string "$"

newimath = IMath . T.pack <$> (string "\\(" *> manyTill' anyChar (string "\\)"))

verbatim = Verbatim False . T.pack <$> (string "\\begin{verbatim}" *> manyTill' anyChar (string "\\end{verbatim}"))

verbatimStar = Verbatim True . T.pack <$> (string "\\begin{verbatim*}" *> manyTill' anyChar (string "\\end{verbatim*}"))

-- indented printer
type Beginning = Bool

type Indent = Int

type SuppressSpace = Bool

data Printer = Printer Beginning SuppressSpace Indent T.Text
  deriving (Show)

print :: T.Text -> State Printer ()
print str = do
  (Printer beg sup i s) <- get
  if sup
    then print' str
    else do
      let indent = T.pack . take (2 * i) $ repeat ' '
          str' = T.concat [s, if beg then indent else " ", str]
      put $ Printer False False i str'

-- force unindented
print' :: T.Text -> State Printer ()
print' str = do
  (Printer beg _ i s) <- get
  let indent = T.pack . take (2 * i) $ repeat ' '
      str' = T.concat [s, if beg then indent else "", str]
  put $ Printer False False i str'

printnl :: State Printer ()
printnl = do
  (Printer beg sup i str) <- get
  when (not sup) $ put $ Printer True False i $ str `T.append` "\n"

printnl' :: State Printer ()
printnl' = do
  (Printer beg sup i str) <- get
  when (not beg && not sup) $ put $ Printer True False i $ str `T.append` "\n"

println :: T.Text -> State Printer ()
println str = do
  print str
  printnl

println' :: T.Text -> State Printer ()
println' str = do
  print' str
  printnl

indent :: State Printer ()
indent = do
  (Printer b sup i s) <- get
  put $ Printer b sup (i + 1) s

unindent :: State Printer ()
unindent = do
  (Printer b sup i s) <- get
  put $ Printer b sup (i - 1) s

suppress :: State Printer ()
suppress = do
  (Printer b _ i s) <- get
  put $ Printer b True i s

-- entry

main = do
  str <- T.getContents
  let Right toks = parseOnly (many' token) str
      Printer _ _ _ str' =
        flip execState (Printer True False 0 "") $ mapM_ toStr toks
  T.putStr str'