1{-# LANGUAGE PatternGuards #-}
2{-# LANGUAGE OverloadedStrings #-}
3module IRTS.CodegenJavaScript (codegenJavaScript, codegenNode, JSTarget(..)) where
5import IRTS.JavaScript.AST
7import Idris.AbsSyntax hiding (TypeCase)
11import IRTS.Defunctionalise
12import IRTS.CodegenCommon
18import Control.Monad (mapM)
19import Control.Applicative ((<$>), (<*>), pure)
20import Control.Monad.RWS hiding (mapM)
21import Control.Monad.State
27import Data.Traversable hiding (mapM)
29import System.Directory
32import qualified Data.Map.Strict as M
33import qualified Data.Text as T
34import qualified Data.Text.IO as TIO
37data CompileInfo = CompileInfo { compileInfoApplyCases :: [Int]
38 , compileInfoEvalCases :: [Int]
39 , compileInfoNeedsBigInt :: Bool
43initCompileInfo :: [(Name, [BC])] -> CompileInfo
45 CompileInfo (collectCases "APPLY" bc) (collectCases "EVAL" bc) (lookupBigInt bc)
47 lookupBigInt :: [(Name, [BC])] -> Bool
48 lookupBigInt = any (needsBigInt . snd)
50 needsBigInt :: [BC] -> Bool
51 needsBigInt bc = any testBCForBigInt bc
53 testBCForBigInt :: BC -> Bool
54 testBCForBigInt (ASSIGNCONST _ c) =
57 testBCForBigInt (CONSTCASE _ c d) =
58 maybe False needsBigInt d
59 || any (needsBigInt . snd) c
60 || any (testConstForBigInt . fst) c
62 testBCForBigInt (CASE _ _ c d) =
63 maybe False needsBigInt d
64 || any (needsBigInt . snd) c
66 testBCForBigInt _ = False
68 testConstForBigInt :: Const -> Bool
69 testConstForBigInt (BI _) = True
70 testConstForBigInt (B64 _) = True
71 testConstForBigInt _ = False
74 collectCases :: String -> [(Name, [BC])] -> [Int]
75 collectCases fun bc = getCases $ findFunction fun bc
77 findFunction :: String -> [(Name, [BC])] -> [BC]
78 findFunction f ((MN 0 fun, bc):_)
80 findFunction f (_:bc) = findFunction f bc
82 getCases :: [BC] -> [Int]
83 getCases = concatMap analyze
85 analyze :: BC -> [Int]
86 analyze (CASE _ _ b _) = map fst b
90data JSTarget = Node | JavaScript deriving Eq
93codegenJavaScript :: CodeGenerator
95 codegenJS_all JavaScript (simpleDecls ci)
96 (includes ci) [] (outputFile ci) (outputType ci)
98codegenNode :: CodeGenerator
100 codegenJS_all Node (simpleDecls ci)
101 (includes ci) (compileLibs ci) (outputFile ci) (outputType ci)
111codegenJS_all target definitions includes libs filename outputType = do
112 let bytecode = map toBC definitions
113 let info = initCompileInfo bytecode
114 let js = concatMap (translateDecl info) bytecode
115 let full = concatMap processFunction js
116 let code = deadCodeElim full
117 let ext = takeExtension filename
118 let isHtml = target == JavaScript && ext == ".html"
119 let htmlPrologue = T.pack "<!doctype html><html><head><script>\n"
120 let htmlEpilogue = T.pack "\n</script></head><body></body></html>"
121 let (cons, opt) = optimizeConstructors code
122 let (header, rt) = case target of
123 Node -> ("#!/usr/bin/env node\n", "-node")
124 JavaScript -> ("", "-browser")
126 included <- concat <$> getIncludes includes
127 path <- (++) <$> getDataDir <*> (pure "/jsrts/")
128 idrRuntime <- readFile $ path ++ "Runtime-common.js"
129 tgtRuntime <- readFile $ concat [path, "Runtime", rt, ".js"]
130 jsbn <- if compileInfoNeedsBigInt info
131 then readFile $ path ++ "jsbn/jsbn.js"
133 let runtime = ( header
140 let jsSource = T.pack runtime
141 `T.append` T.concat (map compileJS opt)
142 `T.append` T.concat (map compileJS cons)
144 `T.append` invokeMain
145 let source = if isHtml
146 then htmlPrologue `T.append` jsSource `T.append` htmlEpilogue
148 writeSourceText filename source
149 setPermissions filename (emptyPermissions { readable = True
150 , executable = target == Node
154 deadCodeElim :: [JS] -> [JS]
155 deadCodeElim js = concatMap collectFunctions js
157 collectFunctions :: JS -> [JS]
158 collectFunctions fun@(JSAlloc name _)
159 | name == translateName (sMN 0 "runMain") = [fun]
161 collectFunctions fun@(JSAlloc name (Just (JSFunction _ body))) =
162 let invokations = sum $ map (
163 \x -> execState (countInvokations name x) 0
165 in if invokations == 0
169 countInvokations :: String -> JS -> State Int ()
170 countInvokations name (JSAlloc _ (Just (JSFunction _ body))) =
171 countInvokations name body
173 countInvokations name (JSSeq seq) =
174 void $ traverse (countInvokations name) seq
176 countInvokations name (JSAssign _ rhs) =
177 countInvokations name rhs
179 countInvokations name (JSCond conds) =
181 runKleisli $ arr id *** Kleisli (countInvokations name)
184 countInvokations name (JSSwitch _ conds def) =
186 runKleisli $ arr id *** Kleisli (countInvokations name)
187 ) conds >> traverse (countInvokations name) def
189 countInvokations name (JSApp lhs rhs) =
190 void $ countInvokations name lhs >> traverse (countInvokations name) rhs
192 countInvokations name (JSNew _ args) =
193 void $ traverse (countInvokations name) args
195 countInvokations name (JSArray args) =
196 void $ traverse (countInvokations name) args
198 countInvokations name (JSIdent name')
199 | name == name' = get >>= put . (+1)
200 | otherwise = return ()
202 countInvokations _ _ = return ()
204 processFunction :: JS -> [JS]
206 collectSplitFunctions . (\x -> evalRWS (splitFunction x) () 0)
208 includeLibs :: [String] -> String
210 concatMap (\lib -> "var " ++ lib ++ " = require(\"" ++ lib ++"\");\n")
212 getIncludes :: [FilePath] -> IO [String]
213 getIncludes = mapM readFile
217 compileJS $ JSAlloc "main" (Just $
227 JSCond [ (exists document `jsAnd` isReady, mainFun)
228 , (exists window, windowMainFun)
233 exists js = jsTypeOf js `jsNotEq` JSString "undefined"
236 window = JSIdent "window"
239 document = JSIdent "document"
243 jsMeth window "addEventListener" [ JSString "DOMContentLoaded"
244 , JSFunction [] ( mainFun )
249 isReady = JSParens $ readyState `jsEq` JSString "complete" `jsOr` readyState `jsEq` JSString "loaded"
252 readyState = JSProj (JSIdent "document") "readyState"
256 JSSeq [ JSAlloc "vm" (Just $ JSNew "i$VM" [])
257 , JSApp (JSIdent "i$SCHED") [JSIdent "vm"]
259 JSIdent (translateName (sMN 0 "runMain"))
260 ) [JSNew "i$POINTER" [JSNum (JSInt 0)]]
261 , JSApp (JSIdent "i$RUN") []
265 invokeMain = compileJS $ JSApp (JSIdent "main") []
267optimizeConstructors :: [JS] -> ([JS], [JS])
268optimizeConstructors js =
269 let (js', cons) = runState (traverse optimizeConstructor' js) M.empty in
270 (map (allocCon . snd) (M.toList cons), js')
272 allocCon :: (String, JS) -> JS
273 allocCon (name, con) = JSAlloc name (Just con)
275 newConstructor :: Int -> String
276 newConstructor n = "i$CON$" ++ show n
278 optimizeConstructor' :: JS -> State (M.Map Int (String, JS)) JS
279 optimizeConstructor' js@(JSNew "i$CON" [ JSNum (JSInt tag)
285 case M.lookup tag s of
286 Just (i, c) -> return $ JSIdent i
287 Nothing -> do let n = newConstructor tag
288 put $ M.insert tag (n, js) s
291 optimizeConstructor' (JSSeq seq) =
292 JSSeq <$> traverse optimizeConstructor' seq
294 optimizeConstructor' (JSSwitch reg cond def) = do
295 cond' <- traverse (runKleisli $ arr id *** Kleisli optimizeConstructor') cond
296 def' <- traverse optimizeConstructor' def
297 return $ JSSwitch reg cond' def'
299 optimizeConstructor' (JSCond cond) =
300 JSCond <$> traverse (runKleisli $ arr id *** Kleisli optimizeConstructor') cond
302 optimizeConstructor' (JSAlloc fun (Just (JSFunction args body))) = do
303 body' <- optimizeConstructor' body
304 return $ JSAlloc fun (Just (JSFunction args body'))
306 optimizeConstructor' (JSAssign lhs rhs) = do
307 lhs' <- optimizeConstructor' lhs
308 rhs' <- optimizeConstructor' rhs
309 return $ JSAssign lhs' rhs'
311 optimizeConstructor' js = return js
313collectSplitFunctions :: (JS, [(Int,JS)]) -> [JS]
314collectSplitFunctions (fun, splits) = map generateSplitFunction splits ++ [fun]
316 generateSplitFunction :: (Int,JS) -> JS
317 generateSplitFunction (depth, JSAlloc name fun) =
318 JSAlloc (name ++ "$" ++ show depth) fun
320splitFunction :: JS -> RWS () [(Int,JS)] Int JS
321splitFunction (JSAlloc name (Just (JSFunction args body@(JSSeq _)))) = do
322 body' <- splitSequence body
323 return $ JSAlloc name (Just (JSFunction args body'))
325 splitCondition :: JS -> RWS () [(Int,JS)] Int JS
327 | JSCond branches <- js =
328 JSCond <$> processBranches branches
329 | JSSwitch cond branches def <- js =
330 JSSwitch cond <$> (processBranches branches) <*> (traverse splitSequence def)
331 | otherwise = return js
333 processBranches :: [(JS,JS)] -> RWS () [(Int,JS)] Int [(JS,JS)]
335 traverse (runKleisli (arr id *** Kleisli splitSequence))
337 splitSequence :: JS -> RWS () [(Int, JS)] Int JS
338 splitSequence js@(JSSeq seq) =
339 let (pre,post) = break isBranch seq in
341 [_] -> JSSeq <$> traverse splitCondition seq
342 [call@(JSCond _),rest@(JSApp _ _)] -> do
343 rest' <- splitCondition rest
344 call' <- splitCondition call
345 return $ JSSeq (pre ++ [rest', call'])
346 [call@(JSSwitch _ _ _),rest@(JSApp _ _)] -> do
347 rest' <- splitCondition rest
348 call' <- splitCondition call
349 return $ JSSeq (pre ++ [rest', call'])
353 new <- splitFunction (newFun rest)
355 call' <- splitCondition call
356 return $ JSSeq (pre ++ (newCall depth : [call']))
357 _ -> JSSeq <$> traverse splitCondition seq
359 splitSequence js = return js
361 isBranch :: JS -> Bool
362 isBranch (JSApp (JSIdent "i$CALL") _) = True
363 isBranch (JSCond _) = True
364 isBranch (JSSwitch _ _ _) = True
369 JSApp (JSIdent "i$CALL") [ JSIdent $ name ++ "$" ++ show depth
370 , JSArray [jsOLDBASE, jsMYOLDBASE]
375 JSAlloc name (Just $ JSFunction ["oldbase", "myoldbase"] (JSSeq seq))
377splitFunction js = return js
379translateDecl :: CompileInfo -> (Name, [BC]) -> [JS]
380translateDecl info (name@(MN 0 fun), bc)
381 | txt "APPLY" == fun =
382 allocCaseFunctions (snd body)
385 ) (Just $ JSFunction ["oldbase"] (
386 JSSeq $ jsFUNPRELUDE ++ map (translateBC info) (fst body) ++ [
387 JSCond [ ( (translateReg $ caseReg (snd body)) `jsInstanceOf` "i$CON" `jsAnd` (JSProj (translateReg $ caseReg (snd body)) "app")
388 , JSApp (JSProj (translateReg $ caseReg (snd body)) "app") [jsOLDBASE, jsMYOLDBASE]
391 , JSSeq $ map (translateBC info) (defaultCase (snd body))
399 | txt "EVAL" == fun =
400 allocCaseFunctions (snd body)
403 ) (Just $ JSFunction ["oldbase"] (
404 JSSeq $ jsFUNPRELUDE ++ map (translateBC info) (fst body) ++ [
405 JSCond [ ( (translateReg $ caseReg (snd body)) `jsInstanceOf` "i$CON" `jsAnd` (JSProj (translateReg $ caseReg (snd body)) "ev")
406 , JSApp (JSProj (translateReg $ caseReg (snd body)) "ev") [jsOLDBASE, jsMYOLDBASE]
409 , JSSeq $ map (translateBC info) (defaultCase (snd body))
418 body = break isCase bc
422 | CASE {} <- bc = True
425 defaultCase :: [BC] -> [BC]
426 defaultCase ((CASE _ _ _ (Just d)):_) = d
428 caseReg :: [BC] -> Reg
429 caseReg ((CASE _ r _ _):_) = r
431 allocCaseFunctions :: [BC] -> [JS]
432 allocCaseFunctions ((CASE _ _ c _):_) = splitBranches c
433 allocCaseFunctions _ = []
435 splitBranches :: [(Int, [BC])] -> [JS]
436 splitBranches = map prepBranch
438 prepBranch :: (Int, [BC]) -> JS
439 prepBranch (tag, code) =
441 translateName name ++ "$" ++ show tag
442 ) (Just $ JSFunction ["oldbase", "myoldbase"] (
443 JSSeq $ map (translateBC info) code
447translateDecl info (name, bc) =
450 ) (Just $ JSFunction ["oldbase"] (
451 JSSeq $ jsFUNPRELUDE ++ map (translateBC info)bc
457jsFUNPRELUDE = [jsALLOCMYOLDBASE]
459jsALLOCMYOLDBASE :: JS
460jsALLOCMYOLDBASE = JSAlloc "myoldbase" (Just $ JSNew "i$POINTER" [])
462translateReg :: Reg -> JS
464 | RVal <- reg = jsRET
465 | Tmp <- reg = JSRaw "//TMPREG"
466 | L n <- reg = jsLOC n
467 | T n <- reg = jsTOP n
469translateConstant :: Const -> JS
470translateConstant (I i) = JSNum (JSInt i)
471translateConstant (Fl f) = JSNum (JSFloat f)
472translateConstant (Ch c) = JSString $ translateChar c
473translateConstant (Str s) = JSString $ concatMap translateChar s
474translateConstant (AType (ATInt ITNative)) = JSType JSIntTy
475translateConstant StrType = JSType JSStringTy
476translateConstant (AType (ATInt ITBig)) = JSType JSIntegerTy
477translateConstant (AType ATFloat) = JSType JSFloatTy
478translateConstant (AType (ATInt ITChar)) = JSType JSCharTy
479translateConstant Forgot = JSType JSForgotTy
480translateConstant (BI 0) = JSNum (JSInteger JSBigZero)
481translateConstant (BI 1) = JSNum (JSInteger JSBigOne)
482translateConstant (BI i) = jsBigInt (JSString $ show i)
483translateConstant (B8 b) = JSWord (JSWord8 b)
484translateConstant (B16 b) = JSWord (JSWord16 b)
485translateConstant (B32 b) = JSWord (JSWord32 b)
486translateConstant (B64 b) = JSWord (JSWord64 b)
488 JSError $ "Unimplemented Constant: " ++ show c
491translateChar :: Char -> String
493 | '\a' <- ch = "\\u0007"
500 | '\SO' <- ch = "\\u000E"
501 | '\DEL' <- ch = "\\u007F"
502 | '\\' <- ch = "\\\\"
503 | '\"' <- ch = "\\\""
504 | '\'' <- ch = "\\\'"
505 | ch `elem` asciiTab = "\\u" ++ fill (showHex (ord ch) "")
506 | ord ch > 255 = "\\u" ++ fill (showHex (ord ch) "")
509 fill :: String -> String
510 fill s = case length s of
517 ['\NUL', '\SOH', '\STX', '\ETX', '\EOT', '\ENQ', '\ACK', '\BEL',
518 '\BS', '\HT', '\LF', '\VT', '\FF', '\CR', '\SO', '\SI',
519 '\DLE', '\DC1', '\DC2', '\DC3', '\DC4', '\NAK', '\SYN', '\ETB',
520 '\CAN', '\EM', '\SUB', '\ESC', '\FS', '\GS', '\RS', '\US']
522translateName :: Name -> String
523translateName n = "_idris_" ++ concatMap cchar (showCG n)
524 where cchar x | isAlphaNum x = [x]
525 | otherwise = "_" ++ show (fromEnum x) ++ "_"
527jsASSIGN :: CompileInfo -> Reg -> Reg -> JS
528jsASSIGN _ r1 r2 = JSAssign (translateReg r1) (translateReg r2)
530jsASSIGNCONST :: CompileInfo -> Reg -> Const -> JS
531jsASSIGNCONST _ r c = JSAssign (translateReg r) (translateConstant c)
533jsCALL :: CompileInfo -> Name -> JS
537 ) [JSIdent (translateName n), JSArray [jsMYOLDBASE]]
539jsTAILCALL :: CompileInfo -> Name -> JS
543 ) [JSIdent (translateName n), JSArray [jsOLDBASE]]
545jsFOREIGN :: CompileInfo -> Reg -> String -> [(FType, Reg)] -> JS
546jsFOREIGN _ reg n args
548 , [(FPtr, arg)] <- args =
552 JSBinOp "==" (translateReg arg) JSNull
556 , [(_, lhs),(_, rhs)] <- args =
560 JSBinOp "==" (translateReg lhs) (translateReg rhs)
566 JSFFI n (map generateWrapper args)
569 generateWrapper :: (FType, Reg) -> JS
570 generateWrapper (ty, reg)
572 JSApp (JSIdent "i$ffiWrap") [ translateReg reg
574 , JSIdent "myoldbase"
576 | FFunctionIO <- ty =
577 JSApp (JSIdent "i$ffiWrap") [ translateReg reg
579 , JSIdent "myoldbase"
582 generateWrapper (_, reg) =
585jsREBASE :: CompileInfo -> JS
586jsREBASE _ = JSAssign jsSTACKBASE (JSProj jsOLDBASE "addr")
588jsSTOREOLD :: CompileInfo ->JS
589jsSTOREOLD _ = JSAssign (JSProj jsMYOLDBASE "addr") jsSTACKBASE
591jsADDTOP :: CompileInfo -> Int -> JS
595 JSBinOp "+=" jsSTACKTOP (JSNum (JSInt n))
597jsTOPBASE :: CompileInfo -> Int -> JS
598jsTOPBASE _ 0 = JSAssign jsSTACKTOP jsSTACKBASE
599jsTOPBASE _ n = JSAssign jsSTACKTOP (JSBinOp "+" jsSTACKBASE (JSNum (JSInt n)))
602jsBASETOP :: CompileInfo -> Int -> JS
603jsBASETOP _ 0 = JSAssign jsSTACKBASE jsSTACKTOP
604jsBASETOP _ n = JSAssign jsSTACKBASE (JSBinOp "+" jsSTACKTOP (JSNum (JSInt n)))
606jsNULL :: CompileInfo -> Reg -> JS
607jsNULL _ r = JSClear (translateReg r)
609jsERROR :: CompileInfo -> String -> JS
612jsSLIDE :: CompileInfo -> Int -> JS
613jsSLIDE _ 1 = JSAssign (jsLOC 0) (jsTOP 0)
614jsSLIDE _ n = JSApp (JSIdent "i$SLIDE") [JSNum (JSInt n)]
616jsMKCON :: CompileInfo -> Reg -> Int -> [Reg] -> JS
618 JSAssign (translateReg r) (
619 JSNew "i$CON" [ JSNum (JSInt t)
620 , JSArray (map translateReg rs)
621 , if t `elem` compileInfoApplyCases info
622 then JSIdent $ translateName (sMN 0 "APPLY") ++ "$" ++ show t
624 , if t `elem` compileInfoEvalCases info
625 then JSIdent $ translateName (sMN 0 "EVAL") ++ "$" ++ show t
630jsCASE :: CompileInfo -> Bool -> Reg -> [(Int, [BC])] -> Maybe [BC] -> JS
631jsCASE info safe reg cases def =
632 JSSwitch (tag safe $ translateReg reg) (
633 map ((JSNum . JSInt) *** prepBranch) cases
634 ) (fmap prepBranch def)
636 tag :: Bool -> JS -> JS
640 prepBranch :: [BC] -> JS
641 prepBranch bc = JSSeq $ map (translateBC info) bc
645 (JSTernary (js `jsInstanceOf` "i$CON") (
647 ) (JSNum (JSInt $ negate 1)))
650 jsCTAG js = JSProj js "tag"
653jsCONSTCASE :: CompileInfo -> Reg -> [(Const, [BC])] -> Maybe [BC] -> JS
654jsCONSTCASE info reg cases def =
656 map (jsEq (translateReg reg) . translateConstant *** prepBranch) cases
657 ) ++ (maybe [] ((:[]) . ((,) JSNoop) . prepBranch) def)
659 prepBranch :: [BC] -> JS
660 prepBranch bc = JSSeq $ map (translateBC info) bc
662jsPROJECT :: CompileInfo -> Reg -> Int -> Int -> JS
663jsPROJECT _ reg loc 0 = JSNoop
664jsPROJECT _ reg loc 1 =
665 JSAssign (jsLOC loc) (
667 JSProj (translateReg reg) "args"
672jsPROJECT _ reg loc ar =
673 JSApp (JSIdent "i$PROJECT") [ translateReg reg
678jsOP :: CompileInfo -> Reg -> PrimFn -> [Reg] -> JS
679jsOP _ reg op args = JSAssign (translateReg reg) jsOP'
683 | LNoOp <- op = translateReg (last args)
686 (_:str:_) <- args = JSApp (JSIdent "i$putStr") [translateReg str]
688 | LReadStr <- op = JSApp (JSIdent "i$getLine") []
690 | (LZExt (ITFixed IT8) ITNative) <- op = jsUnPackBits $ translateReg (last args)
691 | (LZExt (ITFixed IT16) ITNative) <- op = jsUnPackBits $ translateReg (last args)
692 | (LZExt (ITFixed IT32) ITNative) <- op = jsUnPackBits $ translateReg (last args)
694 | (LZExt _ ITBig) <- op = jsBigInt $ JSApp (JSIdent "String") [translateReg (last args)]
695 | (LPlus (ATInt ITBig)) <- op
696 , (lhs:rhs:_) <- args = invokeMeth lhs "add" [rhs]
697 | (LMinus (ATInt ITBig)) <- op
698 , (lhs:rhs:_) <- args = invokeMeth lhs "subtract" [rhs]
699 | (LTimes (ATInt ITBig)) <- op
700 , (lhs:rhs:_) <- args = invokeMeth lhs "multiply" [rhs]
701 | (LSDiv (ATInt ITBig)) <- op
702 , (lhs:rhs:_) <- args = invokeMeth lhs "divide" [rhs]
703 | (LSRem (ATInt ITBig)) <- op
704 , (lhs:rhs:_) <- args = invokeMeth lhs "mod" [rhs]
705 | (LEq (ATInt ITBig)) <- op
706 , (lhs:rhs:_) <- args = JSPreOp "+" $ invokeMeth lhs "equals" [rhs]
707 | (LSLt (ATInt ITBig)) <- op
708 , (lhs:rhs:_) <- args = JSPreOp "+" $ invokeMeth lhs "lesser" [rhs]
709 | (LSLe (ATInt ITBig)) <- op
710 , (lhs:rhs:_) <- args = JSPreOp "+" $ invokeMeth lhs "lesserOrEquals" [rhs]
711 | (LSGt (ATInt ITBig)) <- op
712 , (lhs:rhs:_) <- args = JSPreOp "+" $ invokeMeth lhs "greater" [rhs]
713 | (LSGe (ATInt ITBig)) <- op
714 , (lhs:rhs:_) <- args = JSPreOp "+" $ invokeMeth lhs "greaterOrEquals" [rhs]
716 | (LPlus ATFloat) <- op
717 , (lhs:rhs:_) <- args = translateBinaryOp "+" lhs rhs
718 | (LMinus ATFloat) <- op
719 , (lhs:rhs:_) <- args = translateBinaryOp "-" lhs rhs
720 | (LTimes ATFloat) <- op
721 , (lhs:rhs:_) <- args = translateBinaryOp "*" lhs rhs
722 | (LSDiv ATFloat) <- op
723 , (lhs:rhs:_) <- args = translateBinaryOp "/" lhs rhs
724 | (LEq ATFloat) <- op
725 , (lhs:rhs:_) <- args = translateCompareOp "==" lhs rhs
726 | (LSLt ATFloat) <- op
727 , (lhs:rhs:_) <- args = translateCompareOp "<" lhs rhs
728 | (LSLe ATFloat) <- op
729 , (lhs:rhs:_) <- args = translateCompareOp "<=" lhs rhs
730 | (LSGt ATFloat) <- op
731 , (lhs:rhs:_) <- args = translateCompareOp ">" lhs rhs
732 | (LSGe ATFloat) <- op
733 , (lhs:rhs:_) <- args = translateCompareOp ">=" lhs rhs
735 | (LPlus (ATInt ITChar)) <- op
736 , (lhs:rhs:_) <- args =
737 jsCall "i$fromCharCode" [
739 jsCall "i$charCode" [translateReg lhs]
741 jsCall "i$charCode" [translateReg rhs]
745 | (LTrunc (ITFixed IT16) (ITFixed IT8)) <- op
748 JSBinOp "&" (jsUnPackBits $ translateReg arg) (JSNum (JSInt 0xFF))
751 | (LTrunc (ITFixed IT32) (ITFixed IT16)) <- op
754 JSBinOp "&" (jsUnPackBits $ translateReg arg) (JSNum (JSInt 0xFFFF))
757 | (LTrunc (ITFixed IT64) (ITFixed IT32)) <- op
760 jsMeth (jsMeth (translateReg arg) "and" [
761 jsBigInt (JSString $ show 0xFFFFFFFF)
765 | (LTrunc ITBig (ITFixed IT64)) <- op
767 jsMeth (translateReg arg) "and" [
768 jsBigInt (JSString $ show 0xFFFFFFFFFFFFFFFF)
771 | (LLSHR (ITFixed IT8)) <- op
772 , (lhs:rhs:_) <- args = jsPackUBits8 $ bitsBinaryOp ">>" lhs rhs
774 | (LLSHR (ITFixed IT16)) <- op
775 , (lhs:rhs:_) <- args = jsPackUBits16 $ bitsBinaryOp ">>" lhs rhs
777 | (LLSHR (ITFixed IT32)) <- op
778 , (lhs:rhs:_) <- args = jsPackUBits32 $ bitsBinaryOp ">>" lhs rhs
780 | (LLSHR (ITFixed IT64)) <- op
781 , (lhs:rhs:_) <- args =
782 jsMeth (translateReg lhs) "shiftRight" [translateReg rhs]
784 | (LSHL (ITFixed IT8)) <- op
785 , (lhs:rhs:_) <- args = jsPackUBits8 $ bitsBinaryOp "<<" lhs rhs
787 | (LSHL (ITFixed IT16)) <- op
788 , (lhs:rhs:_) <- args = jsPackUBits16 $ bitsBinaryOp "<<" lhs rhs
790 | (LSHL (ITFixed IT32)) <- op
791 , (lhs:rhs:_) <- args = jsPackUBits32 $ bitsBinaryOp "<<" lhs rhs
793 | (LSHL (ITFixed IT64)) <- op
794 , (lhs:rhs:_) <- args =
795 jsMeth (jsMeth (translateReg lhs) "shiftLeft" [translateReg rhs]) "and" [
796 jsBigInt (JSString $ show 0xFFFFFFFFFFFFFFFF)
799 | (LAnd (ITFixed IT8)) <- op
800 , (lhs:rhs:_) <- args = jsPackUBits8 $ bitsBinaryOp "&" lhs rhs
802 | (LAnd (ITFixed IT16)) <- op
803 , (lhs:rhs:_) <- args = jsPackUBits16 $ bitsBinaryOp "&" lhs rhs
805 | (LAnd (ITFixed IT32)) <- op
806 , (lhs:rhs:_) <- args = jsPackUBits32 $ bitsBinaryOp "&" lhs rhs
808 | (LAnd (ITFixed IT64)) <- op
809 , (lhs:rhs:_) <- args =
810 jsMeth (translateReg lhs) "and" [translateReg rhs]
812 | (LOr (ITFixed IT8)) <- op
813 , (lhs:rhs:_) <- args = jsPackUBits8 $ bitsBinaryOp "|" lhs rhs
815 | (LOr (ITFixed IT16)) <- op
816 , (lhs:rhs:_) <- args = jsPackUBits16 $ bitsBinaryOp "|" lhs rhs
818 | (LOr (ITFixed IT32)) <- op
819 , (lhs:rhs:_) <- args = jsPackUBits32 $ bitsBinaryOp "|" lhs rhs
821 | (LOr (ITFixed IT64)) <- op
822 , (lhs:rhs:_) <- args =
823 jsMeth (translateReg lhs) "or" [translateReg rhs]
825 | (LXOr (ITFixed IT8)) <- op
826 , (lhs:rhs:_) <- args = jsPackUBits8 $ bitsBinaryOp "^" lhs rhs
828 | (LXOr (ITFixed IT16)) <- op
829 , (lhs:rhs:_) <- args = jsPackUBits16 $ bitsBinaryOp "^" lhs rhs
831 | (LXOr (ITFixed IT32)) <- op
832 , (lhs:rhs:_) <- args = jsPackUBits32 $ bitsBinaryOp "^" lhs rhs
834 | (LXOr (ITFixed IT64)) <- op
835 , (lhs:rhs:_) <- args =
836 jsMeth (translateReg lhs) "xor" [translateReg rhs]
838 | (LPlus (ATInt (ITFixed IT8))) <- op
839 , (lhs:rhs:_) <- args = jsPackUBits8 $ bitsBinaryOp "+" lhs rhs
841 | (LPlus (ATInt (ITFixed IT16))) <- op
842 , (lhs:rhs:_) <- args = jsPackUBits16 $ bitsBinaryOp "+" lhs rhs
844 | (LPlus (ATInt (ITFixed IT32))) <- op
845 , (lhs:rhs:_) <- args = jsPackUBits32 $ bitsBinaryOp "+" lhs rhs
847 | (LPlus (ATInt (ITFixed IT64))) <- op
848 , (lhs:rhs:_) <- args =
849 jsMeth (jsMeth (translateReg lhs) "add" [translateReg rhs]) "and" [
850 jsBigInt (JSString $ show 0xFFFFFFFFFFFFFFFF)
853 | (LMinus (ATInt (ITFixed IT8))) <- op
854 , (lhs:rhs:_) <- args = jsPackUBits8 $ bitsBinaryOp "-" lhs rhs
856 | (LMinus (ATInt (ITFixed IT16))) <- op
857 , (lhs:rhs:_) <- args = jsPackUBits16 $ bitsBinaryOp "-" lhs rhs
859 | (LMinus (ATInt (ITFixed IT32))) <- op
860 , (lhs:rhs:_) <- args = jsPackUBits32 $ bitsBinaryOp "-" lhs rhs
862 | (LMinus (ATInt (ITFixed IT64))) <- op
863 , (lhs:rhs:_) <- args =
864 jsMeth (jsMeth (translateReg lhs) "subtract" [translateReg rhs]) "and" [
865 jsBigInt (JSString $ show 0xFFFFFFFFFFFFFFFF)
868 | (LTimes (ATInt (ITFixed IT8))) <- op
869 , (lhs:rhs:_) <- args = jsPackUBits8 $ bitsBinaryOp "*" lhs rhs
871 | (LTimes (ATInt (ITFixed IT16))) <- op
872 , (lhs:rhs:_) <- args = jsPackUBits16 $ bitsBinaryOp "*" lhs rhs
874 | (LTimes (ATInt (ITFixed IT32))) <- op
875 , (lhs:rhs:_) <- args = jsPackUBits32 $ bitsBinaryOp "*" lhs rhs
877 | (LTimes (ATInt (ITFixed IT64))) <- op
878 , (lhs:rhs:_) <- args =
879 jsMeth (jsMeth (translateReg lhs) "multiply" [translateReg rhs]) "and" [
880 jsBigInt (JSString $ show 0xFFFFFFFFFFFFFFFF)
883 | (LEq (ATInt (ITFixed IT8))) <- op
884 , (lhs:rhs:_) <- args = bitsCompareOp "==" lhs rhs
886 | (LEq (ATInt (ITFixed IT16))) <- op
887 , (lhs:rhs:_) <- args = bitsCompareOp "==" lhs rhs
889 | (LEq (ATInt (ITFixed IT32))) <- op
890 , (lhs:rhs:_) <- args = bitsCompareOp "==" lhs rhs
892 | (LEq (ATInt (ITFixed IT64))) <- op
893 , (lhs:rhs:_) <- args = JSPreOp "+" $ invokeMeth lhs "equals" [rhs]
895 | (LLt (ITFixed IT8)) <- op
896 , (lhs:rhs:_) <- args = bitsCompareOp "<" lhs rhs
898 | (LLt (ITFixed IT16)) <- op
899 , (lhs:rhs:_) <- args = bitsCompareOp "<" lhs rhs
901 | (LLt (ITFixed IT32)) <- op
902 , (lhs:rhs:_) <- args = bitsCompareOp "<" lhs rhs
904 | (LLt (ITFixed IT64)) <- op
905 , (lhs:rhs:_) <- args = JSPreOp "+" $ invokeMeth lhs "lesser" [rhs]
907 | (LLe (ITFixed IT8)) <- op
908 , (lhs:rhs:_) <- args = bitsCompareOp "<=" lhs rhs
910 | (LLe (ITFixed IT16)) <- op
911 , (lhs:rhs:_) <- args = bitsCompareOp "<=" lhs rhs
913 | (LLe (ITFixed IT32)) <- op
914 , (lhs:rhs:_) <- args = bitsCompareOp "<=" lhs rhs
916 | (LLe (ITFixed IT64)) <- op
917 , (lhs:rhs:_) <- args = JSPreOp "+" $ invokeMeth lhs "lesserOrEquals" [rhs]
919 | (LGt (ITFixed IT8)) <- op
920 , (lhs:rhs:_) <- args = bitsCompareOp ">" lhs rhs
922 | (LGt (ITFixed IT16)) <- op
923 , (lhs:rhs:_) <- args = bitsCompareOp ">" lhs rhs
924 | (LGt (ITFixed IT32)) <- op
925 , (lhs:rhs:_) <- args = bitsCompareOp ">" lhs rhs
927 | (LGt (ITFixed IT64)) <- op
928 , (lhs:rhs:_) <- args = JSPreOp "+" $ invokeMeth lhs "greater" [rhs]
930 | (LGe (ITFixed IT8)) <- op
931 , (lhs:rhs:_) <- args = bitsCompareOp ">=" lhs rhs
933 | (LGe (ITFixed IT16)) <- op
934 , (lhs:rhs:_) <- args = bitsCompareOp ">=" lhs rhs
935 | (LGe (ITFixed IT32)) <- op
936 , (lhs:rhs:_) <- args = bitsCompareOp ">=" lhs rhs
938 | (LGe (ITFixed IT64)) <- op
939 , (lhs:rhs:_) <- args = JSPreOp "+" $ invokeMeth lhs "greaterOrEquals" [rhs]
941 | (LUDiv (ITFixed IT8)) <- op
942 , (lhs:rhs:_) <- args =
944 JSBinOp "/" (jsUnPackBits (translateReg lhs)) (jsUnPackBits (translateReg rhs))
947 | (LUDiv (ITFixed IT16)) <- op
948 , (lhs:rhs:_) <- args =
950 JSBinOp "/" (jsUnPackBits (translateReg lhs)) (jsUnPackBits (translateReg rhs))
953 | (LUDiv (ITFixed IT32)) <- op
954 , (lhs:rhs:_) <- args =
956 JSBinOp "/" (jsUnPackBits (translateReg lhs)) (jsUnPackBits (translateReg rhs))
959 | (LUDiv (ITFixed IT64)) <- op
960 , (lhs:rhs:_) <- args = invokeMeth lhs "divide" [rhs]
962 | (LSDiv (ATInt (ITFixed IT8))) <- op
963 , (lhs:rhs:_) <- args =
966 jsUnPackBits $ jsPackSBits8 $ jsUnPackBits (translateReg lhs)
968 jsUnPackBits $ jsPackSBits8 $ jsUnPackBits (translateReg rhs)
972 | (LSDiv (ATInt (ITFixed IT16))) <- op
973 , (lhs:rhs:_) <- args =
976 jsUnPackBits $ jsPackSBits16 $ jsUnPackBits (translateReg lhs)
978 jsUnPackBits $ jsPackSBits16 $ jsUnPackBits (translateReg rhs)
982 | (LSDiv (ATInt (ITFixed IT32))) <- op
983 , (lhs:rhs:_) <- args =
986 jsUnPackBits $ jsPackSBits32 $ jsUnPackBits (translateReg lhs)
988 jsUnPackBits $ jsPackSBits32 $ jsUnPackBits (translateReg rhs)
992 | (LSDiv (ATInt (ITFixed IT64))) <- op
993 , (lhs:rhs:_) <- args = invokeMeth lhs "divide" [rhs]
995 | (LSRem (ATInt (ITFixed IT8))) <- op
996 , (lhs:rhs:_) <- args =
999 jsUnPackBits $ jsPackSBits8 $ jsUnPackBits (translateReg lhs)
1001 jsUnPackBits $ jsPackSBits8 $ jsUnPackBits (translateReg rhs)
1005 | (LSRem (ATInt (ITFixed IT16))) <- op
1006 , (lhs:rhs:_) <- args =
1009 jsUnPackBits $ jsPackSBits16 $ jsUnPackBits (translateReg lhs)
1011 jsUnPackBits $ jsPackSBits16 $ jsUnPackBits (translateReg rhs)
1015 | (LSRem (ATInt (ITFixed IT32))) <- op
1016 , (lhs:rhs:_) <- args =
1019 jsUnPackBits $ jsPackSBits32 $ jsUnPackBits (translateReg lhs)
1021 jsUnPackBits $ jsPackSBits32 $ jsUnPackBits (translateReg rhs)
1025 | (LSRem (ATInt (ITFixed IT64))) <- op
1026 , (lhs:rhs:_) <- args = invokeMeth lhs "mod" [rhs]
1028 | (LCompl (ITFixed IT8)) <- op
1030 jsPackSBits8 $ JSPreOp "~" $ jsUnPackBits (translateReg arg)
1032 | (LCompl (ITFixed IT16)) <- op
1034 jsPackSBits16 $ JSPreOp "~" $ jsUnPackBits (translateReg arg)
1036 | (LCompl (ITFixed IT32)) <- op
1038 jsPackSBits32 $ JSPreOp "~" $ jsUnPackBits (translateReg arg)
1040 | (LCompl (ITFixed IT64)) <- op
1041 , (arg:_) <- args = invokeMeth arg "not" []
1044 , (lhs:rhs:_) <- args = translateBinaryOp "+" lhs rhs
1046 , (lhs:rhs:_) <- args = translateBinaryOp "-" lhs rhs
1048 , (lhs:rhs:_) <- args = translateBinaryOp "*" lhs rhs
1050 , (lhs:rhs:_) <- args = translateBinaryOp "/" lhs rhs
1052 , (lhs:rhs:_) <- args = translateBinaryOp "%" lhs rhs
1054 , (lhs:rhs:_) <- args = translateCompareOp "==" lhs rhs
1056 , (lhs:rhs:_) <- args = translateCompareOp "<" lhs rhs
1058 , (lhs:rhs:_) <- args = translateCompareOp "<=" lhs rhs
1060 , (lhs:rhs:_) <- args = translateCompareOp ">" lhs rhs
1062 , (lhs:rhs:_) <- args = translateCompareOp ">=" lhs rhs
1064 , (lhs:rhs:_) <- args = translateBinaryOp "&" lhs rhs
1066 , (lhs:rhs:_) <- args = translateBinaryOp "|" lhs rhs
1068 , (lhs:rhs:_) <- args = translateBinaryOp "^" lhs rhs
1070 , (lhs:rhs:_) <- args = translateBinaryOp "<<" rhs lhs
1072 , (lhs:rhs:_) <- args = translateBinaryOp ">>" rhs lhs
1074 , (arg:_) <- args = JSPreOp "~" (translateReg arg)
1077 , (lhs:rhs:_) <- args = translateBinaryOp "+" lhs rhs
1079 , (lhs:rhs:_) <- args = translateCompareOp "==" lhs rhs
1081 , (lhs:rhs:_) <- args = translateCompareOp "<" lhs rhs
1083 , (arg:_) <- args = JSProj (translateReg arg) "length"
1084 | (LStrInt ITNative) <- op
1085 , (arg:_) <- args = jsCall "parseInt" [translateReg arg]
1086 | (LIntStr ITNative) <- op
1087 , (arg:_) <- args = jsCall "String" [translateReg arg]
1088 | (LSExt ITNative ITBig) <- op
1089 , (arg:_) <- args = jsBigInt $ jsCall "String" [translateReg arg]
1090 | (LTrunc ITBig ITNative) <- op
1091 , (arg:_) <- args = jsMeth (translateReg arg) "intValue" []
1092 | (LIntStr ITBig) <- op
1093 , (arg:_) <- args = jsMeth (translateReg arg) "toString" []
1094 | (LStrInt ITBig) <- op
1095 , (arg:_) <- args = jsBigInt $ translateReg arg
1097 , (arg:_) <- args = jsCall "String" [translateReg arg]
1099 , (arg:_) <- args = jsCall "parseFloat" [translateReg arg]
1100 | (LIntFloat ITNative) <- op
1101 , (arg:_) <- args = translateReg arg
1102 | (LIntFloat ITBig) <- op
1103 , (arg:_) <- args = jsMeth (translateReg arg) "intValue" []
1104 | (LFloatInt ITNative) <- op
1105 , (arg:_) <- args = translateReg arg
1106 | (LChInt ITNative) <- op
1107 , (arg:_) <- args = jsCall "i$charCode" [translateReg arg]
1108 | (LIntCh ITNative) <- op
1109 , (arg:_) <- args = jsCall "i$fromCharCode" [translateReg arg]
1112 , (arg:_) <- args = jsCall "Math.exp" [translateReg arg]
1114 , (arg:_) <- args = jsCall "Math.log" [translateReg arg]
1116 , (arg:_) <- args = jsCall "Math.sin" [translateReg arg]
1118 , (arg:_) <- args = jsCall "Math.cos" [translateReg arg]
1120 , (arg:_) <- args = jsCall "Math.tan" [translateReg arg]
1122 , (arg:_) <- args = jsCall "Math.asin" [translateReg arg]
1124 , (arg:_) <- args = jsCall "Math.acos" [translateReg arg]
1126 , (arg:_) <- args = jsCall "Math.atan" [translateReg arg]
1128 , (arg:_) <- args = jsCall "Math.sqrt" [translateReg arg]
1130 , (arg:_) <- args = jsCall "Math.floor" [translateReg arg]
1132 , (arg:_) <- args = jsCall "Math.ceil" [translateReg arg]
1134 , (arg:_) <- args = JSPreOp "-" (translateReg arg)
1137 , (lhs:rhs:_) <- args = invokeMeth lhs "concat" [rhs]
1139 , (arg:_) <- args = JSIndex (translateReg arg) (JSNum (JSInt 0))
1141 , (arg:_) <- args = JSProj (translateReg arg) "split('').reverse().join('')"
1143 , (lhs:rhs:_) <- args = JSIndex (translateReg lhs) (translateReg rhs)
1146 let v = translateReg arg in
1147 JSApp (JSProj v "substr") [
1149 JSBinOp "-" (JSProj v "length") (JSNum (JSInt 1))
1152 , (offset:length:string:_) <- args =
1153 let off = translateReg offset
1154 len = translateReg length
1155 str = translateReg string
1156 in JSApp (JSProj str "substr") [
1157 jsCall "Math.max" [JSNum (JSInt 0), off],
1158 jsCall "Math.max" [JSNum (JSInt 0), len]
1162 , (arg:_) <- args = jsCall "i$systemInfo" [translateReg arg]
1163 | LExternal nul <- op
1164 , nul == sUN "prim__null"
1165 , _ <- args = JSNull
1166 | LExternal ex <- op
1167 , ex == sUN "prim__eqPtr"
1168 , [lhs, rhs] <- args = translateCompareOp "==" lhs rhs
1169 | otherwise = JSError $ "Not implemented: " ++ show op
1171 translateBinaryOp :: String -> Reg -> Reg -> JS
1172 translateBinaryOp op lhs rhs =
1173 JSBinOp op (translateReg lhs) (translateReg rhs)
1175 translateCompareOp :: String -> Reg -> Reg -> JS
1176 translateCompareOp op lhs rhs =
1177 JSPreOp "+" $ translateBinaryOp op lhs rhs
1179 bitsBinaryOp :: String -> Reg -> Reg -> JS
1180 bitsBinaryOp op lhs rhs =
1181 JSBinOp op (jsUnPackBits (translateReg lhs)) (jsUnPackBits (translateReg rhs))
1183 bitsCompareOp :: String -> Reg -> Reg -> JS
1184 bitsCompareOp op lhs rhs =
1185 JSPreOp "+" $ bitsBinaryOp op lhs rhs
1187 invokeMeth :: Reg -> String -> [Reg] -> JS
1188 invokeMeth obj meth args =
1189 JSApp (JSProj (translateReg obj) meth) $ map translateReg args
1192jsRESERVE :: CompileInfo -> Int -> JS
1193jsRESERVE _ _ = JSNoop
1196jsSTACK = JSIdent "i$valstack"
1199jsCALLSTACK = JSIdent "i$callstack"
1202jsSTACKBASE = JSIdent "i$valstack_base"
1205jsSTACKTOP = JSIdent "i$valstack_top"
1208jsOLDBASE = JSIdent "oldbase"
1211jsMYOLDBASE = JSIdent "myoldbase"
1214jsRET = JSIdent "i$ret"
1217jsLOC 0 = JSIndex jsSTACK jsSTACKBASE
1218jsLOC n = JSIndex jsSTACK (JSBinOp "+" jsSTACKBASE (JSNum (JSInt n)))
1221jsTOP 0 = JSIndex jsSTACK jsSTACKTOP
1222jsTOP n = JSIndex jsSTACK (JSBinOp "+" jsSTACKTOP (JSNum (JSInt n)))
1225jsPUSH args = JSApp (JSProj jsCALLSTACK "push") args
1228jsPOP = JSApp (JSProj jsCALLSTACK "pop") []
1230translateBC :: CompileInfo -> BC -> JS
1232 | ASSIGN r1 r2 <- bc = jsASSIGN info r1 r2
1233 | ASSIGNCONST r c <- bc = jsASSIGNCONST info r c
1234 | UPDATE r1 r2 <- bc = jsASSIGN info r1 r2
1235 | ADDTOP n <- bc = jsADDTOP info n
1236 | NULL r <- bc = jsNULL info r
1237 | CALL n <- bc = jsCALL info n
1238 | TAILCALL n <- bc = jsTAILCALL info n
1239 | FOREIGNCALL r _ (FStr n) args
1240 <- bc = jsFOREIGN info r n (map fcall args)
1241 | FOREIGNCALL _ _ _ _ <- bc = error "JS FFI call not statically known"
1242 | TOPBASE n <- bc = jsTOPBASE info n
1243 | BASETOP n <- bc = jsBASETOP info n
1244 | STOREOLD <- bc = jsSTOREOLD info
1245 | SLIDE n <- bc = jsSLIDE info n
1246 | REBASE <- bc = jsREBASE info
1247 | RESERVE n <- bc = jsRESERVE info n
1248 | MKCON r _ t rs <- bc = jsMKCON info r t rs
1249 | CASE s r c d <- bc = jsCASE info s r c d
1250 | CONSTCASE r c d <- bc = jsCONSTCASE info r c d
1251 | PROJECT r l a <- bc = jsPROJECT info r l a
1252 | OP r o a <- bc = jsOP info r o a
1253 | ERROR e <- bc = jsERROR info e
1254 | otherwise = JSRaw $ "//" ++ show bc
1255 where fcall (t, arg) = (toFType t, arg)
1258 | i == sUN "JS_IntChar" = ATInt ITChar
1259 | i == sUN "JS_IntNative" = ATInt ITNative
1260toAType t = error (show t ++ " not defined in toAType")
1262toFnType (FApp c [_,_,s,t])
1263 | c == sUN "JS_Fn" = toFnType t
1264toFnType (FApp c [_,_,r])
1265 | c == sUN "JS_FnIO" = FFunctionIO
1266toFnType (FApp c [_,r])
1267 | c == sUN "JS_FnBase" = FFunction
1268toFnType t = error (show t ++ " not defined in toFnType")
1271 | c == sUN "JS_Str" = FString
1272 | c == sUN "JS_Float" = FArith ATFloat
1273 | c == sUN "JS_Ptr" = FPtr
1274 | c == sUN "JS_Unit" = FUnit
1275toFType (FApp c [_,ity])
1276 | c == sUN "JS_IntT" = FArith (toAType ity)
1277toFType (FApp c [_,fty])
1278 | c == sUN "JS_FnT" = toFnType fty
1279toFType t = error (show t ++ " not yet defined in toFType")
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