diff --git a/src/Data/Diff.hs b/src/Data/Diff.hs index c28d8a65b..7cd689917 100644 --- a/src/Data/Diff.hs +++ b/src/Data/Diff.hs @@ -1,15 +1,31 @@ {-# LANGUAGE DataKinds, RankNTypes, TypeFamilies, TypeOperators #-} -module Data.Diff where +module Data.Diff +( Diff(..) +, DiffF(..) +, replacing +, inserting +, insertF +, deleting +, deleteF +, merge +, mergeF +, merging +, diffPatches +, beforeTerm +, afterTerm +, stripDiff +) where +import Control.Applicative ((<|>)) import Data.Aeson import Data.Bifoldable import Data.Bifunctor import Data.Bitraversable -import Data.Foldable (toList) +import Data.Foldable (asum, toList) import Data.Functor.Classes import Data.Functor.Foldable hiding (fold) import Data.JSON.Fields -import Data.Mergeable +import Data.Mergeable (Mergeable(sequenceAlt)) import Data.Patch import Data.Record import Data.Term @@ -61,16 +77,6 @@ merging :: Functor syntax => Term syntax ann -> Diff syntax ann ann merging = cata (\ (In ann syntax) -> mergeF (In (ann, ann) syntax)) -diffSum :: (Foldable syntax, Functor syntax) => (forall a b. Patch a b -> Int) -> Diff syntax ann1 ann2 -> Int -diffSum patchCost = cata $ \ diff -> case diff of - Patch patch -> patchCost patch + sum (sum <$> patch) - Merge merge -> sum merge - --- | The sum of the node count of the diff’s patches. -diffCost :: (Foldable syntax, Functor syntax) => Diff syntax ann1 ann2 -> Int -diffCost = diffSum (const 1) - - diffPatch :: Diff syntax ann1 ann2 -> Maybe (Patch (TermF syntax ann1 (Diff syntax ann1 ann2)) (TermF syntax ann2 (Diff syntax ann1 ann2))) diffPatch diff = case unDiff diff of Patch patch -> Just patch @@ -82,21 +88,17 @@ diffPatches = para $ \ diff -> case diff of Merge merge -> foldMap (toList . diffPatch . fst) merge --- | Merge a diff using a function to provide the Term (in Maybe, to simplify recovery of the before/after state) for every Patch. -mergeMaybe :: (Mergeable syntax, Traversable syntax) => (DiffF syntax ann1 ann2 (Maybe (Term syntax combined)) -> Maybe (Term syntax combined)) -> Diff syntax ann1 ann2 -> Maybe (Term syntax combined) -mergeMaybe = cata - -- | Recover the before state of a diff. beforeTerm :: (Mergeable syntax, Traversable syntax) => Diff syntax ann1 ann2 -> Maybe (Term syntax ann1) -beforeTerm = mergeMaybe $ \ diff -> case diff of - Patch patch -> before patch >>= \ (In a l) -> termIn a <$> sequenceAlt l - Merge (In (a, _) l) -> termIn a <$> sequenceAlt l +beforeTerm = cata $ \ diff -> case diff of + Patch patch -> (before patch >>= \ (In a l) -> termIn a <$> sequenceAlt l) <|> (after patch >>= asum) + Merge (In (a, _) l) -> termIn a <$> sequenceAlt l -- | Recover the after state of a diff. afterTerm :: (Mergeable syntax, Traversable syntax) => Diff syntax ann1 ann2 -> Maybe (Term syntax ann2) -afterTerm = mergeMaybe $ \ diff -> case diff of - Patch patch -> after patch >>= \ (In b r) -> termIn b <$> sequenceAlt r - Merge (In (_, b) r) -> termIn b <$> sequenceAlt r +afterTerm = cata $ \ diff -> case diff of + Patch patch -> (after patch >>= \ (In b r) -> termIn b <$> sequenceAlt r) <|> (before patch >>= asum) + Merge (In (_, b) r) -> termIn b <$> sequenceAlt r -- | Strips the head annotation off a diff annotated with non-empty records. diff --git a/src/Data/Mergeable.hs b/src/Data/Mergeable.hs index 4cbf8371b..5a4990751 100644 --- a/src/Data/Mergeable.hs +++ b/src/Data/Mergeable.hs @@ -37,7 +37,8 @@ instance Mergeable [] where merge _ [] = pure [] instance Mergeable NonEmpty where - merge f (x:|xs) = (:|) <$> f x <*> merge f xs + merge f (x:|[]) = (:|) <$> f x <*> pure [] + merge f (x1:|x2:xs) = (:|) <$> f x1 <*> merge f (x2 : xs) <|> merge f (x2:|xs) instance Mergeable Maybe where merge f (Just a) = Just <$> f a diff --git a/src/Data/Record.hs b/src/Data/Record.hs index 17c27dcb5..6073508d3 100644 --- a/src/Data/Record.hs +++ b/src/Data/Record.hs @@ -50,10 +50,10 @@ instance {-# OVERLAPPABLE #-} HasField (field ': fields) field where instance (Show h, Show (Record t)) => Show (Record (h ': t)) where - showsPrec n (h :. t) = showParen (n > 0) $ showsPrec 1 h . (" :. " <>) . shows t + showsPrec n (h :. t) = showParen (n > 0) $ showsPrec 1 h . showString " :. " . showsPrec 0 t instance Show (Record '[]) where - showsPrec n Nil = showParen (n > 0) ("Nil" <>) + showsPrec _ Nil = showString "Nil" instance (Eq h, Eq (Record t)) => Eq (Record (h ': t)) where (h1 :. t1) == (h2 :. t2) = h1 == h2 && t1 == t2 diff --git a/src/RWS.hs b/src/RWS.hs index f67b0a473..216d7b41c 100644 --- a/src/RWS.hs +++ b/src/RWS.hs @@ -1,6 +1,8 @@ {-# LANGUAGE GADTs, DataKinds, RankNTypes, TypeOperators #-} module RWS ( rws +, Options(..) +, defaultOptions , ComparabilityRelation , FeatureVector(..) , defaultFeatureVectorDecorator @@ -14,7 +16,8 @@ module RWS import Control.Applicative (empty) import Control.Arrow ((&&&)) -import Control.Monad.Random +import Control.Monad (replicateM) +import Control.Monad.Random.Strict import Control.Monad.State.Strict import Data.Align.Generic import Data.Array.Unboxed @@ -24,8 +27,7 @@ import Data.Function ((&)) import Data.Functor.Classes import Data.Functor.Foldable import Data.Hashable -import qualified Data.IntMap as IntMap -import Data.KdMap.Static hiding (elems, empty) +import qualified Data.KdMap.Static as KdMap import Data.List (sortOn) import Data.Maybe import Data.Record @@ -46,219 +48,76 @@ type ComparabilityRelation syntax ann1 ann2 = forall a b. TermF syntax ann1 a -> newtype FeatureVector = FV { unFV :: UArray Int Double } deriving (Eq, Ord, Show) --- | A term which has not yet been mapped by `rws`, along with its feature vector summary & index. -data UnmappedTerm syntax ann = UnmappedTerm - { termIndex :: {-# UNPACK #-} !Int -- ^ The index of the term within its root term. - , feature :: {-# UNPACK #-} !FeatureVector -- ^ Feature vector - , term :: Term syntax ann -- ^ The unmapped term - } - --- | Either a `term`, an index of a matched term, or nil. -data TermOrIndexOrNone term = Term term | Index {-# UNPACK #-} !Int | None - rws :: (Foldable syntax, Functor syntax, GAlign syntax) => ComparabilityRelation syntax (Record (FeatureVector ': fields1)) (Record (FeatureVector ': fields2)) -> (Term syntax (Record (FeatureVector ': fields1)) -> Term syntax (Record (FeatureVector ': fields2)) -> Bool) -> [Term syntax (Record (FeatureVector ': fields1))] -> [Term syntax (Record (FeatureVector ': fields2))] - -> RWSEditScript syntax (Record (FeatureVector ': fields1)) (Record (FeatureVector ': fields2)) + -> EditScript (Term syntax (Record (FeatureVector ': fields1))) (Term syntax (Record (FeatureVector ': fields2))) rws _ _ as [] = This <$> as rws _ _ [] bs = That <$> bs rws canCompare _ [a] [b] = if canCompareTerms canCompare a b then [These a b] else [That b, This a] -rws canCompare equivalent as bs = - let sesDiffs = ses equivalent as bs - (featureAs, featureBs, mappedDiffs, allDiffs) = genFeaturizedTermsAndDiffs sesDiffs - (diffs, remaining) = findNearestNeighboursToDiff canCompare allDiffs featureAs featureBs - diffs' = deleteRemaining diffs remaining - rwsDiffs = insertMapped mappedDiffs diffs' - in fmap snd rwsDiffs +rws canCompare equivalent as bs + = ses equivalent as bs + & mapContiguous [] [] + where Options{..} = defaultOptions --- | An IntMap of unmapped terms keyed by their position in a list of terms. -type UnmappedTerms syntax ann = IntMap.IntMap (UnmappedTerm syntax ann) + -- Map contiguous sequences of unmapped terms separated by SES-mapped equivalencies. + mapContiguous as bs [] = mapSimilar (reverse as) (reverse bs) + mapContiguous as bs (first : rest) = case first of + This a -> mapContiguous (a : as) bs rest + That b -> mapContiguous as (b : bs) rest + These _ _ -> mapSimilar (reverse as) (reverse bs) <> (first : mapContiguous [] [] rest) -type Edit syntax ann1 ann2 = These (Term syntax ann1) (Term syntax ann2) + -- Map comparable, mutually similar terms, inserting & deleting surrounding terms. + mapSimilar as' bs' = go as bs + where go as [] = This . snd <$> as + go [] bs = That . snd <$> bs + go [a] [b] | canCompareTerms canCompare (snd a) (snd b) = [These (snd a) (snd b)] + | otherwise = [That (snd b), This (snd a)] + go as@((i, _) : _) ((j, b) : restB) = + fromMaybe (That b : go as restB) $ do + -- Look up the most similar term to b near i. + (i', a) <- mostSimilarMatching (\ i' a -> inRange (i, i + optionsLookaheadPlaces) i' && canCompareTerms canCompare a b) kdMapA b + -- Look up the most similar term to a near j. + (j', _) <- mostSimilarMatching (\ j' b -> inRange (j, j + optionsLookaheadPlaces) j' && canCompareTerms canCompare a b) kdMapB a + -- Fail out if there’s a better match for a nearby. + guard (j == j') + -- Delete any elements of as before the selected element. + let (deleted, _ : restA) = span ((< i') . fst) as + pure $! (This . snd <$> deleted) <> (These a b : go restA restB) + (as, bs) = (zip [0..] as', zip [0..] bs') + (kdMapA, kdMapB) = (toKdMap as, toKdMap bs) --- A Diff paired with both its indices -type MappedDiff syntax ann1 ann2 = (These Int Int, Edit syntax ann1 ann2) + -- Find the most similar term matching a predicate, if any. + -- + -- RWS can produce false positives in the case of e.g. hash collisions. Therefore, we find the _l_ nearest candidates, filter out any which don’t match the predicate, and select the minimum of the remaining by (a constant-time approximation of) edit distance. + -- + -- cf §4.2 of RWS-Diff + mostSimilarMatching isEligible tree term = listToMaybe (sortOn (editDistanceUpTo optionsNodeComparisons term . snd) candidates) + where candidates = filter (uncurry isEligible) (snd <$> KdMap.kNearest tree optionsMaxSimilarTerms (rhead (extract term))) -type RWSEditScript syntax ann1 ann2 = [Edit syntax ann1 ann2] +data Options = Options + { optionsLookaheadPlaces :: {-# UNPACK #-} !Int -- ^ How many places ahead should we look for similar terms? + , optionsMaxSimilarTerms :: {-# UNPACK #-} !Int -- ^ The maximum number of similar terms to consider. + , optionsNodeComparisons :: {-# UNPACK #-} !Int -- ^ The number of nodes to compare when selecting the most similar term. + } -insertMapped :: Foldable t - => t (MappedDiff syntax ann1 ann2) - -> [MappedDiff syntax ann1 ann2] - -> [MappedDiff syntax ann1 ann2] -insertMapped diffs into = foldl' (flip insertDiff) into diffs +defaultOptions :: Options +defaultOptions = Options + { optionsLookaheadPlaces = 0 + , optionsMaxSimilarTerms = 2 + , optionsNodeComparisons = 10 + } -deleteRemaining :: Traversable t - => [MappedDiff syntax ann1 ann2] - -> t (UnmappedTerm syntax ann1) - -> [MappedDiff syntax ann1 ann2] -deleteRemaining diffs unmappedAs = - foldl' (flip insertDiff) diffs ((This . termIndex &&& This . term) <$> unmappedAs) - --- | Inserts an index and diff pair into a list of indices and diffs. -insertDiff :: MappedDiff syntax ann1 ann2 - -> [MappedDiff syntax ann1 ann2] - -> [MappedDiff syntax ann1 ann2] -insertDiff inserted [] = [ inserted ] -insertDiff a@(ij1, _) (b@(ij2, _):rest) = case (ij1, ij2) of - (These i1 i2, These j1 j2) -> if i1 <= j1 && i2 <= j2 then a : b : rest else b : insertDiff a rest - (This i, This j) -> if i <= j then a : b : rest else b : insertDiff a rest - (That i, That j) -> if i <= j then a : b : rest else b : insertDiff a rest - (This i, These j _) -> if i <= j then a : b : rest else b : insertDiff a rest - (That i, These _ j) -> if i <= j then a : b : rest else b : insertDiff a rest - - (This _, That _) -> b : insertDiff a rest - (That _, This _) -> b : insertDiff a rest - - (These i1 i2, _) -> case break (isThese . fst) rest of - (rest, tail) -> let (before, after) = foldr' (combine i1 i2) ([], []) (b : rest) in - case after of - [] -> before <> insertDiff a tail - _ -> before <> (a : after) <> tail - where - combine i1 i2 each (before, after) = case fst each of - This j1 -> if i1 <= j1 then (before, each : after) else (each : before, after) - That j2 -> if i2 <= j2 then (before, each : after) else (each : before, after) - These _ _ -> (before, after) - -findNearestNeighboursToDiff :: (Foldable syntax, Functor syntax, GAlign syntax) - => ComparabilityRelation syntax ann1 ann2 -- ^ A relation determining whether two terms can be compared. - -> [TermOrIndexOrNone (UnmappedTerm syntax ann2)] - -> [UnmappedTerm syntax ann1] - -> [UnmappedTerm syntax ann2] - -> ([MappedDiff syntax ann1 ann2], UnmappedTerms syntax ann1) -findNearestNeighboursToDiff canCompare allDiffs featureAs featureBs = (diffs, remaining) - where - (diffs, (_, remaining, _)) = - traverse (findNearestNeighbourToDiff' canCompare (toKdMap featureAs) (toKdMap featureBs)) allDiffs & - fmap catMaybes & - (`runState` (minimumTermIndex featureAs, toMap featureAs, toMap featureBs)) - -findNearestNeighbourToDiff' :: (Foldable syntax, Functor syntax, GAlign syntax) - => ComparabilityRelation syntax ann1 ann2 -- ^ A relation determining whether two terms can be compared. - -> KdMap Double FeatureVector (UnmappedTerm syntax ann1) - -> KdMap Double FeatureVector (UnmappedTerm syntax ann2) - -> TermOrIndexOrNone (UnmappedTerm syntax ann2) - -> State (Int, UnmappedTerms syntax ann1, UnmappedTerms syntax ann2) - (Maybe (MappedDiff syntax ann1 ann2)) -findNearestNeighbourToDiff' canCompare kdTreeA kdTreeB termThing = case termThing of - None -> pure Nothing - RWS.Term term -> Just <$> findNearestNeighbourTo canCompare kdTreeA kdTreeB term - Index i -> modify' (\ (_, unA, unB) -> (i, unA, unB)) >> pure Nothing - --- | Construct a diff for a term in B by matching it against the most similar eligible term in A (if any), marking both as ineligible for future matches. -findNearestNeighbourTo :: (Foldable syntax, Functor syntax, GAlign syntax) - => ComparabilityRelation syntax ann1 ann2 -- ^ A relation determining whether two terms can be compared. - -> KdMap Double FeatureVector (UnmappedTerm syntax ann1) - -> KdMap Double FeatureVector (UnmappedTerm syntax ann2) - -> UnmappedTerm syntax ann2 - -> State (Int, UnmappedTerms syntax ann1, UnmappedTerms syntax ann2) - (MappedDiff syntax ann1 ann2) -findNearestNeighbourTo canCompare kdTreeA kdTreeB term@(UnmappedTerm j _ b) = do - (previous, unmappedA, unmappedB) <- get - fromMaybe (insertion previous unmappedA unmappedB term) $ do - -- Look up the nearest unmapped term in `unmappedA`. - foundA@(UnmappedTerm i _ a) <- nearestUnmapped canCompare (termsWithinMoveBoundsFrom previous unmappedA) kdTreeA term - -- Look up the nearest `foundA` in `unmappedB` - UnmappedTerm j' _ _ <- nearestUnmapped (flip canCompare) (termsWithinMoveBoundsFrom (pred j) unmappedB) kdTreeB foundA - -- Return Nothing if their indices don't match - guard (j == j') - guard (canCompareTerms canCompare a b) - pure $! do - put (i, IntMap.delete i unmappedA, IntMap.delete j unmappedB) - pure (These i j, These a b) - where termsWithinMoveBoundsFrom bound = IntMap.filterWithKey (\ k _ -> isInMoveBounds bound k) - -isInMoveBounds :: Int -> Int -> Bool -isInMoveBounds previous i = previous < i && i < previous + defaultMoveBound - --- | Finds the most-similar unmapped term to the passed-in term, if any. --- --- RWS can produce false positives in the case of e.g. hash collisions. Therefore, we find the _l_ nearest candidates, filter out any which have already been mapped, and select the minimum of the remaining by (a constant-time approximation of) edit distance. --- --- cf §4.2 of RWS-Diff -nearestUnmapped :: (Foldable syntax, Functor syntax, GAlign syntax) - => ComparabilityRelation syntax ann1 ann2 -- ^ A relation determining whether two terms can be compared. - -> UnmappedTerms syntax ann1 -- ^ A set of terms eligible for matching against. - -> KdMap Double FeatureVector (UnmappedTerm syntax ann1) -- ^ The k-d tree to look up nearest neighbours within. - -> UnmappedTerm syntax ann2 -- ^ The term to find the nearest neighbour to. - -> Maybe (UnmappedTerm syntax ann1) -- ^ The most similar unmapped term, if any. -nearestUnmapped canCompare unmapped tree key = listToMaybe (sortOn approximateEditDistance candidates) - where candidates = toList (IntMap.intersection unmapped (toMap (fmap snd (kNearest tree defaultL (feature key))))) - approximateEditDistance = editDistanceIfComparable (flip canCompare) (term key) . term - -editDistanceIfComparable :: (Foldable syntax, Functor syntax, GAlign syntax) - => ComparabilityRelation syntax ann1 ann2 - -> Term syntax ann1 - -> Term syntax ann2 - -> Int -editDistanceIfComparable canCompare a b = if canCompareTerms canCompare a b - then editDistanceUpTo defaultM (These a b) - else maxBound - -defaultD, defaultL, defaultP, defaultQ, defaultMoveBound :: Int +defaultD, defaultP, defaultQ :: Int defaultD = 15 -defaultL = 2 defaultP = 2 defaultQ = 3 -defaultMoveBound = 2 --- Returns a state (insertion index, old unmapped terms, new unmapped terms), and value of (index, inserted diff), --- given a previous index, two sets of umapped terms, and an unmapped term to insert. -insertion :: Int - -> UnmappedTerms syntax ann1 - -> UnmappedTerms syntax ann2 - -> UnmappedTerm syntax ann2 - -> State (Int, UnmappedTerms syntax ann1, UnmappedTerms syntax ann2) - (MappedDiff syntax ann1 ann2) -insertion previous unmappedA unmappedB (UnmappedTerm j _ b) = do - put (previous, unmappedA, IntMap.delete j unmappedB) - pure (That j, That b) - -genFeaturizedTermsAndDiffs :: Functor syntax - => RWSEditScript syntax (Record (FeatureVector ': fields1)) (Record (FeatureVector ': fields2)) - -> ( [UnmappedTerm syntax (Record (FeatureVector ': fields1))] - , [UnmappedTerm syntax (Record (FeatureVector ': fields2))] - , [MappedDiff syntax (Record (FeatureVector ': fields1)) (Record (FeatureVector ': fields2))] - , [TermOrIndexOrNone (UnmappedTerm syntax (Record (FeatureVector ': fields2)))] - ) -genFeaturizedTermsAndDiffs sesDiffs = let Mapping _ _ a b c d = foldl' combine (Mapping 0 0 [] [] [] []) sesDiffs in (reverse a, reverse b, reverse c, reverse d) - where combine (Mapping counterA counterB as bs mappedDiffs allDiffs) diff = case diff of - This term -> Mapping (succ counterA) counterB (featurize counterA term : as) bs mappedDiffs (None : allDiffs) - That term -> Mapping counterA (succ counterB) as (featurize counterB term : bs) mappedDiffs (RWS.Term (featurize counterB term) : allDiffs) - These a b -> Mapping (succ counterA) (succ counterB) as bs ((These counterA counterB, These a b) : mappedDiffs) (Index counterA : allDiffs) - -data Mapping syntax ann1 ann2 - = Mapping - {-# UNPACK #-} !Int - {-# UNPACK #-} !Int - ![UnmappedTerm syntax ann1] - ![UnmappedTerm syntax ann2] - ![MappedDiff syntax ann1 ann2] - ![TermOrIndexOrNone (UnmappedTerm syntax ann2)] - -featurize :: Functor syntax => Int -> Term syntax (Record (FeatureVector ': fields)) -> UnmappedTerm syntax (Record (FeatureVector ': fields)) -featurize index term = UnmappedTerm index (getField (extract term)) (eraseFeatureVector term) - -eraseFeatureVector :: Functor syntax => Term syntax (Record (FeatureVector ': fields)) -> Term syntax (Record (FeatureVector ': fields)) -eraseFeatureVector (Term.Term (In record functor)) = termIn (setFeatureVector record nullFeatureVector) functor - -nullFeatureVector :: FeatureVector -nullFeatureVector = FV $ listArray (0, 0) [0] - -setFeatureVector :: Record (FeatureVector ': fields) -> FeatureVector -> Record (FeatureVector ': fields) -setFeatureVector = setField - -minimumTermIndex :: [UnmappedTerm syntax ann] -> Int -minimumTermIndex = pred . maybe 0 getMin . getOption . foldMap (Option . Just . Min . termIndex) - -toMap :: [UnmappedTerm syntax ann] -> IntMap.IntMap (UnmappedTerm syntax ann) -toMap = IntMap.fromList . fmap (termIndex &&& id) - -toKdMap :: [UnmappedTerm syntax ann] -> KdMap Double FeatureVector (UnmappedTerm syntax ann) -toKdMap = build (elems . unFV) . fmap (feature &&& id) +toKdMap :: Functor syntax => [(Int, Term syntax (Record (FeatureVector ': fields)))] -> KdMap.KdMap Double FeatureVector (Int, Term syntax (Record (FeatureVector ': fields))) +toKdMap = KdMap.build (elems . unFV) . fmap (rhead . extract . snd &&& id) -- | A `Gram` is a fixed-size view of some portion of a tree, consisting of a `stem` of _p_ labels for parent nodes, and a `base` of _q_ labels of sibling nodes. Collectively, the bag of `Gram`s for each node of a tree (e.g. as computed by `pqGrams`) form a summary of the tree. data Gram label = Gram { stem :: [Maybe label], base :: [Maybe label] } @@ -316,7 +175,7 @@ unitVector :: Int -> Int -> FeatureVector unitVector d hash = FV $ listArray (0, d - 1) ((* invMagnitude) <$> components) where invMagnitude = 1 / sqrt (sum (fmap (** 2) components)) - components = evalRand (sequenceA (replicate d (liftRand randomDouble))) (pureMT (fromIntegral hash)) + components = evalRand (replicateM d (liftRand randomDouble)) (pureMT (fromIntegral hash)) -- | Test the comparability of two root 'Term's in O(1). canCompareTerms :: ComparabilityRelation syntax ann1 ann2 -> Term syntax ann1 -> Term syntax ann2 -> Bool @@ -328,14 +187,11 @@ equalTerms canCompare = go where go a b = canCompareTerms canCompare a b && liftEq go (termOut (unTerm a)) (termOut (unTerm b)) --- | 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 syntax, Foldable syntax, Functor syntax) => Integer -> Edit syntax ann1 ann2 -> Int -editDistanceUpTo m = these termSize termSize (\ a b -> diffCost m (approximateDiff a b)) +-- | Return an edit distance between two terms, up to a certain depth. +-- +-- 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 syntax, Foldable syntax, Functor syntax) => Int -> Term syntax ann1 -> Term syntax ann2 -> Int +editDistanceUpTo m a b = diffCost m (approximateDiff a b) where diffCost = flip . cata $ \ diff m -> case diff of _ | m <= 0 -> 0 Merge body -> sum (fmap ($ pred m) body) diff --git a/test/Data/Functor/Listable.hs b/test/Data/Functor/Listable.hs index e7a088814..93f3f232b 100644 --- a/test/Data/Functor/Listable.hs +++ b/test/Data/Functor/Listable.hs @@ -299,6 +299,9 @@ instance (Listable1 f, Listable1 (Union (g ': fs))) => Listable1 (Union (f ': g instance Listable1 f => Listable1 (Union '[f]) where liftTiers tiers = inj `mapT` ((liftTiers :: [Tier a] -> [Tier (f a)]) tiers) +instance (Listable1 (Union fs), Listable a) => Listable (Union fs a) where + tiers = tiers1 + instance Listable1 Comment.Comment where liftTiers _ = cons1 Comment.Comment diff --git a/test/Data/Mergeable/Spec.hs b/test/Data/Mergeable/Spec.hs index 3179df746..218adc952 100644 --- a/test/Data/Mergeable/Spec.hs +++ b/test/Data/Mergeable/Spec.hs @@ -6,6 +6,7 @@ import Data.Functor.Identity import Data.Functor.Listable import Data.Maybe (catMaybes) import Data.Mergeable +import Data.Syntax import Syntax import Test.Hspec import Test.Hspec.LeanCheck @@ -22,6 +23,9 @@ spec = parallel $ do describe "Identity" $ do withAlternativeInstances sequenceAltLaws (Identity `mapT` tiers :: [Tier (Identity Char)]) withAlternativeInstances mergeLaws (Identity `mapT` tiers :: [Tier (Identity Char)]) + describe "Union" $ do + withAlternativeInstances sequenceAltLaws (tiers :: [Tier (ListableSyntax Char)]) + withAlternativeInstances mergeLaws (tiers :: [Tier (ListableSyntax Char)]) describe "Syntax" $ do withAlternativeInstances sequenceAltLaws (tiers :: [Tier (Syntax Char)]) withAlternativeInstances mergeLaws (tiers :: [Tier (Syntax Char)]) diff --git a/test/DiffSpec.hs b/test/DiffSpec.hs index cb60467eb..75f49c11b 100644 --- a/test/DiffSpec.hs +++ b/test/DiffSpec.hs @@ -2,14 +2,8 @@ module DiffSpec where import Data.Diff -import Data.Functor.Both -import Data.Functor.Foldable (cata) import Data.Functor.Listable (ListableSyntax) import Data.Record -import Data.Term -import Data.Union -import Interpreter -import RWS import Test.Hspec import Test.Hspec.LeanCheck @@ -17,28 +11,3 @@ spec :: Spec spec = parallel $ do prop "equality is reflexive" $ \ diff -> diff `shouldBe` (diff :: Diff ListableSyntax (Record '[]) (Record '[])) - - prop "equal terms produce identity diffs" $ - \ term -> diffCost (diffTerms term (term :: Term ListableSyntax (Record '[]))) `shouldBe` 0 - - describe "beforeTerm" $ do - prop "recovers the before term" $ - \ a b -> let diff = diffTerms a b :: Diff ListableSyntax (Record '[]) (Record '[]) in - beforeTerm diff `shouldBe` Just a - - describe "afterTerm" $ do - prop "recovers the after term" $ - \ a b -> let diff = diffTerms a b :: Diff ListableSyntax (Record '[]) (Record '[]) in - afterTerm diff `shouldBe` Just b - - prop "forward permutations are changes" $ pendingWith "https://github.com/github/semantic-diff/issues/1359" - -- \ a -> let wrap = termIn Nil . inj - -- b = wrap [a] - -- c = wrap [a, b] in - -- diffTerms (wrap [a, b, c]) (wrap [c, a, b :: Term ListableSyntax (Record '[])]) `shouldBe` merge (Nil, Nil) (inj [ inserting c, merging a, merging b, deleting c ]) - - prop "backward permutations are changes" $ - \ a -> let wrap = termIn Nil . inj - b = wrap [a] - c = wrap [a, b] in - diffTerms (wrap [a, b, c]) (wrap [b, c, a :: Term ListableSyntax (Record '[])]) `shouldBe` merge (Nil, Nil) (inj [ deleting a, merging b, merging c, inserting a ]) diff --git a/test/IntegrationSpec.hs b/test/IntegrationSpec.hs index e64112e8e..4020fb27d 100644 --- a/test/IntegrationSpec.hs +++ b/test/IntegrationSpec.hs @@ -26,7 +26,7 @@ spec = parallel $ do describe "go" $ runTestsIn "test/fixtures/go/" [] describe "javascript" $ runTestsIn "test/fixtures/javascript/" [] - describe "python" $ runTestsIn "test/fixtures/python/" [ ("test/fixtures/python/while-statement.diffB-A.txt", "https://github.com/github/semantic-diff/issues/1359") ] + describe "python" $ runTestsIn "test/fixtures/python/" [] describe "ruby" $ runTestsIn "test/fixtures/ruby/" [] describe "typescript" $ runTestsIn "test/fixtures/typescript/" [] diff --git a/test/InterpreterSpec.hs b/test/InterpreterSpec.hs index 5edb9ca55..afa5bbb59 100644 --- a/test/InterpreterSpec.hs +++ b/test/InterpreterSpec.hs @@ -16,7 +16,7 @@ import Test.Hspec.LeanCheck spec :: Spec spec = parallel $ do - describe "interpret" $ do + describe "diffTerms" $ do it "returns a replacement when comparing two unicode equivalent terms" $ let termA = termIn Nil (inj (Syntax.Identifier "t\776")) termB = termIn Nil (inj (Syntax.Identifier "\7831")) in @@ -26,9 +26,9 @@ spec = parallel $ do \ a b -> let diff = diffTerms a b :: Diff ListableSyntax (Record '[]) (Record '[]) in (beforeTerm diff, afterTerm diff) `shouldBe` (Just a, Just b) - prop "constructs zero-cost diffs of equal terms" $ + prop "produces identity diffs for equal terms " $ \ a -> let diff = diffTerms a a :: Diff ListableSyntax (Record '[]) (Record '[]) in - diffCost diff `shouldBe` 0 + length (diffPatches diff) `shouldBe` 0 it "produces unbiased insertions within branches" $ let term s = termIn Nil (inj [ termIn Nil (inj (Syntax.Identifier s)) ]) :: Term ListableSyntax (Record '[]) @@ -37,3 +37,15 @@ spec = parallel $ do prop "compares nodes against context" $ \ a b -> diffTerms a (termIn Nil (inj (Syntax.Context (pure b) a))) `shouldBe` insertF (In Nil (inj (Syntax.Context (pure (inserting b)) (merging (a :: Term ListableSyntax (Record '[])))))) + + prop "diffs forward permutations as changes" $ + \ a -> let wrap = termIn Nil . inj + b = wrap [a] + c = wrap [a, b] in + diffTerms (wrap [a, b, c]) (wrap [c, a, b :: Term ListableSyntax (Record '[])]) `shouldBe` merge (Nil, Nil) (inj [ inserting c, merging a, merging b, deleting c ]) + + prop "diffs backward permutations as changes" $ + \ a -> let wrap = termIn Nil . inj + b = wrap [a] + c = wrap [a, b] in + diffTerms (wrap [a, b, c]) (wrap [b, c, a :: Term ListableSyntax (Record '[])]) `shouldBe` merge (Nil, Nil) (inj [ deleting a, merging b, merging c, inserting a ]) diff --git a/test/TOCSpec.hs b/test/TOCSpec.hs index d0fcad3e0..6e2894c1b 100644 --- a/test/TOCSpec.hs +++ b/test/TOCSpec.hs @@ -66,8 +66,10 @@ spec = parallel $ do sourceBlobs <- blobsForPaths (both "ruby/methods.A.rb" "ruby/methods.B.rb") diff <- runTask $ diffWithParser rubyParser sourceBlobs diffTOC diff `shouldBe` - [ JSONSummary "Method" "self.foo" (sourceSpanBetween (1, 1) (2, 4)) "modified" - , JSONSummary "Method" "bar" (sourceSpanBetween (4, 1) (6, 4)) "modified" ] + [ JSONSummary "Method" "self.foo" (sourceSpanBetween (1, 1) (2, 4)) "added" + , JSONSummary "Method" "bar" (sourceSpanBetween (4, 1) (6, 4)) "modified" + , JSONSummary "Method" "baz" (sourceSpanBetween (4, 1) (5, 4)) "removed" + ] it "dedupes changes in same parent method" $ do sourceBlobs <- blobsForPaths (both "javascript/duplicate-parent.A.js" "javascript/duplicate-parent.B.js") @@ -147,7 +149,7 @@ spec = parallel $ do it "produces JSON output" $ do blobs <- blobsForPaths (both "ruby/methods.A.rb" "ruby/methods.B.rb") output <- runTask (diffBlobPair ToCDiffRenderer blobs) - toOutput output `shouldBe` ("{\"changes\":{\"test/fixtures/toc/ruby/methods.A.rb -> test/fixtures/toc/ruby/methods.B.rb\":[{\"span\":{\"start\":[1,1],\"end\":[2,4]},\"category\":\"Method\",\"term\":\"self.foo\",\"changeType\":\"modified\"},{\"span\":{\"start\":[4,1],\"end\":[6,4]},\"category\":\"Method\",\"term\":\"bar\",\"changeType\":\"modified\"}]},\"errors\":{}}\n" :: ByteString) + toOutput output `shouldBe` ("{\"changes\":{\"test/fixtures/toc/ruby/methods.A.rb -> test/fixtures/toc/ruby/methods.B.rb\":[{\"span\":{\"start\":[1,1],\"end\":[2,4]},\"category\":\"Method\",\"term\":\"self.foo\",\"changeType\":\"added\"},{\"span\":{\"start\":[4,1],\"end\":[6,4]},\"category\":\"Method\",\"term\":\"bar\",\"changeType\":\"modified\"},{\"span\":{\"start\":[4,1],\"end\":[5,4]},\"category\":\"Method\",\"term\":\"baz\",\"changeType\":\"removed\"}]},\"errors\":{}}\n" :: ByteString) it "produces JSON output if there are parse errors" $ do blobs <- blobsForPaths (both "ruby/methods.A.rb" "ruby/methods.X.rb") diff --git a/test/fixtures/go/array-with-implicit-length.diffA-B.txt b/test/fixtures/go/array-with-implicit-length.diffA-B.txt index b9cadd513..703615aa7 100644 --- a/test/fixtures/go/array-with-implicit-length.diffA-B.txt +++ b/test/fixtures/go/array-with-implicit-length.diffA-B.txt @@ -15,5 +15,5 @@ ->(NumberLiteral) } { (NumberLiteral) ->(NumberLiteral) } - {+(NumberLiteral)+} - {-(NumberLiteral)-})))))) + { (NumberLiteral) + ->(NumberLiteral) })))))) diff --git a/test/fixtures/go/array-with-implicit-length.diffB-A.txt b/test/fixtures/go/array-with-implicit-length.diffB-A.txt index b9cadd513..703615aa7 100644 --- a/test/fixtures/go/array-with-implicit-length.diffB-A.txt +++ b/test/fixtures/go/array-with-implicit-length.diffB-A.txt @@ -15,5 +15,5 @@ ->(NumberLiteral) } { (NumberLiteral) ->(NumberLiteral) } - {+(NumberLiteral)+} - {-(NumberLiteral)-})))))) + { (NumberLiteral) + ->(NumberLiteral) })))))) diff --git a/test/fixtures/go/const-declarations-with-types.diffA-B.txt b/test/fixtures/go/const-declarations-with-types.diffA-B.txt index 13a073d55..f4f232b61 100644 --- a/test/fixtures/go/const-declarations-with-types.diffA-B.txt +++ b/test/fixtures/go/const-declarations-with-types.diffA-B.txt @@ -11,8 +11,7 @@ ->(Identifier) } { (Identifier) ->(Identifier) } - {+(Other "expression_list" - {+(NumberLiteral)+} - {+(NumberLiteral)+})+} - {-(Other "expression_list" - {-(NumberLiteral)-})-})))) + (Other "expression_list" + { (NumberLiteral) + ->(NumberLiteral) } + {+(NumberLiteral)+}))))) diff --git a/test/fixtures/javascript/anonymous-function.diffB-A.txt b/test/fixtures/javascript/anonymous-function.diffB-A.txt index 43ee1632e..378fb36fa 100644 --- a/test/fixtures/javascript/anonymous-function.diffB-A.txt +++ b/test/fixtures/javascript/anonymous-function.diffB-A.txt @@ -3,14 +3,13 @@ (Empty) (Empty) (Empty) - (RequiredParameter - (Empty) - (Empty) - (Empty) - (Assignment - { (Identifier) - ->(Identifier) } - (Empty))) + {+(RequiredParameter + {+(Empty)+} + {+(Empty)+} + {+(Empty)+} + {+(Assignment + {+(Identifier)+} + {+(Empty)+})+})+} (RequiredParameter (Empty) (Empty) @@ -18,6 +17,13 @@ (Assignment (Identifier) (Empty))) + {-(RequiredParameter + {-(Empty)-} + {-(Empty)-} + {-(Empty)-} + {-(Assignment + {-(Identifier)-} + {-(Empty)-})-})-} ( (Return { (Times diff --git a/test/fixtures/javascript/export.diffA-B.txt b/test/fixtures/javascript/export.diffA-B.txt index d3c55c9dd..d50078834 100644 --- a/test/fixtures/javascript/export.diffA-B.txt +++ b/test/fixtures/javascript/export.diffA-B.txt @@ -107,37 +107,36 @@ {-(Empty)-} {-(Identifier)-} {-([])-})-}) -{+(Export - {+(TextElement)+})+} -{+(Export - {+(ExportClause - {+(ImportExportSpecifier - {+(Identifier)+} - {+(Empty)+})+} - {+(ImportExportSpecifier - {+(Identifier)+} - {+(Empty)+})+} - {+(ImportExportSpecifier - {+(Identifier)+} - {+(Empty)+})+})+} - {+(TextElement)+})+} -{+(Export - {+(ExportClause - {+(ImportExportSpecifier - {+(Identifier)+} - {+(Identifier)+})+} - {+(ImportExportSpecifier - {+(Identifier)+} - {+(Identifier)+})+} - {+(ImportExportSpecifier - {+(Identifier)+} - {+(Empty)+})+})+} - {+(TextElement)+})+} -{-(Export + (Export + {+(TextElement)+} {-(ExportClause {-(ImportExportSpecifier {-(Identifier)-} - {-(Identifier)-})-})-})-} + {-(Identifier)-})-})-}) +{+(Export + {+(ExportClause + {+(ImportExportSpecifier + {+(Identifier)+} + {+(Empty)+})+} + {+(ImportExportSpecifier + {+(Identifier)+} + {+(Empty)+})+} + {+(ImportExportSpecifier + {+(Identifier)+} + {+(Empty)+})+})+} + {+(TextElement)+})+} +{+(Export + {+(ExportClause + {+(ImportExportSpecifier + {+(Identifier)+} + {+(Identifier)+})+} + {+(ImportExportSpecifier + {+(Identifier)+} + {+(Identifier)+})+} + {+(ImportExportSpecifier + {+(Identifier)+} + {+(Empty)+})+})+} + {+(TextElement)+})+} {-(Export {-(TextElement)-})-} {-(Export diff --git a/test/fixtures/javascript/export.diffB-A.txt b/test/fixtures/javascript/export.diffB-A.txt index 6629857ba..1009f3a02 100644 --- a/test/fixtures/javascript/export.diffB-A.txt +++ b/test/fixtures/javascript/export.diffB-A.txt @@ -25,17 +25,17 @@ {-(Empty)-})-})) (Export (ExportClause + {+(ImportExportSpecifier + {+(Identifier)+} + {+(Identifier)+})+} + (ImportExportSpecifier + (Identifier) + (Identifier)) (ImportExportSpecifier { (Identifier) ->(Identifier) } { (Identifier) - ->(Identifier) }) - (ImportExportSpecifier - (Identifier) - (Identifier)) - {+(ImportExportSpecifier - {+(Identifier)+} - {+(Empty)+})+} + ->(Empty) }) {-(ImportExportSpecifier {-(Identifier)-} {-(Empty)-})-})) @@ -105,14 +105,14 @@ {-(ImportExportSpecifier {-(Identifier)-} {-(Identifier)-})-})-}) -{+(Export + (Export {+(ExportClause {+(ImportExportSpecifier {+(Identifier)+} - {+(Identifier)+})+})+})+} - (Export - { (TextElement) - ->(TextElement) }) + {+(Identifier)+})+})+} + {-(TextElement)-}) +{+(Export + {+(TextElement)+})+} (Export (ExportClause (ImportExportSpecifier @@ -133,27 +133,21 @@ {-(Empty)-})-}) { (TextElement) ->(TextElement) }) -{+(Export - {+(ExportClause - {+(ImportExportSpecifier - {+(Identifier)+} - {+(Identifier)+})+} - {+(ImportExportSpecifier - {+(Identifier)+} - {+(Identifier)+})+} - {+(ImportExportSpecifier - {+(Identifier)+} - {+(Empty)+})+})+} - {+(TextElement)+})+} -{-(Export - {-(ExportClause - {-(ImportExportSpecifier - {-(Identifier)-} - {-(Identifier)-})-} - {-(ImportExportSpecifier - {-(Identifier)-} - {-(Identifier)-})-} - {-(ImportExportSpecifier - {-(Identifier)-} - {-(Empty)-})-})-} - {-(TextElement)-})-}) + (Export + (ExportClause + (ImportExportSpecifier + { (Identifier) + ->(Identifier) } + { (Identifier) + ->(Identifier) }) + (ImportExportSpecifier + { (Identifier) + ->(Identifier) } + { (Identifier) + ->(Identifier) }) + (ImportExportSpecifier + { (Identifier) + ->(Identifier) } + (Empty))) + { (TextElement) + ->(TextElement) })) diff --git a/test/fixtures/javascript/function-call-args.diffB-A.txt b/test/fixtures/javascript/function-call-args.diffB-A.txt index a4b82533e..194e964af 100644 --- a/test/fixtures/javascript/function-call-args.diffB-A.txt +++ b/test/fixtures/javascript/function-call-args.diffB-A.txt @@ -8,14 +8,13 @@ (Empty) (Empty) (Empty) - (RequiredParameter - (Empty) - (Empty) - (Empty) - (Assignment - { (Identifier) - ->(Identifier) } - (Empty))) + {+(RequiredParameter + {+(Empty)+} + {+(Empty)+} + {+(Empty)+} + {+(Assignment + {+(Identifier)+} + {+(Empty)+})+})+} (RequiredParameter (Empty) (Empty) @@ -23,6 +22,13 @@ (Assignment (Identifier) (Empty))) + {-(RequiredParameter + {-(Empty)-} + {-(Empty)-} + {-(Empty)-} + {-(Assignment + {-(Identifier)-} + {-(Empty)-})-})-} ( (Call (MemberAccess diff --git a/test/fixtures/python/async-function-definition.diffA-B.txt b/test/fixtures/python/async-function-definition.diffA-B.txt index 56b1fa7a3..327a2d333 100644 --- a/test/fixtures/python/async-function-definition.diffA-B.txt +++ b/test/fixtures/python/async-function-definition.diffA-B.txt @@ -1,35 +1,36 @@ (Program - (Annotation - (Annotation - (Function - { (Identifier) - ->(Identifier) } - {-(Identifier)-} - (Identifier) - {+(Identifier)+} - ( - { (Identifier) - ->(Identifier) })) - (Empty)) - (Identifier)) - (Annotation - (Annotation - (Function - (Identifier) - ( - { (Identifier) - ->(Identifier) })) - (Empty)) - (Identifier)) {+(Annotation {+(Annotation {+(Function + {+(Identifier)+} {+(Identifier)+} {+(Identifier)+} {+( {+(Identifier)+})+})+} {+(Empty)+})+} {+(Identifier)+})+} + (Annotation + (Annotation + (Function + (Identifier) + ( + { (Identifier) + ->(Identifier) })) + (Empty)) + (Identifier)) + (Annotation + (Annotation + (Function + { (Identifier) + ->(Identifier) } + { (Identifier) + ->(Identifier) } + {-(Identifier)-} + ( + { (Identifier) + ->(Identifier) })) + (Empty)) + (Identifier)) {-(Annotation {-(Annotation {-(Function diff --git a/test/fixtures/python/dictionary.diffA-B.txt b/test/fixtures/python/dictionary.diffA-B.txt index ccd5defe8..1f63215b0 100644 --- a/test/fixtures/python/dictionary.diffA-B.txt +++ b/test/fixtures/python/dictionary.diffA-B.txt @@ -1,10 +1,8 @@ (Program - (Hash - (KeyValue - { (Identifier) - ->(Identifier) } - { (Integer) - ->(Integer) })) +{+(Hash + {+(KeyValue + {+(Identifier)+} + {+(Integer)+})+})+} (Hash) {+(Assignment {+(Identifier)+} @@ -17,6 +15,10 @@ {+(KeyValue {+(Identifier)+} {+(Identifier)+})+})+})+} +{-(Hash + {-(KeyValue + {-(Identifier)-} + {-(Integer)-})-})-} {-(Hash {-(KeyValue {-(Identifier)-} diff --git a/test/fixtures/python/exec-statement.diffA-B.txt b/test/fixtures/python/exec-statement.diffA-B.txt index 8862037e8..cb8df8433 100644 --- a/test/fixtures/python/exec-statement.diffA-B.txt +++ b/test/fixtures/python/exec-statement.diffA-B.txt @@ -12,13 +12,10 @@ {+(Identifier)+} {-(Null)-} (Empty)) -{+(Call - {+(Identifier)+} - {+(TextElement)+} - {+(Empty)+})+} -{-(Call - {-(Identifier)-} - {-(TextElement)-} + (Call + (Identifier) + { (TextElement) + ->(TextElement) } {-(Identifier)-} {-(Identifier)-} - {-(Empty)-})-}) + (Empty))) diff --git a/test/fixtures/python/exec-statement.diffB-A.txt b/test/fixtures/python/exec-statement.diffB-A.txt index 583d2e1de..d61d2f5f2 100644 --- a/test/fixtures/python/exec-statement.diffB-A.txt +++ b/test/fixtures/python/exec-statement.diffB-A.txt @@ -12,13 +12,10 @@ {-(Identifier)-} {-(Identifier)-} (Empty)) -{+(Call - {+(Identifier)+} - {+(TextElement)+} + (Call + (Identifier) + { (TextElement) + ->(TextElement) } {+(Identifier)+} {+(Identifier)+} - {+(Empty)+})+} -{-(Call - {-(Identifier)-} - {-(TextElement)-} - {-(Empty)-})-}) + (Empty))) diff --git a/test/fixtures/python/expression-statement.diffA-B.txt b/test/fixtures/python/expression-statement.diffA-B.txt index 87229cd52..260608ca3 100644 --- a/test/fixtures/python/expression-statement.diffA-B.txt +++ b/test/fixtures/python/expression-statement.diffA-B.txt @@ -9,9 +9,10 @@ (Integer)) {+(Identifier)+} ( + {+(Integer)+} (Integer) (Integer) - (Integer)) + {-(Integer)-}) {+(Plus {+(Identifier)+} {+(Identifier)+})+}) diff --git a/test/fixtures/python/expression-statement.diffB-A.txt b/test/fixtures/python/expression-statement.diffB-A.txt index 3146293b1..1bbccbc08 100644 --- a/test/fixtures/python/expression-statement.diffB-A.txt +++ b/test/fixtures/python/expression-statement.diffB-A.txt @@ -1,6 +1,5 @@ (Program -{ (Identifier) -->(Identifier) } +{+(Identifier)+} {+(Plus {+(Identifier)+} {+(Identifier)+})+} @@ -12,6 +11,7 @@ {+(Integer)+} {+(Integer)+} {+(Integer)+})+} +{-(Identifier)-} {-( {-(Integer)-} {-(Integer)-} diff --git a/test/fixtures/python/function-definition.diffA-B.txt b/test/fixtures/python/function-definition.diffA-B.txt index 168dad37d..7b354fe66 100644 --- a/test/fixtures/python/function-definition.diffA-B.txt +++ b/test/fixtures/python/function-definition.diffA-B.txt @@ -23,28 +23,24 @@ { (Identifier) ->(Identifier) })) (Empty)) -{+(Annotation - {+(Function - {+(Identifier)+} - {+( - {+(Identifier)+})+})+} - {+(Empty)+})+} -{+(Annotation - {+(Function - {+(Identifier)+} - {+(Identifier)+} - {+( - {+(Identifier)+})+})+} - {+(Empty)+})+} -{-(Annotation - {-(Function - {-(Identifier)-} + (Annotation + (Function + { (Identifier) + ->(Identifier) } {-(Assignment {-(Identifier)-} {-(Identifier)-})-} - {-( - {-(Identifier)-})-})-} - {-(Empty)-})-} + ( + { (Identifier) + ->(Identifier) })) + (Empty)) +{+(Annotation + {+(Function + {+(Identifier)+} + {+(Identifier)+} + {+( + {+(Identifier)+})+})+} + {+(Empty)+})+} (Annotation (Function (Identifier) diff --git a/test/fixtures/python/function-definition.diffB-A.txt b/test/fixtures/python/function-definition.diffB-A.txt index bf7d101bf..f781195d0 100644 --- a/test/fixtures/python/function-definition.diffB-A.txt +++ b/test/fixtures/python/function-definition.diffB-A.txt @@ -1,18 +1,14 @@ (Program -{+(Annotation - {+(Function - {+(Identifier)+} - {+( - {+(Identifier)+})+})+} - {+(Empty)+})+} -{-(Annotation - {-(Function + (Annotation + (Function + { (Identifier) + ->(Identifier) } {-(Identifier)-} {-(Identifier)-} - {-(Identifier)-} - {-( - {-(Identifier)-})-})-} - {-(Empty)-})-} + ( + { (Identifier) + ->(Identifier) })) + (Empty)) (Annotation (Function (Identifier) diff --git a/test/fixtures/python/if-statement.diffA-B.txt b/test/fixtures/python/if-statement.diffA-B.txt index 140ca836c..4df75d39b 100644 --- a/test/fixtures/python/if-statement.diffA-B.txt +++ b/test/fixtures/python/if-statement.diffA-B.txt @@ -3,9 +3,9 @@ { (Identifier) ->(Identifier) } ( - { (Identifier) - ->(Identifier) } - (Identifier)) + {+(Identifier)+} + (Identifier) + {-(Identifier)-}) { (If {-(Identifier)-} {-( diff --git a/test/fixtures/python/import-from-statement.diffB-A.txt b/test/fixtures/python/import-from-statement.diffB-A.txt index 56c486f9e..96c7c6c72 100644 --- a/test/fixtures/python/import-from-statement.diffB-A.txt +++ b/test/fixtures/python/import-from-statement.diffB-A.txt @@ -7,12 +7,14 @@ {+(ScopeResolution {+(Identifier)+})+}) (Import + {+(ScopeResolution + {+(Identifier)+})+} (ScopeResolution (Identifier)) (ScopeResolution (Identifier)) - (ScopeResolution - (Identifier))) + {-(ScopeResolution + {-(Identifier)-})-}) (Import (ScopeResolution { (Identifier) diff --git a/test/fixtures/python/print-statement.diffA-B.txt b/test/fixtures/python/print-statement.diffA-B.txt index 08522ada5..0ba7ad0ba 100644 --- a/test/fixtures/python/print-statement.diffA-B.txt +++ b/test/fixtures/python/print-statement.diffA-B.txt @@ -1,9 +1,9 @@ (Program (Call (Identifier) - { (Identifier) - ->(Identifier) } + {+(Identifier)+} (Identifier) + {-(Identifier)-} (Empty)) (Call (Identifier) diff --git a/test/fixtures/python/raise-statement.diffA-B.txt b/test/fixtures/python/raise-statement.diffA-B.txt index 1df340c41..f96cc4efc 100644 --- a/test/fixtures/python/raise-statement.diffA-B.txt +++ b/test/fixtures/python/raise-statement.diffA-B.txt @@ -1,19 +1,29 @@ (Program +{+(Throw + {+( + {+(Call + {+(Identifier)+} + {+(TextElement)+} + {+(Empty)+})+})+})+} +{+(Throw + {+( + {+(Call + {+(Identifier)+} + {+(TextElement)+} + {+(Empty)+})+} + {+(Identifier)+})+})+} (Throw - ( - (Call - (Identifier) - { (TextElement) - ->(TextElement) } - (Empty)))) - (Throw - ( - (Call - (Identifier) - { (TextElement) - ->(TextElement) } - (Empty)) - { (Identifier) - ->(Identifier) })) - (Throw - ([]))) + ([])) +{-(Throw + {-( + {-(Call + {-(Identifier)-} + {-(TextElement)-} + {-(Empty)-})-})-})-} +{-(Throw + {-( + {-(Call + {-(Identifier)-} + {-(TextElement)-} + {-(Empty)-})-} + {-(Identifier)-})-})-}) diff --git a/test/fixtures/python/return-statement.diffA-B.txt b/test/fixtures/python/return-statement.diffA-B.txt index c9e2603b7..0798a925c 100644 --- a/test/fixtures/python/return-statement.diffA-B.txt +++ b/test/fixtures/python/return-statement.diffA-B.txt @@ -1,18 +1,20 @@ (Program - (Return - ( - (Plus - { (Identifier) - ->(Identifier) } - { (Identifier) - ->(Identifier) }) - { (Identifier) - ->(Identifier) })) +{+(Return + {+( + {+(Plus + {+(Identifier)+} + {+(Identifier)+})+} + {+(Identifier)+})+})+} (Return (Empty)) -{+(Return - {+(Not - {+(Identifier)+})+})+} + (Return + { ( + {-(Plus + {-(Identifier)-} + {-(Identifier)-})-} + {-(Identifier)-}) + ->(Not + {+(Identifier)+}) }) {-(Return {-(Not {-(Identifier)-})-})-}) diff --git a/test/fixtures/python/string.diffA-B.txt b/test/fixtures/python/string.diffA-B.txt index 558b37e44..707379f23 100644 --- a/test/fixtures/python/string.diffA-B.txt +++ b/test/fixtures/python/string.diffA-B.txt @@ -1,10 +1,10 @@ (Program -{ (TextElement) -->(TextElement) } +{+(TextElement)+} (TextElement) {+(TextElement)+} {+(TextElement)+} -{+(TextElement)+} +{ (TextElement) +->(TextElement) } {+(TextElement)+} {+(TextElement)+} {-(TextElement)-} diff --git a/test/fixtures/python/string.diffB-A.txt b/test/fixtures/python/string.diffB-A.txt index 55414460b..f96350334 100644 --- a/test/fixtures/python/string.diffB-A.txt +++ b/test/fixtures/python/string.diffB-A.txt @@ -6,9 +6,9 @@ ->(TextElement) } {+(TextElement)+} {+(TextElement)+} -{+(TextElement)+} { (TextElement) ->(TextElement) } +{+(TextElement)+} {-(TextElement)-} {-(TextElement)-} {-(TextElement)-} diff --git a/test/fixtures/python/tuple.diffB-A.txt b/test/fixtures/python/tuple.diffB-A.txt index 718d484cc..eeaeb3a3e 100644 --- a/test/fixtures/python/tuple.diffB-A.txt +++ b/test/fixtures/python/tuple.diffB-A.txt @@ -3,9 +3,10 @@ {+(Identifier)+} {+(Identifier)+})+} (Tuple + {+(Identifier)+} (Identifier) (Identifier) - (Identifier)) + {-(Identifier)-}) {-(Tuple {-(Identifier)-} {-(Identifier)-})-} diff --git a/test/fixtures/python/while-statement.diffB-A.txt b/test/fixtures/python/while-statement.diffB-A.txt index 43bf133ba..bce43a580 100644 --- a/test/fixtures/python/while-statement.diffB-A.txt +++ b/test/fixtures/python/while-statement.diffB-A.txt @@ -3,15 +3,11 @@ { (Identifier) ->(Identifier) } ( - { (Break - {-(Empty)-}) - ->(NoOp - {+(Empty)+}) } - { (Continue - {-(Empty)-}) - ->(Break - {+(Empty)+}) } - { (NoOp - {-(Empty)-}) - ->(Continue - {+(Empty)+}) }))) + {+(NoOp + {+(Empty)+})+} + (Break + (Empty)) + (Continue + (Empty)) + {-(NoOp + {-(Empty)-})-}))) diff --git a/test/fixtures/typescript/ambient-declarations.diffA-B.txt b/test/fixtures/typescript/ambient-declarations.diffA-B.txt index ae97d45c3..fc49501f9 100644 --- a/test/fixtures/typescript/ambient-declarations.diffA-B.txt +++ b/test/fixtures/typescript/ambient-declarations.diffA-B.txt @@ -1,10 +1,4 @@ (Program -{+(AmbientDeclaration - {+(InternalModule - {+(Identifier)+})+})+} -{+(AmbientDeclaration - {+(Class - {+(Identifier)+})+})+} (AmbientDeclaration { (Class {-(Identifier)-} @@ -15,18 +9,23 @@ {-(TypeIdentifier)-})-} {-(Identifier)-} {-(Empty)-})-}) - ->(InterfaceDeclaration - {+(Empty)+} - {+(Empty)+} - {+(Identifier)+} - {+(ObjectType)+}) }) -{-(AmbientDeclaration - {-(VariableDeclaration + ->(InternalModule + {+(Identifier)+}) }) + (AmbientDeclaration + { (VariableDeclaration {-(Assignment {-(Annotation {-(PredefinedType)-})-} {-(Identifier)-} - {-(Empty)-})-})-})-} + {-(Empty)-})-}) + ->(Class + {+(Identifier)+}) }) +{+(AmbientDeclaration + {+(InterfaceDeclaration + {+(Empty)+} + {+(Empty)+} + {+(Identifier)+} + {+(ObjectType)+})+})+} {-(AmbientDeclaration {-(AmbientFunction {-(Empty)-} diff --git a/test/fixtures/typescript/ambient-declarations.diffB-A.txt b/test/fixtures/typescript/ambient-declarations.diffB-A.txt index 8d2ace9cf..81c8abbbd 100644 --- a/test/fixtures/typescript/ambient-declarations.diffB-A.txt +++ b/test/fixtures/typescript/ambient-declarations.diffB-A.txt @@ -1,6 +1,8 @@ (Program -{+(AmbientDeclaration - {+(Class + (AmbientDeclaration + { (InternalModule + {-(Identifier)-}) + ->(Class {+(Identifier)+} {+(PublicFieldDefinition {+(Empty)+} @@ -8,14 +10,16 @@ {+(Annotation {+(TypeIdentifier)+})+} {+(Identifier)+} - {+(Empty)+})+})+})+} -{+(AmbientDeclaration - {+(VariableDeclaration + {+(Empty)+})+}) }) + (AmbientDeclaration + { (Class + {-(Identifier)-}) + ->(VariableDeclaration {+(Assignment {+(Annotation {+(PredefinedType)+})+} {+(Identifier)+} - {+(Empty)+})+})+})+} + {+(Empty)+})+}) }) {+(AmbientDeclaration {+(AmbientFunction {+(Empty)+} @@ -30,8 +34,13 @@ {+(Assignment {+(Identifier)+} {+(Empty)+})+})+})+})+} -{+(AmbientDeclaration - {+(InternalModule + (AmbientDeclaration + { (InterfaceDeclaration + {-(Empty)-} + {-(Empty)-} + {-(Identifier)-} + {-(ObjectType)-}) + ->(InternalModule {+(Identifier)+} {+(AmbientFunction {+(Empty)+} @@ -85,19 +94,7 @@ {+(Empty)+} {+(Annotation {+(PredefinedType)+})+} - {+(Identifier)+})+})+})+})+})+} -{-(AmbientDeclaration - {-(InternalModule - {-(Identifier)-})-})-} -{-(AmbientDeclaration - {-(Class - {-(Identifier)-})-})-} -{-(AmbientDeclaration - {-(InterfaceDeclaration - {-(Empty)-} - {-(Empty)-} - {-(Identifier)-} - {-(ObjectType)-})-})-} + {+(Identifier)+})+})+})+}) }) (AmbientDeclaration (Class (Identifier) diff --git a/test/fixtures/typescript/anonymous-function.diffB-A.txt b/test/fixtures/typescript/anonymous-function.diffB-A.txt index 43ee1632e..378fb36fa 100644 --- a/test/fixtures/typescript/anonymous-function.diffB-A.txt +++ b/test/fixtures/typescript/anonymous-function.diffB-A.txt @@ -3,14 +3,13 @@ (Empty) (Empty) (Empty) - (RequiredParameter - (Empty) - (Empty) - (Empty) - (Assignment - { (Identifier) - ->(Identifier) } - (Empty))) + {+(RequiredParameter + {+(Empty)+} + {+(Empty)+} + {+(Empty)+} + {+(Assignment + {+(Identifier)+} + {+(Empty)+})+})+} (RequiredParameter (Empty) (Empty) @@ -18,6 +17,13 @@ (Assignment (Identifier) (Empty))) + {-(RequiredParameter + {-(Empty)-} + {-(Empty)-} + {-(Empty)-} + {-(Assignment + {-(Identifier)-} + {-(Empty)-})-})-} ( (Return { (Times diff --git a/test/fixtures/typescript/export.diffA-B.txt b/test/fixtures/typescript/export.diffA-B.txt index d3c55c9dd..d50078834 100644 --- a/test/fixtures/typescript/export.diffA-B.txt +++ b/test/fixtures/typescript/export.diffA-B.txt @@ -107,37 +107,36 @@ {-(Empty)-} {-(Identifier)-} {-([])-})-}) -{+(Export - {+(TextElement)+})+} -{+(Export - {+(ExportClause - {+(ImportExportSpecifier - {+(Identifier)+} - {+(Empty)+})+} - {+(ImportExportSpecifier - {+(Identifier)+} - {+(Empty)+})+} - {+(ImportExportSpecifier - {+(Identifier)+} - {+(Empty)+})+})+} - {+(TextElement)+})+} -{+(Export - {+(ExportClause - {+(ImportExportSpecifier - {+(Identifier)+} - {+(Identifier)+})+} - {+(ImportExportSpecifier - {+(Identifier)+} - {+(Identifier)+})+} - {+(ImportExportSpecifier - {+(Identifier)+} - {+(Empty)+})+})+} - {+(TextElement)+})+} -{-(Export + (Export + {+(TextElement)+} {-(ExportClause {-(ImportExportSpecifier {-(Identifier)-} - {-(Identifier)-})-})-})-} + {-(Identifier)-})-})-}) +{+(Export + {+(ExportClause + {+(ImportExportSpecifier + {+(Identifier)+} + {+(Empty)+})+} + {+(ImportExportSpecifier + {+(Identifier)+} + {+(Empty)+})+} + {+(ImportExportSpecifier + {+(Identifier)+} + {+(Empty)+})+})+} + {+(TextElement)+})+} +{+(Export + {+(ExportClause + {+(ImportExportSpecifier + {+(Identifier)+} + {+(Identifier)+})+} + {+(ImportExportSpecifier + {+(Identifier)+} + {+(Identifier)+})+} + {+(ImportExportSpecifier + {+(Identifier)+} + {+(Empty)+})+})+} + {+(TextElement)+})+} {-(Export {-(TextElement)-})-} {-(Export diff --git a/test/fixtures/typescript/export.diffB-A.txt b/test/fixtures/typescript/export.diffB-A.txt index 6629857ba..1009f3a02 100644 --- a/test/fixtures/typescript/export.diffB-A.txt +++ b/test/fixtures/typescript/export.diffB-A.txt @@ -25,17 +25,17 @@ {-(Empty)-})-})) (Export (ExportClause + {+(ImportExportSpecifier + {+(Identifier)+} + {+(Identifier)+})+} + (ImportExportSpecifier + (Identifier) + (Identifier)) (ImportExportSpecifier { (Identifier) ->(Identifier) } { (Identifier) - ->(Identifier) }) - (ImportExportSpecifier - (Identifier) - (Identifier)) - {+(ImportExportSpecifier - {+(Identifier)+} - {+(Empty)+})+} + ->(Empty) }) {-(ImportExportSpecifier {-(Identifier)-} {-(Empty)-})-})) @@ -105,14 +105,14 @@ {-(ImportExportSpecifier {-(Identifier)-} {-(Identifier)-})-})-}) -{+(Export + (Export {+(ExportClause {+(ImportExportSpecifier {+(Identifier)+} - {+(Identifier)+})+})+})+} - (Export - { (TextElement) - ->(TextElement) }) + {+(Identifier)+})+})+} + {-(TextElement)-}) +{+(Export + {+(TextElement)+})+} (Export (ExportClause (ImportExportSpecifier @@ -133,27 +133,21 @@ {-(Empty)-})-}) { (TextElement) ->(TextElement) }) -{+(Export - {+(ExportClause - {+(ImportExportSpecifier - {+(Identifier)+} - {+(Identifier)+})+} - {+(ImportExportSpecifier - {+(Identifier)+} - {+(Identifier)+})+} - {+(ImportExportSpecifier - {+(Identifier)+} - {+(Empty)+})+})+} - {+(TextElement)+})+} -{-(Export - {-(ExportClause - {-(ImportExportSpecifier - {-(Identifier)-} - {-(Identifier)-})-} - {-(ImportExportSpecifier - {-(Identifier)-} - {-(Identifier)-})-} - {-(ImportExportSpecifier - {-(Identifier)-} - {-(Empty)-})-})-} - {-(TextElement)-})-}) + (Export + (ExportClause + (ImportExportSpecifier + { (Identifier) + ->(Identifier) } + { (Identifier) + ->(Identifier) }) + (ImportExportSpecifier + { (Identifier) + ->(Identifier) } + { (Identifier) + ->(Identifier) }) + (ImportExportSpecifier + { (Identifier) + ->(Identifier) } + (Empty))) + { (TextElement) + ->(TextElement) })) diff --git a/test/fixtures/typescript/function-call-args.diffB-A.txt b/test/fixtures/typescript/function-call-args.diffB-A.txt index a4b82533e..194e964af 100644 --- a/test/fixtures/typescript/function-call-args.diffB-A.txt +++ b/test/fixtures/typescript/function-call-args.diffB-A.txt @@ -8,14 +8,13 @@ (Empty) (Empty) (Empty) - (RequiredParameter - (Empty) - (Empty) - (Empty) - (Assignment - { (Identifier) - ->(Identifier) } - (Empty))) + {+(RequiredParameter + {+(Empty)+} + {+(Empty)+} + {+(Empty)+} + {+(Assignment + {+(Identifier)+} + {+(Empty)+})+})+} (RequiredParameter (Empty) (Empty) @@ -23,6 +22,13 @@ (Assignment (Identifier) (Empty))) + {-(RequiredParameter + {-(Empty)-} + {-(Empty)-} + {-(Empty)-} + {-(Assignment + {-(Identifier)-} + {-(Empty)-})-})-} ( (Call (MemberAccess diff --git a/test/fixtures/typescript/public-field-definition.diffA-B.txt b/test/fixtures/typescript/public-field-definition.diffA-B.txt index f76955af6..c56e8c9cc 100644 --- a/test/fixtures/typescript/public-field-definition.diffA-B.txt +++ b/test/fixtures/typescript/public-field-definition.diffA-B.txt @@ -19,33 +19,23 @@ (Empty) (Identifier) (Empty)) - {+(PublicFieldDefinition - {+(Identifier)+} - {+(Readonly)+} + (PublicFieldDefinition + (Identifier) + (Readonly) {+(Annotation {+(TypeIdentifier)+})+} - {+(Identifier)+} - {+(Float)+})+} - {+(PublicFieldDefinition - {+(Identifier)+} - {+(Empty)+} - {+(Annotation - {+(TypeIdentifier)+})+} - {+(Identifier)+} - {+(Float)+})+} - {-(PublicFieldDefinition - {-(Identifier)-} - {-(Readonly)-} {-(Empty)-} - {-(Identifier)-} - {-(Float)-})-} - {-(PublicFieldDefinition - {-(Identifier)-} + (Identifier) + (Float)) + (PublicFieldDefinition + { (Identifier) + ->(Identifier) } + {+(Empty)+} {-(Readonly)-} - {-(Annotation - {-(TypeIdentifier)-})-} - {-(Identifier)-} - {-(Float)-})-} + (Annotation + (TypeIdentifier)) + (Identifier) + (Float)) (PublicFieldDefinition (Empty) (Empty) diff --git a/test/fixtures/typescript/public-field-definition.diffB-A.txt b/test/fixtures/typescript/public-field-definition.diffB-A.txt index 5744d59a4..44f959890 100644 --- a/test/fixtures/typescript/public-field-definition.diffB-A.txt +++ b/test/fixtures/typescript/public-field-definition.diffB-A.txt @@ -19,27 +19,23 @@ (Empty) (Identifier) (Empty)) - {+(PublicFieldDefinition - {+(Identifier)+} - {+(Readonly)+} - {+(Empty)+} - {+(Identifier)+} - {+(Float)+})+} (PublicFieldDefinition (Identifier) (Readonly) - (Annotation - (TypeIdentifier)) - { (Identifier) - ->(Identifier) } - (Float)) - {-(PublicFieldDefinition - {-(Identifier)-} - {-(Empty)-} + {+(Empty)+} {-(Annotation {-(TypeIdentifier)-})-} - {-(Identifier)-} - {-(Float)-})-} + (Identifier) + (Float)) + (PublicFieldDefinition + { (Identifier) + ->(Identifier) } + {+(Readonly)+} + {-(Empty)-} + (Annotation + (TypeIdentifier)) + (Identifier) + (Float)) (PublicFieldDefinition (Empty) (Empty)