on master help sign in
javascript: factor AST into own module
commita50224dbe1006558d2b049976021822cbe1eaf85 authorraichoo <raichoo@googlemail.com> dateMon 14 Jul 2014, 21:25 (12 years ago) parentb669628
3 files changed+358−342
idris.cabal+1 src/IRTS/CodegenJavaScript.hs+2−342 src/IRTS/JavaScript/AST.hs+355
idris.cabal+1
@@ -599,6 +599,7 @@ Library
599599 , IRTS.CodegenCommon
600600 , IRTS.CodegenJava
601601 , IRTS.CodegenJavaScript
602+ , IRTS.JavaScript.AST
602603 , IRTS.Compiler
603604 , IRTS.Defunctionalise
604605 , IRTS.DumpBC
src/IRTS/CodegenJavaScript.hs+2−342
@@ -2,6 +2,8 @@
22{-# LANGUAGE OverloadedStrings #-}
33module IRTS.CodegenJavaScript (codegenJavaScript, codegenNode, JSTarget(..)) where
44
5+import IRTS.JavaScript.AST
6+
57import Idris.AbsSyntax hiding (TypeCase)
68import IRTS.Bytecode
79import IRTS.Lang
@@ -82,290 +84,6 @@ initCompileInfo bc =
8284data JSTarget = Node | JavaScript deriving Eq
8385
8486
85−data JSType = JSIntTy
86− | JSStringTy
87− | JSIntegerTy
88− | JSFloatTy
89− | JSCharTy
90− | JSPtrTy
91− | JSForgotTy
92− deriving Eq
93−
94−
95−data JSInteger = JSBigZero
96− | JSBigOne
97− | JSBigInt Integer
98− | JSBigIntExpr JS
99− deriving Eq
100−
101−
102−data JSNum = JSInt Int
103− | JSFloat Double
104− | JSInteger JSInteger
105− deriving Eq
106−
107−
108−data JSWord = JSWord8 Word8
109− | JSWord16 Word16
110− | JSWord32 Word32
111− | JSWord64 Word64
112− deriving Eq
113−
114−
115−data JSAnnotation = JSConstructor deriving Eq
116−
117−
118−instance Show JSAnnotation where
119− show JSConstructor = "constructor"
120−
121−
122−data JS = JSRaw String
123− | JSIdent String
124− | JSFunction [String] JS
125− | JSType JSType
126− | JSSeq [JS]
127− | JSReturn JS
128− | JSApp JS [JS]
129− | JSNew String [JS]
130− | JSError String
131− | JSBinOp String JS JS
132− | JSPreOp String JS
133− | JSPostOp String JS
134− | JSProj JS String
135− | JSNull
136− | JSUndefined
137− | JSThis
138− | JSTrue
139− | JSFalse
140− | JSArray [JS]
141− | JSString String
142− | JSNum JSNum
143− | JSWord JSWord
144− | JSAssign JS JS
145− | JSAlloc String (Maybe JS)
146− | JSIndex JS JS
147− | JSSwitch JS [(JS, JS)] (Maybe JS)
148− | JSCond [(JS, JS)]
149− | JSTernary JS JS JS
150− | JSParens JS
151− | JSWhile JS JS
152− | JSFFI String [JS]
153− | JSAnnotation JSAnnotation JS
154− | JSNoop
155− deriving Eq
156−
157−
158−data FFI = FFICode Char | FFIArg Int | FFIError String
159−
160−ffi :: String -> [String] -> T.Text
161−ffi code args = let parsed = ffiParse code in
162− case ffiError parsed of
163− Just err -> error err
164− Nothing -> renderFFI parsed args
165− where
166− ffiParse :: String -> [FFI]
167− ffiParse "" = []
168− ffiParse ['%'] = [FFIError $ "FFI - Invalid positional argument"]
169− ffiParse ('%':'%':ss) = FFICode '%' : ffiParse ss
170− ffiParse ('%':s:ss)
171− | isDigit s =
172− FFIArg (read $ s : takeWhile isDigit ss) : ffiParse (dropWhile isDigit ss)
173− | otherwise =
174− [FFIError "FFI - Invalid positional argument"]
175− ffiParse (s:ss) = FFICode s : ffiParse ss
176−
177−
178− ffiError :: [FFI] -> Maybe String
179− ffiError [] = Nothing
180− ffiError ((FFIError s):xs) = Just s
181− ffiError (x:xs) = ffiError xs
182−
183−
184− renderFFI :: [FFI] -> [String] -> T.Text
185− renderFFI [] _ = ""
186− renderFFI (FFICode c : fs) args = c `T.cons` renderFFI fs args
187− renderFFI (FFIArg i : fs) args
188− | i < length args && i >= 0 = T.pack (args !! i) `T.append` renderFFI fs args
189− | otherwise = error "FFI - Argument index out of bounds"
190−
191−compileJS :: JS -> T.Text
192−compileJS = compileJS' 0
193−
194−compileJS' :: Int -> JS -> T.Text
195−compileJS' indent JSNoop = ""
196−
197−compileJS' indent (JSAnnotation annotation js) =
198− "/** @" `T.append` T.pack (show annotation) `T.append` " */\n" `T.append` compileJS' indent js
199−
200−compileJS' indent (JSFFI raw args) =
201− ffi raw (map (T.unpack . compileJS' indent) args)
202−
203−compileJS' indent (JSRaw code) =
204− T.pack code
205−
206−compileJS' indent (JSIdent ident) =
207− T.pack ident
208−
209−compileJS' indent (JSFunction args body) =
210− T.replicate indent " " `T.append` "function("
211− `T.append` T.intercalate "," (map T.pack args)
212− `T.append` "){\n"
213− `T.append` compileJS' (indent + 2) body
214− `T.append` "\n}\n"
215−
216−compileJS' indent (JSType ty)
217− | JSIntTy <- ty = "i$Int"
218− | JSStringTy <- ty = "i$String"
219− | JSIntegerTy <- ty = "i$Integer"
220− | JSFloatTy <- ty = "i$Float"
221− | JSCharTy <- ty = "i$Char"
222− | JSPtrTy <- ty = "i$Ptr"
223− | JSForgotTy <- ty = "i$Forgot"
224−
225−compileJS' indent (JSSeq seq) =
226− T.intercalate ";\n" (
227− map (
228− (T.replicate indent " " `T.append`) . (compileJS' indent)
229− ) $ filter (/= JSNoop) seq
230− ) `T.append` ";"
231−
232−compileJS' indent (JSReturn val) =
233− "return " `T.append` compileJS' indent val
234−
235−compileJS' indent (JSApp lhs rhs)
236− | JSFunction {} <- lhs =
237− T.concat ["(", compileJS' indent lhs, ")(", args, ")"]
238− | otherwise =
239− T.concat [compileJS' indent lhs, "(", args, ")"]
240− where args :: T.Text
241− args = T.intercalate "," $ map (compileJS' 0) rhs
242−
243−compileJS' indent (JSNew name args) =
244− "new " `T.append` T.pack name `T.append` "(" `T.append` T.intercalate "," (map (compileJS' 0) args) `T.append` ")"
245−
246−compileJS' indent (JSError exc) =
247− "throw new Error(\"" `T.append` T.pack exc `T.append` "\")"
248−
249−compileJS' indent (JSBinOp op lhs rhs) =
250− compileJS' indent lhs `T.append` " " `T.append` T.pack op `T.append` " " `T.append` compileJS' indent rhs
251−
252−compileJS' indent (JSPreOp op val) =
253− T.pack op `T.append` compileJS' indent val
254−
255−compileJS' indent (JSProj obj field)
256− | JSFunction {} <- obj =
257− T.concat ["(", compileJS' indent obj, ").", T.pack field]
258− | JSAssign {} <- obj =
259− T.concat ["(", compileJS' indent obj, ").", T.pack field]
260− | otherwise =
261− compileJS' indent obj `T.append` ('.' `T.cons` T.pack field)
262−
263−compileJS' indent JSNull =
264− "null"
265−
266−compileJS' indent JSUndefined =
267− "undefined"
268−
269−compileJS' indent JSThis =
270− "this"
271−
272−compileJS' indent JSTrue =
273− "true"
274−
275−compileJS' indent JSFalse =
276− "false"
277−
278−compileJS' indent (JSArray elems) =
279− "[" `T.append` T.intercalate "," (map (compileJS' 0) elems) `T.append` "]"
280−
281−compileJS' indent (JSString str) =
282− "\"" `T.append` T.pack str `T.append` "\""
283−
284−compileJS' indent (JSNum num)
285− | JSInt i <- num = T.pack (show i)
286− | JSFloat f <- num = T.pack (show f)
287− | JSInteger JSBigZero <- num = T.pack "i$ZERO"
288− | JSInteger JSBigOne <- num = T.pack "i$ONE"
289− | JSInteger (JSBigInt i) <- num = T.pack (show i)
290− | JSInteger (JSBigIntExpr e) <- num = "i$bigInt(" `T.append` compileJS' indent e `T.append` ")"
291−
292−compileJS' indent (JSAssign lhs rhs) =
293− compileJS' indent lhs `T.append` " = " `T.append` compileJS' indent rhs
294−
295−compileJS' 0 (JSAlloc name (Just val@(JSNew _ _))) =
296− "var " `T.append` T.pack name `T.append` " = " `T.append` compileJS' 0 val `T.append` ";\n"
297−
298−compileJS' indent (JSAlloc name val) =
299− "var " `T.append` T.pack name `T.append` maybe "" ((" = " `T.append`) . compileJS' indent) val
300−
301−compileJS' indent (JSIndex lhs rhs) =
302− compileJS' indent lhs `T.append` "[" `T.append` compileJS' indent rhs `T.append` "]"
303−
304−compileJS' indent (JSCond branches) =
305− T.intercalate " else " $ map createIfBlock branches
306− where
307− createIfBlock (JSNoop, e@(JSSeq _)) =
308− "{\n"
309− `T.append` compileJS' (indent + 2) e
310− `T.append` "\n" `T.append` T.replicate indent " " `T.append` "}"
311− createIfBlock (JSNoop, e) =
312− "{\n"
313− `T.append` compileJS' (indent + 2) e
314− `T.append` ";\n" `T.append` T.replicate indent " " `T.append` "}"
315− createIfBlock (cond, e@(JSSeq _)) =
316− "if (" `T.append` compileJS' indent cond `T.append`") {\n"
317− `T.append` compileJS' (indent + 2) e
318− `T.append` "\n" `T.append` T.replicate indent " " `T.append` "}"
319− createIfBlock (cond, e) =
320− "if (" `T.append` compileJS' indent cond `T.append`") {\n"
321− `T.append` T.replicate (indent + 2) " " `T.append` compileJS' (indent + 2) e
322− `T.append` ";\n" `T.append` T.replicate indent " " `T.append` "}"
323−
324−compileJS' indent (JSSwitch val [(_,JSSeq seq)] Nothing) =
325− let (h,t) = splitAt 1 seq in
326− (T.concat (map (compileJS' indent) h) `T.append` ";\n")
327− `T.append` (T.intercalate ";\n" $ map ((T.replicate indent " " `T.append`) . compileJS' indent) t)
328−
329−compileJS' indent (JSSwitch val branches def) =
330− "switch(" `T.append` compileJS' indent val `T.append` "){\n"
331− `T.append` T.concat (map mkBranch branches)
332− `T.append` mkDefault def
333− `T.append` T.replicate indent " " `T.append` "}"
334− where
335− mkBranch :: (JS, JS) -> T.Text
336− mkBranch (tag, code) =
337− T.replicate (indent + 2) " " `T.append` "case " `T.append` compileJS' indent tag `T.append` ":\n"
338− `T.append` compileJS' (indent + 4) code
339− `T.append` "\n" `T.append` (T.replicate (indent + 4) " " `T.append` "break;\n")
340−
341− mkDefault :: Maybe JS -> T.Text
342− mkDefault Nothing = ""
343− mkDefault (Just def) =
344− T.replicate (indent + 2) " " `T.append` "default:\n"
345− `T.append` compileJS' (indent + 4)def
346− `T.append` "\n"
347−
348−
349−compileJS' indent (JSTernary cond true false) =
350− let c = compileJS' indent cond
351− t = compileJS' indent true
352− f = compileJS' indent false in
353− "(" `T.append` c `T.append` ")?(" `T.append` t `T.append` "):(" `T.append` f `T.append` ")"
354−
355−compileJS' indent (JSParens js) =
356− "(" `T.append` compileJS' indent js `T.append` ")"
357−
358−compileJS' indent (JSWhile cond body) =
359− "while (" `T.append` compileJS' indent cond `T.append` ") {\n"
360− `T.append` compileJS' (indent + 2) body
361− `T.append` "\n" `T.append` T.replicate indent " " `T.append` "}"
362−
363−compileJS' indent (JSWord word)
364− | JSWord8 b <- word = "new Uint8Array([" `T.append` T.pack (show b) `T.append` "])"
365− | JSWord16 b <- word = "new Uint16Array([" `T.append` T.pack (show b) `T.append` "])"
366− | JSWord32 b <- word = "new Uint32Array([" `T.append` T.pack (show b) `T.append` "])"
367− | JSWord64 b <- word = "i$bigInt(\"" `T.append` T.pack (show b) `T.append` "\")"
368−
36987codegenJavaScript :: CodeGenerator
37088codegenJavaScript ci =
37189 codegenJS_all JavaScript (simpleDecls ci)
@@ -893,64 +611,6 @@ jsMKCON info r t rs =
893611 ]
894612 )
895613
896−jsInstanceOf :: JS -> String -> JS
897−jsInstanceOf obj cls = JSBinOp "instanceof" obj (JSIdent cls)
898−
899−jsOr :: JS -> JS -> JS
900−jsOr lhs rhs = JSBinOp "||" lhs rhs
901−
902−jsAnd :: JS -> JS -> JS
903−jsAnd lhs rhs = JSBinOp "&&" lhs rhs
904−
905−jsMeth :: JS -> String -> [JS] -> JS
906−jsMeth obj meth args = JSApp (JSProj obj meth) args
907−
908−jsCall :: String -> [JS] -> JS
909−jsCall fun args = JSApp (JSIdent fun) args
910−
911−jsTypeOf :: JS -> JS
912−jsTypeOf js = JSPreOp "typeof " js
913−
914−jsEq :: JS -> JS -> JS
915−jsEq lhs@(JSNum (JSInteger _)) rhs = JSApp (JSProj lhs "equals") [rhs]
916−jsEq lhs rhs@(JSNum (JSInteger _)) = JSApp (JSProj lhs "equals") [rhs]
917−jsEq lhs rhs = JSBinOp "==" lhs rhs
918−
919−jsNotEq :: JS -> JS -> JS
920−jsNotEq lhs rhs = JSBinOp "!=" lhs rhs
921−
922−jsIsNumber :: JS -> JS
923−jsIsNumber js = (jsTypeOf js) `jsEq` (JSString "number")
924−
925−jsIsNull :: JS -> JS
926−jsIsNull js = JSBinOp "==" js JSNull
927−
928−jsBigInt :: JS -> JS
929−jsBigInt (JSString "0") = JSNum (JSInteger JSBigZero)
930−jsBigInt (JSString "1") = JSNum (JSInteger JSBigOne)
931−jsBigInt js = JSNum $ JSInteger $ JSBigIntExpr js
932−
933−jsUnPackBits :: JS -> JS
934−jsUnPackBits js = JSIndex js $ JSNum (JSInt 0)
935−
936−jsPackUBits8 :: JS -> JS
937−jsPackUBits8 js = JSNew "Uint8Array" [JSArray [js]]
938−
939−jsPackUBits16 :: JS -> JS
940−jsPackUBits16 js = JSNew "Uint16Array" [JSArray [js]]
941−
942−jsPackUBits32 :: JS -> JS
943−jsPackUBits32 js = JSNew "Uint32Array" [JSArray [js]]
944−
945−jsPackSBits8 :: JS -> JS
946−jsPackSBits8 js = JSNew "Int8Array" [JSArray [js]]
947−
948−jsPackSBits16 :: JS -> JS
949−jsPackSBits16 js = JSNew "Int16Array" [JSArray [js]]
950−
951−jsPackSBits32 :: JS -> JS
952−jsPackSBits32 js = JSNew "Int32Array" [JSArray [js]]
953−
954614jsCASE :: CompileInfo -> Bool -> Reg -> [(Int, [BC])] -> Maybe [BC] -> JS
955615jsCASE info safe reg cases def =
956616 JSSwitch (tag safe $ translateReg reg) (
src/IRTS/JavaScript/AST.hsnew file+355
@@ -0,0 +1,355 @@
1+{-# LANGUAGE PatternGuards #-}
2+{-# LANGUAGE OverloadedStrings #-}
3+module IRTS.JavaScript.AST where
4+
5+import Data.Word
6+import Data.Char (isDigit)
7+
8+import qualified Data.Text as T
9+
10+data JSType = JSIntTy
11+ | JSStringTy
12+ | JSIntegerTy
13+ | JSFloatTy
14+ | JSCharTy
15+ | JSPtrTy
16+ | JSForgotTy
17+ deriving Eq
18+
19+
20+data JSInteger = JSBigZero
21+ | JSBigOne
22+ | JSBigInt Integer
23+ | JSBigIntExpr JS
24+ deriving Eq
25+
26+
27+data JSNum = JSInt Int
28+ | JSFloat Double
29+ | JSInteger JSInteger
30+ deriving Eq
31+
32+
33+data JSWord = JSWord8 Word8
34+ | JSWord16 Word16
35+ | JSWord32 Word32
36+ | JSWord64 Word64
37+ deriving Eq
38+
39+
40+data JSAnnotation = JSConstructor deriving Eq
41+
42+
43+instance Show JSAnnotation where
44+ show JSConstructor = "constructor"
45+
46+
47+data JS = JSRaw String
48+ | JSIdent String
49+ | JSFunction [String] JS
50+ | JSType JSType
51+ | JSSeq [JS]
52+ | JSReturn JS
53+ | JSApp JS [JS]
54+ | JSNew String [JS]
55+ | JSError String
56+ | JSBinOp String JS JS
57+ | JSPreOp String JS
58+ | JSPostOp String JS
59+ | JSProj JS String
60+ | JSNull
61+ | JSUndefined
62+ | JSThis
63+ | JSTrue
64+ | JSFalse
65+ | JSArray [JS]
66+ | JSString String
67+ | JSNum JSNum
68+ | JSWord JSWord
69+ | JSAssign JS JS
70+ | JSAlloc String (Maybe JS)
71+ | JSIndex JS JS
72+ | JSSwitch JS [(JS, JS)] (Maybe JS)
73+ | JSCond [(JS, JS)]
74+ | JSTernary JS JS JS
75+ | JSParens JS
76+ | JSWhile JS JS
77+ | JSFFI String [JS]
78+ | JSAnnotation JSAnnotation JS
79+ | JSNoop
80+ deriving Eq
81+
82+
83+data FFI = FFICode Char | FFIArg Int | FFIError String
84+
85+ffi :: String -> [String] -> T.Text
86+ffi code args = let parsed = ffiParse code in
87+ case ffiError parsed of
88+ Just err -> error err
89+ Nothing -> renderFFI parsed args
90+ where
91+ ffiParse :: String -> [FFI]
92+ ffiParse "" = []
93+ ffiParse ['%'] = [FFIError $ "FFI - Invalid positional argument"]
94+ ffiParse ('%':'%':ss) = FFICode '%' : ffiParse ss
95+ ffiParse ('%':s:ss)
96+ | isDigit s =
97+ FFIArg (read $ s : takeWhile isDigit ss) : ffiParse (dropWhile isDigit ss)
98+ | otherwise =
99+ [FFIError "FFI - Invalid positional argument"]
100+ ffiParse (s:ss) = FFICode s : ffiParse ss
101+
102+
103+ ffiError :: [FFI] -> Maybe String
104+ ffiError [] = Nothing
105+ ffiError ((FFIError s):xs) = Just s
106+ ffiError (x:xs) = ffiError xs
107+
108+
109+ renderFFI :: [FFI] -> [String] -> T.Text
110+ renderFFI [] _ = ""
111+ renderFFI (FFICode c : fs) args = c `T.cons` renderFFI fs args
112+ renderFFI (FFIArg i : fs) args
113+ | i < length args && i >= 0 = T.pack (args !! i) `T.append` renderFFI fs args
114+ | otherwise = error "FFI - Argument index out of bounds"
115+
116+compileJS :: JS -> T.Text
117+compileJS = compileJS' 0
118+
119+compileJS' :: Int -> JS -> T.Text
120+compileJS' indent JSNoop = ""
121+
122+compileJS' indent (JSAnnotation annotation js) =
123+ "/** @" `T.append` T.pack (show annotation) `T.append` " */\n" `T.append` compileJS' indent js
124+
125+compileJS' indent (JSFFI raw args) =
126+ ffi raw (map (T.unpack . compileJS' indent) args)
127+
128+compileJS' indent (JSRaw code) =
129+ T.pack code
130+
131+compileJS' indent (JSIdent ident) =
132+ T.pack ident
133+
134+compileJS' indent (JSFunction args body) =
135+ T.replicate indent " " `T.append` "function("
136+ `T.append` T.intercalate "," (map T.pack args)
137+ `T.append` "){\n"
138+ `T.append` compileJS' (indent + 2) body
139+ `T.append` "\n}\n"
140+
141+compileJS' indent (JSType ty)
142+ | JSIntTy <- ty = "i$Int"
143+ | JSStringTy <- ty = "i$String"
144+ | JSIntegerTy <- ty = "i$Integer"
145+ | JSFloatTy <- ty = "i$Float"
146+ | JSCharTy <- ty = "i$Char"
147+ | JSPtrTy <- ty = "i$Ptr"
148+ | JSForgotTy <- ty = "i$Forgot"
149+
150+compileJS' indent (JSSeq seq) =
151+ T.intercalate ";\n" (
152+ map (
153+ (T.replicate indent " " `T.append`) . (compileJS' indent)
154+ ) $ filter (/= JSNoop) seq
155+ ) `T.append` ";"
156+
157+compileJS' indent (JSReturn val) =
158+ "return " `T.append` compileJS' indent val
159+
160+compileJS' indent (JSApp lhs rhs)
161+ | JSFunction {} <- lhs =
162+ T.concat ["(", compileJS' indent lhs, ")(", args, ")"]
163+ | otherwise =
164+ T.concat [compileJS' indent lhs, "(", args, ")"]
165+ where args :: T.Text
166+ args = T.intercalate "," $ map (compileJS' 0) rhs
167+
168+compileJS' indent (JSNew name args) =
169+ "new " `T.append` T.pack name `T.append` "(" `T.append` T.intercalate "," (map (compileJS' 0) args) `T.append` ")"
170+
171+compileJS' indent (JSError exc) =
172+ "throw new Error(\"" `T.append` T.pack exc `T.append` "\")"
173+
174+compileJS' indent (JSBinOp op lhs rhs) =
175+ compileJS' indent lhs `T.append` " " `T.append` T.pack op `T.append` " " `T.append` compileJS' indent rhs
176+
177+compileJS' indent (JSPreOp op val) =
178+ T.pack op `T.append` compileJS' indent val
179+
180+compileJS' indent (JSProj obj field)
181+ | JSFunction {} <- obj =
182+ T.concat ["(", compileJS' indent obj, ").", T.pack field]
183+ | JSAssign {} <- obj =
184+ T.concat ["(", compileJS' indent obj, ").", T.pack field]
185+ | otherwise =
186+ compileJS' indent obj `T.append` ('.' `T.cons` T.pack field)
187+
188+compileJS' indent JSNull =
189+ "null"
190+
191+compileJS' indent JSUndefined =
192+ "undefined"
193+
194+compileJS' indent JSThis =
195+ "this"
196+
197+compileJS' indent JSTrue =
198+ "true"
199+
200+compileJS' indent JSFalse =
201+ "false"
202+
203+compileJS' indent (JSArray elems) =
204+ "[" `T.append` T.intercalate "," (map (compileJS' 0) elems) `T.append` "]"
205+
206+compileJS' indent (JSString str) =
207+ "\"" `T.append` T.pack str `T.append` "\""
208+
209+compileJS' indent (JSNum num)
210+ | JSInt i <- num = T.pack (show i)
211+ | JSFloat f <- num = T.pack (show f)
212+ | JSInteger JSBigZero <- num = T.pack "i$ZERO"
213+ | JSInteger JSBigOne <- num = T.pack "i$ONE"
214+ | JSInteger (JSBigInt i) <- num = T.pack (show i)
215+ | JSInteger (JSBigIntExpr e) <- num = "i$bigInt(" `T.append` compileJS' indent e `T.append` ")"
216+
217+compileJS' indent (JSAssign lhs rhs) =
218+ compileJS' indent lhs `T.append` " = " `T.append` compileJS' indent rhs
219+
220+compileJS' 0 (JSAlloc name (Just val@(JSNew _ _))) =
221+ "var " `T.append` T.pack name `T.append` " = " `T.append` compileJS' 0 val `T.append` ";\n"
222+
223+compileJS' indent (JSAlloc name val) =
224+ "var " `T.append` T.pack name `T.append` maybe "" ((" = " `T.append`) . compileJS' indent) val
225+
226+compileJS' indent (JSIndex lhs rhs) =
227+ compileJS' indent lhs `T.append` "[" `T.append` compileJS' indent rhs `T.append` "]"
228+
229+compileJS' indent (JSCond branches) =
230+ T.intercalate " else " $ map createIfBlock branches
231+ where
232+ createIfBlock (JSNoop, e@(JSSeq _)) =
233+ "{\n"
234+ `T.append` compileJS' (indent + 2) e
235+ `T.append` "\n" `T.append` T.replicate indent " " `T.append` "}"
236+ createIfBlock (JSNoop, e) =
237+ "{\n"
238+ `T.append` compileJS' (indent + 2) e
239+ `T.append` ";\n" `T.append` T.replicate indent " " `T.append` "}"
240+ createIfBlock (cond, e@(JSSeq _)) =
241+ "if (" `T.append` compileJS' indent cond `T.append`") {\n"
242+ `T.append` compileJS' (indent + 2) e
243+ `T.append` "\n" `T.append` T.replicate indent " " `T.append` "}"
244+ createIfBlock (cond, e) =
245+ "if (" `T.append` compileJS' indent cond `T.append`") {\n"
246+ `T.append` T.replicate (indent + 2) " " `T.append` compileJS' (indent + 2) e
247+ `T.append` ";\n" `T.append` T.replicate indent " " `T.append` "}"
248+
249+compileJS' indent (JSSwitch val [(_,JSSeq seq)] Nothing) =
250+ let (h,t) = splitAt 1 seq in
251+ (T.concat (map (compileJS' indent) h) `T.append` ";\n")
252+ `T.append` (T.intercalate ";\n" $ map ((T.replicate indent " " `T.append`) . compileJS' indent) t)
253+
254+compileJS' indent (JSSwitch val branches def) =
255+ "switch(" `T.append` compileJS' indent val `T.append` "){\n"
256+ `T.append` T.concat (map mkBranch branches)
257+ `T.append` mkDefault def
258+ `T.append` T.replicate indent " " `T.append` "}"
259+ where
260+ mkBranch :: (JS, JS) -> T.Text
261+ mkBranch (tag, code) =
262+ T.replicate (indent + 2) " " `T.append` "case " `T.append` compileJS' indent tag `T.append` ":\n"
263+ `T.append` compileJS' (indent + 4) code
264+ `T.append` "\n" `T.append` (T.replicate (indent + 4) " " `T.append` "break;\n")
265+
266+ mkDefault :: Maybe JS -> T.Text
267+ mkDefault Nothing = ""
268+ mkDefault (Just def) =
269+ T.replicate (indent + 2) " " `T.append` "default:\n"
270+ `T.append` compileJS' (indent + 4)def
271+ `T.append` "\n"
272+
273+
274+compileJS' indent (JSTernary cond true false) =
275+ let c = compileJS' indent cond
276+ t = compileJS' indent true
277+ f = compileJS' indent false in
278+ "(" `T.append` c `T.append` ")?(" `T.append` t `T.append` "):(" `T.append` f `T.append` ")"
279+
280+compileJS' indent (JSParens js) =
281+ "(" `T.append` compileJS' indent js `T.append` ")"
282+
283+compileJS' indent (JSWhile cond body) =
284+ "while (" `T.append` compileJS' indent cond `T.append` ") {\n"
285+ `T.append` compileJS' (indent + 2) body
286+ `T.append` "\n" `T.append` T.replicate indent " " `T.append` "}"
287+
288+compileJS' indent (JSWord word)
289+ | JSWord8 b <- word =
290+ "new Uint8Array([" `T.append` T.pack (show b) `T.append` "])"
291+ | JSWord16 b <- word =
292+ "new Uint16Array([" `T.append` T.pack (show b) `T.append` "])"
293+ | JSWord32 b <- word =
294+ "new Uint32Array([" `T.append` T.pack (show b) `T.append` "])"
295+ | JSWord64 b <- word =
296+ "i$bigInt(\"" `T.append` T.pack (show b) `T.append` "\")"
297+
298+jsInstanceOf :: JS -> String -> JS
299+jsInstanceOf obj cls = JSBinOp "instanceof" obj (JSIdent cls)
300+
301+jsOr :: JS -> JS -> JS
302+jsOr lhs rhs = JSBinOp "||" lhs rhs
303+
304+jsAnd :: JS -> JS -> JS
305+jsAnd lhs rhs = JSBinOp "&&" lhs rhs
306+
307+jsMeth :: JS -> String -> [JS] -> JS
308+jsMeth obj meth args = JSApp (JSProj obj meth) args
309+
310+jsCall :: String -> [JS] -> JS
311+jsCall fun args = JSApp (JSIdent fun) args
312+
313+jsTypeOf :: JS -> JS
314+jsTypeOf js = JSPreOp "typeof " js
315+
316+jsEq :: JS -> JS -> JS
317+jsEq lhs@(JSNum (JSInteger _)) rhs = JSApp (JSProj lhs "equals") [rhs]
318+jsEq lhs rhs@(JSNum (JSInteger _)) = JSApp (JSProj lhs "equals") [rhs]
319+jsEq lhs rhs = JSBinOp "==" lhs rhs
320+
321+jsNotEq :: JS -> JS -> JS
322+jsNotEq lhs rhs = JSBinOp "!=" lhs rhs
323+
324+jsIsNumber :: JS -> JS
325+jsIsNumber js = (jsTypeOf js) `jsEq` (JSString "number")
326+
327+jsIsNull :: JS -> JS
328+jsIsNull js = JSBinOp "==" js JSNull
329+
330+jsBigInt :: JS -> JS
331+jsBigInt (JSString "0") = JSNum (JSInteger JSBigZero)
332+jsBigInt (JSString "1") = JSNum (JSInteger JSBigOne)
333+jsBigInt js = JSNum $ JSInteger $ JSBigIntExpr js
334+
335+jsUnPackBits :: JS -> JS
336+jsUnPackBits js = JSIndex js $ JSNum (JSInt 0)
337+
338+jsPackUBits8 :: JS -> JS
339+jsPackUBits8 js = JSNew "Uint8Array" [JSArray [js]]
340+
341+jsPackUBits16 :: JS -> JS
342+jsPackUBits16 js = JSNew "Uint16Array" [JSArray [js]]
343+
344+jsPackUBits32 :: JS -> JS
345+jsPackUBits32 js = JSNew "Uint32Array" [JSArray [js]]
346+
347+jsPackSBits8 :: JS -> JS
348+jsPackSBits8 js = JSNew "Int8Array" [JSArray [js]]
349+
350+jsPackSBits16 :: JS -> JS
351+jsPackSBits16 js = JSNew "Int16Array" [JSArray [js]]
352+
353+jsPackSBits32 :: JS -> JS
354+jsPackSBits32 js = JSNew "Int32Array" [JSArray [js]]
355+