1{-# LANGUAGE PatternGuards #-}
2{-# LANGUAGE OverloadedStrings #-}
3module IRTS.JavaScript.AST where
6import Data.Char (isDigit)
8import qualified Data.Text as T
20data JSInteger = JSBigZero
33data JSWord = JSWord8 Word8
40data JSAnnotation = JSConstructor deriving Eq
43instance Show JSAnnotation where
44 show JSConstructor = "constructor"
49 | JSFunction [String] JS
56 | JSBinOp String JS JS
70 | JSAlloc String (Maybe JS)
72 | JSSwitch JS [(JS, JS)] (Maybe JS)
78 | JSAnnotation JSAnnotation JS
85data FFI = FFICode Char | FFIArg Int | FFIError String
87ffi :: String -> [String] -> T.Text
88ffi code args = let parsed = ffiParse code in
89 case ffiError parsed of
91 Nothing -> renderFFI parsed args
93 ffiParse :: String -> [FFI]
95 ffiParse ['%'] = [FFIError $ "FFI - Invalid positional argument"]
96 ffiParse ('%':'%':ss) = FFICode '%' : ffiParse ss
100 read $ s : takeWhile isDigit ss
101 ) : ffiParse (dropWhile isDigit ss)
103 [FFIError "FFI - Invalid positional argument"]
104 ffiParse (s:ss) = FFICode s : ffiParse ss
107 ffiError :: [FFI] -> Maybe String
108 ffiError [] = Nothing
109 ffiError ((FFIError s):xs) = Just s
110 ffiError (x:xs) = ffiError xs
113 renderFFI :: [FFI] -> [String] -> T.Text
115 renderFFI (FFICode c : fs) args = c `T.cons` renderFFI fs args
116 renderFFI (FFIArg i : fs) args
117 | i < length args && i >= 0 =
119 `T.append` renderFFI fs args
120 | otherwise = error "FFI - Argument index out of bounds"
122compileJS :: JS -> T.Text
123compileJS = compileJS' 0
125compileJS' :: Int -> JS -> T.Text
126compileJS' indent JSNoop = ""
128compileJS' indent (JSAnnotation annotation js) =
130 `T.append` T.pack (show annotation)
132 `T.append` compileJS' indent js
134compileJS' indent (JSDelete js) =
135 "delete " `T.append` compileJS' 0 js
137compileJS' indent (JSClear js) =
138 compileJS' 0 js `T.append` " = undefined"
140compileJS' indent (JSFFI raw args) =
141 ffi raw (map (T.unpack . compileJS' indent) args)
143compileJS' indent (JSRaw code) =
146compileJS' indent (JSIdent ident) =
149compileJS' indent (JSFunction args body) =
150 T.replicate indent " " `T.append` "function("
151 `T.append` T.intercalate "," (map T.pack args)
153 `T.append` compileJS' (indent + 2) body
156compileJS' indent (JSType ty)
157 | JSIntTy <- ty = "i$Int"
158 | JSStringTy <- ty = "i$String"
159 | JSIntegerTy <- ty = "i$Integer"
160 | JSFloatTy <- ty = "i$Float"
161 | JSCharTy <- ty = "i$Char"
162 | JSPtrTy <- ty = "i$Ptr"
163 | JSForgotTy <- ty = "i$Forgot"
165compileJS' indent (JSSeq seq) =
166 T.intercalate ";\n" (
168 (T.replicate indent " " `T.append`) . (compileJS' indent)
169 ) $ filter (/= JSNoop) seq
172compileJS' indent (JSReturn val) =
173 "return " `T.append` compileJS' indent val
175compileJS' indent (JSApp lhs rhs)
176 | JSFunction {} <- lhs =
177 T.concat ["(", compileJS' indent lhs, ")(", args, ")"]
179 T.concat [compileJS' indent lhs, "(", args, ")"]
181 args = T.intercalate "," $ map (compileJS' 0) rhs
183compileJS' indent (JSNew name args) =
185 `T.append` T.pack name
187 `T.append` T.intercalate "," (map (compileJS' 0) args)
190compileJS' indent (JSError exc) =
191 "(function(){throw new Error(\"" `T.append` T.pack exc `T.append` "\")})()"
193compileJS' indent (JSBinOp op lhs rhs) =
194 compileJS' indent lhs
198 `T.append` compileJS' indent rhs
200compileJS' indent (JSPreOp op val) =
201 T.pack op `T.append` "(" `T.append` compileJS' indent val `T.append` ")"
203compileJS' indent (JSProj obj field)
204 | JSFunction {} <- obj =
205 T.concat ["(", compileJS' indent obj, ").", T.pack field]
206 | JSAssign {} <- obj =
207 T.concat ["(", compileJS' indent obj, ").", T.pack field]
209 compileJS' indent obj `T.append` ('.' `T.cons` T.pack field)
211compileJS' indent JSNull =
214compileJS' indent JSUndefined =
217compileJS' indent JSThis =
220compileJS' indent JSTrue =
223compileJS' indent JSFalse =
226compileJS' indent (JSArray elems) =
227 "[" `T.append` T.intercalate "," (map (compileJS' 0) elems) `T.append` "]"
229compileJS' indent (JSString str) =
230 "\"" `T.append` T.pack str `T.append` "\""
232compileJS' indent (JSNum num)
233 | JSInt i <- num = T.pack (show i)
234 | JSFloat f <- num = T.pack (show f)
235 | JSInteger JSBigZero <- num = T.pack "i$ZERO"
236 | JSInteger JSBigOne <- num = T.pack "i$ONE"
237 | JSInteger (JSBigInt i) <- num = T.pack (show i)
238 | JSInteger (JSBigIntExpr e) <- num =
239 "i$bigInt(" `T.append` compileJS' indent e `T.append` ")"
241compileJS' indent (JSAssign lhs rhs) =
242 compileJS' indent lhs `T.append` " = " `T.append` compileJS' indent rhs
244compileJS' 0 (JSAlloc name (Just val@(JSNew _ _))) =
246 `T.append` T.pack name
248 `T.append` compileJS' 0 val
251compileJS' indent (JSAlloc name val) =
253 `T.append` T.pack name
254 `T.append` maybe "" ((" = " `T.append`) . compileJS' indent) val
256compileJS' indent (JSIndex lhs rhs) =
257 compileJS' indent lhs
259 `T.append` compileJS' indent rhs
262compileJS' indent (JSCond branches) =
263 T.intercalate " else " $ map createIfBlock branches
265 createIfBlock (JSNoop, e@(JSSeq _)) =
267 `T.append` compileJS' (indent + 2) e
268 `T.append` "\n" `T.append` T.replicate indent " " `T.append` "}"
269 createIfBlock (JSNoop, e) =
271 `T.append` compileJS' (indent + 2) e
272 `T.append` ";\n" `T.append` T.replicate indent " " `T.append` "}"
273 createIfBlock (cond, e@(JSSeq _)) =
274 "if (" `T.append` compileJS' indent cond `T.append`") {\n"
275 `T.append` compileJS' (indent + 2) e
276 `T.append` "\n" `T.append` T.replicate indent " " `T.append` "}"
277 createIfBlock (cond, e) =
278 "if (" `T.append` compileJS' indent cond `T.append`") {\n"
279 `T.append` T.replicate (indent + 2) " "
280 `T.append` compileJS' (indent + 2) e
282 `T.append` T.replicate indent " "
285compileJS' indent (JSSwitch val [(_,JSSeq seq)] Nothing) =
286 let (h,t) = splitAt 1 seq in
287 (T.concat (map (compileJS' indent) h) `T.append` ";\n")
289 T.intercalate ";\n" $ map (
290 (T.replicate indent " " `T.append`) . compileJS' indent
294compileJS' indent (JSSwitch val branches def) =
295 "switch(" `T.append` compileJS' indent val `T.append` "){\n"
296 `T.append` T.concat (map mkBranch branches)
297 `T.append` mkDefault def
298 `T.append` T.replicate indent " " `T.append` "}"
300 mkBranch :: (JS, JS) -> T.Text
301 mkBranch (tag, code) =
302 T.replicate (indent + 2) " "
304 `T.append` compileJS' indent tag
306 `T.append` compileJS' (indent + 4) code
308 `T.append` (T.replicate (indent + 4) " " `T.append` "break;\n")
310 mkDefault :: Maybe JS -> T.Text
311 mkDefault Nothing = ""
312 mkDefault (Just def) =
313 T.replicate (indent + 2) " " `T.append` "default:\n"
314 `T.append` compileJS' (indent + 4)def
318compileJS' indent (JSTernary cond true false) =
319 let c = compileJS' indent cond
320 t = compileJS' indent true
321 f = compileJS' indent false in
330compileJS' indent (JSParens js) =
331 "(" `T.append` compileJS' indent js `T.append` ")"
333compileJS' indent (JSWhile cond body) =
334 "while (" `T.append` compileJS' indent cond `T.append` ") {\n"
335 `T.append` compileJS' (indent + 2) body
336 `T.append` "\n" `T.append` T.replicate indent " " `T.append` "}"
338compileJS' indent (JSWord word)
339 | JSWord8 b <- word =
340 "new Uint8Array([" `T.append` T.pack (show b) `T.append` "])"
341 | JSWord16 b <- word =
342 "new Uint16Array([" `T.append` T.pack (show b) `T.append` "])"
343 | JSWord32 b <- word =
344 "new Uint32Array([" `T.append` T.pack (show b) `T.append` "])"
345 | JSWord64 b <- word =
346 "i$bigInt(\"" `T.append` T.pack (show b) `T.append` "\")"
348jsInstanceOf :: JS -> String -> JS
349jsInstanceOf obj cls = JSBinOp "instanceof" obj (JSIdent cls)
351jsOr :: JS -> JS -> JS
352jsOr lhs rhs = JSBinOp "||" lhs rhs
354jsAnd :: JS -> JS -> JS
355jsAnd lhs rhs = JSBinOp "&&" lhs rhs
357jsMeth :: JS -> String -> [JS] -> JS
358jsMeth obj meth args = JSApp (JSProj obj meth) args
360jsCall :: String -> [JS] -> JS
361jsCall fun args = JSApp (JSIdent fun) args
364jsTypeOf js = JSPreOp "typeof " js
366jsEq :: JS -> JS -> JS
367jsEq lhs@(JSNum (JSInteger _)) rhs = JSApp (JSProj lhs "equals") [rhs]
368jsEq lhs rhs@(JSNum (JSInteger _)) = JSApp (JSProj lhs "equals") [rhs]
369jsEq lhs rhs = JSBinOp "==" lhs rhs
371jsNotEq :: JS -> JS -> JS
372jsNotEq lhs rhs = JSBinOp "!=" lhs rhs
374jsIsNumber :: JS -> JS
375jsIsNumber js = (jsTypeOf js) `jsEq` (JSString "number")
378jsIsNull js = JSBinOp "==" js JSNull
381jsBigInt (JSString "0") = JSNum (JSInteger JSBigZero)
382jsBigInt (JSString "1") = JSNum (JSInteger JSBigOne)
383jsBigInt js = JSNum $ JSInteger $ JSBigIntExpr js
385jsUnPackBits :: JS -> JS
386jsUnPackBits js = JSIndex js $ JSNum (JSInt 0)
388jsPackUBits8 :: JS -> JS
389jsPackUBits8 js = JSNew "Uint8Array" [JSArray [js]]
391jsPackUBits16 :: JS -> JS
392jsPackUBits16 js = JSNew "Uint16Array" [JSArray [js]]
394jsPackUBits32 :: JS -> JS
395jsPackUBits32 js = JSNew "Uint32Array" [JSArray [js]]
397jsPackSBits8 :: JS -> JS
398jsPackSBits8 js = JSNew "Int8Array" [JSArray [js]]
400jsPackSBits16 :: JS -> JS
401jsPackSBits16 js = JSNew "Int16Array" [JSArray [js]]
403jsPackSBits32 :: JS -> JS
404jsPackSBits32 js = JSNew "Int32Array" [JSArray [js]]
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list