/
Printer.hs
250 lines (191 loc) · 10.3 KB
/
Printer.hs
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
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
module Language.JavaScript.Pretty.Printer (
-- * Printing
renderJS
) where
import Data.Char
import Data.List
import Data.Monoid (Monoid, mappend, mempty, mconcat)
-- import Text.Jasmine.Parse
import Language.JavaScript.Parser
import qualified Blaze.ByteString.Builder as BB
import qualified Blaze.ByteString.Builder.Char.Utf8 as BS
import qualified Data.ByteString.Lazy as LB
-- ---------------------------------------------------------------------
-- Pretty printer stuff via blaze-builder
(<>) :: BB.Builder -> BB.Builder -> BB.Builder
(<>) a b = mappend a b
(<+>) :: BB.Builder -> BB.Builder -> BB.Builder
(<+>) a b = mconcat [a, (text " "), b]
hcat :: (Monoid a) => [a] -> a
hcat xs = mconcat xs
empty :: BB.Builder
empty = mempty
text :: String -> BB.Builder
text s = BS.fromString s
char :: Char -> BB.Builder
char c = BS.fromChar c
comma :: BB.Builder
comma = BS.fromChar ','
punctuate :: a -> [a] -> [a]
punctuate p xs = intersperse p xs
-- ---------------------------------------------------------------------
bp :: (Int,Int) -> TokenPosn -> ((Int,Int) -> ((Int,Int),BB.Builder)) -> ((Int,Int),BB.Builder)
bp (r,c) p f = ((r'',c''),bb <> bb')
where
((r',c'),bb) = skipTo (r,c) p
((r'',c''),bb') = f (r',c')
bprJS
:: (Int, Int) -> TokenPosn -> [JSNode] -> ((Int, Int), BB.Builder)
bprJS (r,c) p xs = bp (r,c) p (\(r,c) -> rJS (r,c) xs)
bpText
:: (Int, Int) -> TokenPosn -> [Char] -> ((Int, Int), BB.Builder)
bpText (r,c) p s = bp (r,c) p (\(r,c) -> ((r,c + (length s)),text s))
-- ---------------------------------------------------------------------
renderJS :: JSNode -> BB.Builder
renderJS node = bb
where
(_,bb) = rn (1,1) node
-- Take in the current
rn :: (Int,Int) -> JSNode -> ((Int,Int), BB.Builder)
{-
rn (r,c) (NS (JSEmpty l) p cs) = do
(r',c') <- skipTo (r,c) p
return (rn (r',c') l)
-}
rn (r,c) (NS (JSSourceElementsTop xs) p cs) = bprJS (r,c) p xs
rn (r,c) (NS (JSSourceElements xs) p cs) = bprJS (r,c) p xs
rn (r,c) (NS (JSExpression xs) p cs) = bprJS (r,c) p xs
rn (r,c) (NS (JSIdentifier s) p cs) = bpText (r,c) p s
rn (r,c) (NS (JSOperator s) p cs) = bpText (r,c) p s
rn (r,c) (NS (JSDecimal i) p cs) = bpText (r,c) p i
rn (r,c) (NS (JSLiteral l) p cs) = bpText (r,c) p l
rn (r,c) (NS (JSUnary l) p cs = bpText (r,c) p l
{-
rn (JSFunction s p xs) = (text "function") <+> (renderJS s) <> (text "(") <> (commaList p) <> (text ")") <> (renderJS xs)
rn (JSFunctionBody xs) = (text "{") <> (rJS xs) <> (text "}")
rn (JSFunctionExpression [] p xs) = (text "function") <> (text "(") <> (commaList p) <> (text ")") <> (renderJS xs)
rn (JSFunctionExpression s p xs) = (text "function") <+> (rJS s) <> (text "(") <> (commaList p) <> (text ")") <> (renderJS xs)
rn (JSArguments xs) = (text "(") <> (commaListList xs) <> (text ")")
rn (JSBlock x) = (text "{") <> (renderJS x) <> (text "}")
rn (JSIf c (NS (JSLiteral ";") _ _))= (text "if") <> (text "(") <> (renderJS c) <> (text ")")
rn (JSIf c t) = (text "if") <> (text "(") <> (renderJS c) <> (text ")") <> (renderJS t)
rn (JSIfElse c t (NS (JSLiteral ";") _ _)) = (text "if") <> (text "(") <> (renderJS c) <> (text ")") <> (renderJS t)
<> (text "else")
rn (JSIfElse c t e) = (text "if") <> (text "(") <> (renderJS c) <> (text ")") <> (renderJS t)
<> (text "else") <> (spaceOrBlock e)
rn (JSMemberDot xs y) = (rJS xs) <> (text ".") <> (renderJS y)
rn (JSMemberSquare xs x) = (rJS xs) <> (text "[") <> (renderJS x) <> (text "]")
rn (JSStringLiteral s l) = empty <> (char s) <> (text l) <> (char s)
rn (JSArrayLiteral xs) = (text "[") <> (rJS xs) <> (text "]")
rn (JSBreak [] []) = (text "break")
rn (JSBreak [] _xs) = (text "break") -- <> (rJS xs) -- <> (text ";")
rn (JSBreak is _xs) = (text "break") <+> (rJS is) -- <> (rJS xs)
rn (JSCallExpression "()" xs) = (rJS xs)
rn (JSCallExpression t xs) = (char $ head t) <> (rJS xs) <> (if ((length t) > 1) then (char $ last t) else empty)
-- No space between 'case' and string literal. TODO: what about expression in parentheses?
--rn (JSCase (JSExpression [JSStringLiteral sepa s]) xs) = (text "case") <> (renderJS (JSStringLiteral sepa s))
rn (JSCase (NS (JSExpression [(NS (JSStringLiteral sepa s) s1 c1)]) _ _) xs) = (text "case") <> (renderJS (NS (JSStringLiteral sepa s) s1 c1))
<> (char ':') <> (renderJS xs)
rn (JSCase e xs) = (text "case") <+> (renderJS e) <> (char ':') <> (renderJS xs) -- <> (text ";");
rn (JSCatch i [] s) = (text "catch") <> (char '(') <> (renderJS i) <> (char ')') <> (renderJS s)
rn (JSCatch i c s) = (text "catch") <> (char '(') <> (renderJS i) <>
(text " if ") <> (rJS c) <> (char ')') <> (renderJS s)
rn (JSContinue is) = (text "continue") <> (rJS is) -- <> (char ';')
rn (JSDefault xs) = (text "default") <> (char ':') <> (renderJS xs)
rn (JSDoWhile s e _ms) = (text "do") <> (renderJS s) <> (text "while") <> (char '(') <> (renderJS e) <> (char ')') -- <> (renderJS ms)
--rn (JSElementList xs) = rJS xs
rn (JSElision xs) = (char ',') <> (rJS xs)
rn (JSExpressionBinary o e1 e2) = (rJS e1) <> (text o) <> (rJS e2)
--rn (JSExpressionBinary o e1 e2) = (text o) <> (rJS e1) <> (rJS e2)
rn (JSExpressionParen e) = (char '(') <> (renderJS e) <> (char ')')
rn (JSExpressionPostfix o e) = (rJS e) <> (text o)
rn (JSExpressionTernary c v1 v2) = (rJS c) <> (char '?') <> (rJS v1) <> (char ':') <> (rJS v2)
rn (JSFinally b) = (text "finally") <> (renderJS b)
rn (JSFor e1 e2 e3 s) = (text "for") <> (char '(') <> (commaList e1) <> (char ';')
<> (rJS e2) <> (char ';') <> (rJS e3) <> (char ')') <> (renderJS s)
rn (JSForIn e1 e2 s) = (text "for") <> (char '(') <> (rJS e1) <+> (text "in")
<+> (renderJS e2) <> (char ')') <> (renderJS s)
rn (JSForVar e1 e2 e3 s) = (text "for") <> (char '(') <> (text "var") <+> (commaList e1) <> (char ';')
<> (rJS e2) <> (char ';') <> (rJS e3) <> (char ')') <> (renderJS s)
rn (JSForVarIn e1 e2 s) = (text "for") <> (char '(') <> (text "var") <+> (renderJS e1) <+> (text "in")
<+> (renderJS e2) <> (char ')') <> (renderJS s)
rn (JSHexInteger i) = (text $ show i) -- TODO: need to tweak this
rn (JSLabelled l v) = (renderJS l) <> (text ":") <> (rJS [v])
rn (JSObjectLiteral xs) = (text "{") <> (commaList xs) <> (text "}")
rn (JSPropertyAccessor s n ps b) = (text s) <+> (renderJS n) <> (char '(') <> (rJS ps) <> (text ")") <> (renderJS b)
rn (JSPropertyNameandValue n vs) = (renderJS n) <> (text ":") <> (rJS vs)
rn (JSRegEx s) = (text s)
rn (JSReturn []) = (text "return")
rn (JSReturn [(NS (JSLiteral ";") _ _)]) = (text "return;")
rn (JSReturn xs) = (text "return") <> (if (spaceNeeded xs) then (text " ") else (empty)) <> (rJS xs)
rn (JSThrow e) = (text "throw") <+> (renderJS e)
rn (JSStatementBlock x) = (text "{") <> (renderJS x) <> (text "}")
rn (JSStatementList xs) = rJS xs
rn (JSSwitch e xs) = (text "switch") <> (char '(') <> (renderJS e) <> (char ')') <>
(char '{') <> (rJS xs) <> (char '}')
rn (JSTry e xs) = (text "try") <> (renderJS e) <> (rJS xs)
rn (JSVarDecl i []) = (renderJS i)
rn (JSVarDecl i xs) = (renderJS i) <> (text "=") <> (rJS xs)
rn (JSVariables kw xs) = (text kw) <+> (commaList xs)
rn (JSWhile e (NS (JSLiteral ";") _ _)) = (text "while") <> (char '(') <> (renderJS e) <> (char ')') -- <> (renderJS s)
rn (JSWhile e s) = (text "while") <> (char '(') <> (renderJS e) <> (char ')') <> (renderJS s)
rn (JSWith e s) = (text "with") <> (char '(') <> (renderJS e) <> (char ')') <> (rJS s)
-}
-- Helper functions
rJS :: (Int,Int) -> [JSNode] -> ((Int,Int),BB.Builder)
-- rJS xs = hcat $ map renderJS xs
--rJS (r,c) xs = map rn xs
rJS (r,c) xs = foldl' frn ((r,c),mempty) xs
where
frn :: ((Int,Int),BB.Builder) -> JSNode -> ((Int,Int),BB.Builder)
frn ((rc,cc),bb) n = ((rc',cc'),bb <> bb')
where
((rc',cc'),bb') = rn (rc,cc) n
{-
commaList :: [JSNode] -> BB.Builder
commaList [] = empty
commaList xs = (hcat $ (punctuate comma (toDoc xs') ++ trail))
where
-- (xs', trail) = if (last xs == JSLiteral ",") then (init xs, [comma]) else (xs,[])
(xs', trail) = if (x' == JSLiteral ",") then (init xs, [comma]) else (xs,[])
(NS x' _ _) = last xs
commaListList :: [[JSNode]] -> BB.Builder
commaListList xs = (hcat $ punctuate comma $ map rJS xs)
toDoc :: [JSNode] -> [BB.Builder]
toDoc xs = map renderJS xs
spaceOrBlock :: JSNode -> BB.Builder
spaceOrBlock (NS (JSBlock xs) _ _) = rn (JSBlock xs)
spaceOrBlock (NS (JSStatementBlock xs) _ _) = rn (JSStatementBlock xs)
spaceOrBlock x = (text " ") <> (renderJS x)
-}
{-
TODO: Collapse this into JSLiteral ";"
JSStatementBlock (JSStatementList [JSStatementBlock (JSStatementList [])])
-}
-- ---------------------------------------------------------------
-- Utility stuff
{-
-- A space is needed if this expression starts with an identifier etc, but not if with a '('
spaceNeeded :: [JSNode] -> Bool
spaceNeeded xs =
let
-- str = show $ rJS xs
str = LB.unpack $ BB.toLazyByteString $ rJS xs
in
head str /= (fromIntegral $ ord '(')
-}
skipTo :: (Int,Int) -> TokenPosn -> ((Int,Int), BB.Builder)
skipTo (lcur,ccur) (TokenPn _ ltgt ctgt) = ((lnew,cnew),bb)
where
lnew = if (lcur < ltgt) then ltgt else lcur
cnew = if (ccur < ctgt) then ctgt else ccur
bbline = if (lcur < ltgt) then (text $ take (ltgt - lcur) $ repeat '\n') else mempty
bbcol = if (ccur < ctgt) then (text $ take (ctgt - ccur) $ repeat ' ' ) else mempty
bb = bbline <> bbcol
-- ---------------------------------------------------------------------
-- Test stuff
_r :: JSNode -> [Char]
_r js = map (\x -> chr (fromIntegral x)) $ LB.unpack $ BB.toLazyByteString $ renderJS js
_t :: String -> String
_t str = _r $ readJs str
-- EOF