;;; -*- Mode: Lisp; Package: Prolog; Base: 10. ; -*-
;;; (C) Copyright 1983,1984,1985, Uppsala University

;;This defines the multiple worlds where predicates live in LM-Prolog

(defmacro define-predicate (predicator &body options+clauses)
  ;;options are a list of options beginning with the symbol "options"
  ;;if nothing is there same as if the option list were (options)
  (cond ((null options+clauses)
         (make-definition predicator nil nil))
        ((eq (first (first options+clauses)) ':options)
         (make-definition predicator (first options+clauses)
                          (rest1 options+clauses)))
        (t (make-definition predicator nil options+clauses))))

(defvar *predicator-being-defined*)

(defvar *file-default-options* ()
  "The options default for a specific file. May contain :MULTIPLE.")

(defvar *eval-when*)

(defvar *default-options*
        '(options (:type :static)
		  (:world :user)
;;never used      (:if-unique-variable :warn)
		  (:if-another-definition :ignore)
;;obsolete	  (:execution :interpreted)
		  (:place-clauses :anywhere)
		  (:if-old-definition :flush)
		  (:compile-method :closed)
;;obsolete	  (:save-compiled-code nil)
		  (:VARIABLE-SYNTAX "?")
		  (:lisp-macro-name nil)
		  (:deterministic :sometimes)
		  (:argument-list (&rest :|No argument list provided|))
		  (:documentation "is not documented."))
  "This is the default options for DEFINE-PREDICATE.  All options must be defaulted here.")

(eval-when (compile load eval)
(defprop :type second value-option-selector)

(defprop :variable-syntax second value-option-selector)

(defprop :world second value-option-selector)

(defprop :indexing-patterns rest1 value-option-selector)

;;(defprop :if-unique-variable second value-option-selector)

;;currently can be either :flush or :keep
;; :query and :error to come
(defprop :if-old-definition second value-option-selector)

(defprop :if-another-definition second value-option-selector)

;;(defprop :execution second value-option-selector)

(defprop :deterministic second value-option-selector)

(defprop :place-clauses second value-option-selector)

(defprop :compile-method rest1 value-option-selector)

(defprop :documentation second value-option-selector)

;;(defprop :save-compiled-code second value-option-selector) ;;for old time's sake

(defprop :argument-list second value-option-selector)

(defprop :lisp-macro-name second value-option-selector)

(defprop :for-partial-prolog second value-option-selector)
)

