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

;;this is the LM-Prolog's trace facility

(define-predicate first-member
  :(options (compile-method open))
  ((first-member ?element ?list)
   (prove-once (member ?element ?list))))

(define-predicate trace
  :(options
     (argument-list (+predicator-or-predicators &rest +options))
     (documentation
       "traces each call to +PREDICATOR-OR-PREDICATORS if its a symbol,
 or each of the elements of +PREDICATOR-OR-PREDICATORS if its is list.
 The currently supported options to trace are:

 (:print-if . ?port-names) where the valid port names are
 :TRYING :SUCCEEDING :FAILING and :RE-TRYING the default is all four of them

 (:when . ?patterns) --- the tracing//stepping applies only if the call unifies
 with at least one of the ?patterns 

 :step  --- enter step mode when the predicate is entered
	    this mode is self documenting (type  for help)

 (:before ?predicator) --- ?predicator is called on the ?call upon entry
 (:after ?predicator) ---- ?predicator is called on the ?call after entry

 (:world ?world) --- ?world where the traced predicate is to live, 
		     defaults to prolog:traced-predicates"))
  ((trace ?name-or-names . ?trace-options)
   (cases ((first-member (:world ?world) ?trace-options))
	  ((= ?world traced-predicates)))
   (bag-of ? ?
    (either (member ?predicator ?name-or-names)
	    (= ?predicator ?name-or-names))
    (either (untrace ?predicator) (true))
    (either (prove-once
	      (definition (define-predicate ? (:options . ?old-options) . ?)
			  ?predicator))
	    (and (format t "~%Warning from trace: ~s is not a predicator"
			 ?predicator)
		 (fail)))
    (remove ?options-1 (:compile-method . ?) ?old-options)
    (remove ?options-2 (:indexing-patterns . ?) ?options-1)
    (substitute ?new-options (:world ?old-world) (:world ?world) ?options-2 =)
    (trace-body ?body ?predicator ?trace-options ?old-world)
    (add-world ?world)
    (define-predicate ?predicator (:options . ?new-options)
      ?body))))

(define-predicate trace-body
  ((trace-body ((?predicator . ?arguments) . ?body) ?predicator ?options ?world)
   (= ?call (?predicator . ?arguments))
   (either (first-member (:print-if . ?ports) ?options)
	   (= ?ports :(trying failing succeeding re-trying)))
   (cases ((first-member :trying ?ports) (= ?trying "Trying:    "))
	  ((= ?trying ())))
   (cases ((first-member :failing ?ports) (= ?failing "Failing:   "))
	  ((= ?failing ())))
   (cases ((first-member :succeeding ?ports)
	   (= ?succeeding "Succeeding:"))
	  ((= ?succeeding ())))
   (cases ((first-member :re-trying ?ports) (= ?re-trying "Re trying: "))
	  ((= ?re-trying ())))
   (cases ((and (= ?trying ()) (= ?failing ()) (= ?succeeding ()) (= ?re-trying ()))
	   (= ?print-in ())
	   (= ?print-out ()))
	  ((= ?print-out
	      ((print-trace-out ?succeeding ?re-trying ?indentation ?level ?call)))
	   (= ?print-in
	      ((print-trace-in ?trying ?failing ?indentation ?level ?call)))))
   (cases ((first-member :step ?options)
	   (= ?try ((do-step-response ?call ?world))))
	  ((= ?try ((call ?call ?world)))))
   (cases ((first-member (:before ?before-predicate) ?options)
	   (= ?before ((?before-predicate ?call))))
	  ((= ?before ())))
   (cases ((first-member (:after ?after-predicate) ?options)
	   (= ?after ((?after-predicate ?call))))
	  ((= ?after ())))
   (append ?inner-body ?print-in ?before ?try ?after ?print-out)
   (cases ((first-member (:when . ?patterns) ?options)
	   (= ?body ((cases ((can-prove (member ?call ?patterns)) . ?inner-body)
			    ((call ?call ?world))))))
	  ((= ?body ?inner-body)))))

(define-predicate print-trace-in 
  ((print-trace-in ?trying ?failing ?indentation ?level (?predicator . ?arguments))
   (trace-level ?level ?predicator)
   (indentation ?indentation)
   (print-trace ?trying ?failing ?indentation ?level (?predicator . ?arguments))))

(define-predicate print-trace-out
  ((print-trace-out ?trying ?failing
		    ?indentation ?level (?predicator . ?arguments))
   (print-trace ?trying ?failing ?indentation ?level (?predicator . ?arguments))
   (decrement-indentation)
   (decrement-trace-level ?predicator)))

(defun step-response-help (stream &rest ignore)
  (format stream "~%You are in the stepper.
Type either ~:C or C to step through everything (to CREEP),
Type either ~:C or L to continue (LEAP),
Type either ~:C or S to prove the current predication without any tracing (SKIP),
~:C or control-L redisplays the current predication,
Type F to cause the current predication to be FALSE,
Type T to cause the current predication to be TRUE,
Type A to give the ANSWER,
Type B or ~:C to break to a Prolog//Lisp listener."
	  #\cr #\line #\altmode #\clear-screen #\break))

(deffun step-response (call)
  (let ((response
	  (FQUERY '(:choices
		    (((creep "Creep.") #\cr #/c #/C)
		     ((leap "Leap.") #\line #/l #/L)
		     ((skip "Skip.") #\altmode #/s #/S)
		     ((false "False.") #/f #/F)
		     ((true "True.") #/t #/T)
		     ((accept "Accept.") #/a #/A)
		     ((break "Break.") #/b #/B #\break)
		     ((refresh "Refresh.") #\clear-screen #^L))
		    :list-choices nil
		    :fresh-line nil
		    :help-function
		    step-response-help)
		  " ")))
    (selectq response
      (refresh (send standard-output ':clear-screen)
	       (funcall (current-entrypoint 'print-trace-doit)
			(continuation (true))
			call)
	       (step-response call))
      (break (break "STEP") (step-response call))
      (accept (format t "~%Enter a term to unify with the current predication: ")
	      (prolog-cons 'answer (PARSE-TERM (read))))
      (t response))))

(define-predicate *indentation*
  :(options (type dynamic))
  ((*indentation* 1)))

(define-predicate indentation
  ((indentation ?indentation)
   (prove-once (*indentation* ?indentation))
   (sum ?next-indentation ?indentation 1)
   (assume ((*indentation* ?next-indentation)) :system)))

(define-predicate decrement-indentation
  ((decrement-indentation)
   (prove-once (*indentation* ?next-indentation))
   (sum ?indentation ?next-indentation -1)
   (assume ((*indentation* ?indentation)) :system)))

(define-predicate *trace-level*
  :(options (type dynamic))
  ((*trace-level* 0 ?)))

(define-predicate trace-level
  ((trace-level ?level ?predicator)
   (prove-once (*trace-level* ?old-level ?predicator))
   (sum ?level ?old-level 1)
   (assume ((*trace-level* ?level ?predicator)) :system)))

(define-predicate decrement-trace-level
  ((decrement-trace-level ?predicator)
   (prove-once (*trace-level* ?old-level ?predicator))
   (sum ?level ?old-level -1)
   (assume ((*trace-level* ?level ?predicator)) :system)))

(define-predicate print-trace-doit
  ((print-trace-doit ?call)
   (format t "~S" ?call)))

(define-predicate print-trace
  ((print-trace ?in-label ?out-label ?indentation ?level ?call)
   (lisp-value ?le-10 (min 10 '?indentation) :dont-invoke)
   (or (cases ((= ?in-label ()))
	      ((format t "~%~vt(~d)~a " ?le-10 ?level ?in-label)
	       (print-trace-doit ?call)))
       (cases ((= ?out-label ())
	       (false))
	      ((format t "~%~vt(~d)~a " ?le-10 ?level ?out-label)
	       (print-trace-doit ?call)
	       (false))))))

(define-predicate untrace
  :(options
     (argument-list (&optional predicator))
     (documentation
       "removes a trace from PREDICATOR if given, otherwise removes all traces."))
  ((untrace ?predicator)
   (predicator ?predicator traced-predicates)
   (remove-definition ?predicator traced-predicates))
  ((untrace) ;;all
   (remove-world traced-predicates)
   (bag-of ? ?
     (predicator ?predicator traced-predicates)
     (untrace ?predicator))))
	  
(define-predicate untrace-query
  :(options
     (documentation
       "queries the user for each traced predicate whether it should be untraced."))
  ((untrace-query)
   (bag-of ? ?
    (predicator ?predicator traced-predicates)
    (y-or-n "~%Untrace ~s? " ?predicator)
    (untrace ?predicator))))

(define-predicate who-state
  :(options (argument-list (+label))
	    (documentation "displays +LABEL on the run state part of the who line,
 upon backtracking the original state is restored."))
  ((who-state ?label)
   (lisp-value ?old-label (tv:process-whostate current-process) :dont-invoke)
   (unwind-protect
     (lisp-command (set-who-state '?label))
     (lisp-command (set-who-state '?old-label)))))

(deffun set-who-state (label)
  (setf (process-whostate current-process) (string label))
  (tv:who-line-run-state-update))

;;; The Step facility, by Mats Carlsson.


(defun get-p-and-world (p world)
  (and (unify p (definition-predicator *definition*))
       (unify world (option-value ':world (definition-options *definition*)))))

;;We get here with stepping turned on and with
;;*definition* closure bound to the thing we really want to call.
(define-predicate do-step
  ((do-step . ?args)
   (lisp-command (get-p-and-world '?p '?world) :dont-invoke)
   (= ?call (?p . ?args))
   (unwind-protect
     (lisp-command (step-off) :dont-invoke)
     (lisp-command (step-on) :dont-invoke))
   (print-trace-in "Trying:    " "Failing:   " ?x ?y ?call)
   (do-step-response ?call ?world)
   (print-trace-out "Succeeding:" "Re trying: " ?x ?y ?call)
   (unwind-protect
     (lisp-command (step-on) :dont-invoke)
     (lisp-command (step-off) :dont-invoke))))

(define-predicate do-step-response
  ((do-step-response ?call ?world)
   (lisp-value ?response (step-response '?call) :dont-invoke)
   (RULES ?RESPONSE
	  (CREEP (step ?call ?world))
	  (LEAP (call ?call ?world))
	  (SKIP (without-world traced-predicates (call ?call ?world)))
	  (TRUE)
	  ((ANSWER . ?call)))))

(define-predicate step
  (:options
    (:argument-list (+call &optional +world))
    (:documentation "executes CALL with global stepping turned on."))
  ((step ?call)
   (unwind-protect
     (lisp-command (step-on) :dont-invoke)
     (lisp-command (step-off) :dont-invoke))
   ?call
   (unwind-protect
     (lisp-command (step-off) :dont-invoke)
     (lisp-command (step-on) :dont-invoke)))
  ((step ?call ?world)
   (unwind-protect
     (lisp-command (step-on) :dont-invoke)
     (lisp-command (step-off) :dont-invoke))
   (call ?call ?world)
   (unwind-protect
     (lisp-command (step-off) :dont-invoke)
     (lisp-command (step-on) :dont-invoke))))
