module VM.Instruction.Io
( instOpen
, instRead
, instWrite
, instClose
, instInput
) where
import Control.Exception (try, IOException)
import Control.Monad.State.Strict (get, put, liftIO)
import qualified Data.Map.Strict as Map
import qualified Data.Vector as V
import qualified Data.Text as T
import System.IO
import VM.VMState (VirtualMachine, VMState(..))
import VM.VMValue (VMValue(..), valueToInt, valueToString, stringToValue)
import VM.VMStack (stackPop, stackPush)
import Common.Type.Integer (IntValue(..))
tryOpen :: FilePath -> IOMode -> VirtualMachine (Either IOException Handle)
tryOpen :: FilePath -> IOMode -> VirtualMachine (Either IOException Handle)
tryOpen FilePath
path IOMode
mode = IO (Either IOException Handle)
-> VirtualMachine (Either IOException Handle)
forall a. IO a -> StateT VMState IO a
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO (Either IOException Handle)
-> VirtualMachine (Either IOException Handle))
-> IO (Either IOException Handle)
-> VirtualMachine (Either IOException Handle)
forall a b. (a -> b) -> a -> b
$ IO Handle -> IO (Either IOException Handle)
forall e a. Exception e => IO a -> IO (Either e a)
try (FilePath -> IOMode -> IO Handle
openFile FilePath
path IOMode
mode)
getIoMode :: Int -> IOMode
getIoMode :: Int -> IOMode
getIoMode Int
1 = IOMode
WriteMode
getIoMode Int
2 = IOMode
AppendMode
getIoMode Int
_ = IOMode
ReadMode
registerHandle :: Either IOException Handle -> VirtualMachine ()
registerHandle :: Either IOException Handle -> VirtualMachine ()
registerHandle (Left IOException
_) = VMValue -> VirtualMachine ()
stackPush (IntValue -> VMValue
VInt (Int32 -> IntValue
I32 (-Int32
1)))
registerHandle (Right Handle
h) = do
vm <- StateT VMState IO VMState
forall s (m :: * -> *). MonadState s m => m s
get
put vm { vmFds = Map.insert (nextFd vm) h (vmFds vm),
nextFd = nextFd vm + 1 }
stackPush (VInt (I32 (fromIntegral (nextFd vm))))
instOpen :: VirtualMachine ()
instOpen :: VirtualMachine ()
instOpen = do
mode <- Int -> IOMode
getIoMode (Int -> IOMode) -> (VMValue -> Int) -> VMValue -> IOMode
forall b c a. (b -> c) -> (a -> b) -> a -> c
. VMValue -> Int
valueToInt (VMValue -> IOMode)
-> StateT VMState IO VMValue -> StateT VMState IO IOMode
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> StateT VMState IO VMValue
stackPop
path <- valueToString <$> stackPop
res <- tryOpen (T.unpack path) mode
registerHandle res
tryRead :: Handle -> Int -> VirtualMachine (Either IOException String)
tryRead :: Handle -> Int -> VirtualMachine (Either IOException FilePath)
tryRead Handle
h (-1) = IO (Either IOException FilePath)
-> VirtualMachine (Either IOException FilePath)
forall a. IO a -> StateT VMState IO a
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO (Either IOException FilePath)
-> VirtualMachine (Either IOException FilePath))
-> IO (Either IOException FilePath)
-> VirtualMachine (Either IOException FilePath)
forall a b. (a -> b) -> a -> b
$ IO FilePath -> IO (Either IOException FilePath)
forall e a. Exception e => IO a -> IO (Either e a)
try (Handle -> IO FilePath
hGetContents Handle
h)
tryRead Handle
h Int
size = IO (Either IOException FilePath)
-> VirtualMachine (Either IOException FilePath)
forall a. IO a -> StateT VMState IO a
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO (Either IOException FilePath)
-> VirtualMachine (Either IOException FilePath))
-> IO (Either IOException FilePath)
-> VirtualMachine (Either IOException FilePath)
forall a b. (a -> b) -> a -> b
$ IO FilePath -> IO (Either IOException FilePath)
forall e a. Exception e => IO a -> IO (Either e a)
try (Handle -> Int -> IO FilePath
readChars Handle
h Int
size)
readChars :: Handle -> Int -> IO String
readChars :: Handle -> Int -> IO FilePath
readChars Handle
_ Int
0 = FilePath -> IO FilePath
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return []
readChars Handle
h Int
n = do
eof <- Handle -> IO Bool
hIsEOF Handle
h
case eof of
Bool
True -> FilePath -> IO FilePath
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return []
Bool
False -> (:) (Char -> FilePath -> FilePath)
-> IO Char -> IO (FilePath -> FilePath)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Handle -> IO Char
hGetChar Handle
h IO (FilePath -> FilePath) -> IO FilePath -> IO FilePath
forall a b. IO (a -> b) -> IO a -> IO b
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> Handle -> Int -> IO FilePath
readChars Handle
h (Int
n Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
1)
handleReadResult :: Either IOException String -> VirtualMachine ()
handleReadResult :: Either IOException FilePath -> VirtualMachine ()
handleReadResult (Left IOException
_) = VMValue -> VirtualMachine ()
stackPush (Vector VMValue -> VMValue
VList Vector VMValue
forall a. Vector a
V.empty)
handleReadResult (Right FilePath
content) = VMValue -> VirtualMachine ()
stackPush (Text -> VMValue
stringToValue (FilePath -> Text
T.pack FilePath
content))
instRead :: VirtualMachine ()
instRead :: VirtualMachine ()
instRead = do
size <- VMValue -> Int
valueToInt (VMValue -> Int)
-> StateT VMState IO VMValue -> StateT VMState IO Int
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> StateT VMState IO VMValue
stackPop
fd <- valueToInt <$> stackPop
vm <- get
case Map.lookup fd (vmFds vm) of
Maybe Handle
Nothing -> VMValue -> VirtualMachine ()
stackPush (Vector VMValue -> VMValue
VList Vector VMValue
forall a. Vector a
V.empty)
Just Handle
h -> Handle -> Int -> VirtualMachine (Either IOException FilePath)
tryRead Handle
h Int
size VirtualMachine (Either IOException FilePath)
-> (Either IOException FilePath -> VirtualMachine ())
-> VirtualMachine ()
forall a b.
StateT VMState IO a
-> (a -> StateT VMState IO b) -> StateT VMState IO b
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= Either IOException FilePath -> VirtualMachine ()
handleReadResult
tryWrite :: Handle -> String -> VirtualMachine (Either IOException ())
tryWrite :: Handle -> FilePath -> VirtualMachine (Either IOException ())
tryWrite Handle
h FilePath
c = IO (Either IOException ())
-> VirtualMachine (Either IOException ())
forall a. IO a -> StateT VMState IO a
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO (Either IOException ())
-> VirtualMachine (Either IOException ()))
-> IO (Either IOException ())
-> VirtualMachine (Either IOException ())
forall a b. (a -> b) -> a -> b
$ IO () -> IO (Either IOException ())
forall e a. Exception e => IO a -> IO (Either e a)
try (Handle -> FilePath -> IO ()
hPutStr Handle
h FilePath
c IO () -> IO () -> IO ()
forall a b. IO a -> IO b -> IO b
forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> Handle -> IO ()
hFlush Handle
h)
instWrite :: VirtualMachine ()
instWrite :: VirtualMachine ()
instWrite = do
content <- VMValue -> Text
valueToString (VMValue -> Text)
-> StateT VMState IO VMValue -> StateT VMState IO Text
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> StateT VMState IO VMValue
stackPop
fd <- valueToInt <$> stackPop
vm <- get
case Map.lookup fd (vmFds vm) of
Just Handle
h -> Handle -> FilePath -> VirtualMachine (Either IOException ())
tryWrite Handle
h (Text -> FilePath
T.unpack Text
content) VirtualMachine (Either IOException ())
-> VirtualMachine () -> VirtualMachine ()
forall a b.
StateT VMState IO a -> StateT VMState IO b -> StateT VMState IO b
forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> () -> VirtualMachine ()
forall a. a -> StateT VMState IO a
forall (m :: * -> *) a. Monad m => a -> m a
return ()
Maybe Handle
Nothing -> () -> VirtualMachine ()
forall a. a -> StateT VMState IO a
forall (m :: * -> *) a. Monad m => a -> m a
return ()
instClose :: VirtualMachine ()
instClose :: VirtualMachine ()
instClose = do
fd <- VMValue -> Int
valueToInt (VMValue -> Int)
-> StateT VMState IO VMValue -> StateT VMState IO Int
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> StateT VMState IO VMValue
stackPop
vm <- get
case Map.lookup fd (vmFds vm) of
Just Handle
h -> IO () -> VirtualMachine ()
forall a. IO a -> StateT VMState IO a
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (Handle -> IO ()
hClose Handle
h) VirtualMachine () -> VirtualMachine () -> VirtualMachine ()
forall a b.
StateT VMState IO a -> StateT VMState IO b -> StateT VMState IO b
forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> VMState -> Int -> VirtualMachine ()
removeFd VMState
vm Int
fd
Maybe Handle
Nothing -> () -> VirtualMachine ()
forall a. a -> StateT VMState IO a
forall (m :: * -> *) a. Monad m => a -> m a
return ()
removeFd :: VMState -> Int -> VirtualMachine ()
removeFd :: VMState -> Int -> VirtualMachine ()
removeFd VMState
vm Int
fd = VMState -> VirtualMachine ()
forall s (m :: * -> *). MonadState s m => s -> m ()
put VMState
vm { vmFds = Map.delete fd (vmFds vm) }
tryInput :: Handle -> VirtualMachine (Either IOException String)
tryInput :: Handle -> VirtualMachine (Either IOException FilePath)
tryInput Handle
h = IO (Either IOException FilePath)
-> VirtualMachine (Either IOException FilePath)
forall a. IO a -> StateT VMState IO a
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO (Either IOException FilePath)
-> VirtualMachine (Either IOException FilePath))
-> IO (Either IOException FilePath)
-> VirtualMachine (Either IOException FilePath)
forall a b. (a -> b) -> a -> b
$ IO FilePath -> IO (Either IOException FilePath)
forall e a. Exception e => IO a -> IO (Either e a)
try (Handle -> IO FilePath
hGetLine Handle
h)
instInput :: VirtualMachine ()
instInput :: VirtualMachine ()
instInput = do
fd <- VMValue -> Int
valueToInt (VMValue -> Int)
-> StateT VMState IO VMValue -> StateT VMState IO Int
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> StateT VMState IO VMValue
stackPop
vm <- get
case Map.lookup fd (vmFds vm) of
Maybe Handle
Nothing -> VMValue -> VirtualMachine ()
stackPush (Vector VMValue -> VMValue
VList Vector VMValue
forall a. Vector a
V.empty)
Just Handle
h -> Handle -> VirtualMachine (Either IOException FilePath)
tryInput Handle
h VirtualMachine (Either IOException FilePath)
-> (Either IOException FilePath -> VirtualMachine ())
-> VirtualMachine ()
forall a b.
StateT VMState IO a
-> (a -> StateT VMState IO b) -> StateT VMState IO b
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= Either IOException FilePath -> VirtualMachine ()
handleReadResult