{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE LambdaCase #-}

-- |
-- Module      : Jikka.Subcommand.Convert
-- Description : is the entry point of @convert@ subcommand. / @convert@ サブコマンドのエントリポイントです。
-- Copyright   : (c) Kimiyuki Onaka, 2021
-- License     : Apache License 2.0
-- Maintainer  : kimiyuki95@gmail.com
-- Stability   : experimental
-- Portability : portable
module Jikka.Main.Subcommand.Convert (run) where

import Data.Text (Text, pack)
import qualified Jikka.CPlusPlus.Convert as FromCore
import qualified Jikka.CPlusPlus.Format as FormatCPlusPlus
import qualified Jikka.CPlusPlus.Language.Expr as CPlusPlus
import Jikka.Common.Alpha
import Jikka.Common.Error
import qualified Jikka.Core.Convert as Convert
import qualified Jikka.Core.Format as FormatCore
import qualified Jikka.Core.Language.Expr as Core
import qualified Jikka.Core.Parse as ParseCore
import Jikka.Main.Target
import qualified Jikka.Python.Convert.ToRestrictedPython as ToRestrictedPython
import qualified Jikka.Python.Language.Expr as Python
import qualified Jikka.Python.Parse as ParsePython
import qualified Jikka.RestrictedPython.Convert as ToCore
import qualified Jikka.RestrictedPython.Format as FormatRestrictedPython
import qualified Jikka.RestrictedPython.Language.Expr as RestrictedPython

data Program
  = PythonProgram Python.Program
  | RestrictedPythonProgram RestrictedPython.Program
  | CoreProgram Core.Program
  | CPlusPlusProgram CPlusPlus.Program
  deriving (Program -> Program -> Bool
(Program -> Program -> Bool)
-> (Program -> Program -> Bool) -> Eq Program
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: Program -> Program -> Bool
$c/= :: Program -> Program -> Bool
== :: Program -> Program -> Bool
$c== :: Program -> Program -> Bool
Eq, Eq Program
Eq Program
-> (Program -> Program -> Ordering)
-> (Program -> Program -> Bool)
-> (Program -> Program -> Bool)
-> (Program -> Program -> Bool)
-> (Program -> Program -> Bool)
-> (Program -> Program -> Program)
-> (Program -> Program -> Program)
-> Ord Program
Program -> Program -> Bool
Program -> Program -> Ordering
Program -> Program -> Program
forall a.
Eq a
-> (a -> a -> Ordering)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> a)
-> (a -> a -> a)
-> Ord a
min :: Program -> Program -> Program
$cmin :: Program -> Program -> Program
max :: Program -> Program -> Program
$cmax :: Program -> Program -> Program
>= :: Program -> Program -> Bool
$c>= :: Program -> Program -> Bool
> :: Program -> Program -> Bool
$c> :: Program -> Program -> Bool
<= :: Program -> Program -> Bool
$c<= :: Program -> Program -> Bool
< :: Program -> Program -> Bool
$c< :: Program -> Program -> Bool
compare :: Program -> Program -> Ordering
$ccompare :: Program -> Program -> Ordering
$cp1Ord :: Eq Program
Ord, ReadPrec [Program]
ReadPrec Program
Int -> ReadS Program
ReadS [Program]
(Int -> ReadS Program)
-> ReadS [Program]
-> ReadPrec Program
-> ReadPrec [Program]
-> Read Program
forall a.
(Int -> ReadS a)
-> ReadS [a] -> ReadPrec a -> ReadPrec [a] -> Read a
readListPrec :: ReadPrec [Program]
$creadListPrec :: ReadPrec [Program]
readPrec :: ReadPrec Program
$creadPrec :: ReadPrec Program
readList :: ReadS [Program]
$creadList :: ReadS [Program]
readsPrec :: Int -> ReadS Program
$creadsPrec :: Int -> ReadS Program
Read, Int -> Program -> ShowS
[Program] -> ShowS
Program -> String
(Int -> Program -> ShowS)
-> (Program -> String) -> ([Program] -> ShowS) -> Show Program
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [Program] -> ShowS
$cshowList :: [Program] -> ShowS
show :: Program -> String
$cshow :: Program -> String
showsPrec :: Int -> Program -> ShowS
$cshowsPrec :: Int -> Program -> ShowS
Show)

