@@ -23,26 +23,25 @@ module Language.PureScript.CodeGen.JS (
2323 isIdent
2424) where
2525
26−import Data.Maybe (fromJust, fromMaybe, mapMaybe)
26+import Data.Maybe (catMaybes, fromJust, fromMaybe)
2727import Data.Function (on)
2828import Data.List (nub, (\\))
2929
30−import Control.Arrow (second)
3130import Control.Monad (replicateM, forM)
31+import Control.Applicative
3232
3333import qualified Data.Map as M
3434
3535import Language.PureScript.Names
3636import Language.PureScript.Declarations
37−import Language.PureScript.CodeGen.Monad
3837import Language.PureScript.Options
3938import Language.PureScript.CodeGen.JS.AST as AST
4039import Language.PureScript.Types
4140import Language.PureScript.Optimizer
4241import Language.PureScript.CodeGen.Common
4342import Language.PureScript.Environment
44−
45−import qualified Language.PureScript.Scope as S
43+import Language.PureScript.Supply
44+import Language.PureScript.Traversals (sndM)
4645
4746-- |
4847-- Different types of modules which are supported
@@ -53,22 +52,23 @@ data ModuleType = CommonJS | Globals
5352-- Generate code in the simplified Javascript intermediate representation for all declarations in a
5453-- module.
5554--
56−moduleToJs :: ModuleType -> Options -> Module -> Environment -> [JS]
57−moduleToJs mt opts (Module name decls (Just exps)) env = case mt of
58− CommonJS -> moduleBody ++ [JSAssignment (JSAccessor "exports" (JSVar "module")) moduleExports]
59− Globals | not isModuleEmpty ->
60− [ JSVariableIntroduction (fromJust (optionsBrowserNamespace opts))
61− (Just (JSBinary Or (JSVar (fromJust (optionsBrowserNamespace opts))) (JSObjectLiteral [])) )
62− , JSAssignment (JSAccessor (moduleNameToJs name) (JSVar (fromJust (optionsBrowserNamespace opts))))
63− (JSApp (JSFunction Nothing [] (JSBlock (moduleBody ++ [JSReturn moduleExports]))) [])
64− ]
65− _ -> []
66− where
67− isModuleEmpty = null jsDecls
68− moduleBody = JSStringLiteral "use strict" : jsImports ++ jsDecls
69− moduleExports = JSObjectLiteral $ concatMap exportToJs exps
70− jsDecls = (concat $ mapMaybe (\decl -> fmap (map $ optimize opts) $ declToJs opts name decl env) decls)
71− jsImports = map (importToJs mt opts) . (\\ [name]) . nub $ concatMap imports decls
55+moduleToJs :: (Functor m, Applicative m, Monad m) => ModuleType -> Options -> Module -> Environment -> SupplyT m [JS]
56+moduleToJs mt opts (Module name decls (Just exps)) env = do
57+ let jsImports = map (importToJs mt opts) . (\\ [name]) . nub $ concatMap imports decls
58+ jsDecls <- mapM (\decl -> declToJs opts name decl env) decls
59+ let optimized = concat $ map (map $ optimize opts) $ catMaybes jsDecls
60+ let isModuleEmpty = null optimized
61+ let moduleBody = JSStringLiteral "use strict" : jsImports ++ optimized
62+ let moduleExports = JSObjectLiteral $ concatMap exportToJs exps
63+ return $ case mt of
64+ CommonJS -> moduleBody ++ [JSAssignment (JSAccessor "exports" (JSVar "module")) moduleExports]
65+ Globals | not isModuleEmpty ->
66+ [ JSVariableIntroduction (fromJust (optionsBrowserNamespace opts))
67+ (Just (JSBinary Or (JSVar (fromJust (optionsBrowserNamespace opts))) (JSObjectLiteral [])) )
68+ , JSAssignment (JSAccessor (moduleNameToJs name) (JSVar (fromJust (optionsBrowserNamespace opts))))
69+ (JSApp (JSFunction Nothing [] (JSBlock (moduleBody ++ [JSReturn moduleExports]))) [])
70+ ]
71+ _ -> []
7272moduleToJs _ _ _ _ = error "Exports should have been elaborated in name desugaring"
7373
7474importToJs :: ModuleType -> Options -> ModuleName -> JS
@@ -91,14 +91,17 @@ imports =
9191-- |
9292-- Generate code in the simplified Javascript intermediate representation for a declaration
9393--
94−declToJs :: Options -> ModuleName -> Declaration -> Environment -> Maybe [JS]
95−declToJs opts mp (ValueDeclaration ident _ _ _ val) e =
96− Just [JSVariableIntroduction (identToJs ident) (Just (valueToJs opts mp e val))]
97−declToJs opts mp (BindingGroupDeclaration vals) e =
98− Just $ flip concatMap vals $ \(ident, _, val) ->
99− [JSVariableIntroduction (identToJs ident) (Just (valueToJs opts mp e val))]
100−declToJs _ mp (DataDeclaration _ _ ctors) _ =
101− Just $ flip concatMap ctors $ \(pn@(ProperName ctor), tys) ->
94+declToJs :: (Functor m, Applicative m, Monad m) => Options -> ModuleName -> Declaration -> Environment -> SupplyT m (Maybe [JS])
95+declToJs opts mp (ValueDeclaration ident _ _ _ val) e = do
96+ js <- valueToJs opts mp e val
97+ return $ Just [JSVariableIntroduction (identToJs ident) (Just js)]
98+declToJs opts mp (BindingGroupDeclaration vals) e = do
99+ jss <- flip mapM vals $ \(ident, _, val) -> do
100+ js <- valueToJs opts mp e val
101+ return $ JSVariableIntroduction (identToJs ident) (Just js)
102+ return $ Just jss
103+declToJs _ mp (DataDeclaration _ _ ctors) _ = do
104+ return $ Just $ flip concatMap ctors $ \(pn@(ProperName ctor), tys) ->
102105 [JSVariableIntroduction ctor (Just (go pn 0 tys []))]
103106 where
104107 go :: ProperName -> Integer -> [Type] -> [JS] -> JS
@@ -107,10 +110,12 @@ declToJs _ mp (DataDeclaration _ _ ctors) _ =
107110 go pn index (_ : tys') values =
108111 JSFunction Nothing ["value" ++ show index]
109112 (JSBlock [JSReturn (go pn (index + 1) tys' (JSVar ("value" ++ show index) : values))])
110−declToJs opts mp (DataBindingGroupDeclaration ds) e = Just $ concat $ mapMaybe (flip (declToJs opts mp) e) ds
111−declToJs _ _ (ExternDeclaration _ _ (Just js) _) _ = Just [js]
113+declToJs opts mp (DataBindingGroupDeclaration ds) e = do
114+ jss <- mapM (\decl -> declToJs opts mp decl e) ds
115+ return $ Just $ concat $ catMaybes jss
116+declToJs _ _ (ExternDeclaration _ _ (Just js) _) _ = return $ Just [js]
112117declToJs opts mp (PositionedDeclaration _ d) e = declToJs opts mp d e
113−declToJs _ _ _ _ = Nothing
118+declToJs _ _ _ _ = return Nothing
114119
115120-- |
116121-- Generate key//value pairs for an object literal exporting values from a module.
@@ -144,22 +149,35 @@ accessorString prop | isIdent prop = JSAccessor prop
144149-- |
145150-- Generate code in the simplified Javascript intermediate representation for a value or expression.
146151--
147−valueToJs :: Options -> ModuleName -> Environment -> Value -> JS
148−valueToJs _ _ _ (NumericLiteral n) = JSNumericLiteral n
149−valueToJs _ _ _ (StringLiteral s) = JSStringLiteral s
150−valueToJs _ _ _ (BooleanLiteral b) = JSBooleanLiteral b
151−valueToJs opts m e (ArrayLiteral xs) = JSArrayLiteral (map (valueToJs opts m e) xs)
152−valueToJs opts m e (ObjectLiteral ps) = JSObjectLiteral (map (second (valueToJs opts m e)) ps)
153−valueToJs opts m e (ObjectUpdate o ps) = extendObj (valueToJs opts m e o) (map (second (valueToJs opts m e)) ps)
154−valueToJs _ m _ (Constructor name) = qualifiedToJS m (Ident . runProperName) name
155−valueToJs opts m e (Case values binders) = bindersToJs opts m e binders (map (valueToJs opts m e) values)
156−valueToJs opts m e (IfThenElse cond th el) = JSConditional (valueToJs opts m e cond) (valueToJs opts m e th) (valueToJs opts m e el)
157−valueToJs opts m e (Accessor prop val) = accessorString prop (valueToJs opts m e val)
158−valueToJs opts m e (App val arg) = JSApp (valueToJs opts m e val) [valueToJs opts m e arg]
159−valueToJs opts m e (Let ds val) = JSApp (JSFunction Nothing [] (JSBlock (concat (mapMaybe (flip (declToJs opts m) e) ds) ++ [JSReturn $ valueToJs opts m e val]))) []
160−valueToJs opts m e (Abs (Left arg) val) = JSFunction Nothing [identToJs arg] (JSBlock [JSReturn (valueToJs opts m (bindName m arg e) val)])
161−valueToJs opts m e (TypedValue _ (Abs (Left arg) val) ty) | optionsPerformRuntimeTypeChecks opts = let arg' = identToJs arg in JSFunction Nothing [arg'] (JSBlock $ runtimeTypeChecks arg' ty ++ [JSReturn (valueToJs opts m e val)])
162−valueToJs _ m _ (Var ident) = varToJs m ident
152+valueToJs :: (Functor m, Applicative m, Monad m) => Options -> ModuleName -> Environment -> Value -> SupplyT m JS
153+valueToJs _ _ _ (NumericLiteral n) = return $ JSNumericLiteral n
154+valueToJs _ _ _ (StringLiteral s) = return $ JSStringLiteral s
155+valueToJs _ _ _ (BooleanLiteral b) = return $ JSBooleanLiteral b
156+valueToJs opts m e (ArrayLiteral xs) = JSArrayLiteral <$> mapM (valueToJs opts m e) xs
157+valueToJs opts m e (ObjectLiteral ps) = JSObjectLiteral <$> mapM (sndM (valueToJs opts m e)) ps
158+valueToJs opts m e (ObjectUpdate o ps) = do
159+ obj <- valueToJs opts m e o
160+ sts <- mapM (sndM (valueToJs opts m e)) ps
161+ extendObj obj sts
162+valueToJs _ m _ (Constructor name) = return $ qualifiedToJS m (Ident . runProperName) name
163+valueToJs opts m e (Case values binders) = do
164+ vals <- mapM (valueToJs opts m e) values
165+ bindersToJs opts m e binders vals
166+valueToJs opts m e (IfThenElse cond th el) = JSConditional <$> valueToJs opts m e cond <*> valueToJs opts m e th <*> valueToJs opts m e el
167+valueToJs opts m e (Accessor prop val) = accessorString prop <$> valueToJs opts m e val
168+valueToJs opts m e (App val arg) = JSApp <$> valueToJs opts m e val <*> (return <$> valueToJs opts m e arg)
169+valueToJs opts m e (Let ds val) = do
170+ decls <- concat . catMaybes <$> mapM (flip (declToJs opts m) e) ds
171+ ret <- valueToJs opts m e val
172+ return $ JSApp (JSFunction Nothing [] (JSBlock (decls ++ [JSReturn ret]))) []
173+valueToJs opts m e (Abs (Left arg) val) = do
174+ ret <- valueToJs opts m (bindName m arg e) val
175+ return $ JSFunction Nothing [identToJs arg] (JSBlock [JSReturn ret])
176+valueToJs opts m e (TypedValue _ (Abs (Left arg) val) ty) | optionsPerformRuntimeTypeChecks opts = do
177+ let arg' = identToJs arg
178+ ret <- valueToJs opts m e val
179+ return $ JSFunction Nothing [arg'] (JSBlock $ runtimeTypeChecks arg' ty ++ [JSReturn ret])
180+valueToJs _ m _ (Var ident) = return $ varToJs m ident
163181valueToJs opts m e (TypedValue _ val _) = valueToJs opts m e val
164182valueToJs opts m e (PositionedValue _ val) = valueToJs opts m e val
165183valueToJs _ _ _ (TypeClassDictionary _ _ _) = error "Type class dictionary was not replaced"
@@ -168,20 +186,22 @@ valueToJs _ _ _ _ = error "Invalid argument to valueToJs"
168186-- |
169187-- Shallow copy an object.
170188--
171−extendObj :: JS -> [(String, JS)] -> JS
172−extendObj obj sts = JSApp (JSFunction Nothing [] block) []
189+extendObj :: (Functor m, Applicative m, Monad m) => JS -> [(String, JS)] -> SupplyT m JS
190+extendObj obj sts = do
191+ newObj <- freshName
192+ key <- freshName
193+ let
194+ jsKey = JSVar key
195+ jsNewObj = JSVar newObj
196+ block = JSBlock (objAssign:copy:extend ++ [JSReturn jsNewObj])
197+ objAssign = JSVariableIntroduction newObj (Just $ JSObjectLiteral [])
198+ copy = JSForIn key obj $ JSBlock [JSIfElse cond assign Nothing]
199+ cond = JSApp (JSAccessor "hasOwnProperty" obj) [jsKey]
200+ assign = JSBlock [JSAssignment (JSIndexer jsKey jsNewObj) (JSIndexer jsKey obj)]
201+ stToAssign (s, js) = JSAssignment (JSAccessor s jsNewObj) js
202+ extend = map stToAssign sts
203+ return $ JSApp (JSFunction Nothing [] block) []
173204 where
174− [newObj, key] = take 2 . map identToJs . S.unusedNames $ used
175− used = usedNamesJS obj ++ concatMap (usedNamesJS . snd) sts
176− jsKey = JSVar key
177− jsNewObj = JSVar newObj
178− block = JSBlock (objAssign:copy:extend ++ [JSReturn jsNewObj])
179− objAssign = JSVariableIntroduction newObj (Just $ JSObjectLiteral [])
180− copy = JSForIn key obj $ JSBlock [JSIfElse cond assign Nothing]
181− cond = JSApp (JSAccessor "hasOwnProperty" obj) [jsKey]
182− assign = JSBlock [JSAssignment (JSIndexer jsKey jsNewObj) (JSIndexer jsKey obj)]
183− stToAssign (s, js) = JSAssignment (JSAccessor s jsNewObj) js
184− extend = map stToAssign sts
185205
186206-- |
187207-- Temporarily extends the environment with a single local variable name
@@ -198,7 +218,6 @@ bindNames m idents env = env { names = M.fromList [ ((m, ident), (noType, LocalV
198218 where
199219 noType = error "Temporary lambda variable type was read"
200220
201−
202221-- |
203222-- Generate code in the simplified Javascript intermediate representation for runtime type checks.
204223--
@@ -251,42 +270,30 @@ qualifiedToJS _ f (Qualified _ a) = JSVar $ identToJs (f a)
251270-- Generate code in the simplified Javascript intermediate representation for pattern match binders
252271-- and guards.
253272--
254−bindersToJs :: Options -> ModuleName -> Environment -> [CaseAlternative] -> [JS] -> JS
255−bindersToJs opts m e binders vals = runGen (map identToJs (S.unusedNames usedNames)) $ do
256− valNames <- replicateM (length vals) fresh
257− jss <- forM binders $ \(CaseAlternative bs grd result) -> go valNames [JSReturn (valueToJs opts m (bindNames m (concatMap binderNames bs) e) result)] bs grd
273+bindersToJs :: (Functor m, Applicative m, Monad m) => Options -> ModuleName -> Environment -> [CaseAlternative] -> [JS] -> SupplyT m JS
274+bindersToJs opts m e binders vals = do
275+ valNames <- replicateM (length vals) freshName
276+ jss <- forM binders $ \(CaseAlternative bs grd result) -> do
277+ ret <- valueToJs opts m (bindNames m (concatMap binderNames bs) e) result
278+ go valNames [JSReturn ret] bs grd
258279 return $ JSApp (JSFunction Nothing valNames (JSBlock (concat jss ++ [JSThrow (JSStringLiteral "Failed pattern match")])))
259280 vals
260281 where
261− usedNames = concatMap usedNamesJS vals ++ concatMap S.usedNamesCaseAlternative binders
262−
263− go :: [String] -> [JS] -> [Binder] -> Maybe Guard -> Gen [JS]
282+ go :: (Functor m, Applicative m, Monad m) => [String] -> [JS] -> [Binder] -> Maybe Guard -> SupplyT m [JS]
264283 go _ done [] Nothing = return done
265− go _ done [] (Just cond) = return [JSIfElse (valueToJs opts m e cond) (JSBlock done) Nothing]
284+ go _ done [] (Just cond) = do
285+ cond' <- valueToJs opts m e cond
286+ return [JSIfElse cond' (JSBlock done) Nothing]
266287 go (v:vs) done' (b:bs) grd = do
267288 done'' <- go vs done' bs grd
268289 binderToJs m e v done'' b
269290 go _ _ _ _ = error "Invalid arguments to bindersToJs"
270291
271−-- |
272−-- Gather all used names appearing inside a value
273−--
274−usedNamesJS :: JS -> [Ident]
275−usedNamesJS val = nub $ everythingOnJS (++) namesJS val
276− where
277− namesJS (JSVar name) = [Ident name]
278− namesJS (JSFunction (Just name) args _) = Ident name : map Ident args
279− namesJS (JSFunction Nothing args _) = map Ident args
280− namesJS (JSVariableIntroduction name _) = [Ident name]
281− namesJS (JSFor name _ _ _) = [Ident name]
282− namesJS (JSForIn name _ _) = [Ident name]
283− namesJS _ = []
284−
285292-- |
286293-- Generate code in the simplified Javascript intermediate representation for a pattern match
287294-- binder.
288295--
289−binderToJs :: ModuleName -> Environment -> String -> [JS] -> Binder -> Gen [JS]
296+binderToJs :: (Functor m, Applicative m, Monad m) => ModuleName -> Environment -> String -> [JS] -> Binder -> SupplyT m [JS]
290297binderToJs _ _ _ done NullBinder = return done
291298binderToJs _ _ varName done (StringBinder str) =
292299 return [JSIfElse (JSBinary EqualTo (JSVar varName) (JSStringLiteral str)) (JSBlock done) Nothing]
@@ -308,19 +315,19 @@ binderToJs m e varName done (ConstructorBinder ctor bs) = do
308315 (JSBlock js)
309316 Nothing]
310317 where
311− go :: Integer -> [JS] -> [Binder] -> Gen [JS]
318+ go :: (Functor m, Applicative m, Monad m) => Integer -> [JS] -> [Binder] -> SupplyT m [JS]
312319 go _ done' [] = return done'
313320 go index done' (binder:bs') = do
314− argVar <- fresh
321+ argVar <- freshName
315322 done'' <- go (index + 1) done' bs'
316323 js <- binderToJs m e argVar done'' binder
317324 return (JSVariableIntroduction argVar (Just (JSIndexer (JSNumericLiteral (Left index)) (JSAccessor "values" (JSVar varName)))) : js)
318325binderToJs m e varName done (ObjectBinder bs) = go done bs
319326 where
320− go :: [JS] -> [(String, Binder)] -> Gen [JS]
327+ go :: (Functor m, Applicative m, Monad m) => [JS] -> [(String, Binder)] -> SupplyT m [JS]
321328 go done' [] = return done'
322329 go done' ((prop, binder):bs') = do
323− propVar <- fresh
330+ propVar <- freshName
324331 done'' <- go done' bs'
325332 js <- binderToJs m e propVar done'' binder
326333 return (JSVariableIntroduction propVar (Just (accessorString prop (JSVar varName))) : js)
@@ -328,16 +335,16 @@ binderToJs m e varName done (ArrayBinder bs) = do
328335 js <- go done 0 bs
329336 return [JSIfElse (JSBinary EqualTo (JSAccessor "length" (JSVar varName)) (JSNumericLiteral (Left (fromIntegral $ length bs)))) (JSBlock js) Nothing]
330337 where
331− go :: [JS] -> Integer -> [Binder] -> Gen [JS]
338+ go :: (Functor m, Applicative m, Monad m) => [JS] -> Integer -> [Binder] -> SupplyT m [JS]
332339 go done' _ [] = return done'
333340 go done' index (binder:bs') = do
334− elVar <- fresh
341+ elVar <- freshName
335342 done'' <- go done' (index + 1) bs'
336343 js <- binderToJs m e elVar done'' binder
337344 return (JSVariableIntroduction elVar (Just (JSIndexer (JSNumericLiteral (Left index)) (JSVar varName))) : js)
338345binderToJs m e varName done (ConsBinder headBinder tailBinder) = do
339− headVar <- fresh
340− tailVar <- fresh
346+ headVar <- freshName
347+ tailVar <- freshName
341348 js1 <- binderToJs m e headVar done headBinder
342349 js2 <- binderToJs m e tailVar js1 tailBinder
343350 return [JSIfElse (JSBinary GreaterThan (JSAccessor "length" (JSVar varName)) (JSNumericLiteral (Left 0))) (JSBlock