#lang scheme
(require "bindings.ss"
"facts.ss")
(define (wildcard? x)
(eq? x '?))
(define (variable? x)
(and (symbol? x)
(not (wildcard? x))
(string=? (substring (symbol->string x) 0 1) "?")))
(define (pattern? x)
(or (and (pair? x)
(symbol? (car x))
(not (wildcard? (car x)))
(not (variable? (car x))))
(and (vector? x)
(> (vector-length x) 0)
(symbol? (vector-ref x 0))
(not (wildcard? (vector-ref x 0)))
(not (variable? (vector-ref x 0))))))
(define (pattern-first pattern)
(cond ((pair? pattern)
(car pattern))
((vector? pattern)
(vector-ref pattern 0))))
(define (pattern-for-each pattern proc)
(cond ((pair? pattern)
(let loop ((pattern-tail pattern))
(if (pair? pattern-tail)
(begin
(proc (car pattern-tail))
(loop (cdr pattern-tail)))
(when (not (null? pattern-tail))
(proc pattern-tail))))
(void))
((vector? pattern)
(do ((i 0 (+ i 1)))
((= i (vector-length pattern)) (void))
(proc (vector-ref pattern i))))))
(define (pattern-variables pattern)
(let ((variables '()))
(pattern-for-each
pattern
(lambda (element)
(cond ((variable? element)
(set! variables (cons element variables)))
((and (pair? element)
(variable? (car element)))
(set! variables (cons (car element) variables)))
((and (pair? element)
(or (symbol? (car element))
(keyword? (car element)))
(variable? (cdr element)))
(set! variables (cons (cdr element) variables)))
((and (pair? element)
(or (symbol? (car element))
(keyword? (car element)))
(pair? (cdr element))
(variable? (cadr element)))
(set! variables (cons (cadr element) variables))))))
(reverse variables)))
(define NO-VARIABLES 0)
(define LOCAL-VARIABLES 1)
(define GLOBAL-VARIABLES 2)
(define (classify-constraint constraint variables)
(let ((classification NO-VARIABLES))
(for-each
(lambda (element)
(cond ((variable? element)
(set! classification
(max classification
(if (memq element variables)
LOCAL-VARIABLES
GLOBAL-VARIABLES))))
((pair? element)
(set! classification
(max classification
(classify-constraint element variables))))))
(if (pair? constraint) constraint (list constraint)))
classification))
(define (pattern-match-constraints pattern variables)
(let ((match-constraints '()))
(pattern-for-each
pattern
(lambda (element)
(cond ((and (pair? element)
(variable? (car element)))
(when (< (classify-constraint
(cadr element) variables)
GLOBAL-VARIABLES)
(set! match-constraints
(cons (cadr element) match-constraints))))
((and (pair? element)
(or (symbol? (car element))
(keyword? (car element)))
(pair? (cdr element))
(variable? (cadr element)))
(when (< (classify-constraint
(caddr element) variables)
GLOBAL-VARIABLES)
(set! match-constraints
(cons (caddr element) match-constraints))))
)))
(reverse match-constraints)))
(define (pattern-join-constraints pattern variables)
(let ((join-constraints '()))
(pattern-for-each
pattern
(lambda (element)
(cond ((and (pair? element)
(variable? (car element)))
(when (= (classify-constraint
(cadr element) variables)
GLOBAL-VARIABLES)
(set! join-constraints
(cons (cadr element) join-constraints))))
((and (pair? element)
(or (symbol? (car element))
(keyword? (car element)))
(pair? (cdr element))
(variable? (cadr element)))
(when (= (classify-constraint
(caddr element) variables)
GLOBAL-VARIABLES)
(set! join-constraints
(cons (caddr element) join-constraints))))
)))
(reverse join-constraints)))
(define (pattern-base-pattern pattern)
(cond ((pair? pattern)
(pattern-base-pattern-list pattern))
((vector? pattern)
(let ((base-pattern (make-vector (vector-length pattern))))
(do ((i 0 (+ i 1)))
((= i (vector-length pattern)) base-pattern)
(let ((element (vector-ref pattern i)))
(cond ((and (pair? element)
(variable? (car element)))
(vector-set! base-pattern i (car element)))
((and (pair? element)
(or (symbol? (car element))
(keyword? (car element)))
(pair? (cdr element))
(variable? (cadr element)))
(vector-set! base-pattern i
(cons (car element)
(cadr element))))
(else
(vector-set! base-pattern i element)))))))))
(define (pattern-base-pattern-list pattern)
(cond ((null? pattern)
'())
((pair? pattern)
(let ((element (car pattern)))
(cond ((and (pair? element)
(variable? (car element)))
(cons (car element)
(pattern-base-pattern-list (cdr pattern))))
((and (pair? element)
(or (symbol? (car element))
(keyword? (car element)))
(pair? (cdr element))
(variable? (cadr element)))
(cons (cons (car element) (cadr element))
(pattern-base-pattern-list (cdr pattern))))
(else
(cons element
(pattern-base-pattern-list (cdr pattern)))))))
(else
pattern)))
(define (pattern-substitute pattern bindings)
(cond ((pair? pattern)
(pattern-substitute-list pattern bindings))
((vector? pattern)
(let ((new-pattern (make-vector (vector-length pattern))))
(do ((i 0 (+ i 1)))
((= i (vector-length pattern)) new-pattern)
(vector-set! new-pattern i
(pattern-substitute-list
(vector-ref pattern i) bindings)))))))
(define (pattern-substitute-list pattern bindings)
(cond ((null? pattern)
'())
((pair? pattern)
(cons (pattern-substitute-list (car pattern) bindings)
(pattern-substitute-list (cdr pattern) bindings)))
((and (variable? pattern)
(bindings-bound? bindings pattern))
(bindings-ref bindings pattern))
(else
pattern)))
(define (pattern-unify fact pattern bindings)
(cond ((and (pair? pattern)
(pair? fact))
(pattern-unify-list fact pattern bindings #f))
((and (vector? pattern)
(vector? fact))
(pattern-unify-vector fact pattern bindings))
((and (vector? pattern)
(struct? fact))
(pattern-unify-vector (struct->vector fact) pattern bindings))
(else
#f)))
(define (pattern-unify-list fact pattern bindings alist?)
(cond ((null? pattern)
(if (or (null? fact)
alist?)
bindings
#f))
((null? fact)
#f)
((pair? pattern)
(let ((element (car pattern)))
(cond ((not (pair? fact))
#f)
((wildcard? element)
(pattern-unify-list
(cdr fact) (cdr pattern) bindings #f))
((variable? element)
(if (bindings-bound? bindings element)
(if (eqv? (bindings-ref bindings element)
(car fact))
(pattern-unify-list
(cdr fact) (cdr pattern) bindings #f)
#f)
(pattern-unify-list
(cdr fact) (cdr pattern)
(append bindings
(list
(cons element (car fact))))
#f)))
((and (pair? element)
(or (symbol? (car element))
(keyword? (car element)))
)
(let* ((key (car element))
(association (assq key fact)))
(if association
(let ((new-bindings (pattern-unify-list
(cdr association) (cdr element)
bindings #f)))
(if new-bindings
(pattern-unify-list
fact (cdr pattern) new-bindings #t)
#f))
#f)))
((equal? (car pattern) (car fact))
(pattern-unify-list
(cdr fact) (cdr pattern) bindings #f))
(else
#f))))
((wildcard? pattern)
bindings)
((variable? pattern)
(if (bindings-bound? bindings pattern)
(if (eqv? (bindings-ref bindings pattern)
fact)
bindings
#f)
(append bindings (list (cons pattern fact)))))
((eqv? pattern fact)
bindings)
(else #f)))
(define (pattern-unify-vector fact pattern bindings)
(if (>= (vector-length fact)
(vector-length pattern))
(let/ec return
(do ((i 0 (+ i 1)))
((= i (vector-length pattern)) bindings)
(let ((unified-element (pattern-unify-list
(vector-ref fact i)
(vector-ref pattern i)
bindings #f)))
(if unified-element
(set! bindings unified-element)
(return #f)))))
#f))
(provide
NO-VARIABLES
LOCAL-VARIABLES
GLOBAL-VARIABLES)
(provide/contract
(wildcard?
(-> any/c boolean?))
(variable?
(-> any/c boolean?))
(pattern?
(-> any/c boolean?))
(pattern-first
(-> pattern? symbol?))
(pattern-for-each
(-> pattern? procedure? void?))
(pattern-variables
(-> pattern? (listof variable?)))
(classify-constraint
(-> list? (listof variable?) (one-of/c NO-VARIABLES LOCAL-VARIABLES GLOBAL-VARIABLES)))
(pattern-match-constraints
(-> pattern? (listof variable?) list?))
(pattern-join-constraints
(-> pattern? (listof variable?) list?))
(pattern-base-pattern
(-> pattern? pattern?))
(pattern-substitute
(-> pattern? bindings? pattern?))
(pattern-unify
(-> fact? pattern? bindings? (or/c bindings? false/c))))