;
; from: Programming in MacScheme
;       Eisenberg, Hartheimer, Clinger, Abelson
; chapter 15, pattern matching
;

(define (try-depending-on-type pattern dictionary-list assertions)
  (cond	((eq? (car pattern) 'and)
         (do-and-clauses (cdr pattern) dictionary-list assertions))
        ((eq? (car pattern) 'or)
         (do-or-clauses (cdr pattern) dictionary-list assertions))
        ((eq? (car pattern) 'dont-keep)
         (use-dont-keep-proc (cdr pattern) dictionary-list))
        (else (check-assertions-for-each-dictionary
               pattern dictionary-list assertions))))

(define (do-and-clauses clauses dictionary-list assertions)
  (if (null? clauses)
      dictionary-list
      (do-and-clauses (cdr clauses)
                      (try-depending-on-type (car clauses)
                                             dictionary-list
                                             assertions)
                      assertions)))

(define (do-or-clauses clauses dictionary-list assertions)
  (if (null? clauses)
      '()
      (append (do-or-clauses (cdr clause)
                             dictionary-list
                             assertions)
              (try-depending-on-type (car clauses)
                                     dictionary-list
                                     assertions))))

(define (use-dont-keep-proc clause dictionary-list)
  (let ((dont-keep-proc (car clause))
        (clause-variables (cdr clause)))
    (throw-away (lambda (dictionary)
                  (apply dont-keep-proc
                         (get-list-of-values clause-variables dictionary)))
                dictionary-list)))

(define (get-list-of-values list-of-variables dictionary)
  (map (lambda (var) (get-value var dictionary)) list-of-variables))

(define (get-value variable dictionary)
  (let ((vname (variable-name variable)))
    (let ((dictionary-entry (assq vname dictionary)))
      (if (not dictionary-entry)
          '()
          (cadr dictionary-entry)))))

(define (try-all-assertions pattern assertion-list starting-dictionary)
  (throw-away (lambda (dictionary)
                (eq? dictionary 'failed))
              (map (lambda (assertion)
                     (match pattern assertion starting-dictionary))
                   assertion-list)))

(define (check-assertions-for-each-dictionary 
         pattern dictionary-list assertions)
  (map-append (lambda (dictionary)
                (try-all-assertions pattern assertions dictionary))
              dictionary-list))

(define (map-append procedure lst)
  (if (null? lst)
      '()
      (append (procedure (car lst))
              (map-append procedure (cdr lst)))))

(define (throw-away predicate lst)
  (cond ((null? lst) '())
        ((predicate (car lst)) (throw-away predicate (cdr lst)))
        (else (cons (car lst) (throw-away predicate (cdr lst))))))

(define (extend-dictionary variable expression dictionary)
  (let ((vname (variable-name variable)))
    (let ((v (assq vname dictionary)))
      (cond ((not v) (cons (list vname expression) dictionary))
            ((equal? (cadr v) expression) dictionary)
            (else 'failed)))))

(define (match pattern expression dictionary)
  (cond ((eq? dictionary 'failed) 'failed)
        ((atom? pattern)
         (if (eqv? expression pattern)
             dictionary
             'failed))
        ((null? expression) 'failed)
        ((variable? pattern)
         (extend-dictionary pattern expression dictionary))
        ((atom? expression) 'failed)
        (else
         (match (cdr pattern)
                (cdr expression)
                (match (car pattern) (car expression) dictionary)))))

(define (variable? pattern)
  (eq? (car pattern) '?v))

(define variable-name cadr)

;(define (atom? x)
; (not (pair? x)))

(define (try pattern)
  (try-depending-on-type pattern (list '()) *the-assertions*))

(define *the-assertions*
  '(
    (group (becker walter) steely-dan)
    (group (fagen donald) steely-dan)
    (plays (becker walter) quitar)
    (plays (fagen donald) keyboards)
    (plays (fagen donald) vocals)
    (group (banks tony) genesis)
    (group (collins philip) genesis)
    (group (rutherford mike) genesis)
    (plays (banks tony) keyboards)
    (plays (collins philip) drums)
    (plays (collins philip) vocals)
    (plays (rutherford mike) guitar)
    (plays (rutherford mike) bass)
    (album genesis abacab)
    ))

(try `(and (plays (?v person) quitar)
           (group (?v person) (?v group))
           (dont-keep ,(lambda (g) (eq? g 'genesis)) (?v group))))
