firstify-0.1: Yhc/Core/Firstify/Reynolds.hs
module Yhc.Core.Firstify.Reynolds(reynolds) where
import Data.Char
import Data.List
import qualified Data.Map as Map
import qualified Data.Set as Set
import Yhc.Core
reynolds :: Core -> Core
reynolds c = c3{coreDatas = newDatas ++ coreDatas c3
,coreFuncs = newFuncs ++ coreFuncs c3}
where
-- set up some information
c2 = transformExpr appRules c
arr = Map.fromList [(coreFuncName x, coreFuncArity x) | x <- coreFuncs c]
apFun = findApFun c
apTyp = findApTyp c
a <#> b | isDigit (last a) = a ++ "_" ++ show b
| otherwise = a ++ show b
appRules (CoreFun x) = CoreApp (CoreFun x) []
appRules (CoreApp x []) | not $ isCoreFun x = x
appRules (CoreApp (CoreApp x y) z) = CoreApp x (y++z)
appRules x = x
-- just transform the thing
c3 = transformExpr defunc c2
defunc (CoreApp (CoreFun x) xs) =
case compare (length xs) a of
EQ -> CoreApp (CoreFun x) xs
LT -> ap_ x xs
GT -> ap (CoreApp (CoreFun x) yes) no
where (yes,no) = splitAt a xs
where a = arr Map.! x
defunc (CoreApp x xs) | not $ isCoreCon x = ap x xs
defunc x = x
ap fun args = CoreApp (CoreFun name) (fun:args)
where
name = if n == 1 then apFun else apFun <#> n
n = length args
ap_ fun args = CoreApp (CoreCon $ apTypGen fun (length args)) args
apTypGen fun n = (if n == 0 then apTyp else apTyp <#> n) ++ "_" ++ fun
-- then figure out which functions we required
splitApFun x = if null s then 1 else read s
where s = dropWhile (== '_') $ drop (length apFun) x
aps = [splitApFun x | CoreFun x <- universeExpr c3, apFun `isPrefixOf` x]
arityApps = [CoreFunc (apFun <#> i) ("x":vars) $
foldl (\x y -> CoreApp (CoreFun apFun) [x,CoreVar y]) (CoreVar "x") vars
| i <- Set.toAscList $ Set.fromList aps, i /= 1
, let vars = ['y':show j | j <- [1..i]] ]
splitApTyp x = if not $ isDigit $ head s then (0, s)
else let (a,_:b) = break (== '_') s in (read a, b)
where s = dropWhile (== '_') $ drop (length apTyp) x
dats = map head $ groupBy ((==) `on` snd) $ sort
[splitApTyp x | CoreCon x <- universeExpr c3, apTyp `isPrefixOf` x]
newDatas = [CoreData apTyp [] $
[CoreCtor (apTypGen c j) [('T':show k, Nothing) | k <- [1..j]]
| (i,c) <- dats, j <- [i..(arr Map.! c) - 1]]
]
mainAp = CoreFunc apFun ["x","z"] $ CoreCase (CoreVar "x") $
[(PatCon (apTypGen c j) vars,
CoreApp (if j+1 == n then CoreFun c else CoreCon $ apTypGen c (j+1))
(map CoreVar vars ++ [CoreVar "z"])
)
| (i,c) <- dats, let n = arr Map.! c, j <- [i..n-1]
, let vars = ['y':show k | k <- [1..j]] ]
newFuncs = mainAp : arityApps
findApFun :: Core -> CoreFuncName
findApFun c = findName (map coreFuncName $ coreFuncs c) "ap"
findApTyp :: Core -> String
findApTyp c = findName (concatMap f $ coreDatas c) "Ap"
where f x = coreDataName x : map coreCtorName (coreDataCtors x)
-- find a name pre# (where # is blank or a number)
-- such that pre# is not a prefix of any of the seen set
findName :: [String] -> String -> String
findName seen pre = if null seen2 then pre else pre ++ show (head $ filter isValid [1..])
where
isValid i = not $ any ((pre ++ show i) `isPrefixOf`) seen2
seen2 = filter (pre `isPrefixOf`) seen
g `on` f = \x y -> f x `g` f y