;;;; -*- Mode: Lisp; Syntax: Common-Lisp -*-
;;;; Code from Paradigms of AI Programming
;;;; Copyright (c) 1991 Peter Norvig
;;;; File prologcp.lisp: Primitives for the prolog compiler
;;;; needed to actually run some functions.
(in-package "USER")
(requires "prologc")
(defun read/1 (exp cont)
(if (unify! exp (read))
(funcall cont)))
(defun write/1 (exp cont)
(write (deref-exp exp) :pretty t)
(funcall cont))
(defun nl/0 (cont) (terpri) (funcall cont))
(defun =/2 (?arg1 ?arg2 cont)
(if (unify! ?arg1 ?arg2)
(funcall cont)))
(defun ==/2 (?arg1 ?arg2 cont)
"Are the two arguments EQUAL with no unification,
but with dereferencing? If so, succeed."
(if (deref-equal ?arg1 ?arg2)
(funcall cont)))
(defun deref-equal (x y)
"Are the two arguments EQUAL with no unification,
but with dereferencing?"
(or (eql (deref x) (deref y))
(and (consp x)
(consp y)
(deref-equal (first x) (first y))
(deref-equal (rest x) (rest y)))))
(defun call/1 (goal cont)
"Try to prove goal by calling it."
(deref goal)
(apply (make-predicate (first goal)
(length (args goal)))
(append (args goal) (list cont))))
(<- (or ?a ?b) (call ?a))
(<- (or ?a ?b) (call ?b))
(<- (and ?a ?b) (call ?a) (call ?b))
(defmacro with-undo-bindings (&body body)
"Undo bindings after each expression in body except the last."
(if (length=1 body)
(first body)
`(let ((old-trail (fill-pointer *trail*)))
,(first body)
,@(loop for exp in (rest body)
collect '(undo-bindings! old-trail)
collect exp))))
(defun not/1 (relation cont)
"Negation by failure: If you can't prove G, then (not G) true."
;; Either way, undo the bindings.
(with-undo-bindings
(call/1 relation #'(lambda () (return-from not/1 nil)))
(funcall cont)))
(defun bagof/3 (exp goal result cont)
"Find all solutions to GOAL, and for each solution,
collect the value of EXP into the list RESULT."
;; Ex: Assume (p 1) (p 2) (p 3). Then:
;; (bagof ?x (p ?x) ?l) ==> ?l = (1 2 3)
(let ((answers nil))
(call/1 goal #'(lambda ()
(push (deref-copy exp) answers)))
(if (and (not (null answers))
(unify! result (nreverse answers)))
(funcall cont))))
(defun deref-copy (exp)
"Copy the expression, replacing variables with new ones.
The part without variables can be returned as is."
(sublis (mapcar #'(lambda (var) (cons var (?)))
(unique-find-anywhere-if #'var-p exp))
exp))
(defun setof/3 (exp goal result cont)
"Find all unique solutions to GOAL, and for each solution,
collect the value of EXP into the list RESULT."
;; Ex: Assume (p 1) (p 2) (p 3). Then:
;; (setof ?x (p ?x) ?l) ==> ?l = (1 2 3)
(let ((answers nil))
(call/1 goal #'(lambda ()
(push (deref-copy exp) answers)))
(if (and (not (null answers))
(unify! result (delete-duplicates
answers
:test #'deref-equal)))
(funcall cont))))
(defun is/2 (var exp cont)
;; Example: (is ?x (+ 3 (* ?y (+ ?z 4))))
;; Or even: (is (?x ?y ?x) (cons (first ?z) ?l))
(if (and (not (find-if-anywhere #'unbound-var-p exp))
(unify! var (eval (deref-exp exp))))
(funcall cont)))
(defun unbound-var-p (exp)
"Is EXP an unbound var?"
(and (var-p exp) (not (bound-p exp))))
(defun var/1 (?arg1 cont)
"Succeeds if ?arg1 is an uninstantiated variable."
(if (unbound-var-p ?arg1)
(funcall cont)))
(defun lisp/2 (?result exp cont)
"Apply (first exp) to (rest exp), and return the result."
(if (and (consp (deref exp))
(unify! ?result (apply (first exp) (rest exp))))
(funcall cont)))
(defun repeat/0 (cont)
(loop (funcall cont)))
(<- (if ?test ?then) (if ?then ?else (fail)))
(<- (if ?test ?then ?else)
(call ?test)
!
(call ?then))
(<- (if ?test ?then ?else)
(call ?else))
(<- (member ?item (?item . ?rest)))
(<- (member ?item (?x . ?rest)) (member ?item ?rest))
(<- (length () 0))
(<- (length (?x . ?y) (1+ ?n)) (length ?y ?n))
(defun numberp/1 (x cont)
(when (numberp (deref x))
(funcall cont)))
(defun atom/1 (x cont)
(when (atom (deref x))
(funcall cont)))