ideas-1.9.1: src/Ideas/Service/Apply.hs
-----------------------------------------------------------------------------
-- Copyright 2019, Ideas project team. This file is distributed under the
-- terms of the Apache License 2.0. For more information, see the files
-- "LICENSE.txt" and "NOTICE.txt", which are included in the distribution.
-----------------------------------------------------------------------------
-- |
-- Maintainer : bastiaan.heeren@ou.nl
-- Stability : provisional
-- Portability : portable (depends on ghc)
--
-----------------------------------------------------------------------------
module Ideas.Service.Apply
( ApplyResult(..), tApplyResult, apply
) where
import Data.Either
import Data.List
import Ideas.Common.Library hiding (apply)
import Ideas.Service.State
import Ideas.Service.BasicServices (allfirsts)
import Ideas.Service.Types
import qualified Data.Set as S
----------------------------------------------------------------
-- Result types for apply service
data ApplyResult a
= SyntaxError String
| Correct Bool (State a)
| Buggy Environment (Rule (Context a))
| Incorrect
instance Show (ApplyResult a) where
show (SyntaxError _) = "SyntaxError"
show (Correct _ _) = "Correct"
show (Buggy _ r) = "Buggy(" ++ show r ++ ")"
show Incorrect = "Incorrect"
----------------------------------------------------------------
-- The apply feedback service
-- Two possible scenarios: either I have a prefix and I can return a new one (i.e., still following the
-- strategy), or I return a new term without a prefix. A final scenario is that the rule cannot be applied
-- to the current term at the given location, in which case the request is invalid.
apply :: Rule (Context a) -> Location -> Environment -> State a -> ApplyResult a
apply r loc env st
| withoutPrefix st = applyOff
| otherwise = applyOn
where
applyOn = -- scenario 1: on-strategy
case [ new | (stepInfo, new) <- fromRight [] (allfirsts st), testStep stepInfo ] of
[] -> applyOff
new:_ -> correct new
applyOff = -- scenario 2: off-strategy
case transApplyWith env (transformation r) ca of
(new, _):_ -> correct (restart (st {stateContext = new, statePrefix = noPrefix}))
[] ->
-- first check the environment (exercise-specific property)
case environmentCheck st env of
Just msg ->
SyntaxError msg
Nothing ->
-- try to find a buggy rule
case siblingsFirst [ (envOut, br) | br <- ruleset (exercise st), isBuggy br, (_, envOut) <- transApplyWith env (transformation br) ca ] of
[] -> Incorrect
(envOut, br):_ -> Buggy envOut br
ca = setLocation loc (stateContext st)
siblingsFirst xs = ys ++ zs
where
(ys, zs) = partition (siblingInCommon r . snd) xs
testStep (r1, loc1, env1) =
r == r1 && loc == loc1 && (noBindings env || env == env1)
environmentCheck :: State a -> Environment -> Maybe String
environmentCheck st env = do
p <- getProperty "environment-check" (exercise st)
p env
correct :: State a -> ApplyResult a
correct st = Correct (finished st) st
siblingInCommon :: Rule a -> Rule a -> Bool
siblingInCommon r1 r2 = not (S.null (getSiblings r1 `S.intersection` getSiblings r2))
where
getSiblings r = S.fromList (getId r : ruleSiblings r)
----------------------------------------------------------------
-- Helpers
tApplyResult :: Type a (ApplyResult a)
tApplyResult = Tag "ApplyResult" $ Iso (f <-> g) tp
where
tp = tString :|: tPair tBool tState :|: tPair tEnvironment tRule :|: tUnit
f (Left msg) = SyntaxError msg
f (Right (Left (b, st))) = Correct b st
f (Right (Right (Left (env, r)))) = Buggy env r
f (Right (Right (Right ()))) = Incorrect
g (SyntaxError msg) = Left msg
g (Correct b st) = Right (Left (b, st))
g (Buggy env r) = Right (Right (Left (env, r)))
g Incorrect = Right (Right (Right ()))