1{-# LANGUAGE PatternGuards #-}
3module IRTS.CodegenJavaScript (codegenJavaScript, JSTarget(..)) where
5import Idris.AbsSyntax hiding (TypeCase)
9import IRTS.CodegenCommon
18import System.Directory
21idrNamespace = "__IDR__"
22idrRTNamespace = "__IDRRT__"
24data JSTarget = Node | JavaScript deriving Eq
40 | JSFunction [String] JS
54 | JSObject [(String, JS)]
58 | JSAlloc String (Maybe JS)
62compileJS :: JS -> String
63compileJS (JSRaw code) =
66compileJS (JSFunction args body) =
68 ++ intercalate "," args
74 | JSIntTy <- ty = idrRTNamespace ++ "Int"
75 | JSStringTy <- ty = idrRTNamespace ++ "String"
76 | JSIntegerTy <- ty = idrRTNamespace ++ "Integer"
77 | JSFloatTy <- ty = idrRTNamespace ++ "Float"
78 | JSCharTy <- ty = idrRTNamespace ++ "Char"
79 | JSPtrTy <- ty = idrRTNamespace ++ "Ptr"
80 | JSForgotTy <- ty = idrRTNamespace ++ "Forgot"
82compileJS (JSSeq seq) =
83 intercalate ";\n" (map compileJS seq)
85compileJS (JSReturn val) =
86 "return " ++ compileJS val
88compileJS (JSApp lhs rhs)
89 | JSFunction {} <- lhs =
90 concat ["(", compileJS lhs, ")(", args, ")"]
92 concat [compileJS lhs, "(", args, ")"]
94 args = intercalate "," $ map compileJS rhs
96compileJS (JSNew name args) =
97 "new " ++ name ++ "(" ++ intercalate "," (map compileJS args) ++ ")"
99compileJS (JSError exc) =
100 "(function(){throw '" ++ exc ++ "';})()"
102compileJS (JSOp op lhs rhs) =
103 compileJS lhs ++ " " ++ op ++ " " ++ compileJS rhs
105compileJS (JSProj obj field)
106 | JSFunction {} <- obj =
107 concat ["(", compileJS obj, ").", field]
109 compileJS obj ++ '.' : field
111compileJS (JSVar var) =
112 translateVariableName var
123compileJS (JSArray elems) =
124 "[" ++ intercalate "," (map compileJS elems) ++ "]"
126compileJS (JSObject fields) =
127 "{" ++ intercalate ",\n" (map compileField fields) ++ "}"
129 compileField :: (String, JS) -> String
130 compileField (name, val) = '\'' : name ++ "' : " ++ compileJS val
132compileJS (JSString str) =
136 | JSInt i <- num = show i
137 | JSFloat f <- num = show f
138 | JSInteger i <- num = show i
140compileJS (JSAssign lhs rhs) =
141 compileJS lhs ++ "=" ++ compileJS rhs
143compileJS (JSAlloc name val) =
144 "var " ++ name ++ maybe "" ((" = " ++) . compileJS) val
146compileJS (JSIndex lhs rhs) =
147 compileJS lhs ++ "[" ++ compileJS rhs ++ "]"
149compileJS (JSCond branches) =
150 intercalate " else " $ map createIfBlock branches
152 createIfBlock (cond, e) =
153 "if (" ++ compileJS cond ++") {\n"
154 ++ "return " ++ compileJS e
157jsTailcall :: JS -> JS
159 jsCall (idrRTNamespace ++ "tailcall") [
160 JSFunction [] (JSReturn call)
163jsCall :: String -> [JS] -> JS
164jsCall fun = JSApp (JSRaw fun)
166jsMeth :: JS -> String -> [JS] -> JS
168 JSApp (JSProj obj meth)
170jsInstanceOf :: JS -> JS -> JS
171jsInstanceOf = JSOp "instanceof"
173jsEq :: JS -> JS -> JS
176jsAnd :: JS -> JS -> JS
180jsType = JSRaw $ idrRTNamespace ++ "Type"
183jsCon = JSRaw $ idrRTNamespace ++ "Con"
186jsTag obj = (JSProj obj "tag")
189jsTypeTag obj = (JSProj obj "type")
193 JSApp (JSRaw $ idrRTNamespace ++ "bigInt") [val]
195jsVar :: Int -> String
196jsVar = ("__var_" ++) . show
204codegenJavaScript target definitions filename outputType = do
205 let (header, runtime) = case target of
207 ("#!/usr/bin/env node\n", "-node")
211 idrRuntime <- readFile $ path ++ "/js/Runtime-common.js"
212 tgtRuntime <- readFile $ concat [path, "/js/Runtime", runtime, ".js"]
213 writeFile filename $ intercalate "\n" $ [ header
216 ] ++ functions ++ [mainLoop]
218 setPermissions filename (emptyPermissions { readable = True
219 , executable = target == Node
223 def :: [(String, SDecl)]
224 def = map (first translateNamespace) definitions
226 functions :: [String]
227 functions = map (compileJS . translateDeclaration) def
230 mainLoop = compileJS $
231 JSSeq [ JSAlloc "main" $ Just $ JSFunction [] (
232 jsTailcall $ jsCall "__IDR__runMain0" []
237translateIdentifier :: String -> String
239 replaceReserved . concatMap replaceBadChars
240 where replaceBadChars :: Char -> String
244 | isDigit c = "_" ++ [c] ++ "_"
245 | not (isLetter c && isAscii c) = '_' : show (ord c)
248 | s `elem` reserved = '_' : s
295translateNamespace :: Name -> String
296translateNamespace (UN _) = idrNamespace
297translateNamespace (NS _ ns) = idrNamespace ++ concatMap translateIdentifier ns
298translateNamespace (MN _ _) = idrNamespace
300translateName :: Name -> String
301translateName (UN name) = translateIdentifier name
302translateName (NS name _) = translateName name
303translateName (MN i name) = translateIdentifier name ++ show i
305translateConstant :: Const -> JS
306translateConstant (I i) = JSNum (JSInt i)
307translateConstant (Fl f) = JSNum (JSFloat f)
308translateConstant (Ch c) = JSString [c]
309translateConstant (Str s) = JSString s
310translateConstant (AType (ATInt ITNative)) = JSType JSIntTy
311translateConstant StrType = JSType JSStringTy
312translateConstant (AType (ATInt ITBig)) = JSType JSIntegerTy
313translateConstant (AType ATFloat) = JSType JSFloatTy
314translateConstant (AType (ATInt ITChar)) = JSType JSCharTy
315translateConstant PtrType = JSType JSPtrTy
316translateConstant Forgot = JSType JSForgotTy
317translateConstant (BI i) = jsBigInt $ JSNum (JSInteger i)
319 JSError $ "Unimplemented Constant: " ++ show c
321translateDeclaration :: (String, SDecl) -> JS
322translateDeclaration (path, SFun name params stackSize body) =
323 JSAlloc (path ++ translateName name) $ Just $ JSFunction p (
325 zipWith assignVar [0..] p ++
326 map allocVar [numP .. (numP + stackSize - 1)] ++
327 [JSReturn $ translateExpression body]
333 allocVar :: Int -> JS
334 allocVar n = JSAlloc (jsVar n) Nothing
336 assignVar :: Int -> String -> JS
337 assignVar n s = JSAlloc (jsVar n) (Just $ JSRaw s)
340 p = map translateParameter params
342 translateParameter :: Name -> String
343 translateParameter (MN i name) =
344 translateIdentifier name ++ show i
345 translateParameter (UN name) =
346 translateIdentifier name
348translateVariableName :: LVar -> String
349translateVariableName (Loc i) =
352translateExpression :: SExp -> JS
353translateExpression (SLet name value body) =
355 JSFunction [translateVariableName name] (
356 JSReturn $ translateExpression body
358 ) [translateExpression value]
360translateExpression (SConst cst) =
361 translateConstant cst
363translateExpression (SV var) =
366translateExpression (SApp tc name vars)
368 jsTailcall $ translateFunctionCall name vars
370 JSNew (idrRTNamespace ++ "Tailcall") [JSFunction [] (
371 JSReturn $ translateFunctionCall name vars
374 translateFunctionCall name vars =
375 jsCall (translateNamespace name ++ translateName name) (map JSVar vars)
377translateExpression (SOp op vars)
378 | LNoOp <- op = JSVar (last vars)
380 | (LZExt _ ITBig) <- op = jsBigInt $ JSVar (last vars)
381 | (LPlus (ATInt ITBig)) <- op
382 , (lhs:rhs:_) <- vars = invokeMeth lhs "add" [rhs]
383 | (LMinus (ATInt ITBig)) <- op
384 , (lhs:rhs:_) <- vars = invokeMeth lhs "minus" [rhs]
385 | (LTimes (ATInt ITBig)) <- op
386 , (lhs:rhs:_) <- vars = invokeMeth lhs "times" [rhs]
387 | (LSDiv (ATInt ITBig)) <- op
388 , (lhs:rhs:_) <- vars = invokeMeth lhs "divide" [rhs]
389 | (LSRem (ATInt ITBig)) <- op
390 , (lhs:rhs:_) <- vars = invokeMeth lhs "mod" [rhs]
391 | (LEq (ATInt ITBig)) <- op
392 , (lhs:rhs:_) <- vars = invokeMeth lhs "equals" [rhs]
393 | (LSLt (ATInt ITBig)) <- op
394 , (lhs:rhs:_) <- vars = invokeMeth lhs "lesser" [rhs]
395 | (LSLe (ATInt ITBig)) <- op
396 , (lhs:rhs:_) <- vars = invokeMeth lhs "lesserOrEquals" [rhs]
397 | (LSGt (ATInt ITBig)) <- op
398 , (lhs:rhs:_) <- vars = invokeMeth lhs "greater" [rhs]
399 | (LSGe (ATInt ITBig)) <- op
400 , (lhs:rhs:_) <- vars = invokeMeth lhs "greaterOrEquals" [rhs]
402 | (LPlus ATFloat) <- op
403 , (lhs:rhs:_) <- vars = translateBinaryOp "+" lhs rhs
404 | (LMinus ATFloat) <- op
405 , (lhs:rhs:_) <- vars = translateBinaryOp "-" lhs rhs
406 | (LTimes ATFloat) <- op
407 , (lhs:rhs:_) <- vars = translateBinaryOp "*" lhs rhs
408 | (LSDiv ATFloat) <- op
409 , (lhs:rhs:_) <- vars = translateBinaryOp "/" lhs rhs
410 | (LEq ATFloat) <- op
411 , (lhs:rhs:_) <- vars = translateBinaryOp "==" lhs rhs
412 | (LSLt ATFloat) <- op
413 , (lhs:rhs:_) <- vars = translateBinaryOp "<" lhs rhs
414 | (LSLe ATFloat) <- op
415 , (lhs:rhs:_) <- vars = translateBinaryOp "<=" lhs rhs
416 | (LSGt ATFloat) <- op
417 , (lhs:rhs:_) <- vars = translateBinaryOp ">" lhs rhs
418 | (LSGe ATFloat) <- op
419 , (lhs:rhs:_) <- vars = translateBinaryOp ">=" lhs rhs
422 , (lhs:rhs:_) <- vars = translateBinaryOp "+" lhs rhs
424 , (lhs:rhs:_) <- vars = translateBinaryOp "-" lhs rhs
426 , (lhs:rhs:_) <- vars = translateBinaryOp "*" lhs rhs
428 , (lhs:rhs:_) <- vars = translateBinaryOp "/" lhs rhs
430 , (lhs:rhs:_) <- vars = translateBinaryOp "%" lhs rhs
432 , (lhs:rhs:_) <- vars = translateBinaryOp "==" lhs rhs
434 , (lhs:rhs:_) <- vars = translateBinaryOp "<" lhs rhs
436 , (lhs:rhs:_) <- vars = translateBinaryOp "<=" lhs rhs
438 , (lhs:rhs:_) <- vars = translateBinaryOp ">" lhs rhs
440 , (lhs:rhs:_) <- vars = translateBinaryOp ">=" lhs rhs
442 , (lhs:rhs:_) <- vars = translateBinaryOp "&" lhs rhs
444 , (lhs:rhs:_) <- vars = translateBinaryOp "|" lhs rhs
446 , (lhs:rhs:_) <- vars = translateBinaryOp "^" lhs rhs
448 , (lhs:rhs:_) <- vars = translateBinaryOp "<<" rhs lhs
450 , (lhs:rhs:_) <- vars = translateBinaryOp ">>" rhs lhs
452 , (arg:_) <- vars = JSRaw $ '~' : translateVariableName arg
455 , (lhs:rhs:_) <- vars = translateBinaryOp "+" lhs rhs
457 , (lhs:rhs:_) <- vars = translateBinaryOp "==" lhs rhs
459 , (lhs:rhs:_) <- vars = translateBinaryOp "<" lhs rhs
461 , (arg:_) <- vars = JSProj (JSVar arg) "length"
463 | (LStrInt ITNative) <- op
464 , (arg:_) <- vars = jsCall "parseInt" [JSVar arg]
465 | (LIntStr ITNative) <- op
466 , (arg:_) <- vars = jsCall "String" [JSVar arg]
467 | (LSExt ITNative ITBig) <- op
468 , (arg:_) <- vars = jsBigInt $ JSVar arg
469 | (LTrunc ITBig ITNative) <- op
470 , (arg:_) <- vars = jsMeth (JSVar arg) "valueOf" []
471 | (LIntStr ITBig) <- op
472 , (arg:_) <- vars = jsMeth (JSVar arg) "toString" []
473 | (LStrInt ITBig) <- op
474 , (arg:_) <- vars = jsBigInt $ JSVar arg
476 , (arg:_) <- vars = jsCall "String" [JSVar arg]
478 , (arg:_) <- vars = jsCall "parseFloat" [JSVar arg]
479 | (LIntFloat ITNative) <- op
480 , (arg:_) <- vars = JSVar arg
481 | (LFloatInt ITNative) <- op
482 , (arg:_) <- vars = JSVar arg
483 | (LChInt ITNative) <- op
484 , (arg:_) <- vars = JSProj (JSVar arg) "charCodeAt(0)"
485 | (LIntCh ITNative) <- op
486 , (arg:_) <- vars = jsCall "String.fromCharCode" [JSVar arg]
489 , (arg:_) <- vars = jsCall "Math.exp" [JSVar arg]
491 , (arg:_) <- vars = jsCall "Math.log" [JSVar arg]
493 , (arg:_) <- vars = jsCall "Math.sin" [JSVar arg]
495 , (arg:_) <- vars = jsCall "Math.cos" [JSVar arg]
497 , (arg:_) <- vars = jsCall "Math.tan" [JSVar arg]
499 , (arg:_) <- vars = jsCall "Math.asin" [JSVar arg]
501 , (arg:_) <- vars = jsCall "Math.acos" [JSVar arg]
503 , (arg:_) <- vars = jsCall "Math.atan" [JSVar arg]
505 , (arg:_) <- vars = jsCall "Math.sqrt" [JSVar arg]
507 , (arg:_) <- vars = jsCall "Math.floor" [JSVar arg]
509 , (arg:_) <- vars = jsCall "Math.ceil" [JSVar arg]
512 , (lhs:rhs:_) <- vars = translateBinaryOp "+" lhs rhs
514 , (arg:_) <- vars = JSIndex (JSVar arg) (JSRaw "0")
516 , (arg:_) <- vars = JSProj (JSVar arg) "split('').reverse().join('')"
518 , (lhs:rhs:_) <- vars = JSIndex (JSVar lhs) (JSVar rhs)
520 , (arg:_) <- vars = let v = translateVariableName arg in
521 JSRaw $ v ++ ".substr(1," ++ v ++ ".length-1)"
523 translateBinaryOp :: String -> LVar -> LVar -> JS
524 translateBinaryOp f lhs rhs = JSOp f (JSVar lhs) (JSVar rhs)
526 invokeMeth :: LVar -> String -> [LVar] -> JS
527 invokeMeth obj meth args = jsMeth (JSVar obj) meth (map JSVar args)
529translateExpression (SError msg) =
532translateExpression (SForeign _ _ "putStr" [(FString, var)]) =
533 jsCall (idrRTNamespace ++ "print") [JSVar var]
535translateExpression (SForeign _ _ fun args)
536 | "[]=" `isSuffixOf` fun
537 , (obj:idx:val:[]) <- args =
538 JSRaw $ concat [object obj, index idx, assign val]
540 | "[]" `isSuffixOf` fun
541 , (obj:idx:[]) <- args =
542 JSRaw $ object obj ++ index idx
544 | "[" `isPrefixOf` fun && "]=" `isSuffixOf` fun
545 , (obj:val:[]) <- args =
546 JSRaw $ concat [object obj, '[' : name ++ "]", assign val]
548 | "[" `isPrefixOf` fun && "]" `isSuffixOf` fun
550 JSRaw $ object obj ++ '[' : name ++ "]"
552 | "." `isPrefixOf` fun, "=" `isSuffixOf` fun
553 , (obj:val:[]) <- args =
554 JSRaw $ concat [object obj, field, assign val]
556 | "." `isPrefixOf` fun
558 JSRaw $ object obj ++ field
560 | "." `isPrefixOf` fun
561 , (obj:[(FUnit, _)]) <- args =
562 JSRaw $ concat [object obj, method, "()"]
564 | "." `isPrefixOf` fun
566 JSRaw $ concat [object obj, method, arguments as]
568 | "[]=" `isSuffixOf` fun
569 , (idx:val:[]) <- args =
570 JSRaw $ concat [array, index idx, assign val]
572 | "[]" `isSuffixOf` fun
574 JSRaw $ array ++ index idx
576 | otherwise = JSRaw $ fun ++ arguments args
578 name = filter (`notElem` "[]=") fun
582 object o = translateVariableName (snd o)
583 index i = "[" ++ translateVariableName (snd i) ++ "]"
584 assign v = '=' : generateWrapper v
586 '(' : intercalate "," (map generateWrapper as) ++ ")"
588 generateWrapper (ffunc, name)
589 | FFunction <- ffunc =
590 idrRTNamespace ++ "ffiWrap(" ++ translateVariableName name ++ ")"
591 | FFunctionIO <- ffunc =
592 idrRTNamespace ++ "ffiWrap(" ++ translateVariableName name ++ ")"
594 generateWrapper (_, name) =
595 translateVariableName name
597translateExpression patterncase
598 | (SChkCase var cases) <- patterncase = caseHelper var cases "chk"
599 | (SCase var cases) <- patterncase = caseHelper var cases "cse"
601 caseHelper var cases param =
602 JSApp (JSFunction [param] (
603 JSCond $ map ((expandCase param) . translateCase param) cases
606 expandCase :: String -> (Cond, JS) -> (JS, JS)
607 expandCase _ (RawCond cond, branch) = (cond, branch)
608 expandCase _ (CaseCond DefaultCase, branch) = (JSTrue , branch)
609 expandCase var (CaseCond caseTy, branch)
610 | ConCase tag <- caseTy =
611 let checkCon = JSRaw var `jsInstanceOf` jsCon
612 checkTag = (JSRaw $ show tag) `jsEq` jsTag (JSRaw var) in
613 (checkCon `jsAnd` checkTag, branch)
615 | TypeCase ty <- caseTy =
616 let checkTy = JSRaw var `jsInstanceOf` jsType
617 checkTag = jsTypeTag (JSRaw var) `jsEq` JSType ty in
618 (checkTy `jsAnd` checkTag, branch)
620translateExpression (SCon i name vars) =
621 JSNew (idrRTNamespace ++ "Con") [ JSRaw $ show i
622 , JSArray $ map JSVar vars
625translateExpression (SUpdate var e) =
626 JSAssign (JSVar var) (translateExpression e)
628translateExpression (SProj var i) =
629 JSIndex (JSProj (JSVar var) "vars") (JSRaw $ show i)
631translateExpression SNothing = JSNull
633translateExpression e =
634 JSError $ "Not yet implemented: " ++ filter (/= '\'') (show e)
636data CaseType = ConCase Int
641data Cond = CaseCond CaseType
644translateCase :: String -> SAlt -> (Cond, JS)
645translateCase _ (SDefaultCase e) =
646 (CaseCond DefaultCase, translateExpression e)
648translateCase var (SConstCase ty e)
649 | StrType <- ty = matchHelper JSStringTy
650 | PtrType <- ty = matchHelper JSPtrTy
651 | Forgot <- ty = matchHelper JSForgotTy
652 | (AType ATFloat) <- ty = matchHelper JSFloatTy
653 | (AType (ATInt ITBig)) <- ty = matchHelper JSIntegerTy
654 | (AType (ATInt ITNative)) <- ty = matchHelper JSIntTy
655 | (AType (ATInt ITChar)) <- ty = matchHelper JSCharTy
657 matchHelper :: JSType -> (Cond, JS)
658 matchHelper ty = (CaseCond $ TypeCase ty, translateExpression e)
660translateCase var (SConstCase cst@(BI _) e) =
661 let cond = jsMeth (JSRaw var) "equals" [translateConstant cst] in
662 (RawCond cond, translateExpression e)
664translateCase var (SConstCase cst e) =
665 let cond = JSRaw var `jsEq` translateConstant cst in
666 (RawCond cond, translateExpression e)
668translateCase var (SConCase a tag name vars e) =
669 let params = map jsVar [a .. (a + length vars)] in
670 (CaseCond $ ConCase tag,
671 jsMeth (JSFunction params (JSReturn $ translateExpression e)) "apply" [
672 JSThis, JSProj (JSRaw var) "vars"
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