packages feed

idris-0.1.7: Idris/RunIO.hs

{-# OPTIONS_GHC -fglasgow-exts -fallow-undecidable-instances -fallow-overlapping-instances #-}
{-# LANGUAGE MagicHash, UndecidableInstances, OverlappingInstances #-}

module Idris.RunIO where

-- import SimplDSL

import Ivor.TT
import Ivor.Shell
import Ivor.Construction
import Idris.AbsSyntax

import Data.Typeable
import Data.IORef
import System.IO.Unsafe
import System.IO
import Control.Monad.Error
import Control.Concurrent
import Debug.Trace

newtype Lock = Lock QSem

instance Typeable Lock where
    typeOf a = mkTyConApp (mkTyCon "Lock") []

instance Show Lock where
    show x = "<<Lock>>"

instance Eq Lock where
    (==) x y = False -- Hmm

instance ViewConst Handle where
    typeof x = (name "Handle")

instance ViewConst Lock where
    typeof x = (name "Lock")

exec :: Context -> Term -> IO ()
exec ctxt wurzel = do res <- runIO ctxt (view (whnf ctxt wurzel))
                      putStrLn $ show res

runIO :: Context -> ViewTerm -> IO ViewTerm
runIO ctxt (App (App (App (Name _ d) _) act) k)
    | d == name "IO.IODo" = runAction ctxt (parseAction act) k
runIO ctxt (App (App (Name _ l) _) res)
    | l == name "IO.IOReturn" = return res
runIO _ x = fail $ "Not an IO action: " ++ show x

data Action = ReadStr
            | WriteStr String
            | Fork ViewTerm
            | NewLock Int
            | DoLock Lock
            | DoUnlock Lock
            | NewRef
            | ReadRef Int
            | WriteRef Int ViewTerm
            | CantReduce ViewTerm

parseAction x = parseAction' x [] where
  parseAction' (App f a) args = parseAction' f (a:args)
  parseAction' (Name _ n) args = (getAction n args)

getAction n []
    | n == name "IO.GetStr" = ReadStr
getAction n [Constant str]
    | n == name "IO.PutStr"
        = case cast str of
             Just str' -> WriteStr str'
getAction n [_,t]
    | n == name "IO.Fork"
        = Fork t
getAction n [Constant i]
    | n == name "IO.NewLock" 
        = case cast i of
             Just i' -> NewLock i'
getAction n [lock]
    | n == name "IO.DoLock"
        = DoLock (getLock lock)
    | n == name "IO.DoUnlock"
        = DoUnlock (getLock lock)
getAction n []
    | n == name "IO.NewRef" = NewRef
getAction n [_,Constant i]
    | n == name "IO.ReadRef" 
        = case cast i of
             Just i' -> ReadRef i'
getAction n [_,Constant i,val]
    | n == name "IO.WriteRef"
        = case cast i of
             Just i' -> WriteRef i' val

getAction n args = CantReduce (apply (Name Unknown n) args)

getHandle  (App _ (Constant h)) = case cast h of
                                   Just h' -> h'
getLock (Constant h) = case cast h of
                         Just h' -> h'
                         Nothing -> error ("Lock error in constant " ++ show h)
getLock x = error ("Lock error " ++ show x)

continue ctxt k arg = case fastCheck ctxt (App k arg) of
                        t -> let next = whnf ctxt t in
                             runIO ctxt (view next)
{-                          Right t -> let next = whnf ctxt t in
                                         runIO ctxt (view next)
                          Left err -> fail $ "Can't happen - continue " ++ err ++ "\n" ++ show k ++ "\n" ++ show arg
-}

unit = Name Unknown (name "II")

runAction ctxt (WriteStr str) k
      -- Print the string, then run the continuation with the argument 'II'
        = do putStr str
             hFlush stdout
             continue ctxt k unit
runAction ctxt ReadStr k
      -- Read a string then run the continuation with the constant str
      = do str <- getLine
           continue ctxt k (Constant str)
runAction ctxt (Fork t) k
      = do forkIO (do x <- runIO ctxt t
                      return ())
           continue ctxt k unit
runAction ctxt (NewLock n) k
      = do mv <- newQSem n
           continue ctxt k (Constant (Lock mv))
runAction ctxt (DoLock l) k
      = do primLock l
           continue ctxt k unit
runAction ctxt (DoUnlock l) k
      = do primUnlock l
           continue ctxt k unit
runAction ctxt NewRef k
      = do i <- newRef
           continue ctxt k (Constant i)
runAction ctxt (ReadRef i) k
      = do v <- getMem i
           continue ctxt k v
runAction ctxt (WriteRef i val) k
      = do putMem i val
           continue ctxt k unit
runAction ctxt (CantReduce t) k
      = do fail $ "Stuck at: " ++ show t
           -- hFlush stdout

primLock :: Lock -> IO ()
primLock (Lock lock) = do waitQSem lock

primUnlock :: Lock -> IO ()
primUnlock (Lock lock) = signalQSem lock


-- Some mutable memory, for implementing IORefs idris side.

type Value = ViewTerm
defaultVal = (Constant (0xDEADBEEF::Int))

data MemState = MemState (IORef (Int, [Value]))

memory :: MemState
memory = unsafePerformIO 
               (do mem <- newIORef (0, (take 100 (repeat defaultVal)))
                   return (MemState mem))

newRef :: IO Int
newRef = do let (MemState mem) = memory
            (p,ref) <- readIORef mem
            writeIORef mem (p+1, ref)
            return p

putMem :: Int -> Value -> IO ()
putMem loc val = do let (MemState mem) = memory
                    (p,content) <- readIORef mem
                    writeIORef mem (p, update content loc val)

getMem :: Int -> IO Value
getMem loc = do let (MemState mem) = memory
                (p, content) <- readIORef mem
                return (content!!!(loc, "getMem fail"))

update :: [a] -> Int -> a -> [a]
update [] _ _ = []
update (x:xs) 0 v = (v:xs)
update (x:xs) n v = x:(update xs (n-1) v)