2017-02-21 18:41:46 +03:00
{- # LANGUAGE GADTs, RankNTypes # -}
2017-02-24 18:00:21 +03:00
module Interpreter ( diffTerms , run , runSteps , runStep ) where
2015-11-18 04:05:16 +03:00
import Algorithm
2017-02-24 21:28:44 +03:00
import Control.Monad.Free.Freer
2016-07-11 22:35:13 +03:00
import Data.Align.Generic
2016-02-29 18:12:34 +03:00
import Data.Functor.Both
2017-04-24 22:53:11 +03:00
import RWS
2016-06-23 23:34:13 +03:00
import Data.Record
2016-06-30 18:59:33 +03:00
import Data.These
2016-03-03 07:12:51 +03:00
import Diff
2017-02-24 21:28:44 +03:00
import Info hiding ( Return )
2017-02-24 17:48:16 +03:00
import Patch ( inserting , deleting , replacing , patchSum )
2017-02-24 21:28:44 +03:00
import Prologue hiding ( lookup )
import Syntax as S hiding ( Return )
2016-03-03 07:12:51 +03:00
import Term
2015-11-18 04:05:16 +03:00
2016-08-04 19:13:48 +03:00
-- | Diff two terms recursively, given functions characterizing the diffing.
2017-02-07 21:40:30 +03:00
diffTerms :: ( Eq leaf , HasField fields Category , HasField fields ( Maybe FeatureVector ) )
2017-02-21 23:23:30 +03:00
=> SyntaxTerm leaf fields -- ^ A term representing the old state.
2016-09-12 20:40:22 +03:00
-> SyntaxTerm leaf fields -- ^ A term representing the new state.
-> SyntaxDiff leaf fields
2017-02-24 17:23:03 +03:00
diffTerms = ( run . ) . diff
2015-12-16 21:33:05 +03:00
2017-02-24 17:54:37 +03:00
-- | Run an Algorithm to completion, returning its result.
2017-02-24 17:35:53 +03:00
run :: ( Eq leaf , HasField fields Category , HasField fields ( Maybe FeatureVector ) )
=> Algorithm ( SyntaxTerm leaf fields ) ( SyntaxDiff leaf fields ) result
-> result
2017-02-24 21:42:53 +03:00
run = iterFreer ( \ algorithm cont -> cont ( run ( decompose algorithm ) ) )
2017-02-24 17:35:53 +03:00
2017-02-24 17:58:45 +03:00
-- | Run an Algorithm to completion, returning the list of steps taken.
runSteps :: ( Eq leaf , HasField fields Category , HasField fields ( Maybe FeatureVector ) )
=> Algorithm ( SyntaxTerm leaf fields ) ( SyntaxDiff leaf fields ) result
2017-02-24 17:59:29 +03:00
-> [ Algorithm ( SyntaxTerm leaf fields ) ( SyntaxDiff leaf fields ) result ]
2017-02-24 17:58:45 +03:00
runSteps algorithm = case runStep algorithm of
2017-02-24 21:28:44 +03:00
Left a -> [ Return a ]
2017-02-24 17:59:29 +03:00
Right next -> next : runSteps next
2017-02-24 17:58:45 +03:00
2017-02-24 17:54:37 +03:00
-- | Run a single step of an Algorithm, returning Either its result if it has finished, or the next step otherwise.
2017-02-24 17:35:53 +03:00
runStep :: ( Eq leaf , HasField fields Category , HasField fields ( Maybe FeatureVector ) )
=> Algorithm ( SyntaxTerm leaf fields ) ( SyntaxDiff leaf fields ) result
-> Either result ( Algorithm ( SyntaxTerm leaf fields ) ( SyntaxDiff leaf fields ) result )
2017-03-28 22:32:45 +03:00
runStep step = case step of
2017-02-24 21:28:44 +03:00
Return a -> Left a
2017-02-24 21:34:08 +03:00
algorithm ` Then ` cont -> Right $ decompose algorithm >>= cont
2017-02-24 17:35:53 +03:00
-- | Decompose a step of an algorithm into the next steps to perform.
decompose :: ( Eq leaf , HasField fields Category , HasField fields ( Maybe FeatureVector ) )
=> AlgorithmF ( SyntaxTerm leaf fields ) ( SyntaxDiff leaf fields ) result -- ^ The step in an algorithm to decompose into its next steps.
-> Algorithm ( SyntaxTerm leaf fields ) ( SyntaxDiff leaf fields ) result -- ^ The sequence of next steps to undertake to continue the algorithm.
2017-03-28 22:32:45 +03:00
decompose step = case step of
2017-02-24 17:35:53 +03:00
Diff t1 t2 -> algorithmWithTerms t1 t2
Linear t1 t2 -> case galignWith diffThese ( unwrap t1 ) ( unwrap t2 ) of
Just result -> wrap . ( both ( extract t1 ) ( extract t2 ) :< ) <$> sequenceA result
_ -> byReplacing t1 t2
RWS as bs -> traverse diffThese ( rws ( editDistanceUpTo defaultM ) comparable as bs )
Delete a -> pure ( deleting a )
Insert b -> pure ( inserting b )
Replace a b -> pure ( replacing a b )
2016-08-04 17:46:48 +03:00
2016-08-04 19:47:56 +03:00
-- | Construct an algorithm to diff a pair of terms.
2017-02-24 17:45:43 +03:00
algorithmWithTerms :: SyntaxTerm leaf fields
-> SyntaxTerm leaf fields
-> Algorithm ( SyntaxTerm leaf fields ) ( SyntaxDiff leaf fields ) ( SyntaxDiff leaf fields )
2017-02-21 23:17:35 +03:00
algorithmWithTerms t1 t2 = maybe ( linearly t1 t2 ) ( fmap annotate ) $ case ( unwrap t1 , unwrap t2 ) of
2016-10-31 22:49:39 +03:00
( Indexed a , Indexed b ) ->
2017-02-21 18:46:19 +03:00
Just $ Indexed <$> byRWS a b
2016-10-31 22:49:39 +03:00
( S . Module idA a , S . Module idB b ) ->
2017-02-21 18:46:19 +03:00
Just $ S . Module <$> linearly idA idB <*> byRWS a b
2017-03-29 01:55:57 +03:00
( S . FunctionCall identifierA typeParamsA argsA , S . FunctionCall identifierB typeParamsB argsB ) -> Just $
2017-02-21 18:46:19 +03:00
S . FunctionCall <$> linearly identifierA identifierB
2017-03-29 01:55:57 +03:00
<*> byRWS typeParamsA typeParamsB
2017-02-21 18:46:19 +03:00
<*> byRWS argsA argsB
2016-09-25 09:33:55 +03:00
( S . Switch exprA casesA , S . Switch exprB casesB ) -> Just $
2017-02-21 18:46:19 +03:00
S . Switch <$> byRWS exprA exprB
<*> byRWS casesA casesB
2016-12-07 00:09:04 +03:00
( S . Object tyA a , S . Object tyB b ) -> Just $
2017-02-21 18:46:19 +03:00
S . Object <$> maybeLinearly tyA tyB
<*> byRWS a b
2016-09-25 09:33:55 +03:00
( Commented commentsA a , Commented commentsB b ) -> Just $
2017-02-21 18:46:19 +03:00
Commented <$> byRWS commentsA commentsB
<*> maybeLinearly a b
2016-12-07 00:09:04 +03:00
( Array tyA a , Array tyB b ) -> Just $
2017-02-21 18:46:19 +03:00
Array <$> maybeLinearly tyA tyB
<*> byRWS a b
2017-03-29 01:16:04 +03:00
( S . Class identifierA clausesA expressionsA , S . Class identifierB clausesB expressionsB ) -> Just $
2017-02-21 18:46:19 +03:00
S . Class <$> linearly identifierA identifierB
2017-03-29 01:16:04 +03:00
<*> byRWS clausesA clausesB
2017-02-21 18:46:19 +03:00
<*> byRWS expressionsA expressionsB
2017-03-31 01:00:13 +03:00
( S . Method clausesA identifierA receiverA paramsA expressionsA , S . Method clausesB identifierB receiverB paramsB expressionsB ) -> Just $
S . Method <$> byRWS clausesA clausesB
<*> linearly identifierA identifierB
2017-02-21 18:46:19 +03:00
<*> maybeLinearly receiverA receiverB
<*> byRWS paramsA paramsB
<*> byRWS expressionsA expressionsB
2017-03-29 19:36:05 +03:00
( S . Function idA paramsA bodyA , S . Function idB paramsB bodyB ) -> Just $
2017-02-21 18:46:19 +03:00
S . Function <$> linearly idA idB
<*> byRWS paramsA paramsB
<*> byRWS bodyA bodyB
2016-09-25 09:33:55 +03:00
_ -> Nothing
2017-01-18 23:08:30 +03:00
where
2017-02-21 23:17:35 +03:00
annotate = wrap . ( both ( extract t1 ) ( extract t2 ) :< )
2017-01-18 23:08:30 +03:00
2017-02-24 17:48:16 +03:00
maybeLinearly a b = case ( a , b ) of
( Just a , Just b ) -> Just <$> linearly a b
( Nothing , Just b ) -> Just <$> byInserting b
( Just a , Nothing ) -> Just <$> byDeleting a
( Nothing , Nothing ) -> pure Nothing
2016-08-04 03:08:20 +03:00
2017-02-24 17:12:37 +03:00
2017-02-24 17:35:53 +03:00
-- | Test whether two terms are comparable.
comparable :: ( Functor f , HasField fields Category ) => Term f ( Record fields ) -> Term f ( Record fields ) -> Bool
comparable = ( == ) ` on ` category . extract
2017-02-24 00:42:41 +03:00
2017-02-24 00:13:42 +03:00
-- | How many nodes to consider for our constant-time approximation to tree edit distance.
defaultM :: Integer
defaultM = 10
-- | Return an edit distance as the sum of it's term sizes, given an cutoff and a syntax of terms 'f a'.
-- | Computes a constant-time approximation to the edit distance of a diff. This is done by comparing at most _m_ nodes, & assuming the rest are zero-cost.
2017-02-24 00:27:24 +03:00
editDistanceUpTo :: ( GAlign f , Foldable f , Functor f , HasField fields Category ) => Integer -> These ( Term f ( Record fields ) ) ( Term f ( Record fields ) ) -> Int
editDistanceUpTo m = these termSize termSize ( \ a b -> diffSum ( patchSum termSize ) ( cutoff m ( approximateDiff a b ) ) )
where diffSum patchCost = sum . fmap ( maybe 0 patchCost )
approximateDiff a b = maybe ( replacing a b ) wrap ( galignWith ( these deleting inserting approximateDiff ) ( unwrap a ) ( unwrap b ) )