Files
rlp/src/TIM.hs
crumbtoo 23c324fea2 TI -> TIM
formatting
2023-11-14 11:06:41 -07:00

402 lines
12 KiB
Haskell

{-# LANGUAGE LambdaCase, BlockArguments #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE ViewPatterns #-}
module TIM
where
----------------------------------------------------------------------------------
import Data.Map (Map, (!?), (!))
import Data.Map qualified as M
import Data.Set (Set)
import Data.Set qualified as S
import Data.Maybe (fromJust, fromMaybe)
import Data.List (mapAccumL)
import Control.Monad (guard)
import Data.Foldable (traverse_)
import Data.Function ((&))
import System.IO (Handle, hPutStr)
import Text.Printf (printf)
import Data.Pretty
import Data.Heap
import Core
----------------------------------------------------------------------------------
data TiState = TiState [Addr] Dump TiHeap [(Name, Addr)] Stats
deriving Show
type TiHeap = Heap Node
data Node = NAp Addr Addr
| NSupercomb Name [Name] Expr
| NPrim Name Prim
| NNum Int
| NInd Addr
deriving Show
type Dump = [[Addr]]
type Stats = Int
----------------------------------------------------------------------------------
tiStatIncSteps :: Stats -> Stats
tiStatIncSteps = (+1)
tiStatGetSteps :: Stats -> Int
tiStatGetSteps = id
----------------------------------------------------------------------------------
compile :: Program -> Maybe TiState
compile prog = Just $ TiState s d h g stats
where
s = [mainAddr]
d = []
(h,g) = buildInitialHeap defs
defs = prog <> corePrelude
stats = 0
mainAddr = fromJust $ lookup "main" g
buildInitialHeap :: Program -> (TiHeap, [(Name, Addr)])
buildInitialHeap (Program scDefs) = (h'', scAddrs ++ primAddrs)
where
h = mempty
(h', scAddrs) = mapAccumL allocateSc h scDefs
(h'', primAddrs) = mapAccumL allocatePrim h' primitives
allocateSc :: TiHeap -> ScDef -> (TiHeap, (Name, Addr))
allocateSc h (ScDef n a b) = (h', (n, addr))
where
(h', addr) = alloc h (NSupercomb n a b)
allocatePrim :: TiHeap -> (Name, Prim) -> (TiHeap, (Name, Addr))
allocatePrim h (n, p) = (h', (n, addr))
where (h', addr) = alloc h (NPrim n p)
primitives :: [(Name, Prim)]
primitives =
[ ("negate#", IntNegP)
, ("+#", IntAddP)
, ("-#", IntSubP)
, ("*#", IntMulP)
, ("/#", IntDivP)
]
instantiate :: Expr -> TiHeap -> [(Name, Addr)] -> (TiHeap, Addr)
instantiate (App f x) h g = alloc h'' (NAp f' x')
where
(h', f') = instantiate f h g
(h'', x') = instantiate x h' g
instantiate (Var k) h g =
(h, fromMaybe (error $ "variable `" <> k <> "' not in scope") v)
where v = lookup k g
instantiate (Case _ _) _ _ = error "cannot instantiate case expressions"
instantiate (Let NonRec bs e) h g = instantiate e h' (g' ++ g)
where
-- :t mapAccumL @[] @TiHeap @(Name, Expr) @(Name,Addr)
-- :: (TiHeap -> (Name, Expr) -> (TiHeap, (Name, Addr)))
-- -> TiHeap -> [(Name, Expr)] -> (TiHeap, [(Name, Addr)])
(h', g') = mapAccumL instBinder h bs
instBinder :: TiHeap -> Binding -> (TiHeap, (Name, Addr))
instBinder h (k := v) =
let (h',a) = instantiate v h g
in (h',(k,a))
instantiate (Let Rec bs e) h g = instantiate e h' env
where
env = g' ++ g
(h', g') = mapAccumL instBinder h bs
instBinder :: TiHeap -> Binding -> (TiHeap, (Name, Addr))
instBinder h (k := v) =
let (h',a) = instantiate v h env
in (h',(k,a))
instantiate (Prim (IntP n)) h _ = alloc h (NNum n)
instantiate (Prim p) h _ = alloc h (NPrim n p)
where
n = fromMaybe
(error $ "primitive `" <> show p <> "' has no associated name")
$ lookupV p primitives
lookupV v d = fmap (\ (a,b) -> (b,a)) d
& lookup v
instantiate _ _ _ = error "unimplemented"
-- instantiate and update
instantiateU :: Expr -> Addr -> TiHeap -> [(Name, Addr)] -> TiHeap
instantiateU (App f x) root h g = update h'' root (NAp f' x')
where
(h',f') = instantiate f h g
(h'',x') = instantiate x h' g
instantiateU (Case _ _) _ _ _ = error "cannot instantiate case expressions"
instantiateU (Var k) root h g = update h' root (NInd a)
where (h',a) = instantiate (Var k) h g
-- i don't really know if this is correct tbh i'm gonna cry
instantiateU (Let NonRec bs e) root h g = h''
where
h'' = instantiateU e root h' (g' ++ g)
(h', g') = mapAccumL instBinder h bs
instBinder :: TiHeap -> Binding -> (TiHeap, (Name, Addr))
instBinder h (k := v) =
let (h',a) = instantiate v h g
in (h',(k,a))
instantiateU (Prim (IntP n)) root h _ = update h root (NNum n)
----------------------------------------------------------------------------------
eval :: TiState -> [TiState]
eval st = st : sts
where
sts | isFinal st = []
| otherwise = eval next
next = doAdmin (step st)
step :: TiState -> TiState
step st =
let TiState (top:_) _ h _ _ = st
in case fromMaybe (error "segfault!") (hLookup top h) of
NNum n -> numStep n st
NAp f x -> apStep f x st
NSupercomb n as b -> scStep n as b st
NInd a -> indStep a st
NPrim n p -> primStep n p st
where
numStep :: Int -> TiState -> TiState
-- rule 2.7
numStep _ (TiState [a] (s:d) h g sts) =
case hLookupUnsafe a h of
NNum n -> TiState s d h g sts
numStep _ _ = error "number applied as function..."
apStep :: Addr -> Addr -> TiState -> TiState
apStep f _ (TiState (ap:s) d h g sts) =
case hLookupUnsafe ap h of
-- rule 2.8
NAp f (hViewUnsafe h -> NInd a) ->
TiState (ap:s) d h' g sts
where
h' = (update h ap $ NAp f a)
-- this is bad rewrite later :3
_ ->
TiState (f:ap:s) d h g sts
scStep :: Name -> [Name] -> Expr -> TiState -> TiState
scStep n as b (TiState s d h g sts) =
TiState s' d h'' g sts
where
h'' = update h' (s !! length as) (NInd resAddr)
s' = resAddr : drop (length as + 1) s
(h', resAddr) = instantiate b h env
env = argBinds ++ g
argBinds = as `zip` argAddrs
argAddrs = getArgs h s
-- dereference indirections
indStep :: Addr -> TiState -> TiState
indStep a (TiState (_:s) d h g sts) =
TiState (a:s) d h g sts
primStep :: Name -> Prim -> TiState -> TiState
primStep _ IntNegP (TiState s d h g sts) =
case isDataNode arg of
True -> TiState s'' d h' g sts
where
h' = update h rootAddr (NNum $ negate n)
s'' = rootAddr : s'
(_:rootAddr:s') = s
NNum n = arg
False -> TiState s'' d' h g sts
where
s'' = b : s'
NAp _ b = hLookupUnsafe a1 h
-- a1 is an NAp
(_:a1:s') = s
d' = [a1] : d
where
[argAddr] = getArgs h s
arg = hLookupUnsafe argAddr h
primStep _ IntAddP st = primBinOp (+) st
primStep _ IntSubP st = primBinOp (-) st
primStep _ IntMulP st = primBinOp (*) st
primStep _ IntDivP st = primBinOp (div) st
primBinOp :: (Int -> Int -> Int) -> TiState -> TiState
primBinOp f (TiState s d h g sts) =
case isDataNode xarg of
True -> case isDataNode yarg of
True -> TiState s' d h' g sts
where
h' = update h rootAddr (NNum $ x `f` y)
rootAddr = head s'
-- number of arguments
s' = drop 2 s
NNum x = xarg
NNum y = yarg
False -> TiState s' d' h g sts
where
d' = drop 2 s : d
s' = [yAddr]
False -> TiState s' d' h g sts
where
d' = drop 1 s : d
s' = [xAddr]
where
[xAddr,yAddr] = getArgs h s
xarg = hLookupUnsafe xAddr h
yarg = hLookupUnsafe yAddr h
getArgs :: TiHeap -> [Addr] -> [Addr]
getArgs h (_:s) = fmap f s
where
f addr = case hLookupUnsafe addr h of
NAp _ arg -> arg
_ -> error $ "major uh-oh: " ++ show addr
isFinal :: TiState -> Bool
isFinal (TiState [addr] [] h _ _) =
case hLookup addr h of
Just a -> isDataNode a
_ -> error "isFinal: segfault!"
isFinal (TiState [] _ _ _ _) = error "empty stack..."
isFinal _ = False
isDataNode :: Node -> Bool
isDataNode (NNum _) = True
isDataNode _ = False
doAdmin :: TiState -> TiState
doAdmin (TiState s d h g sts) = TiState s d h g (sts+1)
----------------------------------------------------------------------------------
dbgProg :: Program -> IO Node
dbgProg p = do
prettyPrint `traverse` p'
pure res
where
p' = eval (fromJust $ compile p)
TiState [resAddr] _ h _ _ = last p'
res = hLookupUnsafe resAddr h
hdbgProg :: Program -> Handle -> IO Node
hdbgProg p hio = do
(hPutStr hio . prettyShow) `traverse_` p'
let TiState [a] _ h _ _ = last p'
pure res
where
p' = eval (fromJust $ compile p)
TiState [resAddr] _ h _ _ = last p'
res = hLookupUnsafe resAddr h
letrecExample :: Program
letrecExample = Program
[ ScDef "pair" ["x","y","f"] $ "f" :$ "x" :$ "y"
, ScDef "fst" ["p"] $ "p" :$ "K"
, ScDef "snd" ["p"] $ "p" :$ "K1"
, ScDef "f" ["x","y"] $
Let Rec
[ "a" := "pair" :$ "x" :$ "b"
, "b" := "pair" :$ "y" :$ "a"
]
("fst" :$ ("snd" :$ ("snd" :$ ("snd" :$ "a"))))
, ScDef "main" [] $ "f" :$ Prim (IntP 3) :$ Prim (IntP 4)
]
indExample1 :: Program
indExample1 = Program
[ ScDef "main" [] $ "twice" :$ "twice" :$ "id" :$ Prim (IntP 3)
]
indExample2 :: Program
indExample2 = Program
[ ScDef "main" [] $ "twice" :$ "twice" :$ "twice" :$ "id" :$ Prim (IntP 3)
]
indExample3 :: Program
indExample3 = Program
[ ScDef "main" [] $
Let Rec
[ "x" := Prim (IntP 2)
, "y" := "f" :$ "x" :$ "x"
]
("g" :$ "y" :$ "y")
, ScDef "f" ["a","b"] $ "b"
, ScDef "g" ["a","b"] $ "a"
]
negExample1 :: Program
negExample1 = Program
[ ScDef "main" [] $
Prim IntNegP :$ ("id" :$ Prim (IntP 3))
]
negExample2 :: Program
negExample2 = Program
[ ScDef "main" [] $
Prim IntNegP :$ Prim (IntP 3)
]
negExample3 :: Program
negExample3 = Program
[ ScDef "main" [] $
"twice" :$ Prim IntNegP :$ Prim (IntP 3)
]
arithExample1 :: Program
arithExample1 = Program
[ ScDef "main" [] $
"+#" :$ (Prim $ IntP 3) :$ ("negate#" :$ (Prim $ IntP 2))
]
----------------------------------------------------------------------------------
instance Pretty TiState where
prettyPrec (TiState s d h g sts) _ =
(IStr $ printf "==== TiState Stack %d ====" sts) <> IBreak
<> mconcat (fmap ((<>IBreak) . showAddr) s)
<> (IStr $ printf "==== TiState Heap %d ====" sts) <> IBreak
<> sheap <> IBreak
where
showAddr a = IStr (show a) <> ": " <> pnode (hLookupUnsafe a h) 0
-- showAddr a = IStr (show a) <> ": " <> IStr (show (hLookupUnsafe a h))
sheap = mconcat $ ((<>IBreak) . showAddr) <$> addresses h
pnode :: Node -> Int -> ISeq
pnode (NAp f x) p = bracketPrec 0 p $
f' <> " " <> pnode (hLookupUnsafe x h) (succ p)
where
f' = case hLookupUnsafe f h of
x@(NAp _ _) -> pnode x 0
x -> pnode x (succ p)
pnode (NInd a) p = bracketPrec 0 p $
"NInd (" <> IStr (show a) <> ") -> " <> pnode (hLookupUnsafe a h) 0
pnode (NNum n) _ =
IStr (show n) <> IStr "#"
pnode (NSupercomb n _ _) _ = IStr n
pnode (NPrim n _) _ = IStr n