module Ivory.Language.Ion.Operators where
import Control.Applicative ( (<$>) )
import Control.Exception
import Control.Monad
import Control.Monad.State hiding ( forever )
import qualified Ivory.Language as IL
import qualified Ivory.Language.Monad as ILM
import Ivory.Language.Proc ( Def(..), Proc(..), IvoryCall_,
IvoryProcDef )
import Ivory.Language.Ion.Base
import Ivory.Language.Ion.Schedule
import Ivory.Language.Ion.Util
addAction :: (Schedule -> Schedule) -> Ion a -> Ion a
addAction fn sub = do
start <- get
let temp = IonDef
{ ionId = ionId start
, ionNum = ionNum start
, ionCtxt = fn $ ionCtxt start
, ionDefs = return ()
, ionSched = [fn $ ionCtxt start]
}
(a, def) = runState sub temp
put $ start { ionNum = ionNum def
, ionDefs = ionDefs start >> ionDefs def
, ionSched = ionSched start ++ ionSched def
}
return a
getSched :: Ion Schedule
getSched = ionCtxt <$> get
getPhase :: Ion Integer
getPhase = schedPhase <$> ionCtxt <$> get
ion :: String
-> Ion a
-> Ion a
ion name = addAction setName
where setName sch = case checkCName name of
Just i -> throw $ InvalidCName (schedPath sch) name i
Nothing -> sch { schedName = name
, schedPath = schedPath sch ++ [name]
}
phaseSet :: Integral i => i -> Schedule -> Schedule
phaseSet ph sch = if (ph' >= schedPeriod sch)
then throw $
PhaseExceedsPeriod (schedPath sch) ph' (schedPeriod sch)
else sch { schedPhase = ph' }
where ph' = fromIntegral ph
delay :: Integral i =>
i
-> Ion a
-> Ion a
delay ph = addAction setDelay
where setDelay sch = phaseSet (schedPhase sch + fromIntegral ph) sch
phase :: Integral i =>
i
-> Ion a
-> Ion a
phase ph = addAction (phaseSet ph)
period :: Integral i =>
i
-> Ion a
-> Ion a
period p = addAction setPeriod
where p' = fromIntegral p
setPeriod sch = if (p' <= 0)
then throw $ PeriodMustBePositive (schedPath sch) p'
else sch { schedPeriod = p' }
subPeriod :: Integral i =>
i
-> Ion a
-> Ion a
subPeriod f = addAction divPeriod
where divPeriod sch = let p = schedPeriod sch * fromIntegral f
in if (p <= 0)
then throw $ PeriodMustBePositive (schedPath sch) p
else sch { schedPeriod = p }
disable :: Ion a -> Ion ()
disable _ = return ()
cond :: IvoryAction IL.IBool -> Ion a -> Ion a
cond pred = addAction setCond
where setCond sch = sch { schedCond = pred : schedCond sch }
ivoryEff :: IvoryAction () -> Ion ()
ivoryEff iv = addAction addEff $ return ()
where addEff sch = sch { schedAction = schedAction sch ++ [iv] }
newName :: Ion String
newName = do state <- get
let num' = ionNum state
put $ state { ionNum = num' + 1 }
return $ ionId state ++ "_" ++ show num'
area' :: (IL.IvoryArea area, IL.IvoryZero area) =>
String
-> Maybe (IL.Init area)
-> Ion (IL.Ref IL.Global area)
area' name init = do
let mem = IL.area name init
state <- get
put $ state { ionDefs = ionDefs state >> IL.defMemArea mem }
return $ IL.addrOf mem
areaP' :: (IL.IvoryArea area, IL.IvoryZero area) =>
IL.Proxy area
-> String
-> Maybe (IL.Init area)
-> Ion (IL.Ref IL.Global area)
areaP' _ = area'
newArea :: (IL.IvoryArea area, IL.IvoryZero area) =>
Maybe (IL.Init area) -> Ion (IL.Ref IL.Global area)
newArea init = mkArea =<< newName
where mkArea name = area' name init
newAreaP :: (IL.IvoryArea area, IL.IvoryZero area) =>
IL.Proxy area -> Maybe (IL.Init area) ->
Ion (IL.Ref IL.Global area)
newAreaP _ = newArea
newProc :: (IvoryProcDef proc impl) => impl -> Ion (Def proc)
newProc impl = do
name <- newName
state <- get
let fn sym = IL.proc sym impl
put $ state { ionDefs = ionDefs state >> (IL.incl $ fn name) }
return $ fn name
newProcP :: (IvoryProcDef proc impl) =>
IL.Proxy (Def proc) -> impl -> Ion (Def proc)
newProcP _ = newProc
adapt_0_1 :: (IL.IvoryType a, IL.IvoryVar a) =>
Def ('[] ':-> ()) -> Ion (Def ('[a] ':-> ()))
adapt_0_1 fn0 = newProc $ \_ -> IL.body $ IL.call_ fn0
adapt_1_0 :: (Num a, IL.IvoryType a, IL.IvoryVar a) =>
Def ('[a] ':-> ()) -> Ion (Def ('[] ':-> ()))
adapt_1_0 fn0 = newProc $ IL.body $ IL.call_ fn0 0
adapt_0_2 :: (IL.IvoryType a, IL.IvoryVar a, IL.IvoryType b, IL.IvoryVar b) =>
Def ('[] ':-> ()) -> Ion (Def ('[a,b] ':-> ()))
adapt_0_2 fn0 = newProc $ \_ _ -> IL.body $ IL.call_ fn0
adapt_2_0 :: (Num a, IL.IvoryType a, IL.IvoryVar a, Num b, IL.IvoryType b,
IL.IvoryVar b) =>
Def ('[a, b] ':-> ()) -> Ion (Def ('[] ':-> ()))
adapt_2_0 fn0 = newProc $ IL.body $ IL.call_ fn0 0 0
adapt_0_3 :: (IL.IvoryType a, IL.IvoryVar a, IL.IvoryType b, IL.IvoryVar b,
IL.IvoryType c, IL.IvoryVar c) =>
Def ('[] ':-> ()) -> Ion (Def ('[a,b,c] ':-> ()))
adapt_0_3 fn0 = newProc $ \_ _ _ -> IL.body $ IL.call_ fn0
adapt_3_0 :: (Num a, IL.IvoryType a, IL.IvoryVar a, Num b, IL.IvoryType b,
IL.IvoryVar b, Num c, IL.IvoryType c, IL.IvoryVar c) =>
Def ('[a, b, c] ':-> ()) -> Ion (Def ('[] ':-> ()))
adapt_3_0 fn0 = newProc $ IL.body $ IL.call_ fn0 0 0 0
adapt_0_4 :: (IL.IvoryType a, IL.IvoryVar a, IL.IvoryType b, IL.IvoryVar b,
IL.IvoryType c, IL.IvoryVar c, IL.IvoryType d, IL.IvoryVar d) =>
Def ('[] ':-> ()) -> Ion (Def ('[a,b,c,d] ':-> ()))
adapt_0_4 fn0 = newProc $ \_ _ _ _ -> IL.body $ IL.call_ fn0
adapt_4_0 :: (Num a, IL.IvoryType a, IL.IvoryVar a, Num b, IL.IvoryType b,
IL.IvoryVar b, Num c, IL.IvoryType c, IL.IvoryVar c, Num d,
IL.IvoryType d, IL.IvoryVar d) =>
Def ('[a, b, c, d] ':-> ()) -> Ion (Def ('[] ':-> ()))
adapt_4_0 fn0 = newProc $ IL.body $ IL.call_ fn0 0 0 0 0
adapt_0_5 :: (IL.IvoryType a, IL.IvoryVar a, IL.IvoryType b, IL.IvoryVar b,
IL.IvoryType c, IL.IvoryVar c, IL.IvoryType d, IL.IvoryVar d,
IL.IvoryType e, IL.IvoryVar e) =>
Def ('[] ':-> ()) -> Ion (Def ('[a,b,c,d,e] ':-> ()))
adapt_0_5 fn0 = newProc $ \_ _ _ _ _ -> IL.body $ IL.call_ fn0
timer :: (a ~ 'IL.Stored t, Num t, IL.IvoryStore t, IL.IvoryInit t,
IL.IvoryEq t, IL.IvoryOrd t, IL.IvoryArea a, IL.IvoryZero a) =>
IL.Proxy t
-> Def ('[] ':-> ())
-> Ion (IL.Ref IL.Global (IL.Stored t))
timer _ expFn = do
name <- newName
ion name $ do
var <- area' name $ Just $ IL.ival 0
ion "decr" $ ivoryEff $ do
val <- IL.deref var
IL.ifte_ (val IL.==? 0) (return ())
$ do let val' = val 1
IL.store var (val')
IL.ifte_ (val' IL.>? 0) (return ()) $ IL.call_ expFn
return var
startTimer :: (Num t, IL.IvoryStore t, IL.IvoryZeroVal t) =>
IL.Ref IL.Global (IL.Stored t)
-> Integer
-> ILM.Ivory eff ()
startTimer ref n = IL.store ref $ fromInteger n
stopTimer ref = startTimer ref 0