Redex: parameterized disequations

refactor the pattern unifier and tests to handle
disequations with parameters correctly
This commit is contained in:
Burke Fetscher 2013-03-20 16:28:57 -05:00
parent 564d244039
commit bdf1866e80
2 changed files with 87 additions and 48 deletions

View File

@ -23,7 +23,10 @@
pat*-clause-p?s
bind-names
remove-empty-dqs
unif-fail)
unif-fail
dq)
;(require racket/trace)
;;
;; atom := `any | `number | `string | `integer | `boolean | `real | `variable | `variable-not-otherwise-mentioned
;; var := symbol?
@ -49,6 +52,7 @@
(struct p*e (p e) #:transparent)
(struct env (eqs dqs) #:transparent)
(struct dq (params dq) #:transparent)
(define empty-env (env (hash) '()))
(struct unif-fail () #:transparent)
@ -162,7 +166,7 @@
(list->dq-pairs dq-sides/id)))]
[found-dqs
(for/list ([pdq found-pre-dqs])
(disunify* (first pdq) (second pdq) (hash-copy static-eqs) L))])
(disunify* '() (first pdq) (second pdq) (hash-copy static-eqs) L))])
(and (for/and ([d found-dqs]) d)
(let* ([real-dqs (filter (λ (dq) (not (boolean? dq))) found-dqs)]
[new-dqs (check-and-resimplify static-eqs (append real-dqs (env-dqs e)) L)])
@ -181,7 +185,7 @@
(list->dq-pairs (cdr dq-sides)))]))
;; pat pat env lang -> (or/c env #f)
(define (disunify t u e L)
(define (disunify params t u e L)
;(-> pat? pat? env/c any/c (or/c env/c #f))
(parameterize ([new-eqs (make-hash)])
(define eqs (hash-copy (env-eqs e)))
@ -192,7 +196,7 @@
e]
[else
(define bn-eqs (hash-copy eqs))
(define new-dq (disunify* t* u* eqs L))
(define new-dq (disunify* params t* u* eqs L))
(match new-dq
[#f #f]
[#t
@ -230,11 +234,11 @@
;; simplified - first element in lhs of all inequations is a var not occuring in lhs of eqns
(define (check-and-resimplify eqs dqs L)
(define-values (dqs-notok dqs-ok)
(partition (λ (dq)
(partition (λ (a-dq)
(hash-has-key?
eqs
(lvar (match dq
[`((list (name ,v1 ,(bound)) ,vs ...) (list ,t1 ,ts ...))
(lvar (match a-dq
[(dq ps `((list (name ,v1 ,(bound)) ,vs ...) (list ,t1 ,ts ...)))
v1]))))
(remove-empty-dqs dqs)))
(let loop ([ok dqs-ok]
@ -244,23 +248,52 @@
ok]
[else
(match notok
[`((,vars-p* ,term-p*) ,rest ...)
(let ([new-dq (disunify* vars-p* term-p* (hash-copy eqs) L)])
[`(,(dq ps `(,vars-p* ,term-p*)) ,rest ...)
(let ([new-dq (disunify* ps vars-p* term-p* (hash-copy eqs) L)])
(and new-dq
(match new-dq
[#t (loop ok rest)]
[`((list)(list)) (loop ok rest)]
#;[`((list)(list)) (loop ok rest)]
[else (loop (cons new-dq ok) rest)])))])])))
;; disunfy* pat* pat* eqs lang -> dq or boolean (dq is a pat*)
(define (disunify* u* t* eqs L)
(define (disunify* params u* t* eqs L)
(parameterize ([new-eqs (make-hash)])
(let ([res (unify* u* t* eqs L)])
(cond
[(unif-fail? res) #t]
[(empty? (hash-keys (new-eqs))) #f]
[else
(extend-dq (new-eqs) base-dq)]))))
(define-values (new-ps new-dq)
(param-elim params (extend-dq (new-eqs) base-dq)))
(match new-dq
[`((list) (list))
#f]
[else
(dq new-ps new-dq)])
#;(extend-dq (new-eqs) base-dq)]))))
(define (param-elim params unquantified-dq)
(let loop ([dq-rest unquantified-dq]
[ps params]
[new-dq-l '()]
[new-dq-r '()])
;(printf "~s ~s ~s ~s\n" dq-rest ps new-dq-l new-dq-r)
(match dq-rest
['((list) (list))
(values ps `((list ,@new-dq-l) (list ,@new-dq-r)))]
[`((list (name ,v1,(bound)) ,vs ...) (list ,t1 ,ts ...))
(cond
[(member v1 params)
(loop `((list ,@vs) (list ,@ts))
(remove v1 ps)
new-dq-l
new-dq-r)]
[else
(loop `((list ,@vs) (list ,@ts))
ps
(cons `(name ,v1 ,(bound)) new-dq-l)
(cons t1 new-dq-r))])])))
;; the "root" pats will be pats without names,
@ -609,6 +642,7 @@
(values (lvar id) res)]))
;(trace disunify*)

View File

@ -59,28 +59,30 @@
(env-dqs e-right))))
(define (dqs->dqset dqs)
(for/set ([dq (in-list dqs)])
(for/fold ([dq-pairs (set)])
([ls (first dq)]
[rs (second dq)])
(set-add dq-pairs (cons ls rs)))))
(for/set ([the-dq (in-list dqs)])
(match the-dq
[(dq ps dq-e)
(for/fold ([dq-pairs (set)])
([ls (first dq-e)]
[rs (second dq-e)])
(set-add dq-pairs (cons ls rs)))])))
(check-equal? (dqs->dqset `(((list (name a ,(bound))
(name b ,(bound)))
(list a-cant-be
b-cant-be))))
(check-equal? (dqs->dqset (list (dq '() `((list (name a ,(bound))
(name b ,(bound)))
(list a-cant-be
b-cant-be)))))
(set (set (cons 'list 'list)
(cons `(name a ,(bound)) 'a-cant-be)
(cons `(name b ,(bound)) 'b-cant-be))))
(check-equal? (dqs->dqset `(((list (name a ,(bound))
(name b ,(bound)))
(list a-cant-be
b-cant-be))
((list (name c ,(bound))
(name d ,(bound)))
(list c-cant-be
d-cant-be))))
(check-equal? (dqs->dqset `(,(dq '() `((list (name a ,(bound))
(name b ,(bound)))
(list a-cant-be
b-cant-be)))
,(dq '() `((list (name c ,(bound))
(name d ,(bound)))
(list c-cant-be
d-cant-be)))))
(set (set (cons 'list 'list)
(cons `(name c ,(bound)) 'c-cant-be)
(cons `(name d ,(bound)) 'd-cant-be))
@ -873,42 +875,45 @@
;; TODO tests on the dqs here are currently order-dependent
(check-false
(disunify* `(list (name x_1 ,(bound)))
(disunify* '()
`(list (name x_1 ,(bound)))
`(list (name x_2 ,(bound)))
(make-hash `((,(lvar 'x_1) . a)
(,(lvar 'x_2) . a)))
L0))
(check-not-false
(disunify* `(list (name x_1 ,(bound)))
(disunify* '()
`(list (name x_1 ,(bound)))
`(list (name x_2 ,(bound)))
(make-hash `((,(lvar 'x_1) . (nt x))
(,(lvar 'x_2) . a)))
L0))
(check-not-false
(disunify* `(list (name x_1 ,(bound)))
(disunify* '()
`(list (name x_1 ,(bound)))
`(list (name x_2 ,(bound)))
(make-hash `((,(lvar 'x_1) . (nt x))
(,(lvar 'x_2) . (nt x))))
L0))
(check-false
(disunify* 'a '(cstr (s) a) (make-hash) L0))
(disunify* '() 'a '(cstr (s) a) (make-hash) L0))
(check-false
(let ([h (make-hash (list (cons (lvar 'a2) 'a)))])
(disunify* `(name a2 ,(bound)) '(cstr (s) a) h L0)))
(disunify* '() `(name a2 ,(bound)) '(cstr (s) a) h L0)))
(check-false
(let ([h (make-hash (list (cons (lvar 'a2) 'a)
(cons (lvar 's6) '(cstr (s) a))))])
(disunify* `(name a2 ,(bound)) `(name s6 ,(bound)) h L0)))
(disunify* '() `(name a2 ,(bound)) `(name s6 ,(bound)) h L0)))
(define (make-eqs eqs)
(for/hash ([eq eqs])
(values (car eq) (cdr eq))))
(define (make-dqs dqs)
(for/list ([dq dqs])
(list (car dq) (cdr dq))))
(for/list ([the-dq dqs])
(dq '() (list (car the-dq) (cdr the-dq)))))
(define-syntax (test-disunify stx)
(define-syntax (test-disunify/no-params stx)
(syntax-case stx ()
[(_ t u eqs dqs eqs dqs)
(quasisyntax/loc stx
@ -916,7 +921,7 @@
[eqs-out (make-eqs eqs)]
[dqs-in (check-and-resimplify eqs-in (make-dqs dqs) L0)]
[dqs-out (make-dqs dqs)]
[res (disunify t u (env eqs-in dqs-in) L0)])
[res (disunify '() t u (env eqs-in dqs-in) L0)])
#,(syntax/loc stx
(check-not-false
(env-equal?
@ -926,7 +931,7 @@
(quasisyntax/loc stx
(let* ([eqs-in (make-eqs eqs)]
[dqs-in (check-and-resimplify eqs (make-dqs dqs) L0)]
[res (disunify t u (env eqs-in dqs-in) L0)])
[res (disunify '() t u (env eqs-in dqs-in) L0)])
(if true/false
#,(syntax/loc stx (check-not-false res))
#,(syntax/loc stx (check-false res)))))]))
@ -953,33 +958,33 @@
(check-not-false res)
(check-false res))))]))
(test-disunify 1 2 '() '() '() '())
(test-disunify '(list 1 2) '(list 1 3) '() '() '() '())
(test-disunify `(list (name x any) (name y any)) `(list 1)
(test-disunify/no-params 1 2 '() '() '() '())
(test-disunify/no-params '(list 1 2) '(list 1 3) '() '() '() '())
(test-disunify/no-params `(list (name x any) (name y any)) `(list 1)
`((,(lvar 'x) . any)
(,(lvar 'y) . any))
'()
`((,(lvar 'x) . any)
(,(lvar 'y) . any))
'())
(test-disunify `(name x any) 4
(test-disunify/no-params `(name x any) 4
`((,(lvar 'x) . ,(lvar 'y))
(,(lvar 'y) . 3))
'()
`((,(lvar 'x) . ,(lvar 'y))
(,(lvar 'y) . 3))
'())
(test-disunify `(name x any) 1
(test-disunify/no-params `(name x any) 1
`((,(lvar 'x) . 1)) '() #f)
(test-disunify 1 `(name x any)
(test-disunify/no-params 1 `(name x any)
'() '() `((,(lvar 'x) . any))
`(((list (name x ,(bound))) . (list 1))))
(test-disunify `(name x any) 3
(test-disunify/no-params `(name x any) 3
`((,(lvar 'x) . ,(lvar 'y))
(,(lvar 'y) . 3))
'() #f)
;; does violate the occurs check (stolen from Colmerauer '84)
(test-disunify 1 `(name z any)
(test-disunify/no-params 1 `(name z any)
`((,(lvar 'x) . ,(lvar 'y))
(,(lvar 'y) . (list (name y ,(bound)) (name z ,(bound))))
(,(lvar 'z) . any))