idris-0.9.9.1: src/IRTS/CodegenJavaScript.hs
{-# LANGUAGE PatternGuards #-}
module IRTS.CodegenJavaScript (codegenJavaScript, JSTarget(..)) where
import Idris.AbsSyntax hiding (TypeCase)
import IRTS.Bytecode
import IRTS.Lang
import IRTS.Simplified
import IRTS.CodegenCommon
import Core.TT
import Paths_idris
import Util.System
import Control.Arrow
import Data.Char
import Data.List
import Data.Maybe
import System.IO
import System.Directory
idrNamespace :: String
idrNamespace = "__IDR__"
idrRTNamespace = "__IDRRT__"
idrLTNamespace = "__IDRLT__"
data JSTarget = Node | JavaScript deriving Eq
data JSType = JSIntTy
| JSStringTy
| JSIntegerTy
| JSFloatTy
| JSCharTy
| JSPtrTy
| JSForgotTy
deriving Eq
data JSNum = JSInt Int
| JSFloat Double
| JSInteger Integer
data JS = JSRaw String
| JSFunction [String] JS
| JSType JSType
| JSSeq [JS]
| JSReturn JS
| JSApp JS [JS]
| JSNew String [JS]
| JSError String
| JSOp String JS JS
| JSProj JS String
| JSVar LVar
| JSNull
| JSThis
| JSTrue
| JSArray [JS]
| JSObject [(String, JS)]
| JSString String
| JSNum JSNum
| JSAssign JS JS
| JSAlloc String (Maybe JS)
| JSIndex JS JS
| JSCond [(JS, JS)]
| JSTernary JS JS JS
compileJS :: JS -> String
compileJS (JSRaw code) =
code
compileJS (JSFunction args body) =
"function("
++ intercalate "," args
++ "){\n"
++ compileJS body
++ "\n}"
compileJS (JSType ty)
| JSIntTy <- ty = idrRTNamespace ++ "Int"
| JSStringTy <- ty = idrRTNamespace ++ "String"
| JSIntegerTy <- ty = idrRTNamespace ++ "Integer"
| JSFloatTy <- ty = idrRTNamespace ++ "Float"
| JSCharTy <- ty = idrRTNamespace ++ "Char"
| JSPtrTy <- ty = idrRTNamespace ++ "Ptr"
| JSForgotTy <- ty = idrRTNamespace ++ "Forgot"
compileJS (JSSeq seq) =
intercalate ";\n" (map compileJS seq)
compileJS (JSReturn val) =
"return " ++ compileJS val
compileJS (JSApp lhs rhs)
| JSFunction {} <- lhs =
concat ["(", compileJS lhs, ")(", args, ")"]
| otherwise =
concat [compileJS lhs, "(", args, ")"]
where args :: String
args = intercalate "," $ map compileJS rhs
compileJS (JSNew name args) =
"new " ++ name ++ "(" ++ intercalate "," (map compileJS args) ++ ")"
compileJS (JSError exc) =
"(function(){throw '" ++ exc ++ "';})()"
compileJS (JSOp op lhs rhs) =
compileJS lhs ++ " " ++ op ++ " " ++ compileJS rhs
compileJS (JSProj obj field)
| JSFunction {} <- obj =
concat ["(", compileJS obj, ").", field]
| otherwise =
compileJS obj ++ '.' : field
compileJS (JSVar var) =
translateVariableName var
compileJS JSNull =
"null"
compileJS JSThis =
"this"
compileJS JSTrue =
"true"
compileJS (JSArray elems) =
"[" ++ intercalate "," (map compileJS elems) ++ "]"
compileJS (JSObject fields) =
"{" ++ intercalate ",\n" (map compileField fields) ++ "}"
where
compileField :: (String, JS) -> String
compileField (name, val) = '\'' : name ++ "' : " ++ compileJS val
compileJS (JSString str) =
show str
compileJS (JSNum num)
| JSInt i <- num = show i
| JSFloat f <- num = show f
| JSInteger i <- num = show i
compileJS (JSAssign lhs rhs) =
compileJS lhs ++ "=" ++ compileJS rhs
compileJS (JSAlloc name val) =
"var " ++ name ++ maybe "" ((" = " ++) . compileJS) val
compileJS (JSIndex lhs rhs) =
compileJS lhs ++ "[" ++ compileJS rhs ++ "]"
compileJS (JSCond branches) =
intercalate " else " $ map createIfBlock branches
where
createIfBlock (cond, e) =
"if (" ++ compileJS cond ++") {\n"
++ "return " ++ compileJS e
++ ";\n}"
compileJS (JSTernary cond true false) =
let c = compileJS cond
t = compileJS true
f = compileJS false in
"(" ++ c ++ ")?(" ++ t ++ "):(" ++ f ++ ")"
jsTailcall :: JS -> JS
jsTailcall call =
jsCall (idrRTNamespace ++ "tailcall") [
JSFunction [] (JSReturn call)
]
jsCall :: String -> [JS] -> JS
jsCall fun = JSApp (JSRaw fun)
jsMeth :: JS -> String -> [JS] -> JS
jsMeth obj meth =
JSApp (JSProj obj meth)
jsInstanceOf :: JS -> JS -> JS
jsInstanceOf = JSOp "instanceof"
jsEq :: JS -> JS -> JS
jsEq = JSOp "=="
jsAnd :: JS -> JS -> JS
jsAnd = JSOp "&&"
jsType :: JS
jsType = JSRaw $ idrRTNamespace ++ "Type"
jsCon :: JS
jsCon = JSRaw $ idrRTNamespace ++ "Con"
jsTag :: JS -> JS
jsTag obj = JSProj obj "tag"
jsTypeTag :: JS -> JS
jsTypeTag obj = JSProj obj "type"
jsBigInt :: JS -> JS
jsBigInt val =
JSApp (JSRaw $ idrRTNamespace ++ "bigInt") [val]
jsVar :: Int -> String
jsVar = ("__var_" ++) . show
jsLet :: String -> JS -> JS -> JS
jsLet name value body =
JSApp (
JSFunction [name] (
JSReturn body
)
) [value]
codegenJavaScript
:: JSTarget
-> [(Name, SDecl)]
-> FilePath
-> OutputType
-> IO ()
codegenJavaScript target definitions filename outputType = do
let (header, runtime) = case target of
Node ->
("#!/usr/bin/env node\n", "-node")
JavaScript ->
("", "-browser")
path <- getDataDir
idrRuntime <- readFile $ path ++ "/js/Runtime-common.js"
tgtRuntime <- readFile $ concat [path, "/js/Runtime", runtime, ".js"]
writeFile filename $ intercalate "\n" $ [ header
, idrRuntime
, tgtRuntime
] ++ functions ++ [mainLoop]
setPermissions filename (emptyPermissions { readable = True
, executable = target == Node
, writable = True
})
where
def :: [(String, SDecl)]
def = map (first translateNamespace) definitions
functions :: [String]
functions = map (compileJS . translateDeclaration) def
mainLoop :: String
mainLoop = compileJS $
JSSeq [ JSAlloc "main" $ Just $ JSFunction [] (
jsTailcall $ jsCall mainFun []
)
, jsCall "main" []
]
mainFun :: String
mainFun = idrNamespace ++ translateName (MN 0 "runMain")
translateIdentifier :: String -> String
translateIdentifier =
replaceReserved . concatMap replaceBadChars
where replaceBadChars :: Char -> String
replaceBadChars c
| ' ' <- c = "_"
| '_' <- c = "__"
| isDigit c = '_' : show (ord c)
| not (isLetter c && isAscii c) = '_' : show (ord c)
| otherwise = [c]
replaceReserved s
| s `elem` reserved = '_' : s
| otherwise = s
reserved = [ "break"
, "case"
, "catch"
, "continue"
, "debugger"
, "default"
, "delete"
, "do"
, "else"
, "finally"
, "for"
, "function"
, "if"
, "in"
, "instanceof"
, "new"
, "return"
, "switch"
, "this"
, "throw"
, "try"
, "typeof"
, "var"
, "void"
, "while"
, "with"
, "class"
, "enum"
, "export"
, "extends"
, "import"
, "super"
, "implements"
, "interface"
, "let"
, "package"
, "private"
, "protected"
, "public"
, "static"
, "yield"
]
translateNamespace :: Name -> String
translateNamespace (UN _) = idrNamespace
translateNamespace (NS _ ns) = idrNamespace ++ concatMap translateIdentifier ns
translateNamespace (MN _ _) = idrNamespace
translateNamespace (SN name) = idrNamespace ++ translateSpecialName name
translateNamespace NErased = idrNamespace
translateName :: Name -> String
translateName (UN name) = 'u' : translateIdentifier name
translateName (NS name _) = 'n' : translateName name
translateName (MN i name) = 'm' : translateIdentifier name ++ show i
translateName (SN name) = 's' : translateSpecialName name
translateName NErased = "e"
translateSpecialName :: SpecialName -> String
translateSpecialName name
| WhereN i m n <- name =
'w' : translateName m ++ translateName n ++ show i
| InstanceN n s <- name =
'i' : translateName n ++ concatMap translateIdentifier s
| ParentN n s <- name =
'p' : translateName n ++ translateIdentifier s
| MethodN n <- name =
'm' : translateName n
| CaseN n <- name =
'c' : translateName n
translateConstant :: Const -> JS
translateConstant (I i) = JSNum (JSInt i)
translateConstant (Fl f) = JSNum (JSFloat f)
translateConstant (Ch c) = JSString [c]
translateConstant (Str s) = JSString s
translateConstant (AType (ATInt ITNative)) = JSType JSIntTy
translateConstant StrType = JSType JSStringTy
translateConstant (AType (ATInt ITBig)) = JSType JSIntegerTy
translateConstant (AType ATFloat) = JSType JSFloatTy
translateConstant (AType (ATInt ITChar)) = JSType JSCharTy
translateConstant PtrType = JSType JSPtrTy
translateConstant Forgot = JSType JSForgotTy
translateConstant (BI i) = jsBigInt $ JSNum (JSInteger i)
translateConstant c =
JSError $ "Unimplemented Constant: " ++ show c
translateDeclaration :: (String, SDecl) -> JS
translateDeclaration (path, SFun name params stackSize body)
| (MN _ "APPLY") <- name
, (SLet var val next) <- body
, (SChkCase cvar cases) <- next =
let lvar = translateVariableName var
lookup = "[" ++ lvar ++ ".tag](fn0,arg0," ++ lvar ++ ")" in
JSSeq [ lookupTable [(var, "chk")] var cases
, jsDecl $ JSFunction ["fn0", "arg0"] (
JSSeq [ JSAlloc "__var_0" (Just $ JSRaw "fn0")
, JSReturn $ jsLet (translateVariableName var) (
translateExpression val
) (JSTernary (
(JSVar var `jsInstanceOf` jsCon) `jsAnd`
(hasProp lookupTableName (translateVariableName var))
) (JSRaw $
lookupTableName ++ lookup
) JSNull
)
]
)
]
| (MN _ "EVAL") <- name
, (SChkCase var cases) <- body =
JSSeq [ lookupTable [] var cases
, jsDecl $ JSFunction ["arg0"] (JSReturn $
JSTernary (
(JSRaw "arg0" `jsInstanceOf` jsCon) `jsAnd`
(hasProp lookupTableName "arg0")
) (JSRaw $ lookupTableName ++ "[arg0.tag](arg0)") (JSRaw "arg0")
)
]
| otherwise =
let fun = translateExpression body in
jsDecl $ jsFun fun
where
hasProp :: String -> String -> JS
hasProp table var =
jsMeth (JSRaw table) "hasOwnProperty" [JSRaw $ var ++ ".tag"]
caseFun :: [(LVar, String)] -> LVar -> SAlt -> JS
caseFun aux var cse =
jsFunAux aux (translateCase (Just (translateVariableName var)) cse)
getTag :: SAlt -> Maybe String
getTag (SConCase _ tag _ _ _) = Just $ show tag
getTag _ = Nothing
lookupTableName :: String
lookupTableName = idrLTNamespace ++ translateName name
lookupTable :: [(LVar, String)] -> LVar -> [SAlt] -> JS
lookupTable aux var cases =
JSAlloc lookupTableName $ Just (
JSObject $ catMaybes $ map (lookupEntry aux var) cases
)
where
lookupEntry :: [(LVar, String)] -> LVar -> SAlt -> Maybe (String, JS)
lookupEntry aux var alt = do
tag <- getTag alt
return (tag, caseFun aux var alt)
jsDecl :: JS -> JS
jsDecl = JSAlloc (path ++ translateName name) . Just
jsFun body = jsFunAux [] body
jsFunAux :: [(LVar, String)] -> JS -> JS
jsFunAux aux body =
JSFunction (p ++ map snd aux) (
JSSeq $
zipWith assignVar [0..] p ++
map allocVar [numP .. (numP + stackSize - 1)] ++
map assignAux aux ++
[JSReturn body]
)
where
numP :: Int
numP = length params
allocVar :: Int -> JS
allocVar n = JSAlloc (jsVar n) Nothing
assignVar :: Int -> String -> JS
assignVar n s = JSAlloc (jsVar n) (Just $ JSRaw s)
assignAux :: (LVar, String) -> JS
assignAux (var, val) =
JSAssign (JSRaw $ translateVariableName var) (JSRaw val)
p :: [String]
p = map translateName params
translateVariableName :: LVar -> String
translateVariableName (Loc i) =
jsVar i
translateExpression :: SExp -> JS
translateExpression (SLet name value body) =
jsLet (translateVariableName name) (
translateExpression value
) (translateExpression body)
translateExpression (SConst cst) =
translateConstant cst
translateExpression (SV var) =
JSVar var
translateExpression (SApp tc name vars)
| False <- tc =
jsTailcall $ translateFunctionCall name vars
| True <- tc =
JSNew (idrRTNamespace ++ "Tailcall") [JSFunction [] (
JSReturn $ translateFunctionCall name vars
)]
where
translateFunctionCall name vars =
jsCall (translateNamespace name ++ translateName name) (map JSVar vars)
translateExpression (SOp op vars)
| LNoOp <- op = JSVar (last vars)
| (LZExt _ ITBig) <- op = jsBigInt $ JSVar (last vars)
| (LPlus (ATInt ITBig)) <- op
, (lhs:rhs:_) <- vars = invokeMeth lhs "add" [rhs]
| (LMinus (ATInt ITBig)) <- op
, (lhs:rhs:_) <- vars = invokeMeth lhs "minus" [rhs]
| (LTimes (ATInt ITBig)) <- op
, (lhs:rhs:_) <- vars = invokeMeth lhs "times" [rhs]
| (LSDiv (ATInt ITBig)) <- op
, (lhs:rhs:_) <- vars = invokeMeth lhs "divide" [rhs]
| (LSRem (ATInt ITBig)) <- op
, (lhs:rhs:_) <- vars = invokeMeth lhs "mod" [rhs]
| (LEq (ATInt ITBig)) <- op
, (lhs:rhs:_) <- vars = invokeMeth lhs "equals" [rhs]
| (LSLt (ATInt ITBig)) <- op
, (lhs:rhs:_) <- vars = invokeMeth lhs "lesser" [rhs]
| (LSLe (ATInt ITBig)) <- op
, (lhs:rhs:_) <- vars = invokeMeth lhs "lesserOrEquals" [rhs]
| (LSGt (ATInt ITBig)) <- op
, (lhs:rhs:_) <- vars = invokeMeth lhs "greater" [rhs]
| (LSGe (ATInt ITBig)) <- op
, (lhs:rhs:_) <- vars = invokeMeth lhs "greaterOrEquals" [rhs]
| (LPlus ATFloat) <- op
, (lhs:rhs:_) <- vars = translateBinaryOp "+" lhs rhs
| (LMinus ATFloat) <- op
, (lhs:rhs:_) <- vars = translateBinaryOp "-" lhs rhs
| (LTimes ATFloat) <- op
, (lhs:rhs:_) <- vars = translateBinaryOp "*" lhs rhs
| (LSDiv ATFloat) <- op
, (lhs:rhs:_) <- vars = translateBinaryOp "/" lhs rhs
| (LEq ATFloat) <- op
, (lhs:rhs:_) <- vars = translateBinaryOp "==" lhs rhs
| (LSLt ATFloat) <- op
, (lhs:rhs:_) <- vars = translateBinaryOp "<" lhs rhs
| (LSLe ATFloat) <- op
, (lhs:rhs:_) <- vars = translateBinaryOp "<=" lhs rhs
| (LSGt ATFloat) <- op
, (lhs:rhs:_) <- vars = translateBinaryOp ">" lhs rhs
| (LSGe ATFloat) <- op
, (lhs:rhs:_) <- vars = translateBinaryOp ">=" lhs rhs
| (LPlus _) <- op
, (lhs:rhs:_) <- vars = translateBinaryOp "+" lhs rhs
| (LMinus _) <- op
, (lhs:rhs:_) <- vars = translateBinaryOp "-" lhs rhs
| (LTimes _) <- op
, (lhs:rhs:_) <- vars = translateBinaryOp "*" lhs rhs
| (LSDiv _) <- op
, (lhs:rhs:_) <- vars = translateBinaryOp "/" lhs rhs
| (LSRem _) <- op
, (lhs:rhs:_) <- vars = translateBinaryOp "%" lhs rhs
| (LEq _) <- op
, (lhs:rhs:_) <- vars = translateBinaryOp "==" lhs rhs
| (LSLt _) <- op
, (lhs:rhs:_) <- vars = translateBinaryOp "<" lhs rhs
| (LSLe _) <- op
, (lhs:rhs:_) <- vars = translateBinaryOp "<=" lhs rhs
| (LSGt _) <- op
, (lhs:rhs:_) <- vars = translateBinaryOp ">" lhs rhs
| (LSGe _) <- op
, (lhs:rhs:_) <- vars = translateBinaryOp ">=" lhs rhs
| (LAnd _) <- op
, (lhs:rhs:_) <- vars = translateBinaryOp "&" lhs rhs
| (LOr _) <- op
, (lhs:rhs:_) <- vars = translateBinaryOp "|" lhs rhs
| (LXOr _) <- op
, (lhs:rhs:_) <- vars = translateBinaryOp "^" lhs rhs
| (LSHL _) <- op
, (lhs:rhs:_) <- vars = translateBinaryOp "<<" rhs lhs
| (LASHR _) <- op
, (lhs:rhs:_) <- vars = translateBinaryOp ">>" rhs lhs
| (LCompl _) <- op
, (arg:_) <- vars = JSRaw $ '~' : translateVariableName arg
| LStrConcat <- op
, (lhs:rhs:_) <- vars = translateBinaryOp "+" lhs rhs
| LStrEq <- op
, (lhs:rhs:_) <- vars = translateBinaryOp "==" lhs rhs
| LStrLt <- op
, (lhs:rhs:_) <- vars = translateBinaryOp "<" lhs rhs
| LStrLen <- op
, (arg:_) <- vars = JSProj (JSVar arg) "length"
| (LStrInt ITNative) <- op
, (arg:_) <- vars = jsCall "parseInt" [JSVar arg]
| (LIntStr ITNative) <- op
, (arg:_) <- vars = jsCall "String" [JSVar arg]
| (LSExt ITNative ITBig) <- op
, (arg:_) <- vars = jsBigInt $ JSVar arg
| (LTrunc ITBig ITNative) <- op
, (arg:_) <- vars = jsMeth (JSVar arg) "valueOf" []
| (LIntStr ITBig) <- op
, (arg:_) <- vars = jsMeth (JSVar arg) "toString" []
| (LStrInt ITBig) <- op
, (arg:_) <- vars = jsBigInt $ JSVar arg
| LFloatStr <- op
, (arg:_) <- vars = jsCall "String" [JSVar arg]
| LStrFloat <- op
, (arg:_) <- vars = jsCall "parseFloat" [JSVar arg]
| (LIntFloat ITNative) <- op
, (arg:_) <- vars = JSVar arg
| (LFloatInt ITNative) <- op
, (arg:_) <- vars = JSVar arg
| (LChInt ITNative) <- op
, (arg:_) <- vars = JSProj (JSVar arg) "charCodeAt(0)"
| (LIntCh ITNative) <- op
, (arg:_) <- vars = jsCall "String.fromCharCode" [JSVar arg]
| LFExp <- op
, (arg:_) <- vars = jsCall "Math.exp" [JSVar arg]
| LFLog <- op
, (arg:_) <- vars = jsCall "Math.log" [JSVar arg]
| LFSin <- op
, (arg:_) <- vars = jsCall "Math.sin" [JSVar arg]
| LFCos <- op
, (arg:_) <- vars = jsCall "Math.cos" [JSVar arg]
| LFTan <- op
, (arg:_) <- vars = jsCall "Math.tan" [JSVar arg]
| LFASin <- op
, (arg:_) <- vars = jsCall "Math.asin" [JSVar arg]
| LFACos <- op
, (arg:_) <- vars = jsCall "Math.acos" [JSVar arg]
| LFATan <- op
, (arg:_) <- vars = jsCall "Math.atan" [JSVar arg]
| LFSqrt <- op
, (arg:_) <- vars = jsCall "Math.sqrt" [JSVar arg]
| LFFloor <- op
, (arg:_) <- vars = jsCall "Math.floor" [JSVar arg]
| LFCeil <- op
, (arg:_) <- vars = jsCall "Math.ceil" [JSVar arg]
| LStrCons <- op
, (lhs:rhs:_) <- vars = translateBinaryOp "+" lhs rhs
| LStrHead <- op
, (arg:_) <- vars = JSIndex (JSVar arg) (JSRaw "0")
| LStrRev <- op
, (arg:_) <- vars = JSProj (JSVar arg) "split('').reverse().join('')"
| LStrIndex <- op
, (lhs:rhs:_) <- vars = JSIndex (JSVar lhs) (JSVar rhs)
| LStrTail <- op
, (arg:_) <- vars = let v = translateVariableName arg in
JSRaw $ v ++ ".substr(1," ++ v ++ ".length-1)"
where
translateBinaryOp :: String -> LVar -> LVar -> JS
translateBinaryOp f lhs rhs = JSOp f (JSVar lhs) (JSVar rhs)
invokeMeth :: LVar -> String -> [LVar] -> JS
invokeMeth obj meth args = jsMeth (JSVar obj) meth (map JSVar args)
translateExpression (SError msg) =
JSError msg
translateExpression (SForeign _ _ "putStr" [(FString, var)]) =
jsCall (idrRTNamespace ++ "print") [JSVar var]
translateExpression (SForeign _ _ fun args) =
ffi fun (map generateWrapper args)
where
generateWrapper (ffunc, name)
| FFunction <- ffunc =
idrRTNamespace ++ "ffiWrap(" ++ translateVariableName name ++ ")"
| FFunctionIO <- ffunc =
idrRTNamespace ++ "ffiWrap(" ++ translateVariableName name ++ ")"
generateWrapper (_, name) =
translateVariableName name
translateExpression patterncase
| (SChkCase var cases) <- patterncase = caseHelper var cases "chk"
| (SCase var cases) <- patterncase = caseHelper var cases "cse"
where
caseHelper var cases param =
JSApp (JSFunction [param] (
JSCond $ map (expandCase param . translateCaseCond param) cases
)) [JSVar var]
expandCase :: String -> (Cond, JS) -> (JS, JS)
expandCase _ (RawCond cond, branch) = (cond, branch)
expandCase _ (CaseCond DefaultCase, branch) = (JSTrue , branch)
expandCase var (CaseCond caseTy, branch)
| ConCase tag <- caseTy =
let checkCon = JSRaw var `jsInstanceOf` jsCon
checkTag = (JSRaw $ show tag) `jsEq` jsTag (JSRaw var) in
(checkCon `jsAnd` checkTag, branch)
| TypeCase ty <- caseTy =
let checkTy = JSRaw var `jsInstanceOf` jsType
checkTag = jsTypeTag (JSRaw var) `jsEq` JSType ty in
(checkTy `jsAnd` checkTag, branch)
translateExpression (SCon i name vars) =
JSNew (idrRTNamespace ++ "Con") [ JSRaw $ show i
, JSArray $ map JSVar vars
]
translateExpression (SUpdate var e) =
JSAssign (JSVar var) (translateExpression e)
translateExpression (SProj var i) =
JSIndex (JSProj (JSVar var) "vars") (JSRaw $ show i)
translateExpression SNothing = JSNull
translateExpression e =
JSError $ "Not yet implemented: " ++ filter (/= '\'') (show e)
data FFI = FFICode Char | FFIArg Int | FFIError String
ffi :: String -> [String] -> JS
ffi code args = let parsed = ffiParse code in
case ffiError parsed of
Just err -> JSError err
Nothing -> JSRaw $ renderFFI parsed args
where
ffiParse :: String -> [FFI]
ffiParse "" = []
ffiParse ['%'] = [FFIError "Invalid positional argument"]
ffiParse ('%':'%':ss) = FFICode '%' : ffiParse ss
ffiParse ('%':s:ss)
| isDigit s =
FFIArg (read $ s : takeWhile isDigit ss) : ffiParse (dropWhile isDigit ss)
| otherwise =
[FFIError "Invalid positional argument"]
ffiParse (s:ss) = FFICode s : ffiParse ss
ffiError :: [FFI] -> Maybe String
ffiError [] = Nothing
ffiError ((FFIError s):xs) = Just s
ffiError (x:xs) = ffiError xs
renderFFI :: [FFI] -> [String] -> String
renderFFI [] _ = ""
renderFFI ((FFICode c) : fs) args = c : renderFFI fs args
renderFFI ((FFIArg i) : fs) args
| i < length args && i >= 0 = args !! i ++ renderFFI fs args
| otherwise = "Argument index out of bounds"
data CaseType = ConCase Int
| TypeCase JSType
| DefaultCase
deriving Eq
data Cond = CaseCond CaseType
| RawCond JS
translateCaseCond :: String -> SAlt -> (Cond, JS)
translateCaseCond _ cse@(SDefaultCase _) =
(CaseCond DefaultCase, translateCase Nothing cse)
translateCaseCond var cse@(SConstCase ty _)
| StrType <- ty = matchHelper JSStringTy
| PtrType <- ty = matchHelper JSPtrTy
| Forgot <- ty = matchHelper JSForgotTy
| (AType ATFloat) <- ty = matchHelper JSFloatTy
| (AType (ATInt ITBig)) <- ty = matchHelper JSIntegerTy
| (AType (ATInt ITNative)) <- ty = matchHelper JSIntTy
| (AType (ATInt ITChar)) <- ty = matchHelper JSCharTy
where
matchHelper :: JSType -> (Cond, JS)
matchHelper ty = (CaseCond $ TypeCase ty, translateCase Nothing cse)
translateCaseCond var cse@(SConstCase cst@(BI _) _) =
let cond = jsMeth (JSRaw var) "equals" [translateConstant cst] in
(RawCond cond, translateCase Nothing cse)
translateCaseCond var cse@(SConstCase cst _) =
let cond = JSRaw var `jsEq` translateConstant cst in
(RawCond cond, translateCase Nothing cse)
translateCaseCond var cse@(SConCase _ tag _ _ _) =
(CaseCond $ ConCase tag, translateCase (Just var) cse)
translateCase :: Maybe String -> SAlt -> JS
translateCase _ (SDefaultCase e) = translateExpression e
translateCase _ (SConstCase _ e) = translateExpression e
translateCase (Just var) (SConCase a _ _ vars e) =
let params = map jsVar [a .. (a + length vars)] in
jsMeth (JSFunction params (JSReturn $ translateExpression e)) "apply" [
JSThis, JSProj (JSRaw var) "vars"
]