@@ -30,31 +30,33 @@ import Language.PureScript.Scope
3030import Language.PureScript.Declarations
3131import Language.PureScript.Pretty.Common
3232import Language.PureScript.CodeGen.Monad
33+import Language.PureScript.Options
3334import Language.PureScript.CodeGen.JS.AST as AST
3435import Language.PureScript.TypeChecker.Monad (NameKind(..))
36+import Language.PureScript.Types
3537
36−moduleToJs :: Module -> Environment -> [JS]
37−moduleToJs (Module pname@(ProperName name) decls) env =
38+moduleToJs :: Options -> Module -> Environment -> [JS]
39+moduleToJs opts (Module pname@(ProperName name) decls) env =
3840 [ JSVariableIntroduction (Ident name) Nothing
3941 , JSApp (JSFunction Nothing [Ident name]
40− (JSBlock (concat $ mapMaybe (\decl -> declToJs (ModuleName pname) decl env) decls)))
42+ (JSBlock (concat $ mapMaybe (\decl -> declToJs opts (ModuleName pname) decl env) decls)))
4143 [JSAssignment (JSAssignVariable (Ident name))
4244 (JSBinary Or (JSVar (Ident name)) (JSObjectLiteral []))]
4345 ]
4446
45−declToJs :: ModuleName -> Declaration -> Environment -> Maybe [JS]
46−declToJs mp (ValueDeclaration ident _ _ val) e =
47− Just [ JSVariableIntroduction ident (Just (valueToJs mp e val)),
47+declToJs :: Options -> ModuleName -> Declaration -> Environment -> Maybe [JS]
48+declToJs opts mp (ValueDeclaration ident _ _ val) e =
49+ Just [ JSVariableIntroduction ident (Just (valueToJs opts mp e val)),
4850 setProperty (identToJs ident) (JSVar ident) mp ]
49−declToJs mp (BindingGroupDeclaration vals) e =
51+declToJs opts mp (BindingGroupDeclaration vals) e =
5052 Just $ concatMap (\(ident, val) ->
51− [ JSVariableIntroduction ident (Just (valueToJs mp e val)),
53+ [ JSVariableIntroduction ident (Just (valueToJs opts mp e val)),
5254 setProperty (identToJs ident) (JSVar ident) mp ]
5355 ) vals
54−declToJs mp (ExternMemberDeclaration member ident _) _ =
56+declToJs _ mp (ExternMemberDeclaration member ident _) _ =
5557 Just [ JSFunction (Just ident) [Ident "value"] (JSBlock [JSReturn (JSAccessor member (JSVar (Ident "value")))]),
5658 setProperty (show ident) (JSVar ident) mp ]
57−declToJs mp (DataDeclaration _ _ ctors) _ =
59+declToJs _ mp (DataDeclaration _ _ ctors) _ =
5860 Just $ flip concatMap ctors $ \(pn@(ProperName ctor), maybeTy) ->
5961 let
6062 ctorJs =
@@ -65,31 +67,61 @@ declToJs mp (DataDeclaration _ _ ctors) _ =
6567 (JSObjectLiteral [ ("ctor", JSStringLiteral (show (Qualified (Just mp) pn)))
6668 , ("value", JSVar (Ident "value")) ])])
6769 in [ ctorJs, setProperty ctor (JSVar (Ident ctor)) mp ]
68−declToJs _ _ _ = Nothing
70+declToJs _ _ _ _ = Nothing
6971
7072setProperty :: String -> JS -> ModuleName -> JS
7173setProperty prop val (ModuleName (ProperName moduleName)) = JSAssignment (JSAssignProperty prop (JSAssignVariable (Ident moduleName))) val
7274
73−valueToJs :: ModuleName -> Environment -> Value -> JS
74−valueToJs _ _ (NumericLiteral n) = JSNumericLiteral n
75−valueToJs _ _ (StringLiteral s) = JSStringLiteral s
76−valueToJs _ _ (BooleanLiteral b) = JSBooleanLiteral b
77−valueToJs m e (ArrayLiteral xs) = JSArrayLiteral (map (valueToJs m e) xs)
78−valueToJs m e (ObjectLiteral ps) = JSObjectLiteral (map (second (valueToJs m e)) ps)
79−valueToJs m e (ObjectUpdate o ps) = JSApp (JSAccessor "extend" (JSVar (Ident "Object"))) [ valueToJs m e o, JSObjectLiteral (map (second (valueToJs m e)) ps)]
80−valueToJs _ _ (Constructor name) = qualifiedToJS runProperName name
81−valueToJs m e (Block sts) = JSApp (JSFunction Nothing [] (JSBlock (map (statementToJs m e) sts))) []
82−valueToJs m e (Case values binders) = runGen (bindersToJs m e binders (map (valueToJs m e) values))
83−valueToJs m e (IfThenElse cond th el) = JSConditional (valueToJs m e cond) (valueToJs m e th) (valueToJs m e el)
84−valueToJs m e (Accessor prop val) = JSAccessor prop (valueToJs m e val)
85−valueToJs m e (Indexer index val) = JSIndexer (valueToJs m e index) (valueToJs m e val)
86−valueToJs m e (App val args) = JSApp (valueToJs m e val) (map (valueToJs m e) args)
87−valueToJs m e (Abs args val) = JSFunction Nothing args (JSBlock [JSReturn (valueToJs m e val)])
88−valueToJs m e (Unary op val) = JSUnary op (valueToJs m e val)
89−valueToJs m e (Binary op v1 v2) = JSBinary op (valueToJs m e v1) (valueToJs m e v2)
90−valueToJs m e (Var ident) = varToJs m e ident
91−valueToJs m e (TypedValue val _) = valueToJs m e val
92−valueToJs _ _ _ = error "Invalid argument to valueToJs"
75+valueToJs :: Options -> ModuleName -> Environment -> Value -> JS
76+valueToJs _ _ _ (NumericLiteral n) = JSNumericLiteral n
77+valueToJs _ _ _ (StringLiteral s) = JSStringLiteral s
78+valueToJs _ _ _ (BooleanLiteral b) = JSBooleanLiteral b
79+valueToJs opts m e (ArrayLiteral xs) = JSArrayLiteral (map (valueToJs opts m e) xs)
80+valueToJs opts m e (ObjectLiteral ps) = JSObjectLiteral (map (second (valueToJs opts m e)) ps)
81+valueToJs opts m e (ObjectUpdate o ps) = JSApp (JSAccessor "extend" (JSVar (Ident "Object"))) [ valueToJs opts m e o, JSObjectLiteral (map (second (valueToJs opts m e)) ps)]
82+valueToJs _ _ _ (Constructor name) = qualifiedToJS runProperName name
83+valueToJs opts m e (Block sts) = JSApp (JSFunction Nothing [] (JSBlock (map (statementToJs opts m e) sts))) []
84+valueToJs opts m e (Case values binders) = runGen (bindersToJs opts m e binders (map (valueToJs opts m e) values))
85+valueToJs opts m e (IfThenElse cond th el) = JSConditional (valueToJs opts m e cond) (valueToJs opts m e th) (valueToJs opts m e el)
86+valueToJs opts m e (Accessor prop val) = JSAccessor prop (valueToJs opts m e val)
87+valueToJs opts m e (Indexer index val) = JSIndexer (valueToJs opts m e index) (valueToJs opts m e val)
88+valueToJs opts m e (App val args) = JSApp (valueToJs opts m e val) (map (valueToJs opts m e) args)
89+valueToJs opts m e (Abs args val) = JSFunction Nothing args (JSBlock [JSReturn (valueToJs opts m e val)])
90+valueToJs opts m e (TypedValue (Abs args val) ty) | optionsPerformRuntimeTypeChecks opts = JSFunction Nothing args (JSBlock $ runtimeTypeChecks args ty ++ [JSReturn (valueToJs opts m e val)])
91+valueToJs opts m e (Unary op val) = JSUnary op (valueToJs opts m e val)
92+valueToJs opts m e (Binary op v1 v2) = JSBinary op (valueToJs opts m e v1) (valueToJs opts m e v2)
93+valueToJs _ m e (Var ident) = varToJs m e ident
94+valueToJs opts m e (TypedValue val _) = valueToJs opts m e val
95+valueToJs _ _ _ _ = error "Invalid argument to valueToJs"
96+
97+runtimeTypeChecks :: [Ident] -> Type -> [JS]
98+runtimeTypeChecks args ty =
99+ let
100+ argTys = getFunctionArgumentTypes ty
101+ in
102+ concat $ zipWith argumentCheck (map JSVar args) argTys
103+ where
104+ getFunctionArgumentTypes :: Type -> [Type]
105+ getFunctionArgumentTypes (Function funArgs _) = funArgs
106+ getFunctionArgumentTypes (ForAll _ ty') = getFunctionArgumentTypes ty'
107+ getFunctionArgumentTypes _ = []
108+ argumentCheck :: JS -> Type -> [JS]
109+ argumentCheck val Number = [typeCheck val "number"]
110+ argumentCheck val String = [typeCheck val "string"]
111+ argumentCheck val Boolean = [typeCheck val "boolean"]
112+ argumentCheck val (Array _) = [arrayCheck val]
113+ argumentCheck val (Object row) =
114+ let
115+ (pairs, _) = rowToList row
116+ in
117+ typeCheck val "object" : concatMap (\(prop, ty') -> argumentCheck (JSAccessor prop val) ty') pairs
118+ argumentCheck val (Function _ _) = [typeCheck val "function"]
119+ argumentCheck val (ForAll _ ty') = argumentCheck val ty'
120+ argumentCheck _ _ = []
121+ typeCheck :: JS -> String -> JS
122+ typeCheck js ty' = JSIfElse (JSBinary NotEqualTo (JSTypeOf js) (JSStringLiteral ty')) (JSBlock [JSThrow (JSStringLiteral $ ty' ++ " expected")]) Nothing
123+ arrayCheck :: JS -> JS
124+ arrayCheck js = JSIfElse (JSUnary Not (JSApp (JSAccessor "isArray" (JSVar (Ident "Array"))) [js])) (JSBlock [JSThrow (JSStringLiteral "Array expected")]) Nothing
93125
94126varToJs :: ModuleName -> Environment -> Qualified Ident -> JS
95127varToJs m e qual@(Qualified _ ident) = case M.lookup (qualify m qual) (names e) of
@@ -107,17 +139,17 @@ qualifiedToJS :: (a -> String) -> Qualified a -> JS
107139qualifiedToJS f (Qualified (Just (ModuleName (ProperName m))) a) = JSAccessor (f a) (JSVar (Ident m))
108140qualifiedToJS f (Qualified Nothing a) = JSVar (Ident (f a))
109141
110−bindersToJs :: ModuleName -> Environment -> [([Binder], Maybe Guard, Value)] -> [JS] -> Gen JS
111−bindersToJs m e binders vals = do
142+bindersToJs :: Options -> ModuleName -> Environment -> [([Binder], Maybe Guard, Value)] -> [JS] -> Gen JS
143+bindersToJs opts m e binders vals = do
112144 setNextName $ firstUnusedName (binders, vals)
113145 valNames <- replicateM (length vals) fresh
114− jss <- forM binders $ \(bs, grd, result) -> go valNames [JSReturn (valueToJs m e result)] bs grd
146+ jss <- forM binders $ \(bs, grd, result) -> go valNames [JSReturn (valueToJs opts m e result)] bs grd
115147 return $ JSApp (JSFunction Nothing (map Ident valNames) (JSBlock (concat jss ++ [JSThrow (JSStringLiteral "Failed pattern match")])))
116148 vals
117149 where
118150 go :: [String] -> [JS] -> [Binder] -> Maybe Guard -> Gen [JS]
119151 go _ done [] Nothing = return done
120− go _ done [] (Just cond) = return [JSIfElse (valueToJs m e cond) (JSBlock done) Nothing]
152+ go _ done [] (Just cond) = return [JSIfElse (valueToJs opts m e cond) (JSBlock done) Nothing]
121153 go (v:vs) done' (b:bs) grd = do
122154 done'' <- go vs done' bs grd
123155 binderToJs m e v done'' b
@@ -175,16 +207,16 @@ binderToJs m e varName done (NamedBinder ident binder) = do
175207 js <- binderToJs m e varName done binder
176208 return (JSVariableIntroduction ident (Just (JSVar (Ident varName))) : js)
177209
178−statementToJs :: ModuleName -> Environment -> Statement -> JS
179−statementToJs m e (VariableIntroduction ident value) = JSVariableIntroduction ident (Just (valueToJs m e value))
180−statementToJs m e (Assignment target value) = JSAssignment (JSAssignVariable target) (valueToJs m e value)
181−statementToJs m e (While cond sts) = JSWhile (valueToJs m e cond) (JSBlock (map (statementToJs m e) sts))
182−statementToJs m e (For ident start end sts) = JSFor ident (valueToJs m e start) (valueToJs m e end) (JSBlock (map (statementToJs m e) sts))
183−statementToJs m e (If ifst) = ifToJs ifst
210+statementToJs :: Options -> ModuleName -> Environment -> Statement -> JS
211+statementToJs opts m e (VariableIntroduction ident value) = JSVariableIntroduction ident (Just (valueToJs opts m e value))
212+statementToJs opts m e (Assignment target value) = JSAssignment (JSAssignVariable target) (valueToJs opts m e value)
213+statementToJs opts m e (While cond sts) = JSWhile (valueToJs opts m e cond) (JSBlock (map (statementToJs opts m e) sts))
214+statementToJs opts m e (For ident start end sts) = JSFor ident (valueToJs opts m e start) (valueToJs opts m e end) (JSBlock (map (statementToJs opts m e) sts))
215+statementToJs opts m e (If ifst) = ifToJs ifst
184216 where
185217 ifToJs :: IfStatement -> JS
186− ifToJs (IfStatement cond thens elses) = JSIfElse (valueToJs m e cond) (JSBlock (map (statementToJs m e) thens)) (fmap elseToJs elses)
218+ ifToJs (IfStatement cond thens elses) = JSIfElse (valueToJs opts m e cond) (JSBlock (map (statementToJs opts m e) thens)) (fmap elseToJs elses)
187219 elseToJs :: ElseStatement -> JS
188− elseToJs (Else sts) = JSBlock (map (statementToJs m e) sts)
220+ elseToJs (Else sts) = JSBlock (map (statementToJs opts m e) sts)
189221 elseToJs (ElseIf elif) = ifToJs elif
190−statementToJs m e (Return value) = JSReturn (valueToJs m e value)
222+statementToJs opts m e (Return value) = JSReturn (valueToJs opts m e value)