parseProgram :: (MonadAlpha m, MonadError Error m) => Target -> FilePath -> Text -> m Program
parseProgram :: Target -> String -> Text -> m Program
parseProgram Target
source String
path Text
input = case Target
source of
  Target
PythonTarget -> Program -> Program
PythonProgram (Program -> Program) -> m Program -> m Program
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> String -> Text -> m Program
forall (m :: * -> *).
MonadError Error m =>
String -> Text -> m Program
ParsePython.run String
path Text
input
  Target
RestrictedPythonTarget -> String -> m Program
forall (m :: * -> *) a. MonadError Error m => String -> m a
throwCommandLineError String
"cannot convert from restricted Python"
  Target
CoreTarget -> Program -> Program
CoreProgram (Program -> Program) -> m Program -> m Program
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> String -> Text -> m Program
forall (m :: * -> *).
(MonadAlpha m, MonadError Error m) =>
String -> Text -> m Program
ParseCore.run String
path Text
input
  Target
CPlusPlusTarget -> String -> m Program
forall (m :: * -> *) a. MonadError Error m => String -> m a
throwCommandLineError String
"cannot convert from C++"

convertProgram :: (MonadAlpha m, MonadError Error m) => Program -> Target -> m Program
convertProgram :: Program -> Target -> m Program
convertProgram Program
prog Target
target = case (Program
prog, Target
target) of
  (PythonProgram Program
_, Target
PythonTarget) -> Program -> m Program
forall (m :: * -> *) a. Monad m => a -> m a
return Program
prog
  (RestrictedPythonProgram Program
_, Target
RestrictedPythonTarget) -> Program -> m Program
forall (m :: * -> *) a. Monad m => a -> m a
return Program
prog
  (CoreProgram Program
prog, Target
CoreTarget) -> Program -> Program
CoreProgram (Program -> Program) -> m Program -> m Program
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Program -> m Program
forall (m :: * -> *).
(MonadAlpha m, MonadError Error m) =>
Program -> m Program
Convert.run Program
prog -- optimize
  (CPlusPlusProgram Program
_, Target
CPlusPlusTarget) -> Program -> m Program
forall (m :: * -> *) a. Monad m => a -> m a
return Program
prog
  (RestrictedPythonProgram Program
_, Target
PythonTarget) -> String -> m Program
forall (m :: * -> *) a. MonadError Error m => String -> m a
throwCommandLineError String
"cannot convert from restricted Python to Python"
  (CoreProgram Program
_, Target
PythonTarget) -> String -> m Program
forall (m :: * -> *) a. MonadError Error m => String -> m a
throwCommandLineError String
"cannot convert from core to Python"
  (CoreProgram Program
_, Target
RestrictedPythonTarget) -> String -> m Program
forall (m :: * -> *) a. MonadError Error m => String -> m a
throwCommandLineError String
"cannot convert from core to restricted Python"
  (PythonProgram Program
prog, Target
_) -> do
    Program
prog <- Program -> m Program
forall (m :: * -> *).
(MonadAlpha m, MonadError Error m) =>
Program -> m Program
ToRestrictedPython.run Program
prog
    Program -> Target -> m Program
forall (m :: * -> *).
(MonadAlpha m, MonadError Error m) =>
Program -> Target -> m Program
convertProgram (Program -> Program
RestrictedPythonProgram Program
prog) Target
target
  (RestrictedPythonProgram Program
prog, Target
CoreTarget) -> do
    (Program
prog, IOFormat
_) <- Program -> m (Program, IOFormat)
forall (m :: * -> *).
(MonadAlpha m, MonadError Error m) =>
Program -> m (Program, IOFormat)
ToCore.run Program
prog
    Program -> Program
