elm/compiler/Initialize.hs
2013-04-08 01:48:30 -07:00

97 lines
3.4 KiB
Haskell

module Initialize (build, buildFromSource) where
import Control.Applicative ((<$>))
import Control.Monad.Error
import Data.List (lookup,nub)
import qualified Data.Map as Map
import Ast
import Data.Either (lefts,rights)
import Data.List (intercalate,partition)
import Parse.Parser (parseProgram, preParse)
import Rename
import qualified Libraries as Libs
import Types.Types ((-:))
import Types.Hints (hints)
import Types.Unify
import Types.Alias (dealias, mistakes)
import Optimize
import CompileToJS (jsModule)
checkMistakes :: Module -> Either String Module
checkMistakes modul@(Module name ex im stmts) =
case mistakes stmts of
m:ms -> Left (unlines (m:ms))
[] -> return modul
checkTypes :: Module -> Either String Module
checkTypes modul =
do subs <- unify hints modul
subs `seq` return (optimize (renameModule modul))
check :: Module -> Either String Module
check = checkMistakes >=> checkTypes
buildFromSource :: Bool -> String -> Either String Module
buildFromSource withPrelude src = (check . add) =<< (parseProgram src)
where add = if withPrelude then Libs.addPrelude else id
build :: Bool -> FilePath -> IO (Either String [Module])
build withPrelude root = do
names <- getSortedModuleNames root
case names of
Left err -> return (Left err)
Right ns -> do srcs <- zipWithM buildFile' [1..] ns
return (sequence srcs)
where
buildFile' n name = putStrLn (msg n name) >> buildFile withPrelude name
msg n name = "["++show n++" of "++show (length ns)++"] Compiling "++name
buildFile :: Bool -> String -> IO (Either String Module)
buildFile withPrelude moduleName =
let filePath = toFilePath moduleName in
case isNative moduleName of
True -> return (Right $ Module [moduleName] [] [] [])
--return (Left "Can't do that yet")
--Right `liftM` readFile filePath
False -> do txt <- readFile filePath
return $ buildFromSource withPrelude txt
getSortedModuleNames :: FilePath -> IO (Either String [String])
getSortedModuleNames root = do
deps <- readDeps [] root
return (sortDeps =<< deps)
type Deps = (String, [String])
sortDeps :: [Deps] -> Either String [String]
sortDeps deps = go [] (nub deps)
where
msg = "A cyclical or missing module dependency or was detected in: "
go :: [String] -> [Deps] -> Either String [String]
go sorted [] = Right sorted
go sorted unsorted =
case partition (all (`elem` sorted) . snd) unsorted of
([],m:ms) -> Left (msg ++ intercalate ", " (map fst (m:ms)) ++ show sorted ++ show unsorted)
(srtd,unsrtd) -> go (sorted ++ map fst srtd) unsrtd
readDeps :: [FilePath] -> FilePath -> IO (Either String [Deps])
readDeps seen root = do
txt <- readFile root
case preParse txt of
Left err -> return (Left err)
Right (name,deps) -> do rest <- mapM (readDeps seen' . toFilePath) newDeps
return $ do rs <- sequence rest
return ((name, realDeps) : concat rs)
where realDeps = filter (`notElem` builtIns) deps
newDeps = filter (`notElem` seen) realDeps
seen' = root : seen ++ newDeps
builtIns = Map.keys Libs.libraries
isNative name = takeWhile (/='.') name == "Native"
toFilePath name = map swapDots name ++ ext
where swapDots '.' = '/'
swapDots c = c
ext = if isNative name then ".js" else ".elm"