on master help sign in
Start generating elmi and elmo files to permit sharing of types, values, and infix info between modules.
commit2d1e06b17bbf17021f37a454491e235ee4f792d7 authorevancz <info@elm-lang.org> dateWed 5 Jun 2013, 00:44 (13 years ago) parent2323afc
3 files changed+192−141
compiler/Compiler.hs+137−72 compiler/Gen/GenerateHtml.hs+26−26 compiler/Initialize.hs+29−43
compiler/Compiler.hs+137−72
@@ -1,91 +1,156 @@
11{-# LANGUAGE DeriveDataTypeable #-}
22module Main where
33
4+import Control.Monad (foldM, when)
5+import qualified Data.Map as Map
46import Data.Either (lefts, rights)
5−import Data.List (intersect, intercalate,lookup)
7+import Data.List (intersect, intercalate, lookup)
68import Data.Maybe (fromMaybe)
79import Data.Version (showVersion)
810import System.Console.CmdArgs
11+import System.Directory
912import System.Exit
1013import System.FilePath
11−import Text.Blaze.Html.Renderer.String (renderHtml)
14+import qualified Text.Blaze.Html.Renderer.Pretty as Pretty
15+import qualified Text.Blaze.Html.Renderer.String as Normal
1216
1317import qualified Text.Jasmine as JS
1418import qualified Data.ByteString.Lazy.Char8 as BS
1519
1620import Ast
17−import Initialize (build,buildFromSource)
18−import CompileToJS
19−import GenerateHtml
21+import Initialize (buildFromSource, getSortedModuleNames)
22+import CompileToJS (jsModule)
23+import GenerateHtml (createHtml, JSStyle(..), JSSource(..))
2024import qualified Libraries as Libraries
2125import Paths_Elm
2226
23−data ELM =
24− ELM { make :: Bool
25− , files :: [FilePath]
26− , runtime :: Maybe FilePath
27− , separate_js :: Bool
28− , only_js :: Bool
29− , import_js :: [FilePath]
30− , no_prelude :: Bool
31− , noscript :: Bool
32− , minify :: Bool
33− , output_directory :: Maybe FilePath
34− }
27+data Flags =
28+ Flags { make :: Bool
29+ , files :: [FilePath]
30+ , runtime :: Maybe FilePath
31+ , only_js :: Bool
32+ , scripts :: [FilePath]
33+ , no_prelude :: Bool
34+ , minify :: Bool
35+ , output_directory :: FilePath
36+ }
3537 deriving (Data,Typeable,Show,Eq)
3638
37−elm = ELM { make = False &= help "automatically compile dependencies."
38− , files = def &= args &= typ "FILES"
39− , runtime = Nothing &= typFile &=
40− help "Specify a custom location for Elm's runtime system."
41− , separate_js = False &= help "Compile to separate HTML and JS files."
42− , only_js = False &= help "Compile only to JavaScript."
43− , import_js = [] &= typFile &= help "Include a JavaScript file before the body of the Elm program. Can be used many times. Files will be included in the given order."
44− , no_prelude = False &= help "Do not import Prelude by default, used only when compiling standard libraries."
45− , noscript = True &= help "Add generated <noscript> tag to HTML output."
46− , minify = False &= help "Minify generated JavaScript"
47− , output_directory = Nothing &= typFile &= help "Output files to directory specified. Defaults to the location of original file."
48− } &=
49− help "Compile Elm programs to HTML, CSS, and JavaScript." &=
50− summary ("The Elm Compiler " ++ showVersion version ++ ", (c) Evan Czaplicki")
51−
52−main = do
53− args <- cmdArgs elm
54− mini <- getDataFileName "elm-runtime.js"
55− compileArgs mini args
56−
57−compileArgs mini flags =
58− case files flags of
59− [] -> putStrLn "Usage: elm [OPTIONS] [FILES]\nFor more help: elm --help"
60− fs -> mapM_ (fileTo flags what loc) fs
61− where loc = fromMaybe mini (runtime flags)
62− what | only_js flags = JS
63− | separate_js flags = Split
64− | otherwise = HTML
65−
66−data What = JS | HTML | Split
67−
68−fileTo flags what rtLoc file = do
69− let jsStyle = if minify flags then Minified else Readable
70− formatJS = if minify flags then BS.unpack . JS.minify . BS.pack else id
71− prelude = not (no_prelude flags)
72− ems <- if make flags then build prelude file
73− else do src <- readFile file
74− return (fmap (:[]) (buildFromSource prelude src))
75− jss <- concat `fmap` mapM readFile (import_js flags)
76− case ems of
77− Left err -> do putStrLn $ "Error while compiling " ++ file ++ ":\n" ++ err
78− exitFailure
79− Right ms' ->
80− let path = fromMaybe "" (output_directory flags) </> file
81− js = replaceExtension path ".js"
82− html = replaceExtension path ".html"
83− ms = if no_prelude flags then ms' else map Libraries.addPrelude ms'
84− txt = jss ++ concatMap jsModule ms
85− in case what of
86− JS -> writeFile js (formatJS txt)
87− HTML -> writeFile html . renderHtml $
88− modulesToHtml jsStyle "" rtLoc jss (noscript flags) ms
89− Split ->
90− do writeFile html . renderHtml $ linkedHtml rtLoc js ms
91− writeFile js (formatJS txt)
39+flags = Flags
40+ { make = False
41+ &= help "automatically compile dependencies."
42+ , files = def &= args &= typ "FILES"
43+ , runtime = Nothing &= typFile
44+ &= help "Specify a custom location for Elm's runtime system."
45+ , only_js = False
46+ &= help "Compile only to JavaScript."
47+ , scripts = [] &= typFile
48+ &= help "Load JavaScript files in generated HTML. Files will be included in the given order."
49+ , no_prelude = False
50+ &= help "Do not import Prelude by default, used only when compiling standard libraries."
51+ , minify = False
52+ &= help "Minify generated JavaScript"
53+ , output_directory = "ElmFiles" &= typFile
54+ &= help "Output files to directory specified. Defaults to ElmFiles/ directory."
55+ } &= help "Compile Elm programs to HTML, CSS, and JavaScript."
56+ &= summary ("The Elm Compiler " ++ showVersion version ++ ", (c) Evan Czaplicki")
57+
58+main :: IO ()
59+main = compileArgs =<< cmdArgs flags
60+
61+compileArgs :: Flags -> IO ()
62+compileArgs flags =
63+ let builder file = if make flags then build flags file
64+ else buildFile flags 1 1 file >> return ()
65+ in case files flags of
66+ [] -> putStrLn "Usage: elm [OPTIONS] [FILES]\nFor more help: elm --help"
67+ fs -> mapM_ builder fs
68+
69+
70+type Interface = String
71+
72+buildFile :: Flags -> Int -> Int -> FilePath -> IO Interface
73+buildFile flags moduleNum numModules filePath =
74+ do compiled <- alreadyCompiled
75+ if compiled then getInterface else compile
76+
77+ where
78+ file :: String -> FilePath
79+ file ext = replaceExtension filePath ext
80+
81+ interface :: FilePath
82+ interface = file "elmi"
83+
84+ alreadyCompiled :: IO Bool
85+ alreadyCompiled = do
86+ exists <- doesFileExist interface
87+ if not exists then return False
88+ else do tsrc <- getModificationTime filePath
89+ tint <- getModificationTime interface
90+ return (tsrc < tint)
91+
92+ number :: String
93+ number = "[" ++ show moduleNum ++ " of " ++ show numModules ++ "]"
94+
95+ name :: String
96+ name = intercalate "." (splitDirectories (dropExtensions filePath))
97+
98+ compile :: IO Interface
99+ compile = do
100+ putStrLn (number ++ " Compiling " ++ name)
101+ source <- readFile filePath
102+ js <- if takeExtension filePath == ".js" then return source else
103+ case buildFromSource (no_prelude flags) source of
104+ Left err -> putStrLn err >> exitFailure
105+ Right modul -> return (jsModule modul)
106+ writeFile interface js
107+ writeFile (file "elmo") js
108+ when (only_js flags) (writeFile (file "js") js)
109+ return js
110+
111+ getInterface :: IO Interface
112+ getInterface = do
113+ readFile interface
114+
115+getRuntime :: Flags -> IO FilePath
116+getRuntime flags =
117+ case runtime flags of
118+ Just fp -> return fp
119+ Nothing -> getDataFileName "elm-runtime.js"
120+
121+build :: Flags -> FilePath -> IO ()
122+build flags rootFile = do
123+ files <- getSortedModuleNames rootFile
124+ buildFiles flags (length files) Map.empty files
125+ js <- foldM appendToOutput "" files
126+ case only_js flags of
127+ True -> do
128+ putStr "\nGenerating JavaScript... "
129+ writeFile (replaceExtension rootFile "js") (genJs js)
130+ putStrLn "Done"
131+ False -> do
132+ putStr "\nGenerating HTML... "
133+ runtime <- getRuntime flags
134+ let html = genHtml $ createHtml runtime rootFile (sources js) ""
135+ writeFile (replaceExtension rootFile "html") html
136+ putStrLn "Done"
137+
138+ where
139+ appendToOutput :: String -> FilePath -> IO String
140+ appendToOutput js filePath =
141+ do src <- readFile (replaceExtension filePath "elmo")
142+ return (src ++ js)
143+
144+ genHtml = if minify flags then Normal.renderHtml else Pretty.renderHtml
145+ genJs = if minify flags then BS.unpack . JS.minify . BS.pack else id
146+ sources js = map Link (scripts flags) ++
147+ [ Source (if minify flags then Minified else Readable) js ]
148+
149+
150+buildFiles :: Flags -> Int -> Map.Map String Interface -> [FilePath] -> IO ()
151+buildFiles _ _ _ [] = return ()
152+buildFiles flags numModules interfaces (filePath:rest) = do
153+ interface <- buildFile flags (numModules - length rest) numModules filePath
154+ let moduleName = intercalate "." (splitDirectories (dropExtensions filePath))
155+ interfaces' = Map.insert moduleName interface interfaces
156+ buildFiles flags numModules interfaces' rest
compiler/Gen/GenerateHtml.hs+26−26
@@ -1,7 +1,8 @@
11{-# LANGUAGE OverloadedStrings #-}
22module GenerateHtml (generateHtml,
3− modulesToHtml, linkedHtml,
4− JSStyle (..)
3+ createHtml,
4+ JSStyle (..),
5+ JSSource (..)
56 ) where
67
78import Data.List (intercalate)
@@ -21,15 +22,17 @@ import ExtractNoscript
2122import Libraries as Libraries
2223
2324data JSStyle = Minified | Readable
25+data JSSource = Link String | Source JSStyle String
2426
25−makeScript :: JSStyle -> Either String String -> H.Html
26−makeScript _ (Left s) =
27− H.script ! A.type_ "text/javascript" ! A.src (H.toValue s) $ ""
28−makeScript jsStyle (Right s) =
29− H.script ! A.type_ "text/javascript" $ preEscapedToMarkup content
30− where content = case jsStyle of
31− Minified -> BS.unpack . minify . BS.pack $ s
32− Readable -> s
27+makeScript :: JSSource -> H.Html
28+makeScript source =
29+ case source of
30+ Link src -> H.script ! A.type_ "text/javascript" ! A.src (H.toValue src) $ ""
31+ Source style src ->
32+ H.script ! A.type_ "text/javascript" $
33+ preEscapedToMarkup $ case style of
34+ Minified -> BS.unpack . minify . BS.pack $ src
35+ Readable -> src
3336
3437-- |This function compiles Elm code into simple HTML.
3538--
@@ -40,14 +43,16 @@ generateHtml :: String -- ^ Location of elm-runtime.js as expected by the browse
4043 -> String -- ^ The page title
4144 -> String -- ^ The elm source code.
4245 -> Html
43−generateHtml libLoc title source =
46+generateHtml libLoc title source = H.span "broken for now"
47+{--
4448 case buildFromSource True source of
45− Right modul -> modulesToHtml Readable title libLoc [] True [modul]
49+ Right modul -> createHtml libLoc title [] "" title [modul]
4650 Left err -> createHtml Readable libLoc title (Right $ showErr err)
4751 (H.noscript "") "Main"
48−
49−
50−modulesToHtml jsStyle title libLoc jss nscrpt modules =
52+--}
53+{--
54+modulesToHtml :: FilePath -> String -> []
55+modulesToHtml libLoc title scripts nscrpt modules =
5156 createHtml jsStyle libLoc title' js noscript altTitle
5257 where
5358 js = Right $ jss ++ concatMap jsModule modules
@@ -55,21 +60,16 @@ modulesToHtml jsStyle title libLoc jss nscrpt modules =
5560 title' = if null title then altTitle else title
5661 altTitle = intercalate "." names
5762 where Module names _ _ _ = last modules
58−
59−
60−linkedHtml rtLoc jsLoc modules =
61− createHtml Readable rtLoc title (Left jsLoc) (H.noscript "") title
62− where
63− title = (\(Module names _ _ _) -> intercalate "." names) (last modules)
64−
63+--}
6564
66−createHtml jsStyle libLoc title js noscript moduleToLoad =
65+createHtml :: FilePath -> String -> [JSSource] -> String -> Html
66+createHtml libLoc title scripts noscript =
6767 H.docTypeHtml $ do
6868 H.head $ do
6969 H.meta ! A.charset "UTF-8"
7070 H.title . H.toHtml $ title
71− makeScript Readable (Left libLoc)
72− makeScript jsStyle js
71+ makeScript (Link libLoc)
72+ mapM_ makeScript scripts
7373 H.body $ do
74− H.script ! A.type_ "text/javascript" $ preEscapedToMarkup ("Elm.fullscreen(Elm." ++ moduleToLoad ++ ")" :: String)
74+ H.script ! A.type_ "text/javascript" $ preEscapedToMarkup ("Elm.fullscreen(Elm.Main)" :: String)
7575 H.noscript $ preEscapedToMarkup noscript
compiler/Initialize.hs+29−43
@@ -1,4 +1,4 @@
1−module Initialize (build, buildFromSource) where
1+module Initialize (buildFromSource, getSortedModuleNames) where
22
33import Control.Applicative ((<$>))
44import Control.Monad.Error
@@ -16,7 +16,8 @@ import Types.Hints (hints)
1616import Types.Unify (unify)
1717import Types.Alias (dealias, mistakes)
1818import Optimize
19−import CompileToJS (jsModule)
19+import System.Exit
20+import System.FilePath
2021
2122checkMistakes :: Module -> Either String Module
2223checkMistakes modul@(Module name ex im stmts) =
@@ -33,64 +34,49 @@ check :: Module -> Either String Module
3334check = checkMistakes >=> checkTypes
3435
3536buildFromSource :: Bool -> String -> Either String Module
36−buildFromSource withPrelude src = (check . add) =<< (parseProgram src)
37− where add = if withPrelude then Libs.addPrelude else id
37+buildFromSource noPrelude src =
38+ let add = if noPrelude then id else Libs.addPrelude in
39+ (check . add) =<< (parseProgram src)
3840
39−build :: Bool -> FilePath -> IO (Either String [Module])
40−build withPrelude root = do
41− names <- getSortedModuleNames root
42− case names of
43− Left err -> return (Left err)
44− Right ns -> do srcs <- zipWithM buildFile' [1..] ns
45− return (sequence srcs)
46− where
47− buildFile' n name = putStrLn (msg n name) >> buildFile withPrelude name
48− msg n name = "["++show n++" of "++show (length ns)++"] Compiling "++name
49−
50−buildFile :: Bool -> String -> IO (Either String Module)
51−buildFile withPrelude moduleName =
52− let filePath = toFilePath moduleName in
53− case isNative moduleName of
54− True -> return (Right $ Module [moduleName] [] [] [])
55− --return (Left "Can't do that yet")
56− --Right `liftM` readFile filePath
57− False -> do txt <- readFile filePath
58− return $ buildFromSource withPrelude txt
59−
60−
61−getSortedModuleNames :: FilePath -> IO (Either String [String])
62−getSortedModuleNames root = do
63− deps <- readDeps [] root
64− return (sortDeps =<< deps)
41+getSortedModuleNames :: FilePath -> IO [String]
42+getSortedModuleNames root =
43+ sortDeps =<< readDeps [] root
6544
6645type Deps = (String, [String])
6746
68−sortDeps :: [Deps] -> Either String [String]
47+sortDeps :: [Deps] -> IO [String]
6948sortDeps deps = go [] (nub deps)
7049 where
7150 msg = "A cyclical or missing module dependency or was detected in: "
7251
73− go :: [String] -> [Deps] -> Either String [String]
74− go sorted [] = Right sorted
52+ go :: [String] -> [Deps] -> IO [String]
53+ go sorted [] = return (map toFilePath sorted)
7554 go sorted unsorted =
7655 case partition (all (`elem` sorted) . snd) unsorted of
77− ([],m:ms) -> Left (msg ++ intercalate ", " (map fst (m:ms)) ++ show sorted ++ show unsorted)
56+ ([],m:ms) -> do putStrLn (msg ++ intercalate ", " (map fst (m:ms)) ++ show sorted ++ show unsorted)
57+ exitFailure
7858 (srtd,unsrtd) -> go (sorted ++ map fst srtd) unsrtd
7959
80−readDeps :: [FilePath] -> FilePath -> IO (Either String [Deps])
60+readDeps :: [FilePath] -> FilePath -> IO [Deps]
8161readDeps seen root = do
8262 txt <- readFile root
8363 case preParse txt of
84− Left err -> return (Left err)
85− Right (name,deps) -> do rest <- mapM (readDeps seen' . toFilePath) newDeps
86− return $ do rs <- sequence rest
87− return ((name, realDeps) : concat rs)
88− where realDeps = filter (`notElem` builtIns) deps
89− newDeps = filter (`notElem` seen) realDeps
90− seen' = root : seen ++ newDeps
91− builtIns = Map.keys Libs.libraries
64+ Left err ->
65+ let msg = "Error resolving dependencies in " ++ root ++ ":\n" in
66+ putStrLn (msg ++ err) >> exitFailure
67+ Right (name,deps) ->
68+ do rest <- mapM (readDeps seen' . toFilePath) newDeps
69+ return ((name, realDeps) : concat rest)
70+ where
71+ realDeps = filter (`notElem` builtIns) deps
72+ newDeps = filter (\d -> d `notElem` seen && not (isNative d)) realDeps
73+ seen' = root : seen ++ newDeps
74+ builtIns = Map.keys Libs.libraries
75+
9276
9377isNative name = takeWhile (/='.') name == "Native"
78+
79+toFilePath :: String -> FilePath
9480toFilePath name = map swapDots name ++ ext
9581 where swapDots '.' = '/'
9682 swapDots c = c