module Data.Crjdt.Eval
( Eval(..)
, EvalError(..)
, addReceivedOps
, run
, evalEval
, execEval
, addVariable
, execute
, eval
) where
import Data.Void
import Data.Sequence (ViewL(..), viewl)
import qualified Data.Sequence as Seq
import Data.Map as M
import Data.Maybe (fromMaybe)
import Data.Set as Set
import Data.Foldable (traverse_)
import Control.Exception (Exception)
import Control.Monad.Fix
import Control.Monad.State
import Control.Monad.Except
import Control.Monad.Free (iterM)
import Data.Crjdt.Types
import Data.Crjdt.Context
import Data.Crjdt.Internal.Core
data EvalError
= GetOnHead
| UndefinedVariable Var
deriving (Show, Eq)
instance Exception EvalError
newtype Eval a = Eval
{ runEval :: ExceptT EvalError (State Context) a }
deriving
( Functor
, Applicative
, Monad
, MonadFix
, MonadError EvalError
, MonadState Context
)
initial :: ReplicaId -> Context
initial rid = Context
{ document = BranchDocument (Branch mempty mempty mempty MapT)
, replicaGlobal = 0
, variables = mempty
, replicaId = rid
, queue = mempty
, history = mempty
, received = mempty
}
type Ctx m = (MonadError EvalError m, MonadState Context m)
run :: ReplicaId -> Eval a -> (Either EvalError a, Context)
run rid = (`runState` (initial rid)) . runExceptT . runEval
evalEval :: ReplicaId -> Expr -> Either EvalError Cursor
evalEval rid = (`evalState` (initial rid)) . runExceptT . runEval . eval
execEval :: ReplicaId -> Eval a -> Context
execEval rid = (`execState` (initial rid)) . runExceptT . runEval
valuesOf :: Ctx m => Expr -> m [Val]
valuesOf e = partsOf e RegT $ \case
(LeafDocument l) -> M.elems (registers l)
_ -> mempty
keysOf :: Ctx m => Expr -> m (Set.Set (Key Void))
keysOf e = partsOf e MapT $ \case
(BranchDocument (Branch{branchTag = MapT,..})) -> M.keysSet $ M.filter (not . Set.null) presence
_ -> mempty
partsOf :: (Ctx m, Monoid a) => Expr -> Tag -> (Document Tag -> a) -> m a
partsOf e tag f = eval e >>= \c -> partsOf' c f . document <$> get
where
partsOf' :: Monoid m => Cursor -> (Document Tag -> m) -> Document Tag -> m
partsOf' Cursor{..} getParts d = fromMaybe mempty $ case viewl path of
EmptyL -> getParts <$> findChild (tagWith tag $ basicKey finalKey) d
(x :< xs) -> partsOf' (Cursor xs finalKey) getParts <$> findChild x d
addVariable :: Ctx m => Var -> Cursor -> m ()
addVariable v cur = modify $ \c -> c { variables = M.insert v cur (variables c)}
addReceivedOps :: MonadState Context m => Seq.Seq Operation -> m ()
addReceivedOps ops = modify (\ctx -> ctx {received = ops `mappend` (received ctx)})
applyRemote :: Ctx m => m ()
applyRemote = get >>= \c ->
let alreadyProcessed op cc = opId op `Set.member` history cc
satisfiesDeps op cc = opDeps op `Set.isSubsetOf` history cc
applyRemote' op = do
cc <- get
when (not (alreadyProcessed op cc) && satisfiesDeps op cc) $ put cc
{ replicaGlobal = replicaGlobal cc `max` (sequenceNumber . opId $ op)
, document = applyOp op (document cc)
, history = Set.insert (opId op) (history cc)
}
in traverse_ applyRemote' (received c)
applyLocal :: Ctx m => Mutation -> Cursor -> m ()
applyLocal mut cur = modify $ \c ->
let op = Operation
{ opId = mkId (replicaGlobal c + 1) (replicaId c)
, opDeps = history c
, opCur = cur
, opMutation = mut
}
in c { document = applyOp op (document c)
, replicaGlobal = replicaGlobal c + 1
, history = Set.insert (opId op) (history c)
, queue = queue c Seq.|> op
}
eval :: Ctx m => Expr -> m Cursor
eval Doc = pure $ Cursor Seq.empty $ unTag docKey
eval (GetKey expr k) = do
cursor <- eval expr
case finalKey cursor of
(Key Head) -> throwError GetOnHead
_ -> pure (appendWith MapT k cursor)
eval (Var var) = get >>= maybe (throwError (UndefinedVariable var)) pure . lookupCtx var
eval (Iter expr) = appendWith ListT (Key Head) <$> eval expr
eval (Next expr) = get >>= \(document -> d) -> stepNext d <$> eval expr
execCmd :: Ctx m => Cmd (m a) -> m a
execCmd (Let x expr c) = (eval expr >>= \cur -> addVariable (Variable x) cur *> pure (Var $ Variable x)) >>= c
execCmd (Assign expr v c) = (eval expr >>= applyLocal (AssignMutation v)) >> c
execCmd (InsertAfter expr v c) = (eval expr >>= applyLocal (InsertMutation v)) >> c
execCmd (Delete expr c) = (eval expr >>= applyLocal DeleteMutation) >> c
execCmd (Yield c) = applyRemote >> c
execCmd (Values expr c) = valuesOf expr >>= c
execCmd (Keys expr c) = keysOf expr >>= c
execute :: Ctx m => Command a -> m a
execute = iterM execCmd . runCommand