on 3692669 help sign in
elk/elm/src/Compiler.hs
108 lines 4.3 kB history raw
1{-# LANGUAGE DeriveDataTypeable #-}
2module Main where
4import Data.Either (lefts, rights)
5import Data.List (intersect, intercalate)
6import Data.Maybe (fromMaybe)
7import System.Console.CmdArgs
8import Text.Blaze.Html.Renderer.String (renderHtml)
10import qualified Text.Jasmine as JS
11import qualified Data.ByteString.Lazy.Char8 as BS
13import Ast
14import Initialize
15import CompileToJS
16import GenerateHtml
17import Paths_Elm
20data ELM =
21 ELM { make :: Bool
22 , files :: [FilePath]
23 , runtime :: Maybe FilePath
24 , separate_js :: Bool
25 , only_js :: Bool
26 , import_js :: [FilePath]
27 , generate_noscript :: Bool
28 , minify :: Bool
29 }
30 deriving (Data,Typeable,Show,Eq)
32elm = ELM { make = False &= help "automatically compile dependencies."
33 , files = def &= args &= typ "FILES"
34 , runtime = Nothing &= typFile &=
35 help "Specify a custom location for Elm's runtime system."
36 , separate_js = False &= help "Compile to separate HTML and JS files."
37 , only_js = False &= help "Compile only to JavaScript."
38 , 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."
39 , generate_noscript = True &= help "Add generated <noscript> tag to HTML output."
40 , minify = False &= help "Minify generated JavaScript"
41 } &=
42 help "Compile Elm programs to HTML, CSS, and JavaScript." &=
43 summary "The Elm Compiler v0.4.0, (c) Evan Czaplicki"
45main = do
46 args <- cmdArgs elm
47 mini <- getDataFileName "elm-runtime-0.4.0.js"
48 compileArgs mini args
50compileArgs mini (ELM _ [] _ _ _ _ _ _) =
51 putStrLn "Usage: elm [OPTIONS] [FILES]\nFor more help: elm --help"
52compileArgs mini (ELM make files rtLoc split only js nscrpt isMini) =
53 mapM_ (fileTo isMini get what js nscrpt $ fromMaybe mini rtLoc) files
54 where get = if make then getModules [] else getModule
55 what = if only then JS else
56 if split then Split else HTML
58data What = JS | HTML | Split
60fileTo isMini get what jsFiles noscript rtLoc file = do
61 let jsStyle = if isMini then Minified else Readable
62 let formatJS = if isMini then BS.unpack . JS.minify . BS.pack else id
63 ems <- get file
64 jss <- concat `fmap` mapM readFile jsFiles
65 case ems of
66 Left err -> putStrLn $ "Error while compiling " ++ file ++ ":\n" ++ err
67 Right ms ->
68 let name = reverse . tail . dropWhile (/='.') $ reverse file
69 js = name ++ ".js"
70 html = name ++ ".html"
71 in case what of
72 JS -> writeFile js . formatJS $ jss ++ concatMap jsModule ms
73 HTML -> writeFile html . renderHtml $ modulesToHtml jsStyle "" rtLoc jss noscript ms
74 Split -> do
75 writeFile html . renderHtml $ linkedHtml rtLoc js ms
76 writeFile js . formatJS $ jss ++ concatMap jsModule ms
78getModules :: [String] -> FilePath -> IO (Either String [([String],Module)])
79getModules uses file = do
80 code <- readFile file
81 case initialize code of
82 Left err -> return . Left $ "Error in " ++ file ++ ":\n" ++ err
83 Right (escs, modul@(Module _ _ imports _)) ->
84 let imps = filter (`notElem` builtInModules) $ map fst imports in
85 case intersect uses imps of
86 x:_ -> return . Left $ "Error: Cyclic dependency. Module " ++
87 x ++ " depends on itself."
88 [] -> do
89 ems <- mapM (getModules (uses ++ imps) . toFilePath) imps
90 return $ case lefts ems of
91 [] -> Right $ concat (rights ems) ++ [(escs,modul)]
92 errs -> Left $ intercalate "\n" errs
94getModule :: FilePath -> IO (Either String [([String],Module)])
95getModule file = do
96 code <- readFile file
97 return . fmap (:[]) $ initialize code
99toFilePath :: String -> FilePath
100toFilePath modul = map (\c -> if c == '.' then '/' else c) modul ++ ".elm"
102builtInModules =
103 concat [ map ("Data."++) [ "List", "Char", "Maybe" ]
104 , map ("Signal."++) [ "Mouse", "Keyboard.Raw"
105 , "Window", "Time", "HTTP", "Input", "Random" ]
106 , [ "Element", "Text", "Color", "Line" ]
107 , [ "Prelude" ]
108 ]