(defun merge-options (options defaults)
  (cons ':options (merge-options-1 (rest1 options) (rest1 defaults))))

(defun merge-options-1 (options-body defaults-body)
  (append options-body (alist-difference defaults-body options-body)))

(deffun alist-difference (options exceptions)
  (cond ((not (null options))
         (cond ((and (neq (first (first options)) ':multiple)
		     (assq (first (first options)) exceptions))
                (alist-difference (rest1 options) exceptions))
               (t (cons (first options)
                        (alist-difference (rest1 options) exceptions)))))))

(defun disjunctive-normal-form (options)
  (let ((all (disjunctive-normal-form-1
	       (merge-options options *file-default-options*)))
	unique)
    (mapc #'(lambda (x) (push-if-not-member x unique)) all)
    (nreverse unique)))

;;We want a list ((option option ...) (option option ...) ...).
(defun disjunctive-normal-form-1 (spec)
  (cond (spec
	 (selectq (car spec)
	   (:options
	    (apply #'cartesian (mapcar #'disjunctive-normal-form-1 (cdr spec))))
	   (:multiple (mapcan #'disjunctive-normal-form-1 (cdr spec)))
	   (t (ncons (ncons spec)))))
	(t (ncons spec)) ;;the spec () is taken as an empty conjunction
	))

(defun cartesian (&rest disjunctions)
  (let (*conses-alist*) (cartesian-1 () disjunctions) (nreverse *conses-alist*)))

(defun cartesian-1 (item disjunctions)
  (cond ((null disjunctions) (push item *conses-alist*))
	(t (mapc #'(lambda (x) (cartesian-1 (merge-options-1 item x) (cdr disjunctions)))
		 (car disjunctions)))))

(defun step-on ()
  (setf #'find-and-cache-first-definition #'find-and-cache-first-definition-step)
  (or (memq 'step *universe*) (push 'step *universe*)))

(defun step-off ()
  (setf #'find-and-cache-first-definition #'find-and-cache-first-definition-nostep)
  (cond ((eq 'step (car *universe*)) (pop *universe*))
	(t (setq *universe* (delq 'step *universe*)))))


(deffun find-and-cache-first-definition-step
        (definitions worlds &optional predicator-or-no-error)
  (let ((current-definition (find-first-definition (rest1 definitions) worlds)))
    (cond ((not (null current-definition))
	   (let ((world (option-value ':world (definition-options current-definition))))
	     (cond ((eq world 'traced-predicates)
		    (find-and-cache-first-definition-step
		      definitions (cdr worlds) predicator-or-no-error))
		   (t ;;Return a definition of DO-STEP in :SYSTEM
		      ;;with its prover closing over the real definition.
		      ;;NB. The prover lives in two slots for interpreter's sake.
		    (let* ((d (definition-in-world 'do-step ':system))
			   (prover (let-closed ((*definition* current-definition)) (car d))))
			(prolog-list* prover
				      (prolog-list nil prover (definition-clauses d))
				      (cddr d)))))))
          (predicator-or-no-error
           (undefined-predicate predicator-or-no-error worlds)))))

; The intention of this advice is to have in-buffer compilation obey microcompile declarations.
; FROM FILE SRC:ZWEI;COMC
#-symbolics #8R zwei:
(progn 'compile
(DEFUN MC-DEFUN-P (FORM)
  (AND (CONSP FORM)
       (CONSP (CDR FORM))
       (EQ (CAR FORM) 'DEFUN)
       (GETDECL (CADR FORM) 'COMPILER:MICROCOMPILE)))

(ADVISE COMPILE-BUFFER-FORM :BEFORE OBEY-MICROCOMPILE-DECLARATION 0
  (DECLARE (SPECIAL COMPILE-PROCESSING-MODE))
  (COND ((MC-DEFUN-P (CAR ARGLIST)) (SETQ COMPILE-PROCESSING-MODE 'COMPILER:MICRO-COMPILE))))

(COMPILE-ENCAPSULATIONS 'COMPILE-BUFFER-FORM)
)

(defun make-definition (predicator options lisp-clauses)
  (STEP-OFF)					;In case we aborted out of a computation
						;and then went to the editor.
  `(progn 'compile
	  ,@(mapcan #'(lambda (x) (make-definition-1 predicator	;8.2
						     (cons ':options x)
						     lisp-clauses))
		    (disjunctive-normal-form options))))

(defun forward-value-cell-safe (from to)
  (or (eq (follow-cell-forwarding (value-cell-location from) t)
	  (follow-cell-forwarding (value-cell-location to) t))
      (si:forward-value-cell from to)))

(forward-value-cell-safe 'compile-p
			 #-symbolics 'zwei:compile-p
			 #+symbolics 'compiler:compiler-warnings-context)

(defvar compile-p nil "This is T only if macro-expanding for the compiler.")

#-Symbolics
(forward-value-cell-safe 'compile-processing-mode 'zwei:compile-processing-mode)

#-Symbolics
(defvar compile-processing-mode nil "This is T only if macro-expanding for the compiler.")

(defun make-definition-1 (predicator options lisp-clauses 
			  &aux compilers-forms 
			  prover interpreters-forms)
  (warn-if-unrecognized-option (rest1 options) predicator)
  #-Symbolics
  (cond ((and (eq compile-processing-mode 'compiler:micro-compile)
	      (not (option-value ':compile-method options ())))
	 (setq options (merge-options '(:options (:compile-method :micro-compiled))
				      options))))
  (let* ((indexing-patterns (option-value ':indexing-patterns options))
         (explicit-world (option-value ':world options ()))
         (world (option-value ':world options))
         (*eval-when*
           (cond ((qc-file-in-progress) '(compile load eval))
                 (t '(load eval))))
         (execution
	   (SELECTQ (CAR (OPTION-VALUE ':COMPILE-METHOD OPTIONS))
	     ((:INTRINSIC :MICRO-COMPILED) ':COMPILED)	;convenience
	     (T (COND ((OR (QC-FILE-IN-PROGRESS) COMPILE-P) ':COMPILED)
		      (T ':INTERPRETED)))))
         (*contains-cut* nil)
         (make-indexes-form ;;;this adds to the guy above
           (and indexing-patterns
		(make-indexes-form predicator world indexing-patterns)))
         #+symbolics (copy-of-clauses (cond ((qc-file-in-progress)
                                             (copytree lisp-clauses))
                                            (t lisp-clauses)))
	 ;;some strange bug in dumping out lisp-clauses (they've been clobbered)
	 )
    (LET* ((clauses (make-clauses #-symbolics lisp-clauses
				  #+symbolics copy-of-clauses
				  predicator world
				  (option-value ':variable-syntax options)))	;8.2
	   )
      (selectq (option-value ':if-old-definition options)
        (:flush         
         (selectq execution
           (:interpreted
            (multiple-value (prover interpreters-forms)
              (make-interpreter-definition predicator options indexing-patterns
					   NIL)))
           (:compiled
            (multiple-value (prover compilers-forms)
              (make-compiler-definition predicator options clauses indexing-patterns)))
           (:never (setq prover ''predicate-declared-to-never-be-executed)))
         `((eval-when ,*eval-when*
	     (setq *definition*
		   (create-definition
		     ,prover
		     ',(eq (option-value ':deterministic options) ':always)
		     ',(cond (indexing-patterns 'add-and-index-clause)
			     (t (selectq (option-value ':type options)
				  ((:dynamic) 'add-clause)
				  (otherwise 'error-predicate-is-static))))
		     ',predicator
		     ',(merge-options options *default-options*)
		     ,(cond ((qc-file-in-progress)
			     `(let ((*contains-cut* ',*contains-cut*)
				    (*warn-if-variable-occurs-once* nil))
				(make-clauses ',lisp-clauses ',predicator ',world	;8.2
					      ',(option-value ':variable-syntax options))))
			    (t `',clauses))
		     ,(and (eq execution ':interpreted)
			   (not *contains-cut*)
			   (eq ':ignore (option-value ':if-another-definition options))
			   (null indexing-patterns))))
	     (add-to-world ',predicator *definition*))
	   ,@(cond (indexing-patterns
		    `(,@(cdr make-indexes-form)
		      (setf (definition-indexes *definition*) ,(car make-indexes-form))
		      (add-each-to-indexes (definition-clauses *definition*)
					   (definition-indexes *definition*)
					   ',(option-value ':place-clauses options)))))
	   ,@interpreters-forms
	   ,@compilers-forms
	   ,@(let ((macro-name (option-value ':lisp-macro-name options)))
	       (cond ((not (null macro-name))
		      `((defmacro ,macro-name (&rest arguments)
			  (list 'query-once
				(list 'quote (cons ',predicator arguments))
				'':lisp-term nil))))))
	   ,@(let ((functions
		     (union ()
			    (functions-defined-in-forms
			      `((progn 'compile ,@interpreters-forms)
				(progn 'compile ,@compilers-forms)
				(progn 'compile ,@(cdr make-indexes-form)))
			      ()))))
	       (cond (functions `((mapc #'compile-if-need-be ',functions)))))
	   ,@(ncons '(UPDATE-PROVER))	;Since this is used in a MAPCAN.
	   ))
      (:keep
       (selectq execution
	 (:compiled
	  (multiple-value (prover compilers-forms)
              (make-compiler-definition predicator options clauses indexing-patterns))))
       `((setq *definition*
	       ,(cond (explicit-world
		       `(definition-in-world ',predicator ',explicit-world))
		      (t `(current-definition ',predicator nil))))
	 (revise-definition ',predicator ',options ',lisp-clauses)
	 ,@compilers-forms
	 ',predicator))
      (otherwise
       (prolog-ferror ':bad-define-option "~s should be either :KEEP or :FLUSH"
	       (option-value ':if-old-definition options)))))))

(defun compile-if-need-be (function-spec)
  (cond ((not (qc-file-in-progress))
         (let ((definition (AND (FBOUNDP FUNCTION-SPEC) (FSYMEVAL FUNCTION-SPEC))))
           (cond ((consp definition) (with-who-line "Compile" (compile function-spec))))))))

(deffun functions-defined-in-forms (forms sofar)
  (cond ((null forms) sofar)
	((memq (first (first forms)) '(defun deffun deffsubst))
	 (functions-defined-in-forms (cdr forms) (cons (cadar forms) sofar)))
	((or (EQ (CAAR FORMS) 'LOCAL-DECLARE)
	     (and (eq (caar forms) 'progn) (equal (cadar forms) ''compile)))
	 (FUNCTIONS-DEFINED-IN-FORMS (CDR FORMS)
				     (FUNCTIONS-DEFINED-IN-FORMS (CDDAR FORMS) sofar)))
	(t (functions-defined-in-forms (cdr forms) sofar))))

;;This really returns two values: a prover-form and a list of additional forms.
;;The second value is always NIL as of release 8.0.
(defun make-interpreter-definition (IGNORE options indexing-patterns ignore)
  (let ((default-name
	  (intern (STRING-APPEND
		    "PROVER"
		    (cond (indexing-patterns "-INDEXED") (t ""))
		    (cond ((eq (option-value ':deterministic options) ':always) "-DET")
			  (t ""))
		    (cond ((or *contains-cut*
			       (eq (option-value ':type options) ':dynamic))
			   "-DYNAMIC")
			  (t ""))
		    "-IGNORE")
		  "PROLOG")))
    `(let-closed (*current-clauses* ,@(cond (indexing-patterns `(*current-indexes*))))
       #',default-name)))

(defvar *current-clauses*)

(defvar *current-indexes*)

(eval-when (compile eval)
(defvar *options-det* '(:options (:deterministic :always)))

(defmacro define-provers (if-another-definition)
  (let (*contains-cut*
	(*default-options* `(:options (:if-another-definition ,if-another-definition))))
    `(progn 'compile
	    ,(interpreter-prover-defun
	       (prolog-intern "PROVER~A-~A" "" if-another-definition)
	       '*current-clauses* nil nil nil nil)
	    ,(interpreter-prover-defun
	       (prolog-intern "PROVER~A-~A" '-indexed if-another-definition)
	       '*current-clauses* '*current-indexes* nil nil nil)
	    ,(interpreter-prover-defun
	       (prolog-intern "PROVER~A-~A" '-det if-another-definition)
	       '*current-clauses* nil *options-det* nil nil)
	    ,(interpreter-prover-defun
	       (prolog-intern "PROVER~A-~A" '-indexed-det if-another-definition)
	       '*current-clauses* '*current-indexes* *options-det* nil nil)
	    ,@(prog1 nil (setq *contains-cut* t))
	    ,(interpreter-prover-defun
	       (prolog-intern "PROVER~A-~A" '-dynamic if-another-definition)
	       '*current-clauses* nil nil nil nil)
	    ,(interpreter-prover-defun
	       (prolog-intern "PROVER~A-~A" '-indexed-dynamic if-another-definition)
	       '*current-clauses* '*current-indexes* nil nil nil)
	    ,(interpreter-prover-defun
	       (prolog-intern "PROVER~A-~A" '-det-dynamic if-another-definition)
	       '*current-clauses* nil *options-det* nil nil)
	    ,(interpreter-prover-defun
	       (prolog-intern "PROVER~A-~A" '-indexed-det-dynamic if-another-definition)
	       '*current-clauses* '*current-indexes* *options-det* nil nil)
	    )))

(defun interpreter-prover-defun (prover-name clauses-name indexes-name options IGNORE IGNORE)
  `(defun ,prover-name (continuation &rest arguments)
     ,(cond (*unsafe-rest-argument* `(setq arguments (rest-arg-copy arguments)))
	    (t `(cond ((consp arguments) (rest-arg-fixup-loop arguments)))))
     ,(let* ((DETERMINACY (eq (option-value ':deterministic options) ':always))
	     (BODY-1 `(cond ((contents ,clauses-name)
			     (,@(cond (indexes-name `(try-indexed ,indexes-name))
				      (t `(try-each)))
			       (contents ,clauses-name)
			       ,(COND (DETERMINACY '(CONTINUATION (TRUE)))
				      (T 'CONTINUATION))
			       arguments
			       *trail*))))
	     (BODY-2
	       (COND ((or *contains-cut*
			  (eq (option-value ':type options) ':dynamic))
		      `(LET ((*CUT-TAG* CONTINUATION)) ;;anything unique
			 (*CATCH *CUT-TAG* ,BODY-1)))
		     (T BODY-1))))
	(cond (DETERMINACY `(COND (,BODY-2 (INVOKE CONTINUATION))))
	      ((or *contains-cut* (eq (option-value ':type options) ':dynamic))
	       #-LEXICAL
	       `(let-if
		  (not (memq (first continuation) '(true false invoke-continuation)))
		  ((continuation
		     (CONTINUATION
		       (FUNCALL 'INVOKE-CONTINUATION CONTINUATION *CUT-TAG*))))
		  ,BODY-2)
	       #+lexical
	       `(LET* ((TEMP-TAG *CUT-TAG*)
		       (CONTINUATION
			 (CONTINUATION
			   (INVOKE-CONTINUATION CONTINUATION TEMP-TAG))))
		  ,body-2))
	      (t BODY-2)))))

)

(DEFINE-PROVERS :ignore)

;;(define-provers :try-before) LOSES.
;;(define-provers :try-after) LOSES.

;;A prover for a :IF-ANOTHER-DEFINITION :TRY-BEFORE is a closure of this,
;;closing over *C-DEFINITION* and *C-PROVER*.
(defun try-another-definition-before (&rest arguments)
  (declare (special *c-prover* *C-DEFINITION*))
  (let* ((another-definition
	   (another-definition
	     (definition-predicator *C-DEFINITION*)
	     (option-value ':world (definition-options *C-DEFINITION*))
	     *universe* ()))
	 (mark *trail*))
    (cond ((and another-definition
		(apply (definition-prover another-definition) arguments)))
	  ((untrail mark))
	  ((apply *c-prover* arguments)))))

;;A prover for a :IF-ANOTHER-DEFINITION :TRY-AFTER is a closure of this,
;;closing over *C-DEFINITION* and *C-PROVER*.
(defun try-another-definition-after (&rest arguments)
  (declare (special *c-prover* *C-DEFINITION*))
  (let* ((another-definition
	   (another-definition
	     (definition-predicator *C-DEFINITION*)
	     (option-value ':world (definition-options *C-DEFINITION*))
	     *universe* ()))
	 (mark *trail*))
    (cond ((apply *c-prover* arguments))
	  ((untrail mark))
	  ((and another-definition
		(apply (definition-prover another-definition) arguments))))))

;;This finds a definition ``after'' the one in WORLD, or NIL of there is none.
(deffun another-definition (predicator world universe seen-before)
  (cond ((null universe) nil)
	((null world)
	 (cond ((memq (first universe) seen-before)
		(another-definition predicator nil (rest1 universe) seen-before))
	       ((definition-in-world predicator (first universe)))
	       (t
		(another-definition predicator nil (rest1 universe) seen-before))))
	((eq world (first universe))
	 (another-definition predicator nil
			     (rest1 universe)
			     (prolog-cons world seen-before)))
	(t
	 (another-definition predicator world
			     (rest1 universe)
			     (prolog-cons (first universe) seen-before)))))

(deffun make-indexes-form (predicator world indexing-patterns &optional defuns)
  (cond (indexing-patterns
	 (let* ((selector
		  `(keyify ,(index-selector-form (cdar indexing-patterns) 'x)))
		(defun
		  `(defun
		     ,(prolog-db-symbol predicator world
					(prolog-string "SELECTOR-~D"
						       (length defuns)))
		     (x) ,selector)))
	   (make-indexes-form
	     predicator world (cdr indexing-patterns) (cons defun defuns))))
	(t (let ((defuns (nreverse defuns)))
	     `((list-in-area *structure-area*
                 ,@(mapcar #'(lambda (defun)
                               `(create-index
				  ',(cadr defun)
                                  (make-hash-table ':area *structure-area*)))
                           defuns))
	       ,@defuns)))))

(defmacro add-clause-macro (clause access where &optional (clauses access))
  ;;are the callers of this careful about what area the consing happens?
  `(let ((clauses ,clauses))
     (selectq ,where
       ((:anywhere :before)
        (setf ,access (cons-in-area ,clause clauses *structure-area*)))
       (:after (setf ,access
		     (let ((default-cons-area *structure-area*))
		       (append clauses (list ,clause)))))
       (otherwise (prolog-ferror ':bad-place-clause-option
				 "~s should be either :anywhere, :before, or :after"
			  ,where)))))

(deffun add-each-to-indexes (clauses indexes where)
  ;;this adds them in the reverse order of clauses
  (cond (clauses
         (add-each-to-indexes (rest1 clauses) indexes where)
         (add-to-indexes (first clauses) indexes where))))

(defvar *clauses*)

(defun add-to-indexes (clause indexes where)
  (declare (special clause where))
  (do-to-each-index indexes
                    (%construct (first (send clause ':templates)))
                    (closure '(clause where)
                             #'(lambda (key table value)
                                 (add-clause-macro clause
                                                   (gethash key table)
                                                   where
                                                   value)))))

(defvar *definition* nil "The current definition, both for new and revised ones.")

(defun define-predicate-1 (predicator options clauses)
  (let ((*warn-if-variable-occurs-once* nil))	;Lars-Henrik proposed this
    (mapc #'(lambda (x)
	      (define-predicate-2 predicator (prolog-cons ':options x) clauses))
	  (disjunctive-normal-form (copytree options)))))

(defun define-predicate-2 (predicator options clauses)
  (cond ((eq (option-value ':if-old-definition options) ':keep)
	 (let* ((explicit-world (option-value ':world options ()))
		(*definition*
		  (cond (explicit-world
			 (definition-in-world predicator explicit-world))
			(t (current-definition predicator nil)))))
	   (revise-definition predicator options clauses)))
        (t (mapc #'eval (make-definition-1 predicator options clauses)))))

(defun compile-definition (definition)
  (let ((compile-p t)
	(*warn-if-variable-occurs-once* nil)
	(*options* (definition-options definition))
	(*predicator-being-defined* (definition-predicator definition)))
    (selectq (option-value ':type *options*)
      (:static
       (let ((prover (selectq (option-value ':if-another-definition *options*)
		       (:ignore (definition-prover definition))
		       (t (closure-function (definition-prover definition))))))
	 (cond ((closurep prover)		;then it's interpreted
		(mapc #'eval (make-definition-1 *predicator-being-defined*
						*options*
						(definition-clauses definition)))))))
      (:dynamic
       (mapc #'(lambda (clause)
		 (let ((loc (locate-in-instance clause 'prover)))
		   (cond ((consp (cdr loc))	;then it's interpreted
			  (location-makunbound loc)
			  (unwind-protect
			    (with-who-line "Compile"
			      (compile `(:location ,loc) (compile-clause clause)))
			    (cond ((not (location-boundp loc))
				   (rplacd loc (send clause ':templates)))))))))
	     (definition-clauses definition))))))

;;This installs clauses and indexes in the closure of interpreted provers,
;;and installs :IF-ANOTHER-DEFINITION "encapsulation" if need be.
(defun update-prover ()
  (let ((closure (definition-prover *definition*))
	(clauses (locf (definition-clauses *definition*)))
	(indexes (definition-indexes *definition*)))
    (cond ((closurep closure)
	   (set-in-closure closure '*current-clauses* clauses)
	   (cond (indexes (set-in-closure closure '*current-indexes* indexes)))))
    (selectq (option-value ':if-another-definition (definition-options *definition*))
      (:try-before
       (let ((encapsulation (let-closed ((*c-prover* closure) (*c-definition* *definition*))
			      #'try-another-definition-before)))
	 (setf (definition-prover *definition*) encapsulation)
	 (setf (interpreter-slot-prover (definition-interpreter-slot *definition*))
	       encapsulation)))
      (:try-after
       (let ((encapsulation (let-closed ((*c-prover* closure) (*c-definition* *definition*))
			      #'try-another-definition-after)))
	 (setf (definition-prover *definition*) encapsulation)
	 (setf (interpreter-slot-prover (definition-interpreter-slot *definition*))
	       encapsulation)))))
  (definition-predicator *definition*))

(defun revise-definition (predicator options clauses)
  (let* ((*contains-cut* nil)
	 (*warn-if-variable-occurs-once* nil)	;Warnings have already been issued.
         (world (option-value ':world options (definition-options *definition*))))
    (cond ((null *definition*)
           (mapc #'eval
		 (make-definition-1 predicator
				    (merge-options ':(options (if-old-definition flush))
						   options)
				    clauses)))
          (t (add-clauses clauses
                          *definition* 
                          (option-value ':place-clauses options
					(definition-options *definition*))
                          world)
             (cond (*contains-cut*
                    ;;this makes sure that the interpreter goes through the
                    ;;provers if a clause with a cut has been added
                    (setf (interpreter-slot-prover
                            (definition-interpreter-slot *definition*))
                          (definition-prover *definition*))))))))

(defun revise-clause (prover-spec clauses-location clause/# out-of options)
  "Installs (FDEFINITION PROVER-SPEC) as a clause's prover."
  (let ((clauses (contents clauses-location)))
    (compile-if-need-be prover-spec)
    ;;A crude way of avoiding FDEFINE warnings...
    (remprop prover-spec ':source-file-name)
    (send (cond ((symbolp clause/#) ;;actually the name of a named clause.
		 (first (mem #'(lambda (name clause) (eq name (send clause ':name)))
			     clause/# clauses)))
		(t ;;CLAUSE/#-th out of OUT-OF clauses
		 (nth (selectq
			(option-value ':place-clauses
				      options
				      (definition-options *definition*))
			(:after (+ clause/# (- out-of) (length clauses)))
			(otherwise clause/#))
		      clauses)))
	  ':set-prover
	  (fdefinition prover-spec))))

(defun remove-clause-named (name definition)
  (remove-clause
    (first
      (mem #'(lambda (name clause) (eq (send clause ':name) name))
           name
           (definition-clauses definition)))
    definition))

(defun remove-clause (clause definition)
  (declare (special clause)) ;;for the closure
  (cond (definition
         (setf (definition-clauses definition)
               (delq clause (definition-clauses definition) 1))
         (let ((indexes (definition-indexes definition)))
           (cond (indexes
                  (do-to-each-index
                    indexes
                    (%construct (first (send clause ':templates)))
                    (closure '(clause)
                             #'(lambda (key table value)
                                 (setf (gethash key table)
                                       (delq clause value 1)))))))))))

(defun remove-all-clauses (definition)
  (cond (definition
         (setf (definition-clauses definition) ())
         (mapc #'(lambda (index) (clrhash (index-table index)))
               (definition-indexes definition))
          t)))

(deffun add-clauses (clauses definition where world)
  (cond ((not (null clauses))
	 (cond ((neq where ':after)
		(add-clauses (rest1 clauses) definition where world)))
         (let ((clause
                 (make-clause (first clauses)
                              (definition-predicator definition)
                              world)))
           (or (replace-old-clause-of-same-name clause definition where)
               (funcall (definition-indexer definition)
                        clause definition where)))
	 (cond ((eq where ':after)
		(add-clauses (rest1 clauses) definition where world))))))

(defun replace-old-clause-of-same-name (new-clause definition where)
  (declare (special new-clause where)) ;;for the closures
  (let ((name (send new-clause ':name)))
    (cond ((symbolp name)    
           (let* ((clauses
                    (mem #'(lambda (name clause) (eq (send clause ':name) name))
                         name
                         (definition-clauses definition)))
                  (old-clause (first clauses)))
	     (declare (special old-clause))
             (cond (clauses
                    (setf (first clauses) new-clause)
                    (let ((indexes (definition-indexes definition)))
                      (cond (indexes
                             (do-to-each-index
                               indexes
                               (%construct (first (send old-clause ':templates)))
                               (closure '(new-clause old-clause)
                                        #'(lambda (ignore ignore value)
                                            (setf (first (memq old-clause value))
                                                  new-clause))))
                             (do-to-each-index
                               indexes
                               (%construct (first (send new-clause ':templates)))
                               (closure '(new-clause where)
                                        #'(lambda (key table value)
                                            (or (memq new-clause value)
                                                (add-clause-macro
                                                  new-clause
                                                  (gethash key table)
                                                  where
                                                  value))))))))
                    t)))))))

(deffun prolog-definition-of (predicator world)
  (let ((definition (definition-in-world predicator world)))
    (cond (definition 
           `(define-predicate ,(definition-predicator definition)
              ,(definition-options definition)
              ,@(definition-clauses definition))))))

(defun error-predicate-is-static (ignore definition ignore)
  (prolog-ferror ':assert-not-permitted "~s is not dynamic"
		 (definition-predicator definition)))

(defun error-predicate-is-primitive (ignore definition ignore)
  (prolog-ferror ':assert-not-permitted "~s is primitive"
		 (definition-predicator definition)))

(deffun make-clauses (lisp-clauses *predicator-being-defined* world
				   &optional (prefix *variable-prefix*))	;8.2
  (let-if (qc-file-in-progress) ((*structure-area* default-cons-area))
    (let* ((default-cons-area *structure-area*)
           (clauses (make-list (length lisp-clauses)))
           (IGNORE
	     (cond ((equal prefix *variable-prefix*)
		    (make-clauses-1
		      lisp-clauses clauses *predicator-being-defined* world))
		   (t (with-syntax-customized
			prefix
			(make-clauses-1
			  lisp-clauses clauses *predicator-being-defined* world))))))
      CLAUSES)))

(deffun make-clauses-1 (lisp-clauses clauses *predicator-being-defined* world)
 (cond ((null lisp-clauses) 'IGNORE)
       (t (LET ((CLAUSE
		  (make-clause
		    (first lisp-clauses) *predicator-being-defined* world)))
	    (setf (first clauses) clause)
            (make-clauses-1 (rest1 lisp-clauses) (rest1 clauses)
                            *predicator-being-defined* world)))))

(eval-when (compile load eval)

(defflavor clause (templates
		   (committing nil)
		   predicator
		   (name "No name")
		   contains-cut
		   (prover 'clause-prover-not-compiled)	;obsolete default.
		   world)
	     (prolog-flavor)
    :settable-instance-variables
    :initable-instance-variables
    :gettable-instance-variables)
)

(defmethod (clause :unify) (other)
  (cond ((symbolp name)
	 (cond ((and (consp other) (consp (rest1 other)) (consp (second other)))
		(with-new-*vector*
		  (and (unify name (first other))
		       (unify predicator (first (second other)))
		       (%unify-term-with-template (rest1 (second other))
						  (first templates))
		       (%unify-term-with-template (rest2 other)
						  (rest1 templates)))))
	       (t (unify other (send self ':ordinary-term)))))
	((and (consp other) (consp (first other)))
	 (with-new-*vector*
	   (and (unify predicator (first (first other)))
		(%unify-term-with-template (rest1 (first other))
					   (first templates))
		(%unify-term-with-template (rest1 other)
					   (rest1 templates)))))
	(t (unify other (send self ':ordinary-term)))))


;;Clauses are NOT reached by LISP-FORM, since the list of clauses
;;is in *structure-area*.
(defmethod (clause :print-self) (stream &rest ignore)
  (let ((x (send self ':ordinary-term)))
    ;;Recursive calls to GRIND-TOP-LEVEL are losing.
    (prin1 x stream)))

(defmethod (clause :ordinary-term) ()
  (let ((clause
	  (with-new-*vector*
	    (prolog-cons (prolog-cons predicator (%construct (first templates)))
			 (%construct (rest1 templates))))))
    (cond ((symbolp name) (prolog-cons name clause))
          (t clause))))

(defmethod (clause :query-user) () 'proceed) ;;don't ask user about converting this

(defmethod (clause :resolve) (continuation arguments)
  (funcall prover continuation arguments))

(defun make-clause (lisp-clause *predicator-being-defined* world)
  (cond ((and (typep lisp-clause 'clause) (eq world (send lisp-clause ':world)))
         LISP-CLAUSE)
        (t (multiple-value-bind (templates contains-cut name)
               (parse-clause lisp-clause)
             (setq *contains-cut* (or *contains-cut* contains-cut))
             (make-instance-in-area *structure-area*
	       'clause
	       ':PROVER TEMPLATES
	       ':templates templates
	       ':predicator *predicator-being-defined*
	       ':world world
	       ':contains-cut contains-cut
	       ':name (or name "No name"))))))

(defvar *options*)

(deffun parse-clause (lisp-clause)
  (cond ((atom lisp-clause)
         (cond ((typep lisp-clause 'clause)
                (send lisp-clause ':parse-clause))
               ((typep lisp-clause 'prolog-flavor)
                (multiple-value-bind (ordinary-term prolog-flavor-p)
                      (send lisp-clause ':ordinary-term)
                    (cond (prolog-flavor-p
			   (parse-clause-error ordinary-term))
                          (t (parse-clause ordinary-term)))))
               (t (parse-clause-error lisp-clause))))
        ((symbolp (first lisp-clause)) ;;it has a name
         (cond ((or (atom (rest1 lisp-clause)) (atom (second lisp-clause)))
		(parse-clause-error lisp-clause))
               ((neq *predicator-being-defined*
                     (first (first (rest1 lisp-clause))))
		(parse-clause-error-head lisp-clause))
               (t (multiple-value-bind (templates contains-cut)
                      (clause-template (rest1 lisp-clause))
                    (values templates contains-cut (first lisp-clause))))))
        ((neq *predicator-being-defined* (first (first lisp-clause)))
	 (parse-clause-error-head lisp-clause))
        (t (clause-template lisp-clause))))

(defun parse-clause-error (lisp-clause)
  (parse-clause
    (prolog-error ':bad-clause
		  "bad clause ~S must be a list of predications"
		  lisp-clause)))

(defun parse-clause-error-head (lisp-clause)
  (parse-clause
    (prolog-error ':bad-clause
		  "bad clause ~S the head of predicate ~s's clauses must begin with ~s"
		  lisp-clause *predicator-being-defined* *predicator-being-defined*)))

(defmethod (clause :parse-clause) ()
  (values templates contains-cut name))

(defun add-and-index-clause (clause definition where)
  (add-to-indexes clause (definition-indexes definition) where)
  (add-clause clause definition where))

(deffun add-clause (clause definition where)
  (add-clause-macro clause (definition-clauses definition) where))

(defvar *all-worlds* nil "All worlds ever created")

(defvar *protected-worlds* '(:system)
  "Masking of predicates in any of these worlds queries the user first.")

(defun add-to-world (predicator definition &optional world)
  (cond ((qc-file-in-progress)
	 (compiler:file-declare predicator 'prolog-definition definition))
	((let* ((world (or world (option-value ':world (definition-options definition))))
		(type
		  (cond ((get world 'define-predicate-type))
			(t ;;otherwise its a new world
			 (and (symbol-package world) (push world *all-worlds*))
			 ;;if the world is not interned this aught to be more clever
			 ;;so that stuff can be garbage collected...
			 (putprop world
				  (prolog-intern "DEFINE-PREDICATE-IN-~s" world)
				  'define-predicate-type)))))
	   (cond ((or inhibit-fdefine-warnings
		      (not-masking-protected-predicates predicator world))
		  (si:record-source-file-name predicator type)
		  (cond ((null (definition-in-world predicator world))
			 (setf (predicators world)
			       (cons predicator (predicators world)))))
		  (setf (definition-in-world predicator world) definition)
		  world))))))

(defun not-masking-protected-predicates (predicator world)
  (let ((definitions (definitions predicator)))
    (cond ((null definitions))
          (t (let ((universe-left (memq world *universe*)))
               (cond ((null universe-left))
                     (t (let ((masked-worlds
                                (intersection *protected-worlds*
                                              (rest1 universe-left))))
                          (cond ((null masked-worlds))
                                (t (let ((old-definition
                                           (find-first-definition definitions
                                                                  masked-worlds)))
                                     (cond ((null old-definition))
                                           (t (y-or-n-p
                                                (prolog-string
                                                        "~%You are about to mask the definition of ~s in ~s.~%Do you want to proceed? "
                                                        predicator
                                                        (option-value
                                                          ':world
                                                          (definition-options
                                                            old-definition)))))))))))))))))

(defun remove-from-world (predicator world)
  (setf (predicators world) (remove predicator (predicators world)))
  (setf (definition-in-world predicator world) nil)
  (setf (last-definition-universe (definitions predicator)) nil)) ;;flush cache

(defun predicate-documentation (predicator)
  (let ((definition (current-definition predicator nil)))
    (cond ((not (null definition))
           (values
             (option-value ':documentation (definition-options definition))
             t)))))

(defun predicate-argument-list (predicator)
  (let ((definition (current-definition predicator nil)))
    (cond ((not (null definition))
           (values
             (option-value ':argument-list (definition-options definition))
             t)))))

;;now for file attribute (-*- ... -*-) stuff

(defvar *add-world-when-loading* ':yes
  "If ':yes then the world of a file being loaded will be added to the current universe,
if ':ask then you will be asked,
otherwise it will not be added.")

;; A much simpler scheme in which :OPTIONS is the only file attribute.

(defprop :options t fs:known-file-attribute)

#+symbolics
(push ':options zwei:*attributes-remembered-in-buffer*)

(defun (:property :options fs:file-attribute-bindings) (ignore attribute value)
  (warn-if-unrecognized-option value nil)
  (let ((name (conjuncts-option-value ':world value)))
    (cond ((and name
		(not (memq name *universe*))
                (or (eq *add-world-when-loading* ':yes)
                    (and (eq *add-world-when-loading* ':ask)
                         (y-or-n-p
                           (prolog-string
                                   "~%The world ~s is not in the current universe, should I add it?"
                                   name)))))
           (set-universe (cons name *universe*))
           nil)))
  (values (list '*file-default-options*)
          `((,attribute
             ,@value
             ;;,@(alist-difference (rest1 *default-options*) value)
	     ))))

(deffun warn-if-unrecognized-option (options-left defining)
  (cond ((not (null options-left))
         (let ((option (first options-left)))
           (cond ((eq (first option) ':multiple)
		  (mapc #'(lambda (x) (warn-if-unrecognized-option (rest1 x) defining))
			(rest1 option)))
		 ((and (consp option) (get (first option) 'value-option-selector)))
		 ((null defining)
		  (format t "~%Warning: the file's option ~s is not recognized"
			  option))
		 (t ;;Used to signal an error here, but a warning seems more sensible.
		    (format t "~%Warning: While defining ~S,
~S is an option not recognized by DEFINE-PREDICATE"
                              defining option))))
	 (warn-if-unrecognized-option (rest1 options-left) defining))))

(deffun conjuncts-option-value (option conjuncts)
  (cond (conjuncts
	 (select (caar conjuncts)
	   (option (funcall (get option 'value-option-selector) (car conjuncts)))
	   ((':multiple) (or (disjuncts-option-value option (cdar conjuncts))
			     (conjuncts-option-value option (cdr conjuncts))))
	   (t (conjuncts-option-value option (cdr conjuncts)))))))

(deffun disjuncts-option-value (option disjuncts)
  (cond (disjuncts
	 (or (conjuncts-option-value option (cdar disjuncts))
	     (disjuncts-option-value option (cdr disjuncts))))))

#-prolog-micro-code
(defun %current-entrypoint (predicator definitions-location)
  (let ((definitions #-symbolics (DONT-OPTIMIZE (CDR definitions-location))
	             #+symbolics (contents definitions-location)))
    (definition-prover
      (cond ((eq (last-definition-universe definitions) *universe*)
	     (last-definition definitions))
	    ((find-and-cache-first-definition
	       definitions *universe* predicator))))))
