1{-# LANGUAGE PatternGuards #-}
3module IRTS.CodegenJavaScript (codegenJavaScript, JSTarget(..)) where
5import Idris.AbsSyntax hiding (TypeCase)
9import IRTS.CodegenCommon
19import System.Directory
22idrNamespace = "__IDR__"
23idrRTNamespace = "__IDRRT__"
24idrLTNamespace = "__IDRLT__"
26data JSTarget = Node | JavaScript deriving Eq
42 | JSFunction [String] JS
56 | JSObject [(String, JS)]
60 | JSAlloc String (Maybe JS)
65compileJS :: JS -> String
66compileJS (JSRaw code) =
69compileJS (JSFunction args body) =
71 ++ intercalate "," args
77 | JSIntTy <- ty = idrRTNamespace ++ "Int"
78 | JSStringTy <- ty = idrRTNamespace ++ "String"
79 | JSIntegerTy <- ty = idrRTNamespace ++ "Integer"
80 | JSFloatTy <- ty = idrRTNamespace ++ "Float"
81 | JSCharTy <- ty = idrRTNamespace ++ "Char"
82 | JSPtrTy <- ty = idrRTNamespace ++ "Ptr"
83 | JSForgotTy <- ty = idrRTNamespace ++ "Forgot"
85compileJS (JSSeq seq) =
86 intercalate ";\n" (map compileJS seq)
88compileJS (JSReturn val) =
89 "return " ++ compileJS val
91compileJS (JSApp lhs rhs)
92 | JSFunction {} <- lhs =
93 concat ["(", compileJS lhs, ")(", args, ")"]
95 concat [compileJS lhs, "(", args, ")"]
97 args = intercalate "," $ map compileJS rhs
99compileJS (JSNew name args) =
100 "new " ++ name ++ "(" ++ intercalate "," (map compileJS args) ++ ")"
102compileJS (JSError exc) =
103 "(function(){throw '" ++ exc ++ "';})()"
105compileJS (JSOp op lhs rhs) =
106 compileJS lhs ++ " " ++ op ++ " " ++ compileJS rhs
108compileJS (JSProj obj field)
109 | JSFunction {} <- obj =
110 concat ["(", compileJS obj, ").", field]
112 compileJS obj ++ '.' : field
114compileJS (JSVar var) =
115 translateVariableName var
126compileJS (JSArray elems) =
127 "[" ++ intercalate "," (map compileJS elems) ++ "]"
129compileJS (JSObject fields) =
130 "{" ++ intercalate ",\n" (map compileField fields) ++ "}"
132 compileField :: (String, JS) -> String
133 compileField (name, val) = '\'' : name ++ "' : " ++ compileJS val
135compileJS (JSString str) =
139 | JSInt i <- num = show i
140 | JSFloat f <- num = show f
141 | JSInteger i <- num = show i
143compileJS (JSAssign lhs rhs) =
144 compileJS lhs ++ "=" ++ compileJS rhs
146compileJS (JSAlloc name val) =
147 "var " ++ name ++ maybe "" ((" = " ++) . compileJS) val
149compileJS (JSIndex lhs rhs) =
150 compileJS lhs ++ "[" ++ compileJS rhs ++ "]"
152compileJS (JSCond branches) =
153 intercalate " else " $ map createIfBlock branches
155 createIfBlock (cond, e) =
156 "if (" ++ compileJS cond ++") {\n"
157 ++ "return " ++ compileJS e
160compileJS (JSTernary cond true false) =
161 let c = compileJS cond
163 f = compileJS false in
164 "(" ++ c ++ ")?(" ++ t ++ "):(" ++ f ++ ")"
166jsTailcall :: JS -> JS
168 jsCall (idrRTNamespace ++ "tailcall") [
169 JSFunction [] (JSReturn call)
172jsCall :: String -> [JS] -> JS
173jsCall fun = JSApp (JSRaw fun)
175jsMeth :: JS -> String -> [JS] -> JS
177 JSApp (JSProj obj meth)
179jsInstanceOf :: JS -> JS -> JS
180jsInstanceOf = JSOp "instanceof"
182jsEq :: JS -> JS -> JS
185jsAnd :: JS -> JS -> JS
189jsType = JSRaw $ idrRTNamespace ++ "Type"
192jsCon = JSRaw $ idrRTNamespace ++ "Con"
195jsTag obj = JSProj obj "tag"
198jsTypeTag obj = JSProj obj "type"
202 JSApp (JSRaw $ idrRTNamespace ++ "bigInt") [val]
204jsVar :: Int -> String
205jsVar = ("__var_" ++) . show
207jsLet :: String -> JS -> JS -> JS
208jsLet name value body =
221codegenJavaScript target definitions filename outputType = do
222 let (header, runtime) = case target of
224 ("#!/usr/bin/env node\n", "-node")
228 idrRuntime <- readFile $ path ++ "/js/Runtime-common.js"
229 tgtRuntime <- readFile $ concat [path, "/js/Runtime", runtime, ".js"]
230 writeFile filename $ intercalate "\n" $ [ header
233 ] ++ functions ++ [mainLoop]
235 setPermissions filename (emptyPermissions { readable = True
236 , executable = target == Node
240 def :: [(String, SDecl)]
241 def = map (first translateNamespace) definitions
243 functions :: [String]
244 functions = map (compileJS . translateDeclaration) def
247 mainLoop = compileJS $
248 JSSeq [ JSAlloc "main" $ Just $ JSFunction [] (
249 jsTailcall $ jsCall "__IDR__runMain0" []
254translateIdentifier :: String -> String
256 replaceReserved . concatMap replaceBadChars
257 where replaceBadChars :: Char -> String
261 | isDigit c = "_" ++ [c] ++ "_"
262 | not (isLetter c && isAscii c) = '_' : show (ord c)
265 | s `elem` reserved = '_' : s
312translateNamespace :: Name -> String
313translateNamespace (UN _) = idrNamespace
314translateNamespace (NS _ ns) = idrNamespace ++ concatMap translateIdentifier ns
315translateNamespace (MN _ _) = idrNamespace
317translateName :: Name -> String
318translateName (UN name) = translateIdentifier name
319translateName (NS name _) = translateName name
320translateName (MN i name) = translateIdentifier name ++ show i
322translateConstant :: Const -> JS
323translateConstant (I i) = JSNum (JSInt i)
324translateConstant (Fl f) = JSNum (JSFloat f)
325translateConstant (Ch c) = JSString [c]
326translateConstant (Str s) = JSString s
327translateConstant (AType (ATInt ITNative)) = JSType JSIntTy
328translateConstant StrType = JSType JSStringTy
329translateConstant (AType (ATInt ITBig)) = JSType JSIntegerTy
330translateConstant (AType ATFloat) = JSType JSFloatTy
331translateConstant (AType (ATInt ITChar)) = JSType JSCharTy
332translateConstant PtrType = JSType JSPtrTy
333translateConstant Forgot = JSType JSForgotTy
334translateConstant (BI i) = jsBigInt $ JSNum (JSInteger i)
336 JSError $ "Unimplemented Constant: " ++ show c
338translateDeclaration :: (String, SDecl) -> JS
339translateDeclaration (path, SFun name params stackSize body)
340 | (MN _ "APPLY") <- name
341 , (SLet var val next) <- body
342 , (SChkCase cvar cases) <- next =
343 let lvar = translateVariableName var
344 lookup = "[" ++ lvar ++ ".tag](fn0,arg0," ++ lvar ++ ")" in
345 JSSeq [ lookupTable [(var, "chk")] var cases
346 , jsDecl $ JSFunction ["fn0", "arg0"] (
347 JSSeq [ JSAlloc "__var_0" (Just $ JSRaw "fn0")
348 , JSReturn $ jsLet (translateVariableName var) (
349 translateExpression val
351 (JSVar var `jsInstanceOf` jsCon) `jsAnd`
352 (hasProp lookupTableName (translateVariableName var))
354 lookupTableName ++ lookup
361 | (MN _ "EVAL") <- name
362 , (SChkCase var cases) <- body =
363 JSSeq [ lookupTable [] var cases
364 , jsDecl $ JSFunction ["arg0"] (JSReturn $
366 (JSRaw "arg0" `jsInstanceOf` jsCon) `jsAnd`
367 (hasProp lookupTableName "arg0")
368 ) (JSRaw $ lookupTableName ++ "[arg0.tag](arg0)") (JSRaw "arg0")
372 let fun = translateExpression body in
376 hasProp :: String -> String -> JS
378 jsMeth (JSRaw table) "hasOwnProperty" [JSRaw $ var ++ ".tag"]
380 caseFun :: [(LVar, String)] -> LVar -> SAlt -> JS
381 caseFun aux var cse =
382 jsFunAux aux (translateCase (Just (translateVariableName var)) cse)
384 getTag :: SAlt -> Maybe String
385 getTag (SConCase _ tag _ _ _) = Just $ show tag
388 lookupTableName :: String
389 lookupTableName = idrLTNamespace ++ translateName name
391 lookupTable :: [(LVar, String)] -> LVar -> [SAlt] -> JS
392 lookupTable aux var cases =
393 JSAlloc lookupTableName $ Just (
394 JSObject $ catMaybes $ map (lookupEntry aux var) cases
397 lookupEntry :: [(LVar, String)] -> LVar -> SAlt -> Maybe (String, JS)
398 lookupEntry aux var alt = do
400 return (tag, caseFun aux var alt)
403 jsDecl = JSAlloc (path ++ translateName name) . Just
405 jsFun body = jsFunAux [] body
407 jsFunAux :: [(LVar, String)] -> JS -> JS
409 JSFunction (p ++ map snd aux) (
411 zipWith assignVar [0..] p ++
412 map allocVar [numP .. (numP + stackSize - 1)] ++
420 allocVar :: Int -> JS
421 allocVar n = JSAlloc (jsVar n) Nothing
423 assignVar :: Int -> String -> JS
424 assignVar n s = JSAlloc (jsVar n) (Just $ JSRaw s)
426 assignAux :: (LVar, String) -> JS
427 assignAux (var, val) = JSAssign (JSRaw $ translateVariableName var) (JSRaw val)
430 p = map translateName params
432translateVariableName :: LVar -> String
433translateVariableName (Loc i) =
436translateExpression :: SExp -> JS
437translateExpression (SLet name value body) =
438 jsLet (translateVariableName name) (
439 translateExpression value
440 ) (translateExpression body)
442translateExpression (SConst cst) =
443 translateConstant cst
445translateExpression (SV var) =
448translateExpression (SApp tc name vars)
450 jsTailcall $ translateFunctionCall name vars
452 JSNew (idrRTNamespace ++ "Tailcall") [JSFunction [] (
453 JSReturn $ translateFunctionCall name vars
456 translateFunctionCall name vars =
457 jsCall (translateNamespace name ++ translateName name) (map JSVar vars)
459translateExpression (SOp op vars)
460 | LNoOp <- op = JSVar (last vars)
462 | (LZExt _ ITBig) <- op = jsBigInt $ JSVar (last vars)
463 | (LPlus (ATInt ITBig)) <- op
464 , (lhs:rhs:_) <- vars = invokeMeth lhs "add" [rhs]
465 | (LMinus (ATInt ITBig)) <- op
466 , (lhs:rhs:_) <- vars = invokeMeth lhs "minus" [rhs]
467 | (LTimes (ATInt ITBig)) <- op
468 , (lhs:rhs:_) <- vars = invokeMeth lhs "times" [rhs]
469 | (LSDiv (ATInt ITBig)) <- op
470 , (lhs:rhs:_) <- vars = invokeMeth lhs "divide" [rhs]
471 | (LSRem (ATInt ITBig)) <- op
472 , (lhs:rhs:_) <- vars = invokeMeth lhs "mod" [rhs]
473 | (LEq (ATInt ITBig)) <- op
474 , (lhs:rhs:_) <- vars = invokeMeth lhs "equals" [rhs]
475 | (LSLt (ATInt ITBig)) <- op
476 , (lhs:rhs:_) <- vars = invokeMeth lhs "lesser" [rhs]
477 | (LSLe (ATInt ITBig)) <- op
478 , (lhs:rhs:_) <- vars = invokeMeth lhs "lesserOrEquals" [rhs]
479 | (LSGt (ATInt ITBig)) <- op
480 , (lhs:rhs:_) <- vars = invokeMeth lhs "greater" [rhs]
481 | (LSGe (ATInt ITBig)) <- op
482 , (lhs:rhs:_) <- vars = invokeMeth lhs "greaterOrEquals" [rhs]
484 | (LPlus ATFloat) <- op
485 , (lhs:rhs:_) <- vars = translateBinaryOp "+" lhs rhs
486 | (LMinus ATFloat) <- op
487 , (lhs:rhs:_) <- vars = translateBinaryOp "-" lhs rhs
488 | (LTimes ATFloat) <- op
489 , (lhs:rhs:_) <- vars = translateBinaryOp "*" lhs rhs
490 | (LSDiv ATFloat) <- op
491 , (lhs:rhs:_) <- vars = translateBinaryOp "/" lhs rhs
492 | (LEq ATFloat) <- op
493 , (lhs:rhs:_) <- vars = translateBinaryOp "==" lhs rhs
494 | (LSLt ATFloat) <- op
495 , (lhs:rhs:_) <- vars = translateBinaryOp "<" lhs rhs
496 | (LSLe ATFloat) <- op
497 , (lhs:rhs:_) <- vars = translateBinaryOp "<=" lhs rhs
498 | (LSGt ATFloat) <- op
499 , (lhs:rhs:_) <- vars = translateBinaryOp ">" lhs rhs
500 | (LSGe ATFloat) <- op
501 , (lhs:rhs:_) <- vars = translateBinaryOp ">=" lhs rhs
504 , (lhs:rhs:_) <- vars = translateBinaryOp "+" lhs rhs
506 , (lhs:rhs:_) <- vars = translateBinaryOp "-" lhs rhs
508 , (lhs:rhs:_) <- vars = translateBinaryOp "*" lhs rhs
510 , (lhs:rhs:_) <- vars = translateBinaryOp "/" lhs rhs
512 , (lhs:rhs:_) <- vars = translateBinaryOp "%" lhs rhs
514 , (lhs:rhs:_) <- vars = translateBinaryOp "==" lhs rhs
516 , (lhs:rhs:_) <- vars = translateBinaryOp "<" lhs rhs
518 , (lhs:rhs:_) <- vars = translateBinaryOp "<=" lhs rhs
520 , (lhs:rhs:_) <- vars = translateBinaryOp ">" lhs rhs
522 , (lhs:rhs:_) <- vars = translateBinaryOp ">=" lhs rhs
524 , (lhs:rhs:_) <- vars = translateBinaryOp "&" lhs rhs
526 , (lhs:rhs:_) <- vars = translateBinaryOp "|" lhs rhs
528 , (lhs:rhs:_) <- vars = translateBinaryOp "^" lhs rhs
530 , (lhs:rhs:_) <- vars = translateBinaryOp "<<" rhs lhs
532 , (lhs:rhs:_) <- vars = translateBinaryOp ">>" rhs lhs
534 , (arg:_) <- vars = JSRaw $ '~' : translateVariableName arg
537 , (lhs:rhs:_) <- vars = translateBinaryOp "+" lhs rhs
539 , (lhs:rhs:_) <- vars = translateBinaryOp "==" lhs rhs
541 , (lhs:rhs:_) <- vars = translateBinaryOp "<" lhs rhs
543 , (arg:_) <- vars = JSProj (JSVar arg) "length"
545 | (LStrInt ITNative) <- op
546 , (arg:_) <- vars = jsCall "parseInt" [JSVar arg]
547 | (LIntStr ITNative) <- op
548 , (arg:_) <- vars = jsCall "String" [JSVar arg]
549 | (LSExt ITNative ITBig) <- op
550 , (arg:_) <- vars = jsBigInt $ JSVar arg
551 | (LTrunc ITBig ITNative) <- op
552 , (arg:_) <- vars = jsMeth (JSVar arg) "valueOf" []
553 | (LIntStr ITBig) <- op
554 , (arg:_) <- vars = jsMeth (JSVar arg) "toString" []
555 | (LStrInt ITBig) <- op
556 , (arg:_) <- vars = jsBigInt $ JSVar arg
558 , (arg:_) <- vars = jsCall "String" [JSVar arg]
560 , (arg:_) <- vars = jsCall "parseFloat" [JSVar arg]
561 | (LIntFloat ITNative) <- op
562 , (arg:_) <- vars = JSVar arg
563 | (LFloatInt ITNative) <- op
564 , (arg:_) <- vars = JSVar arg
565 | (LChInt ITNative) <- op
566 , (arg:_) <- vars = JSProj (JSVar arg) "charCodeAt(0)"
567 | (LIntCh ITNative) <- op
568 , (arg:_) <- vars = jsCall "String.fromCharCode" [JSVar arg]
571 , (arg:_) <- vars = jsCall "Math.exp" [JSVar arg]
573 , (arg:_) <- vars = jsCall "Math.log" [JSVar arg]
575 , (arg:_) <- vars = jsCall "Math.sin" [JSVar arg]
577 , (arg:_) <- vars = jsCall "Math.cos" [JSVar arg]
579 , (arg:_) <- vars = jsCall "Math.tan" [JSVar arg]
581 , (arg:_) <- vars = jsCall "Math.asin" [JSVar arg]
583 , (arg:_) <- vars = jsCall "Math.acos" [JSVar arg]
585 , (arg:_) <- vars = jsCall "Math.atan" [JSVar arg]
587 , (arg:_) <- vars = jsCall "Math.sqrt" [JSVar arg]
589 , (arg:_) <- vars = jsCall "Math.floor" [JSVar arg]
591 , (arg:_) <- vars = jsCall "Math.ceil" [JSVar arg]
594 , (lhs:rhs:_) <- vars = translateBinaryOp "+" lhs rhs
596 , (arg:_) <- vars = JSIndex (JSVar arg) (JSRaw "0")
598 , (arg:_) <- vars = JSProj (JSVar arg) "split('').reverse().join('')"
600 , (lhs:rhs:_) <- vars = JSIndex (JSVar lhs) (JSVar rhs)
602 , (arg:_) <- vars = let v = translateVariableName arg in
603 JSRaw $ v ++ ".substr(1," ++ v ++ ".length-1)"
605 translateBinaryOp :: String -> LVar -> LVar -> JS
606 translateBinaryOp f lhs rhs = JSOp f (JSVar lhs) (JSVar rhs)
608 invokeMeth :: LVar -> String -> [LVar] -> JS
609 invokeMeth obj meth args = jsMeth (JSVar obj) meth (map JSVar args)
611translateExpression (SError msg) =
614translateExpression (SForeign _ _ "putStr" [(FString, var)]) =
615 jsCall (idrRTNamespace ++ "print") [JSVar var]
617translateExpression (SForeign _ _ fun args)
618 | "[]=" `isSuffixOf` fun
619 , (obj:idx:val:[]) <- args =
620 JSRaw $ concat [object obj, index idx, assign val]
622 | "[]" `isSuffixOf` fun
623 , (obj:idx:[]) <- args =
624 JSRaw $ object obj ++ index idx
626 | "[" `isPrefixOf` fun && "]=" `isSuffixOf` fun
627 , (obj:val:[]) <- args =
628 JSRaw $ concat [object obj, '[' : name ++ "]", assign val]
630 | "[" `isPrefixOf` fun && "]" `isSuffixOf` fun
632 JSRaw $ object obj ++ '[' : name ++ "]"
634 | "." `isPrefixOf` fun, "=" `isSuffixOf` fun
635 , (obj:val:[]) <- args =
636 JSRaw $ concat [object obj, field, assign val]
638 | "." `isPrefixOf` fun
640 JSRaw $ object obj ++ field
642 | "." `isPrefixOf` fun
643 , (obj:[(FUnit, _)]) <- args =
644 JSRaw $ concat [object obj, method, "()"]
646 | "." `isPrefixOf` fun
648 JSRaw $ concat [object obj, method, arguments as]
650 | "[]=" `isSuffixOf` fun
651 , (idx:val:[]) <- args =
652 JSRaw $ concat [array, index idx, assign val]
654 | "[]" `isSuffixOf` fun
656 JSRaw $ array ++ index idx
658 | otherwise = JSRaw $ fun ++ arguments args
660 name = filter (`notElem` "[]=") fun
664 object o = translateVariableName (snd o)
665 index i = "[" ++ translateVariableName (snd i) ++ "]"
666 assign v = '=' : generateWrapper v
668 '(' : intercalate "," (map generateWrapper as) ++ ")"
670 generateWrapper (ffunc, name)
671 | FFunction <- ffunc =
672 idrRTNamespace ++ "ffiWrap(" ++ translateVariableName name ++ ")"
673 | FFunctionIO <- ffunc =
674 idrRTNamespace ++ "ffiWrap(" ++ translateVariableName name ++ ")"
676 generateWrapper (_, name) =
677 translateVariableName name
679translateExpression patterncase
680 | (SChkCase var cases) <- patterncase = caseHelper var cases "chk"
681 | (SCase var cases) <- patterncase = caseHelper var cases "cse"
683 caseHelper var cases param =
684 JSApp (JSFunction [param] (
685 JSCond $ map (expandCase param . translateCaseCond param) cases
688 expandCase :: String -> (Cond, JS) -> (JS, JS)
689 expandCase _ (RawCond cond, branch) = (cond, branch)
690 expandCase _ (CaseCond DefaultCase, branch) = (JSTrue , branch)
691 expandCase var (CaseCond caseTy, branch)
692 | ConCase tag <- caseTy =
693 let checkCon = JSRaw var `jsInstanceOf` jsCon
694 checkTag = (JSRaw $ show tag) `jsEq` jsTag (JSRaw var) in
695 (checkCon `jsAnd` checkTag, branch)
697 | TypeCase ty <- caseTy =
698 let checkTy = JSRaw var `jsInstanceOf` jsType
699 checkTag = jsTypeTag (JSRaw var) `jsEq` JSType ty in
700 (checkTy `jsAnd` checkTag, branch)
702translateExpression (SCon i name vars) =
703 JSNew (idrRTNamespace ++ "Con") [ JSRaw $ show i
704 , JSArray $ map JSVar vars
707translateExpression (SUpdate var e) =
708 JSAssign (JSVar var) (translateExpression e)
710translateExpression (SProj var i) =
711 JSIndex (JSProj (JSVar var) "vars") (JSRaw $ show i)
713translateExpression SNothing = JSNull
715translateExpression e =
716 JSError $ "Not yet implemented: " ++ filter (/= '\'') (show e)
718data CaseType = ConCase Int
723data Cond = CaseCond CaseType
726translateCaseCond :: String -> SAlt -> (Cond, JS)
727translateCaseCond _ cse@(SDefaultCase _) =
728 (CaseCond DefaultCase, translateCase Nothing cse)
730translateCaseCond var cse@(SConstCase ty _)
731 | StrType <- ty = matchHelper JSStringTy
732 | PtrType <- ty = matchHelper JSPtrTy
733 | Forgot <- ty = matchHelper JSForgotTy
734 | (AType ATFloat) <- ty = matchHelper JSFloatTy
735 | (AType (ATInt ITBig)) <- ty = matchHelper JSIntegerTy
736 | (AType (ATInt ITNative)) <- ty = matchHelper JSIntTy
737 | (AType (ATInt ITChar)) <- ty = matchHelper JSCharTy
739 matchHelper :: JSType -> (Cond, JS)
740 matchHelper ty = (CaseCond $ TypeCase ty, translateCase Nothing cse)
742translateCaseCond var cse@(SConstCase cst@(BI _) _) =
743 let cond = jsMeth (JSRaw var) "equals" [translateConstant cst] in
744 (RawCond cond, translateCase Nothing cse)
746translateCaseCond var cse@(SConstCase cst _) =
747 let cond = JSRaw var `jsEq` translateConstant cst in
748 (RawCond cond, translateCase Nothing cse)
750translateCaseCond var cse@(SConCase _ tag _ _ _) =
751 (CaseCond $ ConCase tag, translateCase (Just var) cse)
753translateCase :: Maybe String -> SAlt -> JS
754translateCase _ (SDefaultCase e) = translateExpression e
755translateCase _ (SConstCase _ e) = translateExpression e
756translateCase (Just var) (SConCase a _ _ vars e) =
757 let params = map jsVar [a .. (a + length vars)] in
758 jsMeth (JSFunction params (JSReturn $ translateExpression e)) "apply" [
759 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