racket/collects/datalog/runtime.rkt
2010-08-13 15:06:13 -06:00

152 lines
5.1 KiB
Racket

#lang racket
(require "ast.rkt"
"private/env.rkt"
"private/subst.rkt"
"private/unify.rkt"
"private/variant.rkt")
; A clause is safe if every variable in its head occurs in some literal in its body.
(define (safe-clause? c)
(define head-vars (filter variable? (literal-terms (clause-head c))))
(andmap (lambda (v)
(ormap (lambda (l)
(ormap (lambda (t) (term-equal? t v))
(cond
[(literal? l)
(literal-terms l)]
[(external? l)
(append (external-arg-terms l)
(external-ans-terms l))])))
(clause-body c)))
head-vars))
(define theory/c (and/c hash? (not/c immutable?)))
(define (literal-key l)
(format "~a/~a" (literal-predicate l) (length (literal-terms l))))
(define (clause-key c)
(literal-key (clause-head c)))
(define (make-theory)
(make-hash))
(define ((mk-assume hash-update) thy c)
(hash-update
thy (clause-key c)
(lambda (clauses)
(list* c clauses))
empty))
(define ((mk-retract hash-update) thy rc)
(hash-update
thy (clause-key rc)
(lambda (clauses)
(filter (lambda (c)
(not (clause-equal? c rc)))
clauses))
empty))
(define assume (mk-assume hash-update))
(define retract (mk-retract hash-update))
(define assume! (mk-assume hash-update!))
(define retract! (mk-retract hash-update!))
(define (get thy lit)
(hash-ref thy (literal-key lit) empty))
(define-struct subgoal
(question
[facts #:mutable]
[waiters #:mutable]))
(define (resolve c q)
(define body (clause-body c))
(and (not (empty? body))
(cond
[(unify (first body) (rename-question q))
=> (lambda (env)
(subst-clause env (make-clause (clause-srcloc c) (clause-head c) (rest body))))]
[else #f])))
(define (prove thy q)
(define subgoals (make-literal-tbl))
(define (fact! sg lit)
(unless (mem-literal lit (subgoal-facts sg))
(set-subgoal-facts! sg (list* lit (subgoal-facts sg)))
(for-each (lambda (w)
(cond
[(resolve (cdr w) lit)
=> (lambda (cs-p) (add-clause! (car w) cs-p))]))
(subgoal-waiters sg))))
(define (rule! sg1 c s)
(define sg2 (literal-tbl-find subgoals s))
(if sg2
(begin
(set-subgoal-waiters! sg2 (list* (cons sg1 c) (subgoal-waiters sg2)))
(for-each (lambda (fact)
(cond
[(resolve c fact)
=> (lambda (cs) (add-clause! sg1 cs))]))
(subgoal-facts sg2)))
(let ([sg2 (make-subgoal s empty (list (cons sg1 c)))])
(literal-tbl-replace! subgoals s sg2)
(search! sg2))))
(define (add-clause! sg c)
(match c
[(struct clause (_ lit (list)))
(fact! sg lit)]
[(struct clause (_ _ (list-rest selected _)))
(rule! sg c selected)]))
(define (search-theory! sg)
(for-each
(lambda (clause)
(define renamed (rename-clause clause))
(define selected (clause-head renamed))
(cond
[(unify (subgoal-question sg) selected)
=> (lambda (env)
(add-clause! sg (subst-clause env renamed)))]))
(get thy (subgoal-question sg))))
(define (search! sg)
(match (subgoal-question sg)
[(external srcloc pred-sym pred args anss)
(and (andmap constant? args)
(call-with-values
(λ ()
(apply pred (map constant-value args)))
(λ resolved-vals
(define resolved-anss
(map (curry constant #f)
resolved-vals))
(cond
[(unify-terms (empty-env) anss resolved-anss)
=> (λ (env)
(fact! sg (external srcloc pred-sym pred args (subst-terms env anss))))]))))]
[(struct literal (srcloc '= (list a b)))
(define (equal-test a b)
(when (term-equal? a b)
(fact! sg (make-literal srcloc '= (list a b)))))
(cond
[(unify-term (empty-env) a b)
=> (lambda (env) (equal-test (subst-term env a) (subst-term env b)))]
[else (equal-test a b)])]
[(struct literal (srcloc '!= (list a b)))
(define (equal-test a b)
(unless (term-equal? a b)
(fact! sg (make-literal srcloc '!= (list a b)))))
(cond
[(unify-term (empty-env) a b)
=> (lambda (env) (equal-test (subst-term env a) (subst-term env b)))]
[else (equal-test a b)])]
[_
(search-theory! sg)]))
(define sg (make-subgoal q empty empty))
(literal-tbl-replace! subgoals q sg)
(search! sg)
(subgoal-facts sg))
(provide/contract
[safe-clause? (clause? . -> . boolean?)]
[theory/c contract?]
[make-theory (-> theory/c)]
[assume! (theory/c safe-clause? . -> . void)]
[retract! (theory/c clause? . -> . void)]
[prove (theory/c question/c . -> . (listof question/c))])