summaryrefslogtreecommitdiff
path: root/src/LambdaCube/Compiler.hs
diff options
context:
space:
mode:
authorPéter Diviánszky <divipp@gmail.com>2016-02-22 10:09:38 +0100
committerPéter Diviánszky <divipp@gmail.com>2016-02-22 10:12:05 +0100
commita0b58620c52fbdc430f595185fbabede3be33d86 (patch)
tree2ee518ed721dab753384f4c207dd02ceac8c2ebe /src/LambdaCube/Compiler.hs
parent453a0839d7d4c2c83b4b74031c3e073495092b53 (diff)
refactoring
Diffstat (limited to 'src/LambdaCube/Compiler.hs')
-rw-r--r--src/LambdaCube/Compiler.hs232
1 files changed, 127 insertions, 105 deletions
diff --git a/src/LambdaCube/Compiler.hs b/src/LambdaCube/Compiler.hs
index 54013b74..91c65666 100644
--- a/src/LambdaCube/Compiler.hs
+++ b/src/LambdaCube/Compiler.hs
@@ -15,7 +15,7 @@ module LambdaCube.Compiler
15 , MM, runMM 15 , MM, runMM
16 , Err 16 , Err
17 , catchMM, catchErr 17 , catchMM, catchErr
18 , ioFetch 18 , ioFetch, decideFilePath
19 , getDef, compileMain, preCompile 19 , getDef, compileMain, preCompile
20 , removeFromCache 20 , removeFromCache
21 21
@@ -25,6 +25,7 @@ module LambdaCube.Compiler
25 ) where 25 ) where
26 26
27import Data.List 27import Data.List
28import Data.Maybe
28import Data.Function 29import Data.Function
29import Data.Map.Strict (Map) 30import Data.Map.Strict (Map)
30import qualified Data.Map.Strict as Map 31import qualified Data.Map.Strict as Map
@@ -46,34 +47,102 @@ import qualified Text.Show.Pretty as PP
46import LambdaCube.IR as IR 47import LambdaCube.IR as IR
47import LambdaCube.Compiler.Pretty hiding ((</>)) 48import LambdaCube.Compiler.Pretty hiding ((</>))
48import LambdaCube.Compiler.Parser (Module(..), Export(..), ImportItems (..), runDefParser, parseLC) 49import LambdaCube.Compiler.Parser (Module(..), Export(..), ImportItems (..), runDefParser, parseLC)
50import LambdaCube.Compiler.Lexer (DesugarInfo)
49import LambdaCube.Compiler.Lexer as Exported (Range(..)) 51import LambdaCube.Compiler.Lexer as Exported (Range(..))
50import LambdaCube.Compiler.Infer (PolyEnv(..), showError, joinPolyEnvs, filterPolyEnv, inference_) 52import LambdaCube.Compiler.Infer (showError, inference, GlobalEnv, initEnv)
51import LambdaCube.Compiler.Infer as Exported (Infos, listAllInfos, listTypeInfos, listTraceInfos, Exp, outputType, boolType, trueExp, unfixlabel) 53import LambdaCube.Compiler.Infer as Exported (Infos, listAllInfos, listTypeInfos, listTraceInfos, Exp, outputType, boolType, trueExp, unfixlabel)
52import LambdaCube.Compiler.CoreToIR 54import LambdaCube.Compiler.CoreToIR
53 55
54-- inlcude path for: Builtins, Internals and Prelude 56-- inlcude path for: Builtins, Internals and Prelude
55import Paths_lambdacube_compiler (getDataDir) 57import Paths_lambdacube_compiler (getDataDir)
56 58
57type EName = String 59--------------------------------------------------------------------------------
58type MName = String
59 60
60type Modules = Map FilePath (Either Doc (PolyEnv, String)) 61readFileStrict :: FilePath -> IO String
61type ModuleFetcher m = Maybe FilePath -> MName -> m (FilePath, MName, String) 62readFileStrict = fmap T.unpack . TIO.readFile
62 63
63-- todo: use RWS 64readFile' :: FilePath -> IO (Maybe (IO String))
64newtype MMT m a = MMT { runMMT :: ExceptT String (ReaderT (ModuleFetcher (MMT m)) (StateT Modules (WriterT Infos m))) a } 65readFile' fname = do
65 deriving (Functor, Applicative, Monad, MonadReader (ModuleFetcher (MMT m)), MonadState Modules, MonadError String, MonadIO, MonadThrow, MonadCatch, MonadMask, MonadWriter Infos) 66 b <- doesFileExist fname
66type MM = MMT IO 67 return $ if b then Just $ readFileStrict fname else Nothing
67 68
68instance MonadMask m => MonadMask (ExceptT e m) where 69instance MonadMask m => MonadMask (ExceptT e m) where
69 mask f = ExceptT $ mask $ \u -> runExceptT $ f (mapExceptT u) 70 mask f = ExceptT $ mask $ \u -> runExceptT $ f (mapExceptT u)
70 uninterruptibleMask = error "not implemented: uninterruptibleMask for ExcpetT" 71 uninterruptibleMask = error "not implemented: uninterruptibleMask for ExcpetT"
71 72
73prettyShowUnlines :: Show a => a -> String
74prettyShowUnlines = goPP 0 . PP.ppShow
75 where
76 goPP _ [] = []
77 goPP n ('"':xs) | isMultilineString xs = "\"\"\"\n" ++ indent ++ go xs where
78 indent = replicate n ' '
79 go ('\\':'n':xs) = "\n" ++ indent ++ go xs
80 go ('\\':c:xs) = '\\':c:go xs
81 go ('"':xs) = "\n" ++ indent ++ "\"\"\"" ++ goPP n xs
82 go (x:xs) = x : go xs
83 goPP n (x:xs) = x : goPP (if x == '\n' then 0 else n+1) xs
84
85 isMultilineString ('\\':'n':xs) = True
86 isMultilineString ('\\':c:xs) = isMultilineString xs
87 isMultilineString ('"':xs) = False
88 isMultilineString (x:xs) = isMultilineString xs
89 isMultilineString [] = False
90
91--------------------------------------------------------------------------------
92
93type MName = String
94type SName = String
95type SourceCode = String
96
97-- file name or module name?
98decideFilePath n
99 | takeExtension n == ".lc" = Left n
100 | otherwise = Right n
101
102dropExtension' e f
103 | takeExtension f == e = dropExtension f
104 | otherwise = error $ "dropExtension: expcted extension: " ++ e ++ " ; filename: " ++ f
105
106fileNameToModuleName n
107 = intercalate "." $ remDot $ (\(a, b) -> map takeDirectory (splitPath a) ++ [b]) $ splitFileName $ dropExtension' ".lc" $ normalise n
108 where
109 remDot (".": xs) = xs
110 remDot xs = xs
111
112moduleNameToFileName n = hn n ++ ".lc"
113 where
114 hn = h []
115 h acc [] = reverse acc
116 h acc ('.':cs) = reverse acc </> hn cs
117 h acc (c: cs) = h (c: acc) cs
118
119type ModuleFetcher m = Maybe FilePath -> Either FilePath MName -> m (FilePath, MName, m SourceCode)
120
121ioFetch :: MonadIO m => [FilePath] -> ModuleFetcher (MMT m x)
122ioFetch paths imp n = do
123 preludePath <- (</> "lc") <$> liftIO getDataDir
124 find $ nubBy ((==) `on` fst) $ map (first normalise . lcModuleFile) $ paths ++ [preludePath]
125 where
126 find ((x, mn): xs) = liftIO (readFile' x) >>= maybe (find xs) (\src -> return (x, mn, liftIO src))
127 find [] = throwError $ show $ "can't find " <+> either (("lc file" <+>) . text) (("module" <+>) . text) n
128 <+> "in path" <+> hsep (map text paths)
129
130 lcModuleFile path = case n of
131 Left n -> (path </> n, fileNameToModuleName n)
132 Right n -> (path </> moduleNameToFileName n, n)
133
134--------------------------------------------------------------------------------
135
136-- todo: use RWS
137newtype MMT m x a = MMT { runMMT :: ExceptT String (ReaderT (ModuleFetcher (MMT m x)) (StateT (Modules x) (WriterT Infos m))) a }
138 deriving (Functor, Applicative, Monad, MonadReader (ModuleFetcher (MMT m x)), MonadState (Modules x), MonadError String, MonadIO, MonadThrow, MonadCatch, MonadMask, MonadWriter Infos)
139type MM = MMT IO Infos
140
72mapMMT f (MMT m) = MMT $ f m 141mapMMT f (MMT m) = MMT $ f m
73 142
74type Err a = (Either String a, Infos) 143type Err a = (Either String a, Infos)
75 144
76runMM :: Monad m => ModuleFetcher (MMT m) -> MMT m a -> m (Err a) 145runMM :: Monad m => ModuleFetcher (MMT m x) -> MMT m x a -> m (Err a)
77runMM fetcher 146runMM fetcher
78 = runWriterT 147 = runWriterT
79 . flip evalStateT mempty 148 . flip evalStateT mempty
@@ -87,102 +156,74 @@ catchErr er m = (force <$> m >>= liftIO . evaluate) `catch` getErr `catch` getPM
87 getErr (e :: ErrorCall) = catchErr er $ er $ show e 156 getErr (e :: ErrorCall) = catchErr er $ er $ show e
88 getPMatchFail (e :: PatternMatchFail) = catchErr er $ er $ show e 157 getPMatchFail (e :: PatternMatchFail) = catchErr er $ er $ show e
89 158
90catchMM :: Monad m => MMT m a -> (String -> Infos -> MMT m a) -> MMT m a 159catchMM :: Monad m => MMT m x a -> (String -> Infos -> MMT m x a) -> MMT m x a
91catchMM m e = mapMMT (lift . mapReaderT (mapStateT $ lift . runWriterT >=> f) . runExceptT) m >>= either (uncurry e) return 160catchMM m e = mapMMT (lift . mapReaderT (mapStateT $ lift . runWriterT >=> f) . runExceptT) m >>= either (uncurry e) return
92 where 161 where
93 f ((Right x, m), is) = tell is >> return (Right x, m) 162 f ((Right x, m), is) = tell is >> return (Right x, m)
94 f ((Left e, m), is) = return (Left (e, is), m) 163 f ((Left e, m), is) = return (Left (e, is), m)
95 164
96-- TODO: remove dependent modules from cache too? 165-- TODO: remove dependent modules from cache too?
97removeFromCache :: Monad m => FilePath -> MMT m () 166removeFromCache :: Monad m => FilePath -> MMT m x ()
98removeFromCache f = modify $ Map.delete f 167removeFromCache f = modify $ Map.delete f
99 168
100readFileStrict :: FilePath -> IO String 169type Module' x = (SourceCode, DesugarInfo, GlobalEnv, x)
101readFileStrict = fmap T.unpack . TIO.readFile
102 170
103readFile' :: FilePath -> IO (Maybe String) 171type Modules x = Map FilePath (Either (SourceCode, Module) (Module' x))
104readFile' fname = do
105 b <- doesFileExist fname
106 if b then Just <$> readFileStrict fname else return Nothing
107 172
108ioFetch :: MonadIO m => [FilePath] -> ModuleFetcher (MMT m) 173loadModule :: MonadMask m => (Infos -> x) -> Maybe FilePath -> Either FilePath MName -> MMT m x (FilePath, Module' x)
109ioFetch paths imp n = do 174loadModule ex imp mname_ = do
110 preludePath <- (</> "lc") <$> liftIO getDataDir 175 (fname, mname, srcm) <- ask >>= \fetch -> fetch imp mname_
111 let
112 fnames = nubBy ((==) `on` fst) $ map (first normalise) $ concatMap lcModuleFile $ paths ++ [preludePath]
113 lcModuleFile path = g imp
114 where
115 g _ | takeExtension n == ".lc" = [(path </> n, intercalate "." $ remDot $ (\(a, b) -> a ++ [b]) $ map takeDirectory . splitPath *** id $ splitFileName $ dropExtension $ normalise n)]
116 | otherwise = [(path </> hn n ++ ".lc", n)]
117
118 remDot (".": xs) = xs
119 remDot xs = xs
120
121 hn = h []
122 h acc [] = reverse acc
123 h acc ('.':cs) = reverse acc </> hn cs
124 h acc (c: cs) = h (c: acc) cs
125
126 f [] = throwError $ show $ "can't find module" <+> text n <+> "in path" <+> hsep (map text paths)
127 f ((x, mn): xs) = liftIO (readFile' x) >>= \case
128 Nothing -> f xs
129 Just src -> do
130 --liftIO $ putStrLn $ "loading " ++ x
131 return (x, mn, src)
132 f fnames
133
134loadModule :: MonadMask m => Maybe FilePath -> MName -> MMT m (FilePath, PolyEnv)
135loadModule imp mname_ = do
136 fetch <- ask
137 (fname, mname, src) <- fetch imp mname_
138 c <- gets $ Map.lookup fname 176 c <- gets $ Map.lookup fname
139 case c of 177 case c of
140 Just (Right (m, _)) -> return (fname, m) 178 Just (Right m) -> return (fname, m)
141 Just (Left e) -> throwError $ show $ "cycles in module imports:" <+> pShow mname <+> e 179 Just (Left (_, e)) -> throwError $ show $ "cycles in module imports:" <+> pShow mname <+> pShow (fst <$> moduleImports e)
142 _ -> do 180 _ -> do
181 src <- srcm
143 e <- either (throwError . show) return $ parseLC fname src 182 e <- either (throwError . show) return $ parseLC fname src
144 modify $ Map.insert fname $ Left $ pShow $ map fst $ moduleImports e 183 modify $ Map.insert fname $ Left (src, e)
145 let 184 let
146 loadModuleImports (m, is) = do 185 loadModuleImports (m, is) = do
147 filterPolyEnv (filterImports is) . snd <$> loadModule (Just fname) (snd m) 186 (_, (_, ds, ge, _)) <- loadModule ex (Just fname) (Right $ snd m)
187 return (ds{-todo: filter-}, Map.filterWithKey (\k _ -> filterImports is k) ge)
188
189 filterImports (ImportAllBut ns) = not . (`elem` map snd ns)
190 filterImports (ImportJust ns) = (`elem` map snd ns)
148 do 191 do
149 ms <- mapM loadModuleImports $ moduleImports e 192 ms <- mapM loadModuleImports $ moduleImports e
150 x' <- {-trace ("loading " ++ fname) $-} do 193 let (ds, ge) = mconcat ms
151 env@(PolyEnv ge _ ds) <- joinPolyEnvs False ms 194 (defs, dsinfo) <- MMT $ mapExceptT (return . runIdentity) $ runDefParser ds $ definitions e
152 defs <- MMT $ mapExceptT (return . runIdentity) $ runDefParser ds $ definitions e 195 srcs <- gets $ fmap $ either fst (\(src, _, _, _) -> src)
153 srcs <- gets $ Map.mapMaybe (either (const Nothing) (Just . snd)) 196 let
154 x <- MMT $ mapExceptT (lift . lift . mapWriterT (return . first (left $ showError (Map.insert fname src srcs)) . runIdentity)) $ inference_ env (extensions e) defs 197 -- todo: better story for info handling
155 case moduleExports e of 198 ff (Left e, is) = Left (showError srcs e) <$ tell is
156 Nothing -> return x 199 ff (Right ge, is) = return $ Right (mconcat ge, is)
157 Just es -> joinPolyEnvs False $ flip map es $ \exp -> case exp of 200 (newge, is) <- MMT $ mapExceptT (lift . lift . mapWriterT (return . runIdentity) . (ff <=< runWriterT . flip runReaderT (extensions e, initEnv <> ge))) $ inference defs
158 ExportId (snd -> d) -> case Map.lookup d $ getPolyEnv x of 201 (ds', ge') <- fmap mconcat $ forM (fromMaybe [ExportModule (mempty, mname)] $ moduleExports e) $ \exp -> case exp of
159 Just def -> PolyEnv (Map.singleton d def) mempty mempty{-TODO-} 202 ExportId (snd -> d) -> case Map.lookup d newge of
160 Nothing -> error $ d ++ " is not defined" 203 Just def -> return (mempty{-TODO-}, Map.singleton d def)
161 ExportModule (snd -> m) | m == mname -> x 204 Nothing -> error $ d ++ " is not defined"
162 ExportModule m -> case [ ms 205 ExportModule (snd -> m) | m == mname -> return (dsinfo, newge)
163 | ((m', is), ms) <- zip (moduleImports e) ms, m' == m] of 206 ExportModule m -> case [ x | ((m', _), x) <- zip (moduleImports e) ms, m' == m] of
164 [PolyEnv x infos ds] -> PolyEnv x mempty{-TODO-} ds 207 [x] -> return x
165 [] -> error $ "empty export list: " ++ show (fname, m, map fst $ moduleImports e, mname) 208 [] -> throwError $ "empty export list: " ++ show (fname, m, map fst $ moduleImports e, mname)
166 _ -> error "export list: internal error" 209 _ -> error "export list: internal error"
167 modify $ Map.insert fname $ Right (x', src) 210 let m = (src, ds', ge', ex is)
168 return (fname, x') 211 modify $ Map.insert fname $ Right m
212 return (fname, m)
169 `catchMM` (\e is -> modify (Map.delete fname) >> tell is >> throwError e) 213 `catchMM` (\e is -> modify (Map.delete fname) >> tell is >> throwError e)
170 214
171filterImports (ImportAllBut ns) = not . (`elem` map snd ns)
172filterImports (ImportJust ns) = (`elem` map snd ns)
173
174-- used in runTests 215-- used in runTests
175getDef :: MonadMask m => MName -> EName -> Maybe Exp -> MMT m (FilePath, Either String (Exp, Exp), Infos) 216getDef :: MonadMask m => FilePath -> SName -> Maybe Exp -> MMT m Infos (FilePath, Either String (Exp, Exp), Infos)
176getDef m d ty = do 217getDef m d ty = do
177 (fname, pe) <- loadModule Nothing m 218 (fname, (_, _, ge, infos)) <- loadModule id Nothing $ Left m
178 return 219 return
179 ( fname 220 ( fname
180 , case Map.lookup d $ getPolyEnv pe of 221 , case Map.lookup d ge of
181 Just (e, thy, si) 222 Just (e, thy, si)
182 | Just False <- (== thy) <$> ty -> Left $ "type of " ++ d ++ " should be " ++ show ty ++ " instead of " ++ ppShow thy -- TODO: better type comparison 223 | Just False <- (== thy) <$> ty -> Left $ "type of " ++ d ++ " should be " ++ show ty ++ " instead of " ++ ppShow thy -- TODO: better type comparison
183 | otherwise -> Right (e, thy) 224 | otherwise -> Right (e, thy)
184 Nothing -> Left $ d ++ " is not found" 225 Nothing -> Left $ d ++ " is not found"
185 , infos pe 226 , infos
186 ) 227 )
187 228
188parseAndToCoreMain m = either throwError return . (\(_, e, i) -> flip (,) i <$> e) =<< getDef m "main" (Just outputType) 229parseAndToCoreMain m = either throwError return . (\(_, e, i) -> flip (,) i <$> e) =<< getDef m "main" (Just outputType)
@@ -190,41 +231,22 @@ parseAndToCoreMain m = either throwError return . (\(_, e, i) -> flip (,) i <$>
190-- | most commonly used interface for end users 231-- | most commonly used interface for end users
191compileMain :: [FilePath] -> IR.Backend -> MName -> IO (Either String IR.Pipeline) 232compileMain :: [FilePath] -> IR.Backend -> MName -> IO (Either String IR.Pipeline)
192compileMain path backend fname 233compileMain path backend fname
193 = fmap ((id +++ fst) . fst) $ runMM (ioFetch path) $ first (compilePipeline backend) <$> parseAndToCoreMain fname 234 = fmap (right fst . fst) $ runMM (ioFetch path) $ first (compilePipeline backend) <$> parseAndToCoreMain fname
194
195-- | Removes the escaping characters from the error message
196removeEscapes = first (removeEscs +++ id)
197 235
198-- used by the compiler-service of the online editor 236-- used by the compiler-service of the online editor
199preCompile :: (MonadMask m, MonadIO m) => [FilePath] -> [FilePath] -> Backend -> String -> IO (String -> m (Err (IR.Pipeline, Infos))) 237preCompile :: (MonadMask m, MonadIO m) => [FilePath] -> [FilePath] -> Backend -> FilePath -> IO (String -> m (Err (IR.Pipeline, Infos)))
200preCompile paths paths' backend mod = do 238preCompile paths paths' backend mod = do
201 res <- runMM (ioFetch paths) $ loadModule Nothing mod 239 res <- runMM (ioFetch paths) $ loadModule id Nothing $ Left mod
202 case res of 240 case res of
203 (Left err, i) -> error $ "Prelude could not compiled: " ++ err 241 (Left err, i) -> error $ "Prelude could not compiled: " ++ err
204 (Right (_, prelude), _) -> return compile 242 (Right (_, prelude), _) -> return compile
205 where 243 where
206 compile src = fmap removeEscapes . runMM fetch $ do 244 compile src = fmap (first (left removeEscs)) . runMM fetch $ do
207 modify $ Map.insert ("." </> "Prelude.lc") $ Right (prelude, "<<TODO>>") 245 modify $ Map.insert ("." </> "Prelude.lc") $ Right prelude
208 first (compilePipeline backend) <$> parseAndToCoreMain "Main" 246 first (compilePipeline backend) <$> parseAndToCoreMain "Main"
209 where 247 where
210 fetch imp = \case 248 fetch imp = \case
211 "Prelude" -> return ("./Prelude.lc", "Prelude", undefined) 249 Right "Prelude" -> return ("./Prelude.lc", "Prelude", undefined)
212 "Main" -> return ("./Main.lc", "Main", src) 250 Right "Main" -> return ("./Main.lc", "Main", return src)
213 n -> ioFetch paths' imp n 251 n -> ioFetch paths' imp n
214 252
215prettyShowUnlines :: Show a => a -> String
216prettyShowUnlines = goPP 0 . PP.ppShow
217 where goPP _ [] = []
218 goPP n ('"':xs) | isMultilineString xs = "\"\"\"\n" ++ indent ++ go xs where
219 indent = replicate n ' '
220 go ('\\':'n':xs) = "\n" ++ indent ++ go xs
221 go ('\\':c:xs) = '\\':c:go xs
222 go ('"':xs) = "\n" ++ indent ++ "\"\"\"" ++ goPP n xs
223 go (x:xs) = x : go xs
224 goPP n (x:xs) = x : goPP (if x == '\n' then 0 else n+1) xs
225
226 isMultilineString ('\\':'n':xs) = True
227 isMultilineString ('\\':c:xs) = isMultilineString xs
228 isMultilineString ('"':xs) = False
229 isMultilineString (x:xs) = isMultilineString xs
230 isMultilineString [] = False