(module patterns mzscheme
(provide (all-defined))
(require "bindings.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)))
(if (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))))))
constraint)
classification))
(define (pattern-match-constraints pattern variables)
(let ((match-constraints '()))
(pattern-for-each pattern
(lambda (element)
(cond ((and (pair? element)
(variable? (car element))
(< (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))
(< (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))
(= (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))
(= (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-get 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-get 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)))
(variable? (cdr 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)))
((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-get 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))
)