{- This is a part of the Controls module that is separated in order to prevent an import cycle. -} module ControlsBase where import qualified Exception import qualified Term import Term ( Term ) import qualified Data.Map as M import qualified Control.Monad.Exception.Synchronous as Exc import qualified Control.Monad.Trans.Class as MT import qualified Control.Monad.Trans.State as MS import qualified Data.Traversable as Trav data Control = CheckBox Bool | Slider Int Int Int data Value = Bool Bool | Number Int deriving Show data Values = Values { boolValues :: M.Map Name Bool, numberValues :: M.Map Name Int } deriving Show newtype Name = Name String deriving (Eq, Ord, Show) deconsName :: Name -> String deconsName (Name name) = name emptyValues :: Values emptyValues = Values M.empty M.empty updateValues :: Values -> Assignments -> Assignments updateValues (Values bools numbers) assigns = M.union (M.intersectionWith (\b (rng, a) -> (rng, case a of CheckBox _deflt -> CheckBox b _ -> a)) bools assigns) $ M.union (M.intersectionWith (\x (rng, a) -> (rng, case a of Slider lower upper _deflt -> Slider lower upper x _ -> a)) numbers assigns) $ assigns type Assignments = M.Map Name (Term.Range, Control) exc :: Term.Range -> String -> Exception.Message exc rng msg = Exception.Message Exception.Parse rng msg excDuplicate :: Name -> Term.Range -> Exception.Message excDuplicate name rng = exc rng $ "duplicate controller definition with name " ++ deconsName name union :: Assignments -> Assignments -> Exc.Exceptional Exception.Message Assignments union m0 m1 = let f = fmap Exc.Success in Trav.sequenceA $ M.unionWithKey (\name _ a -> do (rng, _c) <- a Exc.throw $ excDuplicate name rng) (f m0) (f m1) collect :: Term -> Exc.Exceptional Exception.Message Assignments collect topTerm = flip MS.execStateT M.empty $ mapM_ (\ea -> do (name, rc@(rng, _ctrl)) <- MT.lift ea MT.lift . Exc.assert (excDuplicate name rng) =<< MS.gets (not . M.member name) MS.modify (M.insert name rc)) $ do ( _pos, term ) <- Term.subterms topTerm case Term.viewNode term of Just ( "checkBox" , args ) -> return $ case args of [ Term.StringLiteral _rng tag, Term.Node deflt [] ] -> case reads $ Term.name deflt of [(b, "")] -> Exc.Success $ (Name tag, (Term.termRange term, CheckBox b)) _ -> Exc.Exception $ exc (Term.range deflt) $ "cannot parse Bool value " ++ show (Term.name deflt) ++ " for checkBox" _ -> Exc.Exception $ exc (Term.termRange term) "invalid checkBox arguments" Just ( "slider" , args ) -> return $ case args of [ Term.StringLiteral _rngT tag, lower, upper, deflt ] -> do let milliard = 1000000000 number arg = Exception.checkRange Exception.Parse arg id id (-milliard) milliard l <- number "lower slider bound" lower u <- number "upper slider bound" upper x <- number "default slider value" deflt return (Name tag, (Term.termRange term, Slider l u x)) _ -> Exc.Exception $ exc (Term.termRange term) "invalid slider arguments" _ -> []