NXTDSL-0.2: NXT/Interpretation.hs
{-# LANGUAGE DeriveDataTypeable, DoAndIfThenElse #-}
-- |
-- Module : NXT.Interpretation
-- Copyright : Alexander Thiemann <mail@agrafix.net>
-- License : BSD3
--
-- Maintainer : Alexander Thiemann <mail@agrafix.net>
-- Stability : experimental
-- Portability : non-portable (GHC extensions)
--
module NXT.Interpretation
( runVM
, RealEnv (..)
, MotorState(..)
)
where
import NXT.Types
import Data.IORef
import Control.Applicative
import Control.Concurrent
import Control.Concurrent.STM
import Control.Monad.Trans
import Control.Monad.Trans.Resource
import qualified Data.HashMap.Strict as HM
type EnvChan = TBQueue RealEnv
data RealEnv
= RealEnv
{ re_light :: Int
, re_ultra :: Int
} deriving (Show)
data MotorState
= MotorState
{ m_a :: TVar Int
, m_b :: TVar Int
, m_c :: TVar Int
}
getMS 0 = m_a
getMS 1 = m_b
getMS 2 = m_c
data SensorState
= SensorState
{ s_1 :: TVar Int
, s_2 :: TVar Int
, s_3 :: TVar Int
, s_4 :: TVar Int
}
data SensorReads
= SensorReads
{ sr_1 :: TVar (RealEnv -> Int)
, sr_2 :: TVar (RealEnv -> Int)
, sr_3 :: TVar (RealEnv -> Int)
, sr_4 :: TVar (RealEnv -> Int)
}
getSS :: Int -> (SensorState -> TVar Int)
getSS 0 = s_1
getSS 1 = s_2
getSS 2 = s_3
getSS 3 = s_4
getSR :: Int -> (SensorReads -> TVar (RealEnv -> Int))
getSR 0 = sr_1
getSR 1 = sr_2
getSR 2 = sr_3
getSR 3 = sr_4
data FunMap
= FunMap
{ fm_funs :: HM.HashMap String FunDefinition
, fm_apiCall :: String -> [NXTVal] -> IO NXTVal
}
type Env = IORef (HM.HashMap String (IORef NXTVal))
data NXTVal
= NInt Int
| NFloat Float
| NStr String
| NBool Bool
| NVoid
deriving (Eq, Show, Ord)
emptyEnv :: IO Env
emptyEnv = newIORef HM.empty
isBound :: Env -> String -> IO Bool
isBound envR var =
readIORef envR >>= return . maybe False (const True) . HM.lookup var
getVar :: Env -> String -> IO NXTVal
getVar envR var =
do env <- liftIO $ readIORef envR
maybe (error $ "Getting an unbound variable " ++ var)
(liftIO . readIORef)
(HM.lookup var env)
setVar :: Env -> String -> NXTVal -> IO NXTVal
setVar envR var value =
do env <- liftIO $ readIORef envR
maybe (error $ "Setting an unbound variable " ++ var)
(liftIO . (flip writeIORef value))
(HM.lookup var env)
return value
defineVar :: Env -> String -> NXTVal -> IO NXTVal
defineVar envR var value = do
alreadyDefined <- liftIO $ isBound envR var
if alreadyDefined
then setVar envR var value >> return value
else liftIO $ do
valueR <- newIORef value
env <- readIORef envR
writeIORef envR (HM.insert var valueR env)
return value
runVM :: MonadResource m => [FunDefinition] -> m (EnvChan, MotorState)
runVM fd =
do env <- liftIO emptyEnv
envChan <- liftIO $ atomically $ newTBQueue 20
b <- liftIO motorState
c <- liftIO sensorState
d <- liftIO sensorReads
case HM.lookup "main" funMap of
Just fd ->
do _ <- allocate (forkIO (updateLoop envChan c d)) (killThread)
_ <- allocate (forkIO $ runDef (FunMap funMap (apiCall envChan b c d)) env fd) (killThread)
return (envChan, b)
Nothing ->
error "No main task found."
where
updateLoop envQ sensor sensorR =
do logE <- atomically $
do env <- readTBQueue envQ
mapM_ (\sensorId ->
do extractFun <- readTVar ((getSR sensorId) sensorR)
let envVal = (extractFun env)
writeTVar ((getSS sensorId) sensor) envVal
) [0..3]
return env
putStrLn $ "New Environment: " ++ (show logE)
updateLoop envQ sensor sensorR
funMap = foldl (\hm f@(FunDefinition name _ _ _) -> HM.insert name f hm) HM.empty fd
apiCall env motor sensor sensorR funName input =
do ret <- apiCall' env motor sensor sensorR funName input
putStrLn $ "Called " ++ (show (funName, input))
putStrLn $ "Got: " ++ show ret
return ret
apiCall' env motor sensor sensorR funName input =
case (funName, input) of
("Wait", [NInt time]) ->
do threadDelay (1000 * time)
return NVoid
("SetSensorLight", [NInt sensorId]) ->
do atomically $ writeTVar ((getSR sensorId) sensorR) re_light
return NVoid
("Sensor", [NInt sensorId]) ->
do ct <- atomically $ readTVar ((getSS sensorId) sensor)
return $ NInt ct
("OnFwd", [NInt motorId, NInt motorPwr]) ->
do atomically $ writeTVar ((getMS motorId) motor) motorPwr
return NVoid
x ->
error $ "Call to undefined function " ++ (show x)
motorState = MotorState <$> zero <*> zero <*> zero
sensorState = SensorState <$> zero <*> zero <*> zero <*> zero
sensorReads = SensorReads
<$> unknown
<*> unknown
<*> unknown
<*> unknown
zero = atomically $ newTVar 0
unknown = atomically $ newTVar $ (\_ -> -1)
runDef :: FunMap -> Env -> FunDefinition -> IO ()
runDef funMap env (FunDefinition _ _ _ body) =
mapM_ (runStmt funMap env) body
runStmt :: FunMap -> Env -> Stmt -> IO ()
runStmt funMap env (If condR t f) =
do (NBool cond) <- runT funMap env condR
if cond
then mapM_ (runStmt funMap env) t
else mapM_ (runStmt funMap env) f
runStmt funMap env l@(While condR loop) =
do (NBool cond) <- runT funMap env condR
if cond
then do mapM_ (runStmt funMap env) loop
runStmt funMap env l
else return ()
runStmt funMap env (DeclVar _ (VarP p)) =
do defineVar env p NVoid
return ()
runStmt funMap env (AssignVar (VarP p) valR) =
do val <- runT funMap env valR
setVar env p val
return ()
runStmt funMap env (Eval v) =
do _ <- runT funMap env v
return ()
runStmt funMap env (FunReturn v) =
do val <- runT funMap env v
defineVar env "__funReturnVal" val
return ()
runV :: FunMap -> Env -> V a -> IO NXTVal
runV funMap env ct = runT funMap env (unpack ct)
runT :: FunMap -> Env -> T -> IO NXTVal
runT _ _ Void = return NVoid
runT _ env (VarP p) = getVar env p
runT _ env (Lit i) = return $ NInt i
runT _ env (Rat i) = return $ NFloat i
runT _ env (StrLit str) = return $ NStr str
runT _ env (BoolLit b) = return $ NBool b
runT funMap env (CastOp "cast2int" val) =
do evaluated <- runT funMap env val
case evaluated of
NFloat f -> return $ NInt $ floor f
_ -> error "Invalid cast."
runT funMap env (CastOp "cast2float" val) =
do evaluated <- runT funMap env val
case evaluated of
NInt i -> return $ NFloat $ fromIntegral i
_ -> error "Invalid cast."
runT funMap env (BinOp op xR yR) =
do x <- runT funMap env xR
y <- runT funMap env yR
case (x, op, y) of
(NInt a, BAdd, NInt b) -> return $ NInt (a + b)
(NInt a, BSub, NInt b) -> return $ NInt (a - b)
(NInt a, BMul, NInt b) -> return $ NInt (a * b)
(NFloat a, BAdd, NFloat b) -> return $ NFloat (a + b)
(NFloat a, BSub, NFloat b) -> return $ NFloat (a - b)
(NFloat a, BMul, NFloat b) -> return $ NFloat (a * b)
(NFloat a, BDiv, NFloat b) -> return $ NFloat (a / b)
(NInt a, BSub, NFloat b) -> return $ NFloat ((fromIntegral a) - b)
(NFloat a, BSub, NInt b) -> return $ NFloat (a - (fromIntegral b))
(NInt a, BMul, NFloat b) -> return $ NFloat ((fromIntegral a) * b)
(NFloat a, BMul, NInt b) -> return $ NFloat (a * (fromIntegral b))
(NInt a, BAdd, NFloat b) -> return $ NFloat ((fromIntegral a) + b)
(NFloat a, BAdd, NInt b) -> return $ NFloat (a + (fromIntegral b))
(a, BEq, b) -> return $ NBool (a == b)
(a, BNEq, b) -> return $ NBool (a /= b)
(a, BLt, b) -> return $ NBool (a > b)
(a, BSt, b) -> return $ NBool (a < b)
(a, BLEq, b) -> return $ NBool (a >= b)
(a, BSEq, b) -> return $ NBool (a <= b)
(NBool a, BAnd, NBool b) -> return $ NBool (a && b)
(NBool a, BOr, NBool b) -> return $ NBool (a || b)
x -> error $ "Unknown operation: " ++ show x
runT funMap env (FunCall name args) =
do evaled <- mapM (runT funMap env) args
apply funMap name evaled
runT _ env t = error $ "Not implemented:" ++ (prettyT t)
reqV (VarP p) = p
reqV x = error $ "Require a variable, but got: " ++ (prettyT x)
getArgs funMap funName =
case HM.lookup funName (fm_funs funMap) of
Just (FunDefinition _ _ args _) ->
map (\(DeclVar _ arg) -> reqV arg) args
Nothing ->
error $ "Function " ++ funName ++ " is not defined."
apply :: FunMap -> String -> [NXTVal] -> IO NXTVal
apply funMap funName inpVals =
do funEnv <- emptyEnv
case HM.lookup funName (fm_funs funMap) of
Just fd ->
do let args = getArgs funMap funName
vars = zip inpVals args
mapM_ (\(src, tgt) ->
defineVar funEnv tgt src
) vars
runDef funMap funEnv fd
getVar funEnv "__funReturnVal"
Nothing ->
(fm_apiCall funMap) funName inpVals