git.iko.soy/rowtype-yoga/purescript-native
on golang help sign in
Merge pull request #192 from garyb/master

Update psc options to allow main module to be specified and multiple modules to be kept by DCE

commit20e7de41968b64682ecc944978d79e56abb62eae authorPhil Freeman <paf31@cantab.net> dateSat 15 Feb 2014, 13:40 (13 years ago) parent18082c7f2c97bd
9 files changed+67−64
docs/source/intro.rst+2−2 psc/Main.hs+7−7 psci/Main.hs+1−1 src/Language/PureScript.hs+15−13 src/Language/PureScript/DeadCodeElimination.hs+3−3 src/Language/PureScript/Options.hs+6−5 src/Language/PureScript/TypeChecker.hs+28−28 src/Language/PureScript/TypeChecker/Types.hs+4−4 tests/Main.hs+1−1
docs/source/intro.rst+2−2
@@ -47,9 +47,9 @@ The following options are supported:
4747--tco Perform tail-call elimination on the generated Javascript.
4848--no-prelude Do not include the Prelude in the generated Javascript.
4949--magic-do Overload the `do` keyword to inline calls to `bind` for the `Eff` monad, to generate more efficient code.
50−--run-main Generate a call to `Main.main` after all other generated Javascript.
50+--main Generate a call to `main` in the specified module after all other generated Javascript. Defaults to `Main` if the option is used but no value is provided.
51+--module If specified, any code which is not referenced transitively from this module will be removed. This argument can be used multiple times.
5152--browser-namespace Specify the namespace that PureScript modules will be exported to when running in the browser.
52−--entry-point Specify the entry-point module (usually Main). If specified, code which is not referenced transitively from this module will be removed.
5353
5454Motivation
5555----------
psc/Main.hs+7−7
@@ -86,9 +86,9 @@ magicDo :: Term Bool
8686magicDo = value $ flag $ (optInfo [ "magic-do" ])
8787 { optDoc = "Overload the do keyword to generate efficient code specifically for the Eff monad." }
8888
89−runMain :: Term Bool
90−runMain = value $ flag $ (optInfo [ "run-main" ])
91− { optDoc = "Generate code to run the main method in the Main module." }
89+runMain :: Term (Maybe String)
90+runMain = value $ defaultOpt (Just "Main") Nothing $ (optInfo [ "main" ])
91+ { optDoc = "Generate code to run the main method in the specified module." }
9292
9393noOpts :: Term Bool
9494noOpts = value $ flag $ (optInfo [ "no-opts" ])
@@ -98,12 +98,12 @@ browserNamespace :: Term String
9898browserNamespace = value $ opt "PS" $ (optInfo [ "browser-namespace" ])
9999 { optDoc = "Specify the namespace that PureScript modules will be exported to when running in the browser." }
100100
101−entryPoint :: Term (Maybe String)
102−entryPoint = value $ opt Nothing $ (optInfo [ "entry-point" ])
103− { optDoc = "Specify the module which is the entry point. All code which is not a transitive dependency of this module will be removed." }
101+dceModules :: Term [String]
102+dceModules = value $ optAll [] $ (optInfo [ "m", "module" ])
103+ { optDoc = "Enables dead code elimination, all code which is not a transitive dependency of a specified module will be removed. This argument can be used multiple times." }
104104
105105options :: Term P.Options
106−options = P.Options <$> tco <*> performRuntimeTypeChecks <*> magicDo <*> runMain <*> noOpts <*> browserNamespace <*> entryPoint
106+options = P.Options <$> tco <*> performRuntimeTypeChecks <*> magicDo <*> runMain <*> noOpts <*> browserNamespace <*> dceModules
107107
108108stdInOrInputFiles :: FilePath -> Term (Maybe [FilePath])
109109stdInOrInputFiles prelude = combine <$> useStdIn <*> (not <$> noPrelude) <*> inputFiles
psci/Main.hs+1−1
@@ -35,7 +35,7 @@ getPreludeFilename :: IO FilePath
3535getPreludeFilename = Paths.getDataFileName "prelude/prelude.purs"
3636
3737options :: P.Options
38−options = P.Options True False True True True "PS" Nothing
38+options = P.Options True False True Nothing True "PS" []
3939
4040completion :: [P.Module] -> CompletionFunc IO
4141completion ms = completeWord Nothing " \t\n\r" findCompletions
src/Language/PureScript.hs+15−13
@@ -30,10 +30,10 @@ import Language.PureScript.ModuleDependencies as P
3030import Language.PureScript.DeadCodeElimination as P
3131
3232import Data.List (intercalate)
33−import Data.Maybe (mapMaybe)
33+import Data.Maybe (mapMaybe, fromMaybe)
3434import Control.Monad (when, forM)
3535import Control.Monad.State.Lazy
36−import Control.Applicative ((<$>))
36+import Control.Applicative ((<$>), (<|>))
3737import qualified Data.Map as M
3838
3939-- |
@@ -47,9 +47,9 @@ import qualified Data.Map as M
4747--
4848-- * Type check, and elaborate values to include type annotations and type class dictionaries.
4949--
50−-- * Regroup values to take into account new value dependencies introduced by elaboration
50+-- * Regroup values to take into account new value dependencies introduced by elaboration.
5151--
52−-- * Eliminate dead code
52+-- * Eliminate dead code.
5353--
5454-- * Generate Javascript, and perform optimization passes.
5555--
@@ -61,16 +61,18 @@ compile opts ms = do
6161 desugared <- desugar sorted
6262 (elaborated, env) <- runCheck $ forM desugared $ \(Module moduleName decls) -> do
6363 modify (\s -> s { checkCurrentModule = Just (ModuleName moduleName) })
64− Module moduleName <$> typeCheckAll (ModuleName moduleName) decls
64+ Module moduleName <$> typeCheckAll mainModuleIdent (ModuleName moduleName) decls
6565 regrouped <- createBindingGroupsModule . collapseBindingGroupsModule $ elaborated
66− let entryPoint = optionsEntryPoint opts
67− let elim = maybe regrouped (\ep -> eliminateDeadCode env ep regrouped) entryPoint
66+ let entryPoints = optionsModules opts
67+ let elim = if null entryPoints then regrouped else eliminateDeadCode env entryPoints regrouped
6868 let js = mapMaybe (flip (moduleToJs opts) env) elim
6969 let exts = intercalate "\n" . map (flip moduleToPs env) $ elim
70− js' <- case () of
71− _ | optionsRunMain opts -> do
72− when ((ModuleName (ProperName "Main"), Ident "main") `M.notMember` (names env)) $
73− Left "Main.main is undefined"
74− return $ js ++ [JSApp (JSAccessor "main" (JSAccessor "Main" (JSVar "_ps"))) []]
75− | otherwise -> return js
70+ js' <- case optionsMain opts of
71+ Just mainModuleName -> do
72+ when ((ModuleName (ProperName mainModuleName), Ident "main") `M.notMember` (names env)) $
73+ Left $ mainModuleName ++ ".main is undefined"
74+ return $ js ++ [JSApp (JSAccessor "main" (JSAccessor mainModuleName (JSVar "_ps"))) []]
75+ _ -> return js
7676 return (prettyPrintJS [(wrapExportsContainer opts js')], exts, env)
77+ where
78+ mainModuleIdent = ModuleName . ProperName <$> (optionsMain opts)
src/Language/PureScript/DeadCodeElimination.hs+3−3
@@ -30,11 +30,11 @@ import Language.PureScript.TypeChecker.Monad
3030-- |
3131-- Eliminate all declarations which are not a transitive dependency of the entry point module
3232--
33−eliminateDeadCode :: Environment -> String -> [Module] -> [Module]
34−eliminateDeadCode env entryPoint ms =
33+eliminateDeadCode :: Environment -> [String] -> [Module] -> [Module]
34+eliminateDeadCode env entryPoints ms =
3535 let declarations = concatMap (declarationsByModule env) ms
3636 (graph, _, vertexFor) = graphFromEdges $ map (\(key, deps) -> (key, key, deps)) declarations
37− entryPointVertices = mapMaybe (vertexFor . fst) . filter (\((ModuleName (ProperName mn), _), _) -> mn == entryPoint) $ declarations
37+ entryPointVertices = mapMaybe (vertexFor . fst) . filter (\((ModuleName (ProperName mn), _), _) -> mn `elem` entryPoints) $ declarations
3838 in flip map ms $ \(Module moduleName ds) -> Module moduleName (filter (isUsed (ModuleName moduleName) graph vertexFor entryPointVertices) ds)
3939
4040type Key = (ModuleName, Either Ident ProperName)
src/Language/PureScript/Options.hs+6−5
@@ -32,9 +32,10 @@ data Options = Options {
3232 --
3333 , optionsMagicDo :: Bool
3434 -- |
35− -- Check the type of Main.main and generate its code
35+ -- When specified, checks the type of `main` in the module, and generate a call to run main
36+ -- after the module definitions.
3637 --
37− , optionsRunMain :: Bool
38+ , optionsMain :: Maybe String
3839 -- |
3940 -- Skip all optimizations
4041 --
@@ -45,13 +46,13 @@ data Options = Options {
4546 --
4647 , optionsBrowserNamespace :: String
4748 -- |
48− -- The entry point module, for dead code elimination
49+ -- The modules to keep while enabling dead code elimination
4950 --
50− , optionsEntryPoint :: Maybe String
51+ , optionsModules :: [String]
5152 } deriving Show
5253
5354-- |
5455-- Default compiler options
5556--
5657defaultOptions :: Options
57−defaultOptions = Options False False False False False "PS" Nothing
58+defaultOptions = Options False False False Nothing False "PS" []
src/Language/PureScript/TypeChecker.hs+28−28
@@ -111,16 +111,16 @@ checkTypeClassInstance _ _ = throwError "Type class instance must be of the form
111111--
112112-- * Process module imports
113113--
114−typeCheckAll :: ModuleName -> [Declaration] -> Check [Declaration]
115−typeCheckAll _ [] = return []
116−typeCheckAll moduleName (d@(DataDeclaration name args dctors) : rest) = do
114+typeCheckAll :: Maybe ModuleName -> ModuleName -> [Declaration] -> Check [Declaration]
115+typeCheckAll _ _ [] = return []
116+typeCheckAll mainModuleName moduleName (d@(DataDeclaration name args dctors) : rest) = do
117117 rethrow (("Error in type constructor " ++ show name ++ ":\n") ++) $ do
118118 typeIsNotDefined moduleName name
119119 ctorKind <- kindsOf moduleName name args (concatMap snd dctors)
120120 addDataType moduleName name args dctors ctorKind
121− ds <- typeCheckAll moduleName rest
121+ ds <- typeCheckAll mainModuleName moduleName rest
122122 return $ d : ds
123−typeCheckAll moduleName (d@(DataBindingGroupDeclaration tys) : rest) = do
123+typeCheckAll mainModuleName moduleName (d@(DataBindingGroupDeclaration tys) : rest) = do
124124 rethrow ("Error in data binding group:\n" ++) $ do
125125 let syns = mapMaybe toTypeSynonym tys
126126 let dataDecls = mapMaybe toDataDecl tys
@@ -131,48 +131,48 @@ typeCheckAll moduleName (d@(DataBindingGroupDeclaration tys) : rest) = do
131131 forM_ (zip syns syn_ks) $ \((name, args, ty), kind) -> do
132132 typeIsNotDefined moduleName name
133133 addTypeSynonym moduleName name args ty kind
134− ds <- typeCheckAll moduleName rest
134+ ds <- typeCheckAll mainModuleName moduleName rest
135135 return $ d : ds
136136 where
137137 toTypeSynonym (TypeSynonymDeclaration nm args ty) = Just (nm, args, ty)
138138 toTypeSynonym _ = Nothing
139139 toDataDecl (DataDeclaration nm args dctors) = Just (nm, args, dctors)
140140 toDataDecl _ = Nothing
141−typeCheckAll moduleName (d@(TypeSynonymDeclaration name args ty) : rest) = do
141+typeCheckAll mainModuleName moduleName (d@(TypeSynonymDeclaration name args ty) : rest) = do
142142 rethrow (("Error in type synonym " ++ show name ++ ":\n") ++) $ do
143143 typeIsNotDefined moduleName name
144144 kind <- kindsOf moduleName name args [ty]
145145 addTypeSynonym moduleName name args ty kind
146− ds <- typeCheckAll moduleName rest
146+ ds <- typeCheckAll mainModuleName moduleName rest
147147 return $ d : ds
148−typeCheckAll _ (TypeDeclaration _ _ : _) = error "Type declarations should have been removed"
149−typeCheckAll moduleName (ValueDeclaration name [] Nothing val : rest) = do
148+typeCheckAll _ _ (TypeDeclaration _ _ : _) = error "Type declarations should have been removed"
149+typeCheckAll mainModuleName moduleName (ValueDeclaration name [] Nothing val : rest) = do
150150 d <- rethrow (("Error in declaration " ++ show name ++ ":\n") ++) $ do
151151 valueIsNotDefined moduleName name
152− [(_, (val', ty))] <- typesOf moduleName [(name, val)]
152+ [(_, (val', ty))] <- typesOf mainModuleName moduleName [(name, val)]
153153 addValue moduleName name ty
154154 return $ ValueDeclaration name [] Nothing val'
155− ds <- typeCheckAll moduleName rest
155+ ds <- typeCheckAll mainModuleName moduleName rest
156156 return $ d : ds
157−typeCheckAll _ (ValueDeclaration _ _ _ _ : _) = error "Binders were not desugared"
158−typeCheckAll moduleName (BindingGroupDeclaration vals : rest) = do
157+typeCheckAll _ _ (ValueDeclaration _ _ _ _ : _) = error "Binders were not desugared"
158+typeCheckAll mainModuleName moduleName (BindingGroupDeclaration vals : rest) = do
159159 d <- rethrow (("Error in binding group " ++ show (map fst vals) ++ ":\n") ++) $ do
160160 forM_ (map fst vals) $ \name ->
161161 valueIsNotDefined moduleName name
162− tys <- typesOf moduleName vals
162+ tys <- typesOf mainModuleName moduleName vals
163163 vals' <- forM (zip (map fst vals) (map snd tys)) $ \(name, (val, ty)) -> do
164164 addValue moduleName name ty
165165 return (name, val)
166166 return $ BindingGroupDeclaration vals'
167− ds <- typeCheckAll moduleName rest
167+ ds <- typeCheckAll mainModuleName moduleName rest
168168 return $ d : ds
169−typeCheckAll moduleName (d@(ExternDataDeclaration name kind) : rest) = do
169+typeCheckAll mainModuleName moduleName (d@(ExternDataDeclaration name kind) : rest) = do
170170 env <- getEnv
171171 guardWith (show name ++ " is already defined") $ not $ M.member (moduleName, name) (types env)
172172 putEnv $ env { types = M.insert (moduleName, name) (kind, TypeSynonym) (types env) }
173− ds <- typeCheckAll moduleName rest
173+ ds <- typeCheckAll mainModuleName moduleName rest
174174 return $ d : ds
175−typeCheckAll moduleName (d@(ExternDeclaration importTy name _ ty) : rest) = do
175+typeCheckAll mainModuleName moduleName (d@(ExternDeclaration importTy name _ ty) : rest) = do
176176 rethrow (("Error in foreign import declaration " ++ show name ++ ":\n") ++) $ do
177177 env <- getEnv
178178 kind <- kindOf moduleName ty
@@ -180,14 +180,14 @@ typeCheckAll moduleName (d@(ExternDeclaration importTy name _ ty) : rest) = do
180180 case M.lookup (moduleName, name) (names env) of
181181 Just _ -> throwError $ show name ++ " is already defined"
182182 Nothing -> putEnv (env { names = M.insert (moduleName, name) (qualifyAllUnqualifiedNames moduleName env ty, Extern importTy) (names env) })
183− ds <- typeCheckAll moduleName rest
183+ ds <- typeCheckAll mainModuleName moduleName rest
184184 return $ d : ds
185−typeCheckAll moduleName (d@(FixityDeclaration _ name) : rest) = do
186− ds <- typeCheckAll moduleName rest
185+typeCheckAll mainModuleName moduleName (d@(FixityDeclaration _ name) : rest) = do
186+ ds <- typeCheckAll mainModuleName moduleName rest
187187 env <- getEnv
188188 guardWith ("Fixity declaration with no binding: " ++ name) $ M.member (moduleName, Op name) $ names env
189189 return $ d : ds
190−typeCheckAll currentModule (d@(ImportDeclaration moduleName idents) : rest) = do
190+typeCheckAll mainModuleName currentModule (d@(ImportDeclaration moduleName idents) : rest) = do
191191 env <- getEnv
192192 rethrow errorMessage $ do
193193 guardWith ("Module " ++ show moduleName ++ " does not exist") $ moduleExists env
@@ -199,7 +199,7 @@ typeCheckAll currentModule (d@(ImportDeclaration moduleName idents) : rest) = do
199199 shadowIdents (lefts idents') env
200200 shadowTypes (rights idents') env
201201 shadowTypeClassInstances env
202− ds <- typeCheckAll currentModule rest
202+ ds <- typeCheckAll mainModuleName currentModule rest
203203 return $ d : ds
204204 where
205205 errorMessage = (("Error in import declaration " ++ show moduleName ++ ":\n") ++)
@@ -243,16 +243,16 @@ typeCheckAll currentModule (d@(ImportDeclaration moduleName idents) : rest) = do
243243 constructs (TypeApp (TypeApp t _) ty) pn | t == tyFunction = ty `constructs` pn
244244 constructs (TypeApp ty _) pn = ty `constructs` pn
245245 constructs fn _ = error $ "Invalid arguments to constructs: " ++ show fn
246−typeCheckAll moduleName (d@(TypeClassDeclaration _ _ _) : rest) = do
246+typeCheckAll mainModuleName moduleName (d@(TypeClassDeclaration _ _ _) : rest) = do
247247 env <- getEnv
248− ds <- typeCheckAll moduleName rest
248+ ds <- typeCheckAll mainModuleName moduleName rest
249249 return $ qualifyAllUnqualifiedNames moduleName env d : ds
250−typeCheckAll moduleName (d@(TypeInstanceDeclaration deps className ty _) : rest) = do
250+typeCheckAll mainModuleName moduleName (d@(TypeInstanceDeclaration deps className ty _) : rest) = do
251251 env <- getEnv
252252 dictName <- Check . lift $ mkDictionaryValueName moduleName className ty
253253 checkTypeClassInstance moduleName ty
254254 forM_ deps $ checkTypeClassInstance moduleName . snd
255255 addTypeClassDictionaries (qualifyAllUnqualifiedNames moduleName env
256256 [TypeClassDictionaryInScope (Qualified (Just moduleName) dictName) className ty (Just deps) TCDRegular])
257− ds <- typeCheckAll moduleName rest
257+ ds <- typeCheckAll mainModuleName moduleName rest
258258 return $ qualifyAllUnqualifiedNames moduleName env d : ds
src/Language/PureScript/TypeChecker/Types.hs+4−4
@@ -163,8 +163,8 @@ typeConstructorsAreEqual env moduleName = (==) `on` canonicalizeType moduleName
163163-- Infer the types of multiple mutually-recursive values, and return elaborated values including
164164-- type class dictionaries and type annotations.
165165--
166−typesOf :: ModuleName -> [(Ident, Value)] -> Check [(Ident, (Value, Type))]
167−typesOf moduleName vals = do
166+typesOf :: Maybe ModuleName -> ModuleName -> [(Ident, Value)] -> Check [(Ident, (Value, Type))]
167+typesOf mainModuleName moduleName vals = do
168168 tys <- fmap tidyUp . liftUnify $ do
169169 let
170170 -- Map each declaration to a name/value pair, with an optional type, if the declaration is typed
@@ -203,8 +203,8 @@ typesOf moduleName vals = do
203203 TypedValue _ val' ty <- bindNames dict' $ infer val
204204 ty =?= fromMaybe (error "name not found in dictionary") (lookup ident untypedDict)
205205 return (ident, (TypedValue True val' ty, ty))
206− -- If run-main is enabled, need to check that Main.main has type Eff eff a for some eff, a
207− when (moduleName == ModuleName (ProperName "Main") && fst e == Ident "main") $ do
206+ -- If --main is enabled, need to check that `main` has type Eff eff a for some eff, a
207+ when (Just moduleName == mainModuleName && fst e == Ident "main") $ do
208208 [eff, a] <- replicateM 2 fresh
209209 ty =?= TypeApp (TypeApp (TypeConstructor (Qualified (Just (ModuleName (ProperName "Eff"))) (ProperName "Eff"))) eff) a
210210 -- Make sure unification variables do not escape
tests/Main.hs+1−1
@@ -66,7 +66,7 @@ assertCompiles :: FilePath -> IO ()
6666assertCompiles inputFile = do
6767 putStrLn $ "assert " ++ inputFile ++ " compiles successfully"
6868 prelude <- preludeFilename
69− assert (P.defaultOptions { P.optionsRunMain = True, P.optionsNoOptimizations = True, P.optionsEntryPoint = Just "Main" }) [prelude, inputFile] $ either (return . Just) $ \js -> do
69+ assert (P.defaultOptions { P.optionsMain = Just "Main", P.optionsNoOptimizations = True, P.optionsModules = ["Main"] }) [prelude, inputFile] $ either (return . Just) $ \js -> do
7070 args <- getArgs
7171 if "--run-js" `elem` args
7272 then do