Rebol [
Title: "SimpleDiff"
Date: 25-Aug-2016
Author: "Christopher Ross-Gill"
Type: 'module
Home: https://github.com/paulgb/simplediff
Name: 'diff-mod
Exports: [diff]
Version: 1.1.0
Comment: {
Based on Simple Diff for Python, CoffeeScript v0.1
(C) Paul Butler 2008 <http://www.paulbutler.org/>
}
History: [
22-May-2014 1.0.0 "Original Version"
25-Aug-2016 1.1.0 "Tweaked to be compatible with Ren/C and Red"
]
Usage: [
probe diff probe [a b c d] probe [b c d e]
probe diff probe [a b c d] probe [e f g h d]
probe diff probe [a b c d] probe [e f g h]
probe diff probe [A B c D] probe [a b c d]
probe diff probe ["A" "B" "c" "D"] probe ["a" "b" "c" "d"]
probe diff probe [a b b b c] probe [a b b b b c]
probe diff
probe parse "you might say that, I couldn't possibly comment" none
probe parse "You may wish to say that, couldn't possibly comment either way." none
]
]
diff: func [
{
Find the differences between two blocks. Returns a block of pairs, where the first value
is in [+ - =] and represents an insertion, deletion, or no change for that list.
The second value of the pair is the element.
}
before [block! string!] after [block! string!]
/local items-before starts-before starts-after run this-run test tests limit
][
assert [equal? type? before type? after]
run: 0
; Build a block with elements from 'before as keys, and
; each position starting with each element as values.
items-before: copy []
forall before [
append/only any [
select/case items-before first before
last repend items-before [first before copy []]
] before
]
; Find the largest subseries common to before and after
forall after [
if tests: select/case items-before first after [
limit: length? after
foreach test tests [
repeat offset min limit length? test [
this-run: :offset
unless test/:offset == after/:offset [
this-run: offset - 1
break
]
]
if this-run > run [
run: :this-run
starts-before: :test
starts-after: :after
]
]
]
]
collect [
either zero? run [
; If no common subseries is found, assume that an
; insert and delete has taken place
unless tail? before [keep reduce ['- before]]
unless tail? after [keep reduce ['+ after]]
][
; Otherwise he common subseries is considered to have no change, and we
; recurse on the text before and after the substring
keep diff copy/part before starts-before copy/part after starts-after
keep reduce ['= copy/part starts-after run]
keep diff copy skip starts-before run copy skip starts-after run
]
]
]