;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; This version of $example implements the following
;;; changes/improvements in the original version of example: 1)
;;; It handles %TH(2) correctly; 2) It makes effort to protect
;;; user-defined functions, variables, labels and arrays from
;;; being overwritten by an example; while protecting variables
;;; is quite straightforward, protecting functions is quite
;;; involved; it is done by moving the value of the property
;;; 'mprops' in a symbol property list to a property with a name
;;; generated by gensym; this happens before the examples are
;;; evaluated; afterwards the value of the property 'mprops' is
;;; restored; 3) rules and letrules are not being protected; it
;;; would be more complicated to make this feature work sanely;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

(in-package "MAXIMA")
(defmspec $example (item &optional (file
				 (merge-pathnames "manual.demo"
						  $describe_documentation))
		      &aux tmp-name
		      )
  (and (symbolp file) (setq file (stripdollar file)))
  (or (probe-file file)
      (return-from $example "Please supply a file name as the second arg"))
  (and (symbolp item) (setq item (symbol-name item))
       (setq item (subseq item 1))
       (with-open-file
	(st file)
	(sloop with tem
	       while (setq tem (read-char st nil))
	       do
	       (cond ((and (eql tem #\&)
			   (eql (setq tem (read-char st nil)) #\&))
		      (cond
		       ((and (symbolp (setq tem (read st nil)))
			     (string-search item (symbol-name tem)))
			(format t "~%Examples for ~a :~%" tem)
			;; This code fulls maxima into thinking that it just
			;; started, by resetting the values of the special
			;; variables $labels and $linenum to their initial
			;; values. They will be reset just after $example
			;; is done. The d-labels will also not be disturbed
			;; by calling example.
			;;
			;; Hide the definitions of user functions.
			(setq tmp-name 
			      (hide-maxima-props
			       (mapcar #'caar (cdr $functions))))
			(unwind-protect
			    (progv
			     ;; Protect the user labels, variables and functions
			     ;; from being overwritten.
			     (append '($linenum
				       $labels
				       $values
				       $functions
				       $arrays
				       $%)
				     (cdr $labels)
				     (cdr $values)
				     (cdr $arrays))
			     (list 1
				   '((mlist simp))
				   '((mlist simp))
				   '((mlist simp))
				   '((mlist simp)))
			     ;; Run the example.
			     (sloop until
				    (or (null (setq tem (peek-char nil st nil)))
					(eql tem #\&))
				    for expr = (mread st nil)
				    do
				    (let ($display2d) (displa (third  expr)))
				    (let ((c-label (makelabel $inchar))
					  (d-label (makelabel $outchar)))
				      (set c-label (third expr))
				      (format t "<~d>==>" $linenum)
				      (displa (setq $% (meval* (third  expr))))
				      (terpri )
				      (set d-label $%)
				      (incf $linenum)
				      ))
			     ;; Clean-up time. Make all symbols used in
			     ;; the example unbound.
			     (mapc #'makunbound
				   (append
				    (cdr $labels)
				    (cdr $values)
				    (cdr $arrays))))
			  ;; Restore the defintions of functions.
			  (unhide-maxima-props
			   (mapcar #'caar (cdr $functions))
			   tmp-name))))))))))


(defun hide-maxima-props (symbols
			  &aux tmp-name)
  ;; Rename the property mprops, under which the function
  ;; definition e.t.c. is stored, to tmp-name.
  (setq tmp-name (gensym))
  (dolist (symbol symbols)
	  (putprop symbol (get symbol 'mprops) tmp-name)
	  (remprop symbol 'mprops))
  ;; Return the temporary name of the property.
  tmp-name)

(defun unhide-maxima-props (symbols tmp-name)
  ;; Undo the action of hide-maxima-props.
  (dolist (symbol symbols)
	  (putprop symbol (get symbol tmp-name) 'mprops)
	  (remprop symbol tmp-name)))

