@@ -23,26 +23,25 @@ module Language.PureScript.CodeGen.JS (
26−import Data.Maybe (fromJust, fromMaybe, mapMaybe)
26+import Data.Maybe (catMaybes, fromJust, fromMaybe)
27import Data.Function (on)
27import Data.Function (on)
28import Data.List (nub, (\\))
28import Data.List (nub, (\\))
30−import Control.Arrow (second)
31import Control.Monad (replicateM, forM)
30import Control.Monad (replicateM, forM)
31+import Control.Applicative
33import qualified Data.Map as M
33import qualified Data.Map as M
35import Language.PureScript.Names
35import Language.PureScript.Names
36import Language.PureScript.Declarations
36import Language.PureScript.Declarations
37−import Language.PureScript.CodeGen.Monad
38import Language.PureScript.Options
37import Language.PureScript.Options
39import Language.PureScript.CodeGen.JS.AST as AST
38import Language.PureScript.CodeGen.JS.AST as AST
40import Language.PureScript.Types
39import Language.PureScript.Types
41import Language.PureScript.Optimizer
40import Language.PureScript.Optimizer
42import Language.PureScript.CodeGen.Common
41import Language.PureScript.CodeGen.Common
43import Language.PureScript.Environment
42import Language.PureScript.Environment
44−
43+import Language.PureScript.Supply
45−import qualified Language.PureScript.Scope as S
44+import Language.PureScript.Traversals (sndM)
48-- Different types of modules which are supported
47-- Different types of modules which are supported
@@ -53,22 +52,23 @@ data ModuleType = CommonJS | Globals
53-- Generate code in the simplified Javascript intermediate representation for all declarations in a
52-- Generate code in the simplified Javascript intermediate representation for all declarations in a
56−moduleToJs :: ModuleType -> Options -> Module -> Environment -> [JS]
55+moduleToJs :: (Functor m, Applicative m, Monad m) => ModuleType -> Options -> Module -> Environment -> SupplyT m [JS]
57−moduleToJs mt opts (Module name decls (Just exps)) env = case mt of
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
58− CommonJS -> moduleBody ++ [JSAssignment (JSAccessor "exports" (JSVar "module")) moduleExports]
64+ CommonJS -> moduleBody ++ [JSAssignment (JSAccessor "exports" (JSVar "module")) moduleExports]
59− Globals | not isModuleEmpty ->
65+ Globals | not isModuleEmpty ->
60− [ JSVariableIntroduction (fromJust (optionsBrowserNamespace opts))
66+ [ JSVariableIntroduction (fromJust (optionsBrowserNamespace opts))
61− (Just (JSBinary Or (JSVar (fromJust (optionsBrowserNamespace opts))) (JSObjectLiteral [])) )
67+ (Just (JSBinary Or (JSVar (fromJust (optionsBrowserNamespace opts))) (JSObjectLiteral [])) )
62− , JSAssignment (JSAccessor (moduleNameToJs name) (JSVar (fromJust (optionsBrowserNamespace opts))))
68+ , JSAssignment (JSAccessor (moduleNameToJs name) (JSVar (fromJust (optionsBrowserNamespace opts))))
63− (JSApp (JSFunction Nothing [] (JSBlock (moduleBody ++ [JSReturn moduleExports]))) [])
69+ (JSApp (JSFunction Nothing [] (JSBlock (moduleBody ++ [JSReturn moduleExports]))) [])
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
72moduleToJs _ _ _ _ = error "Exports should have been elaborated in name desugaring"
72moduleToJs _ _ _ _ = error "Exports should have been elaborated in name desugaring"
74importToJs :: ModuleType -> Options -> ModuleName -> JS
74importToJs :: ModuleType -> Options -> ModuleName -> JS
@@ -91,14 +91,17 @@ imports =
92-- Generate code in the simplified Javascript intermediate representation for a declaration
92-- Generate code in the simplified Javascript intermediate representation for a declaration
94−declToJs :: Options -> ModuleName -> Declaration -> Environment -> Maybe [JS]
94+declToJs :: (Functor m, Applicative m, Monad m) => Options -> ModuleName -> Declaration -> Environment -> SupplyT m (Maybe [JS])
95−declToJs opts mp (ValueDeclaration ident _ _ _ val) e =
95+declToJs opts mp (ValueDeclaration ident _ _ _ val) e = do
96+ js <- valueToJs opts mp e val
96− Just [JSVariableIntroduction (identToJs ident) (Just (valueToJs opts mp e val))]
97+ return $ Just [JSVariableIntroduction (identToJs ident) (Just js)]
97−declToJs opts mp (BindingGroupDeclaration vals) e =
98+declToJs opts mp (BindingGroupDeclaration vals) e = do
98− Just $ flip concatMap vals $ \(ident, _, val) ->
99+ jss <- flip mapM vals $ \(ident, _, val) -> do
100+ js <- valueToJs opts mp e val
99− [JSVariableIntroduction (identToJs ident) (Just (valueToJs opts mp e val))]
101+ return $ JSVariableIntroduction (identToJs ident) (Just js)
100−declToJs _ mp (DataDeclaration _ _ ctors) _ =
103+declToJs _ mp (DataDeclaration _ _ ctors) _ = do
101− Just $ flip concatMap ctors $ \(pn@(ProperName ctor), tys) ->
104+ return $ Just $ flip concatMap ctors $ \(pn@(ProperName ctor), tys) ->
102 [JSVariableIntroduction ctor (Just (go pn 0 tys []))]
105 [JSVariableIntroduction ctor (Just (go pn 0 tys []))]
104 go :: ProperName -> Integer -> [Type] -> [JS] -> JS
107 go :: ProperName -> Integer -> [Type] -> [JS] -> JS
@@ -107,10 +110,12 @@ declToJs _ mp (DataDeclaration _ _ ctors) _ =
107 go pn index (_ : tys') values =
110 go pn index (_ : tys') values =
108 JSFunction Nothing ["value" ++ show index]
111 JSFunction Nothing ["value" ++ show index]
109 (JSBlock [JSReturn (go pn (index + 1) tys' (JSVar ("value" ++ show index) : values))])
112 (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
113+declToJs opts mp (DataBindingGroupDeclaration ds) e = do
114+ jss <- mapM (\decl -> declToJs opts mp decl e) ds
115+ return $ Just $ concat $ catMaybes jss
111−declToJs _ _ (ExternDeclaration _ _ (Just js) _) _ = Just [js]
116+declToJs _ _ (ExternDeclaration _ _ (Just js) _) _ = return $ Just [js]
112declToJs opts mp (PositionedDeclaration _ d) e = declToJs opts mp d e
117declToJs opts mp (PositionedDeclaration _ d) e = declToJs opts mp d e
113−declToJs _ _ _ _ = Nothing
118+declToJs _ _ _ _ = return Nothing
116-- Generate key//value pairs for an object literal exporting values from a module.
121-- Generate key//value pairs for an object literal exporting values from a module.
@@ -144,22 +149,35 @@ accessorString prop | isIdent prop = JSAccessor prop
145-- Generate code in the simplified Javascript intermediate representation for a value or expression.
150-- Generate code in the simplified Javascript intermediate representation for a value or expression.
147−valueToJs :: Options -> ModuleName -> Environment -> Value -> JS
152+valueToJs :: (Functor m, Applicative m, Monad m) => Options -> ModuleName -> Environment -> Value -> SupplyT m JS
148−valueToJs _ _ _ (NumericLiteral n) = JSNumericLiteral n
153+valueToJs _ _ _ (NumericLiteral n) = return $ JSNumericLiteral n
149−valueToJs _ _ _ (StringLiteral s) = JSStringLiteral s
154+valueToJs _ _ _ (StringLiteral s) = return $ JSStringLiteral s
150−valueToJs _ _ _ (BooleanLiteral b) = JSBooleanLiteral b
155+valueToJs _ _ _ (BooleanLiteral b) = return $ JSBooleanLiteral b
151−valueToJs opts m e (ArrayLiteral xs) = JSArrayLiteral (map (valueToJs opts m e) xs)
156+valueToJs opts m e (ArrayLiteral xs) = JSArrayLiteral <$> mapM (valueToJs opts m e) xs
152−valueToJs opts m e (ObjectLiteral ps) = JSObjectLiteral (map (second (valueToJs opts m e)) ps)
157+valueToJs opts m e (ObjectLiteral ps) = JSObjectLiteral <$> mapM (sndM (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)
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
154−valueToJs _ m _ (Constructor name) = qualifiedToJS m (Ident . runProperName) name
162+valueToJs _ m _ (Constructor name) = return $ qualifiedToJS m (Ident . runProperName) name
155−valueToJs opts m e (Case values binders) = bindersToJs opts m e binders (map (valueToJs opts m e) values)
163+valueToJs opts m e (Case values binders) = do
164+ vals <- mapM (valueToJs opts m e) values
165+ bindersToJs opts m e binders vals
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)
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
157−valueToJs opts m e (Accessor prop val) = accessorString prop (valueToJs opts m e val)
167+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]
168+valueToJs opts m e (App val arg) = JSApp <$> valueToJs opts m e val <*> (return <$> 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]))) []
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
160−valueToJs opts m e (Abs (Left arg) val) = JSFunction Nothing [identToJs arg] (JSBlock [JSReturn (valueToJs opts m (bindName m arg e) val)])
174+ ret <- valueToJs opts m (bindName m arg e) val
175+ return $ JSFunction Nothing [identToJs arg] (JSBlock [JSReturn ret])
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)])
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])
162−valueToJs _ m _ (Var ident) = varToJs m ident
180+valueToJs _ m _ (Var ident) = return $ varToJs m ident
163valueToJs opts m e (TypedValue _ val _) = valueToJs opts m e val
181valueToJs opts m e (TypedValue _ val _) = valueToJs opts m e val
164valueToJs opts m e (PositionedValue _ val) = valueToJs opts m e val
182valueToJs opts m e (PositionedValue _ val) = valueToJs opts m e val
165valueToJs _ _ _ (TypeClassDictionary _ _ _) = error "Type class dictionary was not replaced"
183valueToJs _ _ _ (TypeClassDictionary _ _ _) = error "Type class dictionary was not replaced"
@@ -168,20 +186,22 @@ valueToJs _ _ _ _ = error "Invalid argument to valueToJs"
169-- Shallow copy an object.
187-- Shallow copy an object.
171−extendObj :: JS -> [(String, JS)] -> JS
189+extendObj :: (Functor m, Applicative m, Monad m) => JS -> [(String, JS)] -> SupplyT m JS
190+extendObj obj sts = do
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
172−extendObj obj sts = JSApp (JSFunction Nothing [] block) []
203+ return $ JSApp (JSFunction Nothing [] block) []
174− [newObj, key] = take 2 . map identToJs . S.unusedNames $ used
175− used = usedNamesJS obj ++ concatMap (usedNamesJS . snd) sts
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
187-- Temporarily extends the environment with a single local variable name
207-- 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
199 noType = error "Temporary lambda variable type was read"
219 noType = error "Temporary lambda variable type was read"
203-- Generate code in the simplified Javascript intermediate representation for runtime type checks.
222-- Generate code in the simplified Javascript intermediate representation for runtime type checks.
@@ -251,42 +270,30 @@ qualifiedToJS _ f (Qualified _ a) = JSVar $ identToJs (f a)
251-- Generate code in the simplified Javascript intermediate representation for pattern match binders
270-- Generate code in the simplified Javascript intermediate representation for pattern match binders
252-- and guards.
271-- and guards.
254−bindersToJs :: Options -> ModuleName -> Environment -> [CaseAlternative] -> [JS] -> JS
273+bindersToJs :: (Functor m, Applicative m, Monad m) => Options -> ModuleName -> Environment -> [CaseAlternative] -> [JS] -> SupplyT m JS
255−bindersToJs opts m e binders vals = runGen (map identToJs (S.unusedNames usedNames)) $ do
274+bindersToJs opts m e binders vals = do
256− valNames <- replicateM (length vals) fresh
275+ valNames <- replicateM (length vals) freshName
276+ jss <- forM binders $ \(CaseAlternative bs grd result) -> do
257− jss <- forM binders $ \(CaseAlternative bs grd result) -> go valNames [JSReturn (valueToJs opts m (bindNames m (concatMap binderNames bs) e) result)] bs grd
277+ ret <- valueToJs opts m (bindNames m (concatMap binderNames bs) e) result
278+ go valNames [JSReturn ret] bs grd
258 return $ JSApp (JSFunction Nothing valNames (JSBlock (concat jss ++ [JSThrow (JSStringLiteral "Failed pattern match")])))
279 return $ JSApp (JSFunction Nothing valNames (JSBlock (concat jss ++ [JSThrow (JSStringLiteral "Failed pattern match")])))
261− usedNames = concatMap usedNamesJS vals ++ concatMap S.usedNamesCaseAlternative binders
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]
264 go _ done [] Nothing = return done
283 go _ done [] Nothing = return done
284+ go _ done [] (Just cond) = do
265− go _ done [] (Just cond) = return [JSIfElse (valueToJs opts m e cond) (JSBlock done) Nothing]
285+ cond' <- valueToJs opts m e cond
286+ return [JSIfElse cond' (JSBlock done) Nothing]
266 go (v:vs) done' (b:bs) grd = do
287 go (v:vs) done' (b:bs) grd = do
267 done'' <- go vs done' bs grd
288 done'' <- go vs done' bs grd
268 binderToJs m e v done'' b
289 binderToJs m e v done'' b
269 go _ _ _ _ = error "Invalid arguments to bindersToJs"
290 go _ _ _ _ = error "Invalid arguments to bindersToJs"
272−-- Gather all used names appearing inside a value
274−usedNamesJS :: JS -> [Ident]
275−usedNamesJS val = nub $ everythingOnJS (++) namesJS val
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]
286-- Generate code in the simplified Javascript intermediate representation for a pattern match
293-- Generate code in the simplified Javascript intermediate representation for a pattern match
287-- binder.
294-- binder.
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]
290binderToJs _ _ _ done NullBinder = return done
297binderToJs _ _ _ done NullBinder = return done
291binderToJs _ _ varName done (StringBinder str) =
298binderToJs _ _ varName done (StringBinder str) =
292 return [JSIfElse (JSBinary EqualTo (JSVar varName) (JSStringLiteral str)) (JSBlock done) Nothing]
299 return [JSIfElse (JSBinary EqualTo (JSVar varName) (JSStringLiteral str)) (JSBlock done) Nothing]
@@ -308,19 +315,19 @@ binderToJs m e varName done (ConstructorBinder ctor bs) = do
308 (JSBlock js)
315 (JSBlock js)
311− go :: Integer -> [JS] -> [Binder] -> Gen [JS]
318+ go :: (Functor m, Applicative m, Monad m) => Integer -> [JS] -> [Binder] -> SupplyT m [JS]
312 go _ done' [] = return done'
319 go _ done' [] = return done'
313 go index done' (binder:bs') = do
320 go index done' (binder:bs') = do
314− argVar <- fresh
321+ argVar <- freshName
315 done'' <- go (index + 1) done' bs'
322 done'' <- go (index + 1) done' bs'
316 js <- binderToJs m e argVar done'' binder
323 js <- binderToJs m e argVar done'' binder
317 return (JSVariableIntroduction argVar (Just (JSIndexer (JSNumericLiteral (Left index)) (JSAccessor "values" (JSVar varName)))) : js)
324 return (JSVariableIntroduction argVar (Just (JSIndexer (JSNumericLiteral (Left index)) (JSAccessor "values" (JSVar varName)))) : js)
318binderToJs m e varName done (ObjectBinder bs) = go done bs
325binderToJs m e varName done (ObjectBinder bs) = go done bs
320− go :: [JS] -> [(String, Binder)] -> Gen [JS]
327+ go :: (Functor m, Applicative m, Monad m) => [JS] -> [(String, Binder)] -> SupplyT m [JS]
321 go done' [] = return done'
328 go done' [] = return done'
322 go done' ((prop, binder):bs') = do
329 go done' ((prop, binder):bs') = do
323− propVar <- fresh
330+ propVar <- freshName
324 done'' <- go done' bs'
331 done'' <- go done' bs'
325 js <- binderToJs m e propVar done'' binder
332 js <- binderToJs m e propVar done'' binder
326 return (JSVariableIntroduction propVar (Just (accessorString prop (JSVar varName))) : js)
333 return (JSVariableIntroduction propVar (Just (accessorString prop (JSVar varName))) : js)
@@ -328,16 +335,16 @@ binderToJs m e varName done (ArrayBinder bs) = do
328 js <- go done 0 bs
335 js <- go done 0 bs
329 return [JSIfElse (JSBinary EqualTo (JSAccessor "length" (JSVar varName)) (JSNumericLiteral (Left (fromIntegral $ length bs)))) (JSBlock js) Nothing]
336 return [JSIfElse (JSBinary EqualTo (JSAccessor "length" (JSVar varName)) (JSNumericLiteral (Left (fromIntegral $ length bs)))) (JSBlock js) Nothing]
331− go :: [JS] -> Integer -> [Binder] -> Gen [JS]
338+ go :: (Functor m, Applicative m, Monad m) => [JS] -> Integer -> [Binder] -> SupplyT m [JS]
332 go done' _ [] = return done'
339 go done' _ [] = return done'
333 go done' index (binder:bs') = do
340 go done' index (binder:bs') = do
334− elVar <- fresh
341+ elVar <- freshName
335 done'' <- go done' (index + 1) bs'
342 done'' <- go done' (index + 1) bs'
336 js <- binderToJs m e elVar done'' binder
343 js <- binderToJs m e elVar done'' binder
337 return (JSVariableIntroduction elVar (Just (JSIndexer (JSNumericLiteral (Left index)) (JSVar varName))) : js)
344 return (JSVariableIntroduction elVar (Just (JSIndexer (JSNumericLiteral (Left index)) (JSVar varName))) : js)
338binderToJs m e varName done (ConsBinder headBinder tailBinder) = do
345binderToJs m e varName done (ConsBinder headBinder tailBinder) = do
339− headVar <- fresh
346+ headVar <- freshName
340− tailVar <- fresh
347+ tailVar <- freshName
341 js1 <- binderToJs m e headVar done headBinder
348 js1 <- binderToJs m e headVar done headBinder
342 js2 <- binderToJs m e tailVar js1 tailBinder
349 js2 <- binderToJs m e tailVar js1 tailBinder
343 return [JSIfElse (JSBinary GreaterThan (JSAccessor "length" (JSVar varName)) (JSNumericLiteral (Left 0))) (JSBlock
350 return [JSIfElse (JSBinary GreaterThan (JSAccessor "length" (JSVar varName)) (JSNumericLiteral (Left 0))) (JSBlock