build: Install naive diff.
[mes.git] / scripts / diff.scm
1 #! /bin/sh
2 # -*-scheme-*-
3 mes_p=$(command -v mes)
4 if [ "$mes_p" -a -z "$MES" ]; then
5     MES=guile
6 fi
7 exec ${MES-mes} -L $(dirname 0) -e '(diff)' -s "$0" "$@"
8 !#
9
10 ;;; Mes --- Maxwell Equations of Software
11 ;;; Copyright © 2016,2017,2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
12 ;;;
13 ;;; mes-snarf.scm: This file is part of Mes.
14 ;;;
15 ;;; Mes is free software; you can redistribute it and/or modify it
16 ;;; under the terms of the GNU General Public License as published by
17 ;;; the Free Software Foundation; either version 3 of the License, or (at
18 ;;; your option) any later version.
19 ;;;
20 ;;; Mes is distributed in the hope that it will be useful, but
21 ;;; WITHOUT ANY WARRANTY; without even the implied warranty of
22 ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
23 ;;; GNU General Public License for more details.
24 ;;;
25 ;;; You should have received a copy of the GNU General Public License
26 ;;; along with Mes.  If not, see <http://www.gnu.org/licenses/>.
27
28 (define-module (diff)
29   #:use-module (srfi srfi-1)
30   #:use-module (srfi srfi-9)
31   #:use-module (ice-9 rdelim)
32   #:export (main))
33
34 (cond-expand
35  (mes
36   (define %scheme "mes"))
37  (guile-2
38   (define %scheme "guile")
39   (define-macro (mes-use-module . rest) #t))
40  (guile
41   (use-modules (ice-9 syncase))
42   (define %scheme "guile")
43   (define-macro (mes-use-module . rest) #t)))
44
45 (mes-use-module (mes guile))
46
47 (format (current-error-port) "diff[~a]...\n" %scheme)
48
49 (define (plus a)
50   (string-append "+" a))
51 (define (minus a)
52   (string-append "-" a))
53 (define (keep a)
54   (string-append " " a))
55
56 (define-record-type <hunk> (make-hunk context after removed added)
57   hunk?
58   (context hunk.context)
59   (after hunk.after)
60   (removed hunk.removed)
61   (added hunk.added))
62
63 (define (hunk->lines o)
64   (append `(,(format #f "@@ -~a,~a +~a,~a" (length (hunk.removed o)) (+ 3 (car (hunk.context o))) (length (hunk.added o)) (+ 3 (cadr (hunk.context o))))
65             ,@(map keep (filter identity (cddr (hunk.context o)))))
66           (map minus (hunk.removed o))
67           (map plus (hunk.added o))
68           (map keep (hunk.after o))))
69
70 (define (safe-list-head lst n)
71   (list-head lst (min n (length lst))))
72
73 (define (line-equal? a b)
74   (equal? (string-trim-right a) (string-trim-right b)))
75
76 ;; naive diff
77 (define (diff a b)
78   (let ((a-lines (string-split (with-input-from-file a read-string) #\newline))
79         (b-lines (string-split (with-input-from-file b read-string) #\newline)))
80     (let loop ((context '(1 1 #f #f #f)) (a-lines a-lines) (b-lines b-lines))
81       ;;(format (current-error-port) "loop context=~s\n" context)
82       (cond ((and (null? a-lines) (null? b-lines)) '())
83             ((null? a-lines)
84              (list (make-hunk context (safe-list-head a-lines 3) '() b-lines)))
85             ((null? b-lines)
86              (list (make-hunk context (safe-list-head a-lines 3) a-lines '())))
87             ((line-equal? (car a-lines) (car b-lines))
88              (loop `(,(1+ (car context))
89                      ,(1+ (cadr context))
90                      ,@(cdddr context)
91                      ,(car a-lines))
92                    (cdr a-lines) (cdr b-lines)))
93             (else
94              (cond ((and (pair? (cdr b-lines)) (line-equal? (car a-lines) (cadr b-lines)))
95                     (cons (make-hunk context (safe-list-head a-lines 3) '() (list (car b-lines)))
96                           (loop `(,(+ 1 (car context))
97                                   ,(+ 2 (cadr context))
98                                   ,@(cdddr context)
99                                   ,(car a-lines))
100                                 (cdr a-lines) (cddr b-lines))))
101                    ((and (pair? (cdr a-lines)) (line-equal? (cadr a-lines) (car b-lines)))
102                     (cons (make-hunk context (safe-list-head a-lines 3) (list (car a-lines)) '())
103                           (loop `(,(+ 2 (car context))
104                                   ,(+ 1 (cadr context))
105                                   ,@(cddddr context)
106                                   ,(car a-lines)
107                                   ,(cadr a-lines))
108                                 (cddr a-lines) (cdr b-lines))))
109                    (else (cons (make-hunk context (safe-list-head a-lines 3) (list (car a-lines)) (list (car b-lines)))
110                                (loop `(,(1+ (car context))
111                                        ,(1+ (cadr context))
112                                        ,@(cdddr context)
113                                        ,(car a-lines))
114                                      (cdr a-lines) (cdr b-lines))))))))))
115
116 (define (main args)
117   (let* ((files (cdr args))
118          (files (if (string-prefix? "-" (car files)) (cdr files) files))
119          (hunks (apply diff (list-head files 2))))
120     (when (pair? hunks)
121       (display (string-join (append-map hunk->lines hunks) "\n"))
122       (newline)
123       (exit 1))))