Jikka-5.2.0.0: src/Jikka/CPlusPlus/Convert/UnpackTuples.hs
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE LambdaCase #-}
-- |
-- Module : Jikka.CPlusPlus.Convert.UnpackTuples
-- Description : unpack tuples. / タプルを展開します。
-- Copyright : (c) Kimiyuki Onaka, 2020
-- License : Apache License 2.0
-- Maintainer : kimiyuki95@gmail.com
-- Stability : experimental
-- Portability : portable
module Jikka.CPlusPlus.Convert.UnpackTuples
( run,
)
where
import Control.Monad.State.Strict
import qualified Data.Map as M
import qualified Data.Set as S
import Jikka.CPlusPlus.Language.Expr
import Jikka.CPlusPlus.Language.Util
import Jikka.Common.Alpha
import Jikka.Common.Error
-- | `runExpr` replaces variables using the @mapping :: M.Map VarName [(Type, VarName)]@.
runExpr :: (MonadAlpha m, MonadError Error m, MonadState (M.Map VarName [(Type, VarName)]) m) => Expr -> m Expr
runExpr = \case
Var x -> do
ys <- gets (M.lookup x)
return $ case ys of
Nothing -> Var x
Just ys ->
let es = map (Var . snd) ys
in if shouldBeArray (map fst ys)
then
let t = fst (head ys)
in Call (ArrayExt t) es
else
let ts = map fst ys
in Call (StdTuple ts) es
Lit lit -> return $ Lit lit
UnOp op e -> UnOp op <$> runExpr e
BinOp op e1 e2 -> BinOp op <$> runExpr e1 <*> runExpr e2
Cond e1 e2 e3 -> Cond <$> runExpr e1 <*> runExpr e2 <*> runExpr e3
Lam args ret body -> Lam args ret <$> runStatements body []
Call f args -> runCall f args
CallExpr e args -> CallExpr <$> runExpr e <*> mapM runExpr args
-- | `runCall` does the same thing to `runExpr` and also reduces `std::get<i>(e)` and `e[i]`.
runCall :: (MonadAlpha m, MonadError Error m, MonadState (M.Map VarName [(Type, VarName)]) m) => Function -> [Expr] -> m Expr
runCall f args = do
args <- mapM runExpr args
case (f, args) of
-- std::get<n>(x)
(StdGet n, [Var x]) -> do
ys <- gets (M.lookup x)
case ys of
Just ys -> do
let es = map (Var . snd) ys
when (n < 0 || toInteger (length ys) <= n) $ do
throwInternalError "index out of range"
return $ es !! fromInteger n
Nothing -> return $ Call f args
-- std::get<n>(std::tuple<T1, T2, ...>(e1, e2, ...))
(StdGet n, [Call (StdTuple _) es]) -> do
when (n < 0 || toInteger (length es) <= n) $ do
throwInternalError "index out of range"
return $ es !! fromInteger n
-- x[i]
(At, [Var x, e2]) -> do
ys <- gets (M.lookup x)
case ys of
Just ys -> do
let es = map (Var . snd) ys
let n = case e2 of
Lit (LitInt32 n) -> Just n
Lit (LitInt64 n) -> Just n
_ -> Nothing
case n of
Just n -> do
when (n < 0 || toInteger (length ys) <= n) $ do
throwInternalError "index out of range"
return (es !! fromInteger n)
Nothing -> return $ Call f args
Nothing -> return $ Call f args
-- (std::array<T, n>{e1, e2, ...})[i]
(At, [Call (ArrayExt _) es, e2]) -> do
let n = case e2 of
Lit (LitInt32 n) -> Just n
Lit (LitInt64 n) -> Just n
_ -> Nothing
case n of
Just n -> do
when (n < 0 || toInteger (length es) <= n) $ do
throwInternalError "index out of range"
return (es !! fromInteger n)
Nothing -> return $ Call f args
_ -> return $ Call f args
runLeftExpr :: (MonadAlpha m, MonadError Error m, MonadState (M.Map VarName [(Type, VarName)]) m) => LeftExpr -> m LeftExpr
runLeftExpr = \case
LeftVar x -> return $ LeftVar x -- do nothing
LeftAt e1 e2 -> LeftAt <$> runLeftExpr e1 <*> runExpr e2
LeftGet n e -> LeftGet n <$> runLeftExpr e
runAssignExpr :: (MonadAlpha m, MonadError Error m, MonadState (M.Map VarName [(Type, VarName)]) m) => AssignExpr -> m AssignExpr
runAssignExpr = \case
AssignExpr op e1 e2 -> AssignExpr op <$> runLeftExpr e1 <*> runExpr e2
AssignIncr e -> AssignIncr <$> runLeftExpr e
AssignDecr e -> AssignDecr <$> runLeftExpr e
-- | `runStatement` expands assignments to variables of @std::tuple<T1, T2, ...>@ and @std::array<T, n>@.
runStatement :: (MonadAlpha m, MonadError Error m, MonadState (M.Map VarName [(Type, VarName)]) m) => Statement -> [[Statement]] -> m [Statement]
runStatement stmt cont = case stmt of
ExprStatement e -> do
e <- runExpr e
return [ExprStatement e]
Block stmts -> do
runStatements stmts cont
If e body1 body2 -> do
e <- runExpr e
body1 <- runStatements body1 cont
body2 <- traverse (`runStatements` cont) body2
return [If e body1 body2]
For t x init pred incr body -> do
init <- runExpr init
pred <- runExpr pred
incr <- runAssignExpr incr
body <- runStatements body cont
return [For t x init pred incr body]
ForEach t x e body -> do
e <- runExpr e
body <- runStatements body cont
return [ForEach t x e body]
While e body -> do
e <- runExpr e
body <- runStatements body cont
return [While e body]
Declare t x init -> do
init <- case init of
DeclareDefault -> return DeclareDefault
DeclareCopy e -> DeclareCopy <$> runExpr e
DeclareInitialize es -> DeclareInitialize <$> mapM runExpr es
case init of
-- std::tuple<T1, T2, ...> x = std::tuple<...>(e1, e2, ...);
DeclareCopy (Call (StdTuple ts) es) -> do
ys <- replicateM (length es) (renameVarName LocalNameKind (unVarName x))
modify' (M.insert x (zip ts ys))
return $ zipWith3 (\t y e -> Declare t y (DeclareCopy e)) ts ys es
-- std::array<T, n> x = std::array<T, n>{e1, e2, ...};
DeclareCopy (Call (ArrayExt t) es) -> do
let ts = replicate (length es) t
ys <- replicateM (length es) (renameVarName LocalNameKind (unVarName x))
modify' (M.insert x (zip ts ys))
return $ zipWith3 (\t y e -> Declare t y (DeclareCopy e)) ts ys es
_ -> do
return [Declare t x init]
DeclareDestructure xs e -> do
e <- runExpr e
return [DeclareDestructure xs e]
Assign e -> do
e <- runAssignExpr e
case e of
-- x = e;
AssignExpr SimpleAssign (LeftVar x) e -> do
ys <- gets (M.lookup x)
case ys of
Just ys -> do
let ts = map fst ys
let n = toInteger (length ts)
let es = case e of
Call (StdTuple _) es -> es
Call (ArrayExt _) es -> es
_ ->
if shouldBeArray ts
then map (\i -> Call At [e, litInt32 i]) [0 .. n - 1]
else map (\i -> Call (StdGet i) [e]) [0 .. n - 1]
tmpys <- replicateM (length ts) (newFreshName LocalNameKind)
return $ zipWith3 (\t y e -> Declare t y (DeclareCopy e)) ts tmpys es ++ zipWith (\y e -> Assign (AssignExpr SimpleAssign (LeftVar y) (Var e))) (map snd ys) tmpys
Nothing -> return [Assign (AssignExpr SimpleAssign (LeftVar x) e)]
_ -> do
forM_ (S.toList (freeVarsAssignExpr e)) $ \x -> do
ys <- gets (M.lookup x)
case ys of
Just _ -> throwInternalError $ "wrong assignment to a tuple: " ++ unVarName x
Nothing -> return ()
return [Assign e]
Assert e -> do
e <- runExpr e
return [Assert e]
Return e -> do
e <- runExpr e
return [Return e]
runStatements :: (MonadAlpha m, MonadError Error m, MonadState (M.Map VarName [(Type, VarName)]) m) => [Statement] -> [[Statement]] -> m [Statement]
runStatements stmts cont = case stmts of
[] -> return []
stmt : stmts -> do
stmt <- runStatement stmt (stmts : cont)
stmts <- runStatements stmts cont
return (stmt ++ stmts)
runToplevelStatement :: (MonadAlpha m, MonadError Error m, MonadState (M.Map VarName [(Type, VarName)]) m) => ToplevelStatement -> m ToplevelStatement
runToplevelStatement = \case
VarDef t x e -> VarDef t x <$> runExpr e
FunDef ret f args body -> FunDef ret f args <$> runStatements body []
StaticAssert e msg -> StaticAssert <$> runExpr e <*> pure msg
runProgram :: (MonadAlpha m, MonadError Error m) => Program -> m Program
runProgram (Program decls) = (`evalStateT` M.empty) $ do
Program <$> mapM runToplevelStatement decls
-- | `run` unpack tuples.
--
-- == Examples
--
-- Before:
--
-- > tuple<int, int> c = make_tuple(a, b);
-- > func(get<0>(c), get<1>(c));
--
-- After:
--
-- > int c0 = a;
-- > int c1 = b;
-- > func(c0, c1);
run :: (MonadAlpha m, MonadError Error m) => Program -> m Program
run prog = wrapError' "Jikka.CPlusPlus.Convert.UnpackTuples" $ do
runProgram prog