-
Notifications
You must be signed in to change notification settings - Fork 3
/
Front.hs
512 lines (397 loc) · 16.8 KB
/
Front.hs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
-- | Frontend: Haskell -> 'CoreP' desugarer
--
-- design policy: minimize the amount of code
--
-- [0. 'collectModules' :: IO \[Hs\]]
-- Parse necesarry modules.
--
-- [1. 'WeakDesugar' :: Hs -> Hs]
-- Shallow desugaring (including unguarding, if removal)
--
-- [2. 'MidDesugar' :: Hs -> Hs]
-- A little bit deep desugaring (where -> let, pattern(PatBind,Lambda) -> case, merge FunBinds)
-- Introduces dummy variables
--
-- [3. 'mergeModules' :: \[Hs\] -> Hs]
-- Resolve all name references and make them 'UnQual'.
-- Unknown identifiers are detected in this process.
--
-- [4. 'sds' :: Hs -> 'CoreP']
-- Explicit pattern matching
--
-- Dummy variable naming convention:
--
-- * #aa_2... : arguments ('HsMatch')
--
-- * #xa_1... : pattern matching
module Front where
import Control.Arrow
import Control.Exception
import Control.Monad
import Control.Monad.Error
import Data.Char
import Data.Either
import Data.List
import Data.Maybe
import qualified Data.Map as M
import qualified Data.Set as S
import Language.Haskell.Parser
import Language.Haskell.Syntax
import System.Directory
import System.FilePath.Posix
import Util
import Core hiding(multiApp)
-- | Necessary information for translating module name to 'FilePath'.
data ModuleEnv=ModuleEnv [FilePath]
compile :: [HsModule] -> Process Core
compile ms=return $ sds $ mergeModules $ map (mds . wds) ms
-- | Search all necesarry modules and parse them all (if possible).
-- If an parser error occurs, parse as many other files as possible to report further errors.
collectModules :: ModuleEnv -> String -> IO (Process [HsModule])
collectModules env mod=do
x<-aux env (S.singleton mod) M.empty
let (ls,rs)=partitionEithers $ M.elems x
if null ls
then return $ return $ rs
else return $ throwError $ concat ls
aux :: ModuleEnv
-> S.Set String -- ^ modules to be parsed
-> M.Map String (Either [CompileError] HsModule) -- ^ already parsed module
-> IO (M.Map String (Either [CompileError] HsModule)) -- ^ all modules
aux env s m=case S.minView s of
Nothing -> return m
Just (s0,s') -> do x<-parseModule1 env s0
aux env (flip S.difference (M.keysSet m) $ S.union s' $ S.fromList $ either (const []) collectDep x) (M.insert s0 x m)
parseModule1 :: ModuleEnv -> String -> IO (Either [CompileError] HsModule)
parseModule1 env mod=do
x<-modToPath env mod
case x of
Nothing -> return $ Left [CompileError "frontend" "" ("module "++mod++" not found\n")]
Just ph -> do x<-readFile ph
case parseModuleWithMode (ParseMode ph) x of
ParseFailed loc msg -> return $ Left [CompileError "frontend" (show loc) msg]
ParseOk x -> return $ Right x
collectDep :: HsModule -> [String]
collectDep (HsModule _ _ _ is _)="Prelude":map (uM . importModule) is
where uM (Module x)=x
-- | convert module name to full file path.
modToPath :: ModuleEnv -> String -> IO (Maybe FilePath)
modToPath (ModuleEnv dirs) name=
handle (\(SomeException _)->return Nothing) $ liftM Just $ firstM doesFileExist $ map (</>(name++".hs")) dirs
-- | Returns first element which satisfies the predicate.
firstM :: Monad m => (a -> m Bool) -> [a] -> m a
firstM f []=fail "firstM: not found"
firstM f (x:xs)=do
e<-f x
if e then return x else firstM f xs
-- | /strong/ desugar
-- Replaces all implicit pattern matching with explicit cases.
-- Explicit modification of SrcLoc for error reporting in later process.
sds :: HsModule -> Core
sds (HsModule _ _ _ _ decls)=Core (map convDataDecl ds) (map convFunDecl fs)
where
ds=filter isDataDecl decls
fs=filter isFunBind decls
isDataDecl (HsDataDecl _ _ _ _ _ _)=True
isDataDecl _=False
isFunBind (HsFunBind _)=True
isFunBind _=False
isPatBind (HsPatBind _ _ _ _)=True
isPatBind _=False
convDataDecl :: HsDecl -> CrData
convDataDecl (HsDataDecl loc ctx (HsIdent name) vars cons derv)=
CrData name [] $ map convDataCon cons
convDataCon :: HsConDecl -> (CrName,[(Bool,CrType)])
convDataCon (HsConDecl loc (HsIdent name) ts)=(name,map f ts)
where f (HsBangedTy ty)=(True,convType ty)
f (HsUnBangedTy ty)=(False,convType ty)
convFunDecl :: HsDecl -> CrProc
convFunDecl (HsFunBind [HsMatch loc (HsIdent n) args (HsUnGuardedRhs e) []])
=CrProc n (map f args) (convExp e)
where
f (HsPVar (HsIdent x))=x
f x=error $ show x
convDecl d=error $ "convDecl:ERROR:"++show d
convExp :: HsExp -> CrExpr
convExp (HsLambda loc as e)=CrLm (map f as) (convExp e)
where
f (HsPVar (HsIdent x))=x
convExp (HsVar (UnQual (HsIdent x)))=CrVar x
convExp (HsApp e0 e1)=CrApp (convExp e0) (convExp e1)
convExp e@(HsCase _ _)=convFullCase e
-- convExp (HsLit (HsInt n))=error "convExp: int"-- CrA (h,Nothing) $ CrInt $ fromIntegral n
convExp (HsLit (HsInt n))=CrByte $ fromIntegral n
convExp (HsLit (HsChar ch))=CrByte $ fromIntegral $ ord ch
convExp (HsLet bs e)=CrLet True (map (toVP . convFunDecl) bs) (convExp e)
where
toVP (CrProc v [] e)=(v,e)
toVP (CrProc v as e)=(v,CrLm as e)
-- convExp h (HsExpTyeSig
convExp e=error $ "ERROR:convExp:"++show e
convType :: HsType -> CrType
convType (HsTyVar (HsIdent x))=CrTyVar x
convType (HsTyCon (UnQual (HsIdent x)))=CrTyCon x
convType (HsTyApp t0 t1)=CrTyApp (convType t0) (convType t1)
convType t=error $ "convType: "++show t
-- | Convert 'HsCase'(desugared) to 'CrExpr'
--
-- Sort 'HsAlt's by constructor and use 'procPartialCase' for each constructor.
--
-- One of the following:
--
-- * HsAlt (HsPApp ...
--
-- * HsAlt (HsPVar ...
convFullCase :: HsExp -> CrExpr
convFullCase (HsCase e as)=case md of
Nothing -> CrCase (convExp e) cs
Just (v,f) -> CrLet False [(v,convExp e)] $ CrCase (CrVar v) $ ("",[],convExp f):cs
where
cs=mapMaybe cv $ M.assocs $ M.map (second $ map $ second $ convExp) mn
(mn,md)=sortAlts as
cv (cons,(arity,cs))=let vs=take arity $ stringSeq "#x"
in do{x<-convSeqCase (liftM (convExp . snd) md) (map CrVar vs) cs; return (cons,vs,x)}
-- | Transform case of multiple vars. Note that constructors are already removed by 'procFullCase'.
--
-- example:
--
-- > case v1 v2 v3 of
-- > p11 p12 p13 -> e1
-- > p21 p22 p23 -> e2
-- > _ -> fail
--
-- to
--
-- > case v1 v2 v3 of
-- > p11 p12 p13 -> e1
-- > _ -> case v1 v2 v3 of
-- > p21 p22 p23 -> e2
-- > _ -> fail
convSeqCase :: Maybe CrExpr -> [CrExpr] -> [([HsPat],CrExpr)] -> Maybe CrExpr
convSeqCase fail vs []=fail
convSeqCase fail vs ((ps,e):as)=return $ convAndCase (convSeqCase fail vs as) e (zip vs ps)
-- | Transform multiple vars to
--
-- >case v1 v2 v3 of
-- > p1 u2 p3 -> succ
--
-- to
--
-- >case v1 of
-- > p1 -> let u2=v2 in
-- > case v3 of
-- > p3 -> succ
-- > _ -> fail(other 'HsAlt')
-- > _ -> fail(other 'HsAlt')
--
convAndCase :: Maybe CrExpr -> CrExpr -> [(CrExpr,HsPat)] -> CrExpr
convAndCase fail succ []=succ
convAndCase fail succ ((v,pat):cs)=case pat of
HsPVar (HsIdent p) ->
CrLet False [(p,v)] next
HsPApp (UnQual (HsIdent n)) args ->
let das=take (length args) $ stringSeq "#x"
in CrCase v $ maybe [] (\x->[("",[],x)]) fail++[(n,das,convAndCase fail next $ zip (map CrVar das) args)]
where
next=convAndCase fail succ cs
-- | Sort ['HsAlt'] by constructors.
sortAlts :: [HsAlt] -> (M.Map String (Int,[([HsPat],HsExp)]),Maybe (String,HsExp))
sortAlts []=(M.empty,Nothing)
sortAlts ((HsAlt loc pat (HsUnGuardedAlt e) []):as)=
case pat of
HsPApp (UnQual (HsIdent n)) args ->
(M.insertWith merge n (length args,[(args,e)]) mn,md)
HsPVar (HsIdent v) -> (M.empty,Just (v,e))
x -> error $ "sortAlts:"++show x
where
merge (n0,xs0) (n1,xs1)=if n0==n1 then (n0,xs0++xs1) else error $ "Unmatched arity@"++show loc
(mn,md)=sortAlts as
showLoc :: SrcLoc -> LocHint
showLoc (SrcLoc file line col)=concat [file,":",show line,":",show col,":"]
-- convExp (HsCase e as)=CrCase
-- | Merge 'HsModule's. TODO: implement qualification resolver.
mergeModules :: [HsModule] -> HsModule
mergeModules ms=HsModule (SrcLoc "<whole>" 0 0) (Module "<whole>") Nothing [] (concatMap f ms)
where f (HsModule _ _ _ _ decls)=decls
-- | /medium/ desugaring
--
-- * introduces dummy variables
--
-- * 'HsPat' is handled manually(not via 'mds').
--
-- You might find that 'HsLambda','HsPatBind'->'HsCase' conversion here duplicates 'sds'.
-- But they're completely different because 'sds' considers evaluation sequence of 'HsAlt's.
class MidDesugar a where
mds :: a -> a
instance MidDesugar HsModule where
mds (HsModule loc mod exp imp ds)=HsModule loc mod exp imp (eliminatePBind $ map mds ds)
instance MidDesugar HsDecl where
mds (HsFunBind ms)=mergeMatches $ map mds ms
mds (HsPatBind loc pat (HsUnGuardedRhs e) decls)
=HsPatBind loc pat (HsUnGuardedRhs $ mds $ moveDecls e decls) []
mds d=d
instance MidDesugar HsExp where
mds (HsCase e als)=HsCase (mds e) (map mds als)
mds (HsLet decls e)=HsLet (eliminatePBind $ map mds decls) (mds e)
mds (HsLambda loc ps e)=eliminatePLambda $ HsLambda loc ps (mds e)
mds e=e
instance MidDesugar HsMatch where
mds (HsMatch loc name ps (HsUnGuardedRhs e) decls)
=HsMatch loc name ps (HsUnGuardedRhs $ mds $ moveDecls e decls) []
instance MidDesugar HsAlt where
mds (HsAlt loc pat (HsUnGuardedAlt e) decls)
=HsAlt loc pat (HsUnGuardedAlt $ mds $ moveDecls e decls) []
-- | Generate let with given decls and 'HsExp'
moveDecls :: HsExp -> [HsDecl] -> HsExp
moveDecls e []=e
moveDecls e ds=HsLet ds e
-- | Merge multiple 'HsMatch' in HsFunBind into one.
mergeMatches :: [HsMatch] -> HsDecl
mergeMatches []=error "Front: mergeMatches: empty [HsMatch] found!"
-- mergeMatches [m]=HsFunBind [m]
mergeMatches ms=HsFunBind [HsMatch loc0 n0 (map HsPVar args) (HsUnGuardedRhs expr) []]
where
HsMatch loc0 n0 ps0 _ _=head ms
args=map (HsIdent . ("#a"++) . show) [0..length ps0-1]
expr=HsCase (multiApp (stdTuple $ length args) $ map (HsVar . UnQual) args) $ map genAlt ms
genAlt (HsMatch loc _ ps (HsUnGuardedRhs e) [])=HsAlt loc (wds $ HsPTuple ps) (HsUnGuardedAlt e) []
-- | Eliminate pattern matching in lambda arguments.
eliminatePLambda :: HsExp -> HsExp
eliminatePLambda (HsLambda loc ps e)=HsLambda loc (map fst vars) (f (map snd vars) e)
where
cat :: HsPat -> HsName -> (HsPat,Maybe (HsExp,HsPat))
cat p@(HsPVar v) _=(p,Nothing)
cat p r=(HsPVar r,Just (HsVar (UnQual r),p))
vars=zipWith cat ps (map HsIdent $ stringSeq "#x")
f :: [Maybe (HsExp,HsPat)] -> HsExp -> HsExp
f [] e=e
f (Nothing:vs) e=f vs e
f ((Just (v,p)):vs) e=HsCase v [HsAlt loc p (HsUnGuardedAlt $ f vs e) []]
-- | Eliminate 'HsPatBind'
-- x:xs=f
-- ->
-- #t0=f
-- x=case #t0 of x:xs -> x
-- xs=case #t0 of x:xs -> x
--
eliminatePBind :: [HsDecl] -> [HsDecl]
eliminatePBind ds=concat $ zipWith (convertPBind True) (stringSeq "#x") $ map convSimple ds
-- | Convert obvious 'HsPatBind' to 'HsFunBind'
convSimple :: HsDecl -> HsDecl
convSimple (HsPatBind loc (HsPVar n) rhs [])=HsFunBind [HsMatch loc n [] rhs []]
convSimple d=d
-- | Convert 'HsPatBind' to ['HsFunBind'] recursively.
-- operation:
--
-- from: T x y=z
--
-- to: #=z; x=case # of T x y -> x; y=case # of T x y -> y
--
convertPBind :: Bool -> String -> HsDecl -> [HsDecl]
convertPBind _ _ (HsPatBind loc (HsPVar n) rhs [])=[HsFunBind [HsMatch loc n [] rhs []]]
convertPBind flag prefix (HsPatBind loc p0@(HsPApp n args) rhs [])
|flag = pre:concat (zipWith3 f vars p0s args)
|otherwise = concat (zipWith3 f vars p0s args)
where
pre=HsFunBind [HsMatch loc (HsIdent prefix) [] rhs []]
vars=map (((prefix++"_")++) . show) [0..]
p0s=map (HsPApp n) $ change1 (map (HsPVar . HsIdent) vars) args
f _ _ p@(HsPVar (HsIdent n))=[genDecl n p0]
f v p0' p=genDecl v p0':convertPBind False v (HsPatBind loc p (HsUnGuardedRhs (stdVar v)) [])
genDecl :: String -> HsPat -> HsDecl
genDecl v p=HsFunBind [HsMatch loc (HsIdent v) [] (HsUnGuardedRhs e) []]
where e=HsCase (stdVar prefix) [HsAlt loc p (HsUnGuardedAlt (stdVar v)) []]
convertPBind _ prefix x=[x]
-- | Literally convert 'HsPat' to 'HsExp'.
pat2con :: HsPat -> Maybe HsExp
pat2con (HsPVar n)=return $ HsVar (UnQual n)
pat2con (HsPApp n vs)=liftM (multiApp $ HsVar n) (mapM pat2con vs)
pat2con HsPWildCard=Nothing
-- | /weak/ desugaring
class WeakDesugar a where
wds :: a -> a
instance WeakDesugar HsModule where
wds (HsModule loc mod exp imp ds)=HsModule loc mod exp imp (map wds ds)
instance WeakDesugar HsDecl where
wds (HsFunBind ms)=HsFunBind $ map wds ms
wds (HsPatBind loc pat rhs decls)=HsPatBind loc (wds pat) (wds rhs) (map wds decls)
wds (HsTypeSig loc ns ty)=HsTypeSig loc ns ty
wds (HsDataDecl loc [] n vs cdecls [])=HsDataDecl loc [] n vs cdecls []
wds (HsDataDecl _ _ _ _ _ _)=error "WeakDesugar: HsDataDecl: context/deriving is not supported"
wds (HsInfixDecl loc assoc lv ops)=HsInfixDecl loc assoc lv ops
instance WeakDesugar HsExp where
wds (HsApp e0 e1)=HsApp (wds e0) (wds e1)
wds (HsInfixApp e0 op e1)=HsApp (HsApp (wds $ opToExp op) (wds e0)) (wds e1)
wds (HsNegApp e)=HsApp (HsVar (UnQual (HsIdent "negate"))) e
wds (HsParen e)=wds e
wds (HsLeftSection e op)=HsApp (opToExp op) (wds e)
wds (HsRightSection op e)=HsApp (HsApp (HsVar (UnQual (HsIdent "flip"))) (opToExp op)) (wds e)
wds (HsIf c e0 e1)=HsCase (wds c)
[HsAlt wdsDummySrc (HsPApp (UnQual (HsIdent "True" )) []) (HsUnGuardedAlt (wds e0)) []
,HsAlt wdsDummySrc (HsPApp (UnQual (HsIdent "False")) []) (HsUnGuardedAlt (wds e1)) []]
wds (HsCase e als)=HsCase (wds e) (map wds als)
wds (HsLet decls e)=HsLet (map wds decls) (wds e)
wds (HsLambda loc ps e)=HsLambda loc (map wds ps) (wds e)
wds (HsEnumFrom e)=HsApp (stdVar "enumFrom") (wds e)
wds (HsEnumFromTo e0 e1)=HsApp (HsApp (stdVar "enumFromTo") (wds e0)) (wds e1)
wds (HsEnumFromThen e0 e1)=HsApp (HsApp (stdVar "enumFromThen") (wds e0)) (wds e1)
wds (HsEnumFromThenTo e0 e1 e2)=HsApp (HsApp (HsApp (stdVar "enumFromThen") (wds e0)) (wds e1)) (wds e2)
wds (HsCon f)=wds $ HsVar f
wds (HsVar (Special HsCons))=stdVar "XCons"
wds (HsVar (UnQual (HsSymbol v)))=HsVar (UnQual (HsIdent v))
wds (HsVar v)=HsVar v
wds (HsTuple es)=multiApp (stdTuple $ length es) (map wds es)
wds (HsList es)=foldr (\x y->multiApp (stdVar "XCons") [wds x,y]) (stdVar "XNil") es
wds (HsLit (HsString s))=wds $ HsList $ map (HsLit . HsChar) s
wds l@(HsLit _)=l
wds e=error $ "WeakDesugar:unsupported expression:"++show e
instance WeakDesugar HsMatch where
wds (HsMatch loc name ps rhs decls)=HsMatch loc (wds name) (map wds ps) (wds rhs) (map wds decls)
instance WeakDesugar HsName where
wds (HsSymbol x)=HsIdent x
wds n=n
instance WeakDesugar HsQName where
wds (Qual mod n)=Qual mod (wds n)
wds (UnQual n)=UnQual (wds n)
wds (Special sc)=UnQual $ HsIdent n
where n=case sc of
HsUnitCon->"XT0"
HsListCon-> "XNil"
HsFunCon-> "XApp"
HsTupleCon n-> "XT"++show n
HsCons-> "XCons"
instance WeakDesugar HsPat where
wds (HsPInfixApp p0 q p1)=HsPApp (wds q) [wds p0,wds p1]
wds (HsPParen p)=wds p
wds (HsPList ps)=foldr (\x y->HsPApp c [x,y]) n $ map wds ps
where c=UnQual (HsIdent "XCons"); n=HsPApp (UnQual $ HsIdent "XNil") []
wds (HsPTuple ps)=HsPApp (UnQual (HsIdent $ "XT"++show (length ps))) (map wds ps)
wds (HsPApp n ps)=HsPApp (wds n) (map wds ps)
wds (HsPVar n)=HsPVar $ wds n
wds (HsPWildCard)=HsPWildCard
-- wds p=p
wds p=error $ "WeakDesugar:unsupported HsPat:"++show p
instance WeakDesugar HsAlt where
wds (HsAlt loc pat al decls)=HsAlt loc (wds pat) (wds al) (map wds decls)
-- | Only HsUnGuardedAlt will remain.
instance WeakDesugar HsGuardedAlts where
wds (HsUnGuardedAlt e)=HsUnGuardedAlt $ wds e
wds (HsGuardedAlts als)=HsUnGuardedAlt $ wds $ unguardG (\(HsGuardedAlt _ c e)->(c,e)) als
-- | Only HsUnGuardedRhs will remain.
instance WeakDesugar HsRhs where
wds (HsUnGuardedRhs e)=HsUnGuardedRhs $ wds e
wds (HsGuardedRhss rs)=HsUnGuardedRhs $ wds $ unguardG (\(HsGuardedRhs _ c e)->(c,e)) rs
-- | Generic unguarding routine (nested if generation)
unguardG :: (a -> (HsExp,HsExp)) -> [a] -> HsExp
unguardG _ []=HsVar $ UnQual $ HsIdent "undefined"
unguardG f (x:xs)=let (cond,exp)=f x in HsIf cond exp $ unguardG f xs
wdsDummySrc=SrcLoc "<WeakDesugar>" 0 0
stdVar :: String -> HsExp
stdVar=HsVar . UnQual . HsIdent
stdTuple :: Int -> HsExp
stdTuple=stdVar . ("XT"++) . show
opToExp :: HsQOp -> HsExp
opToExp (HsQVarOp n)=HsVar n
opToExp (HsQConOp n)=HsCon n
multiApp :: HsExp -> [HsExp] -> HsExp
multiApp=foldl HsApp