CoreProgram (Program -> Program) -> m Program -> m Program
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Program -> m Program
forall (m :: * -> *).
(MonadAlpha m, MonadError Error m) =>
Program -> m Program
Convert.run Program
prog
  (RestrictedPythonProgram Program
prog, Target
CPlusPlusTarget) -> do
    (Program
prog, IOFormat
format) <- Program -> m (Program, IOFormat)
forall (m :: * -> *).
(MonadAlpha m, MonadError Error m) =>
Program -> m (Program, IOFormat)
ToCore.run Program
prog
    Program
prog <- Program -> m Program
forall (m :: * -> *).
(MonadAlpha m, MonadError Error m) =>
Program -> m Program
Convert.run Program
prog
    Program -> Program
CPlusPlusProgram (Program -> Program) -> m Program -> m Program
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Program -> Maybe IOFormat -> m Program
forall (m :: * -> *).
(MonadAlpha m, MonadError Error m) =>
Program -> Maybe IOFormat -> m Program
FromCore.run Program
prog (IOFormat -> Maybe IOFormat
forall a. a -> Maybe a
Just IOFormat
format)
  (CoreProgram Program
prog, Target
CPlusPlusTarget) -> do
    Program
prog <- Program -> m Program
forall (m :: * -> *).
(MonadAlpha m, MonadError Error m) =>
Program -> m Program
Convert.run Program
prog
    Program -> Program
CPlusPlusProgram (Program -> Program) -> m Program -> m Program
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Program -> Maybe IOFormat -> m Program
forall (m :: * -> *).
(MonadAlpha m, MonadError Error m) =>
Program -> Maybe IOFormat -> m Program
FromCore.run Program
prog Maybe IOFormat
forall a. Maybe a
Nothing
  (CPlusPlusProgram Program
_, Target
_) -> String -> m Program
forall (m :: * -> *) a. MonadError Error m => String -> m a
throwCommandLineError String
"cannot convert from C++"

formatProgram :: (MonadAlpha m, MonadError Error m) => Program -> m Text
formatProgram :: Program -> m Text
formatProgram = \case
  PythonProgram Program
prog -> Text -> m Text
forall (m :: * -> *) a. Monad m => a -> m a
return (Text -> m Text) -> (String -> Text) -> String -> m Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> Text
pack (String -> m Text) -> String -> m Text
forall a b. (a -> b) -> a -> b
$ Program -> String
forall a. Show a => a -> String
show Program
prog -- TODO
  RestrictedPythonProgram Program
prog -> Program -> m Text
forall (m :: * -> *). Applicative m => Program -> m Text
FormatRestrictedPython.run Program
prog
  CoreProgram Program
prog -> Program -> m Text
forall (m :: * -> *). Applicative m => Program -> m Text
FormatCore.run Program
prog
  CPlusPlusProgram Program
prog -> Program -> m Text
forall (m :: * -> *). Applicative m => Program -> m Text
FormatCPlusPlus.run Program
prog

run :: Target -> Target -> FilePath -> Text -> Either Error Text
run :: Target -> Target -> String -> Text -> Either Error Text
run Target
source Target
target String
path Text
input = (AlphaT (Either Error) Text -> Int -> Either Error Text)
-> Int -> AlphaT (Either Error) Text -> Either Error Text
forall a b c. (a -> b -> c) -> b -> a -> c
flip AlphaT (Either Error) Text -> Int -> Either Error Text
forall (m :: * -> *) a. Functor m => AlphaT m a -> Int -> m a
evalAlphaT Int
0 (AlphaT (Either Error) Text -> Either Error Text)
-> AlphaT (Either Error) Text -> Either Error Text
forall a b. (a -> b) -> a -> b
$ do
  Program
prog <- Target -> String -> Text -> AlphaT (Either Error) Program
forall (m :: * -> *).
(MonadAlpha m, MonadError Error m) =>
Target -> String -> Text -> m Program
parseProgram Target
source String
path Text
input
  Program
prog <- Program -> Target -> AlphaT (Either Error) Program
forall (m :: * -> *).
(MonadAlpha m, MonadError Error m) =>
Program -> Target -> m Program
convertProgram Program
prog Target
target
  Program -> AlphaT (Either Error) Text
forall (m :: * -> *).
(MonadAlpha m, MonadError Error m) =>
Program -> m Text
formatProgram Program
prog