diff --git a/src/org/rascalmpl/library/analysis/diff/edits/HiFiTreeDiff.rsc b/src/org/rascalmpl/library/analysis/diff/edits/HiFiTreeDiff.rsc index 2fcb2d3b3d5..c0f3b90ddc3 100644 --- a/src/org/rascalmpl/library/analysis/diff/edits/HiFiTreeDiff.rsc +++ b/src/org/rascalmpl/library/analysis/diff/edits/HiFiTreeDiff.rsc @@ -226,6 +226,94 @@ list[TextEdit] treeDiff( = [replace(t@\loc, learnIndentation(t@\loc, "", ""))] when t != r; +// Several ways of changing recursive expressions can be exploited to create very small diffs. +// This is comparable to finding large common sublists in the case of lists. If you look at +// unary and binary associative operators as a kind of operator-separated lists, this makes sense. + +// remove a unary prefix operator +list[TextEdit] treeDiff( + appl(prod(sort(str exp), [lit(_), _, sort(exp)], _), [Tree op, _, Tree r]), + r) + = [delete(fromUntil(op@\loc, r@\loc))]; + +// add a unary prefix operator +list[TextEdit] treeDiff( + Tree r, + appl(prod(sort(str exp), [lit(_), _, sort(exp)], _), [Tree op, Tree sp, r])) + = [replace(beginOf(r@\loc), "")]; + +// remove a unary postfix operator +list[TextEdit] treeDiff( + appl(prod(sort(str exp), [sort(str exp), _, lit(_)], _), [Tree l, _, Tree r]), + r) + = [delete(cover([endOf(l@\loc), r@\loc]))]; + +// add a unary postfix operator +list[TextEdit] treeDiff( + Tree l, + appl(prod(sort(str exp), [sort(str exp), _, lit(_)], _), [l, Tree sp, Tree op])) + = [replace(endOf(l), "")]; + +// remove the left hand side of a binary operator +list[TextEdit] treeDiff( + appl(prod(sort(str exp), [sort(exp), _, lit(_), _, sort(exp)], _), [Tree l, _, _, _, Tree r]), + r) + = [delete(fromUntil(l@\loc, r@\loc))]; + +// add the left hand side of a binary operator +list[TextEdit] treeDiff( + Tree r, + appl(prod(sort(str exp), [sort(exp), _, lit(_), _, sort(exp)], _), [Tree l, Tree sp1, Tree op, Tree sp2, r])) + = [replace(beginOf(r@\loc), "")]; + +// remove the right hand side of a binary operator +list[TextEdit] treeDiff( + appl(prod(sort(str exp), [sort(exp), _, lit(_), _, sort(exp)], _), [Tree l, _, _, _, Tree r]), + l) + = [delete(cover([endOf(l@\loc), r@\loc]))]; + +// add the right hand side of a binary operator +list[TextEdit] treeDiff( + Tree l, + appl(prod(sort(str exp), [sort(exp), _, lit(_), _, sort(exp)], _), [l, Tree sp1, Tree op, Tree sp2, Tree r])) + = [replace(endOf(l@\loc), "")]; + +// change a binary operator +list[TextEdit] treeDiff( + appl(prod(sort(str exp), [sort(exp), _, lit(str x), _, sort(exp)], _), [Tree l, _, Tree op, _, Tree r]), + appl(prod(sort(str exp), [sort(exp), _, lit(str y), _, sort(exp)], _), [l, _, Tree newOp, _, r])) + = [replace(op@\loc, "")] when x != y; + +// change a prefix operator +list[TextEdit] treeDiff( + appl(prod(sort(str exp), [lit(str x), _, sort(exp)], _), [Tree op, _, Tree r]), + appl(prod(sort(str exp), [lit(str y), _, sort(exp)], _), [Tree newOp, _, r])) + = [replace(op@\loc, "")] when x != y; + +// change a postfix operator +list[TextEdit] treeDiff( + appl(prod(sort(str exp), [sort(exp), _, lit(str x)], _), [Tree l, _, Tree op]), + appl(prod(sort(str exp), [sort(exp), _, lit(str y)], _), [l, _, Tree newOp])) + = [replace(op@\loc, "")] when x != y; + +// remove brackets +list[TextEdit] treeDiff( + t:appl(prod(sort(str exp), [lit(_), _, sort(exp), _, lit(_)], {*_, \bracket()}), [_, _, Tree e, _, _]), + e) + = [ + delete(fromUntil(beginOf(t@\loc), e@\loc)), + delete(fromUntil(endOf(e@\loc), endOf(t@\loc))) + ]; + +// add brackets +list[TextEdit] treeDiff( + Tree e, + t:appl(prod(sort(str exp), [lit(_), _, sort(exp), _, lit(_)], {*_, \bracket()}), [Tree open, Tree sp1, Tree e, Tree sp2, Tree close])) + = [ + replace(beginOf(e@\loc), ""), + replace(endOf(e@\loc), "") + ]; + // When the productions are different, we've found an edit, and there is no need to recurse deeper. default list[TextEdit] treeDiff( t:appl(Production p:prod(_,_,_), list[Tree] _), @@ -383,6 +471,9 @@ up to `until`. private loc fromUntil(loc from, loc until) = from.top(from.offset, until.offset - from.offset); private int end(loc src) = src.offset + src.length; +private loc endOf(loc src) = src.top[offset=src.offset + src.length][length=0]; +private loc beginOf(loc src) = src.top[offset=src.offset][length=0]; + private loc after(loc src) = src(end(src), 0); private loc endCover(loc span, []) = span(span.offset + span.length, 0);