aboutsummaryrefslogtreecommitdiff
path: root/latexfmt.hs
blob: c1ecfe16780b324d025ebfd0d32a6e079c4a53ff (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
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE FlexibleContexts #-}

import           Control.Applicative
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
  deriving Show

isCtrl x = not $ isAlpha x || isDigit x

-- tokens to strings
toStr (Wrd w) = do
  if T.length w >= 1 && T.head w `elem` (".,;:!?)" :: [Char])
    then print' w
    else print w
  when (T.last w == '.') printnl
toStr (Cmd c) = if T.length c > 0 && isCtrl (T.head c)
  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

-- Tokeniser
ws = inClass " \t\n"
pad = takeWhile ws
token = nl <|> pad *> (nobs <|> 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'))

-- 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'