1:- module(unified_diff, 2 [ unified_diff_ranges/3 3 , unified_diff//2 4 ]). 5:- use_module(library(dcg/basics)). 6:- use_module(library(pure_input)).
34try(Intro,Files) :-
35 phrase_from_file(unified_diff(Intro,Files), '/tmp/a.patch').
file(OldPath, NewPath, Hunks)
. OldPath
is file's
path before applying this patch. NewPath
is the file's path
after.
Hunks
is a list of terms:
hunk(OldLine, NewLine, Heading, Deltas)
. OldLine
is the line number of the original file to which this hunk applies.
NewLine
is the line number of the new file.
Heading
is optional text on the same line as the hunk header
(usually a function name).
An empty list indicates a missing heading.
Deltas
is a list of terms with three possible functors. The
functor ' '
indicates a context line, -
indicates a deletion
and +
indicates an insertion. All three functors have arity 1
and the first argument is a list of codes indicating the content.
For example, this patch in unified diff format:
Patch explanation --- alphabet +++ alphabet @@ -1,3 +1,3 @@ function: alpha -b +beta gamma
is equivalent to this term:
[ file(alphabet, alphabet, [ hunk(1,1,"function:", [ ' '("alpha") , -"b" , +"beta" , ' '("gamma") ])])]
83unified_diff(Intro, Files) --> 84 intro(Intro), 85 files(Files), 86 end, 87 !. % we only want the first solution 88 89intro(Intro) --> 90 s(Intro), "\n". 91 92files([file(Old,New,Hunks)|Files]) --> 93 { when(ground(OldCodes);ground(Old), atom_codes(Old, OldCodes)) }, 94 { when(ground(NewCodes);ground(New), atom_codes(New, NewCodes)) }, 95 file_header(OldCodes, NewCodes), 96 %{ format('old=~s new=~s~n', [OldCodes,NewCodes]) }, 97 hunks(Hunks), 98 files(Files). 99files([]) --> "". 100 101file_header(Old, New) --> 102 "--- ", s(Old), "\n", 103 "+++ ", s(New), "\n". 104 105hunks([hunk(OL, NL, Heading, Deltas)|Hunks]) --> 106 { when(ground(Deltas), unified_diff_ranges(Deltas, OS, NS)) }, 107 hunk_header(OL,OS, NL,NS, Heading), 108 %{ format('l=~d h=~s~n', [Line,Heading]) }, 109 deltas(Deltas), 110 hunks(Hunks). 111hunks([]) --> "". 112 113hunk_header(Line,OS, NL,NS, Heading) --> 114 "@@ -", i(Line), ",", i(OS), " +", i(NL), ",", i(NS), " @@", 115 ( " ", s(Heading) 116 ; "", {Heading=""} 117 ), 118 "\n". 119 120end([],[]).
@@ -118,6 +118,10 @@ sub profile {
unified_diff_ranges/3 calculates the
values OldSize and NewSize (6
and 10
in this example,
respectively) based on Deltas.
Range sizes are automatically calculated when generating patches. They're automatically verified when parsing. Therefore, it's unlikely that consumers will need this predicate.
138unified_diff_ranges([],0,0). 139unified_diff_ranges([' '(_)|Deltas],OS, NS) :- 140 unified_diff_ranges(Deltas,OS0, NS0), 141 succ(OS0, OS), 142 succ(NS0, NS). 143unified_diff_ranges([+(_)|Deltas],OS, NS) :- 144 unified_diff_ranges(Deltas,OS, NS0), 145 succ(NS0, NS). 146unified_diff_ranges([-(_)|Deltas],OS, NS) :- 147 unified_diff_ranges(Deltas,OS0, NS), 148 succ(OS0, OS). 149 150 151deltas([Delta|Deltas]) --> 152 delta(Delta), 153 deltas(Deltas). 154deltas([]) --> "". 155 156delta(' '(Content)) --> 157 " ", swo(Content, "\n"). 158delta(+(Content)) --> 159 "+", swo(Content, "\n"). 160delta(-(Content)) --> 161 "-", swo(Content, "\n"). 162 163 164s(S) --> string(S). 165swo(S, Stop) --> string_without(Stop, S), . 166i(I) --> integer(I), {I>0}
Convert unified diff format patches to and from Prolog terms. unified_diff//2 can be used to parse and generate patches. This module intends to support only well-formed patches. It makes no attempt to handle arbitrary patches as they might occur in the wild.
Installation
Using SWI-Prolog 6.3 or later:
Source code available and pull requests accepted on GitHub: https://github.com/mndrix/unified_diff
*/