1
1
mirror of https://github.com/github/semantic.git synced 2025-01-09 00:56:32 +03:00
semantic/src/Interpreter.hs

128 lines
6.1 KiB
Haskell
Raw Normal View History

{-# LANGUAGE GADTs, RankNTypes #-}
module Interpreter (diffTerms, run, runSteps, runStep) where
2015-11-18 04:05:16 +03:00
import Algorithm
import Control.Monad.Free.Freer
2016-07-11 22:35:13 +03:00
import Data.Align.Generic
import Data.Functor.Both
2017-04-24 22:53:11 +03:00
import RWS
import Data.Record
import Data.These
2016-03-03 07:12:51 +03:00
import Diff
import Info hiding (Return)
import Patch (inserting, deleting, replacing, patchSum)
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.
diffTerms :: (Eq leaf, HasField fields Category, HasField fields (Maybe FeatureVector))
=> SyntaxTerm leaf fields -- ^ A term representing the old state.
-> SyntaxTerm leaf fields -- ^ A term representing the new state.
-> SyntaxDiff leaf fields
diffTerms = (run .) . diff
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
-- | 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]
runSteps algorithm = case runStep algorithm of
Left a -> [Return a]
2017-02-24 17:59:29 +03:00
Right next -> next : runSteps next
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
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.
algorithmWithTerms :: SyntaxTerm leaf fields
-> SyntaxTerm leaf fields
-> Algorithm (SyntaxTerm leaf fields) (SyntaxDiff leaf fields) (SyntaxDiff leaf fields)
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) ->
Just $ Indexed <$> byRWS a b
2016-10-31 22:49:39 +03:00
(S.Module idA a, S.Module idB b) ->
Just $ S.Module <$> linearly idA idB <*> byRWS a b
(S.FunctionCall identifierA typeParamsA argsA, S.FunctionCall identifierB typeParamsB argsB) -> Just $
S.FunctionCall <$> linearly identifierA identifierB
<*> byRWS typeParamsA typeParamsB
<*> byRWS argsA argsB
2016-09-25 09:33:55 +03:00
(S.Switch exprA casesA, S.Switch exprB casesB) -> Just $
S.Switch <$> byRWS exprA exprB
<*> byRWS casesA casesB
(S.Object tyA a, S.Object tyB b) -> Just $
S.Object <$> maybeLinearly tyA tyB
<*> byRWS a b
2016-09-25 09:33:55 +03:00
(Commented commentsA a, Commented commentsB b) -> Just $
Commented <$> byRWS commentsA commentsB
<*> maybeLinearly a b
(Array tyA a, Array tyB b) -> Just $
Array <$> maybeLinearly tyA tyB
<*> byRWS a b
(S.Class identifierA clausesA expressionsA, S.Class identifierB clausesB expressionsB) -> Just $
S.Class <$> linearly identifierA identifierB
<*> byRWS clausesA clausesB
<*> byRWS expressionsA expressionsB
(S.Method clausesA identifierA receiverA paramsA expressionsA, S.Method clausesB identifierB receiverB paramsB expressionsB) -> Just $
S.Method <$> byRWS clausesA clausesB
<*> linearly identifierA identifierB
<*> maybeLinearly receiverA receiverB
<*> byRWS paramsA paramsB
<*> byRWS expressionsA expressionsB
(S.Function idA paramsA bodyA, S.Function idB paramsB bodyB) -> Just $
S.Function <$> linearly idA idB
<*> byRWS paramsA paramsB
<*> byRWS bodyA bodyB
2016-09-25 09:33:55 +03:00
_ -> Nothing
where
annotate = wrap . (both (extract t1) (extract t2) :<)
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
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
-- | 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.
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))