The CPC (Command Processor Context) object has been enhanced to handle logging and dribble file output. The logfile is mandatory but the dribble file is not.
First, the upgraded CPC structure:
Code: Select all
;;; CPC: Command Processor Context
(defstruct
(cpc
(:print-function
(lambda (my-cpc my-stream my-level)
(declare (ignore my-level))
(encode-cpc my-stream my-cpc))))
(cpid 0 :type fixnum) ; Command processor identification enumeration constant
(cpstr nil) ; Command processor identification name string
(args nil) ; Program ommand line arguments
(lfpstr nil) ; Log file pathname string
(dfpstr nil) ; Dribble file pathname string
(stin nil) ; Input stream
(stout nil) ; Output stream
(stlog nil) ; Logging output stream
(stdrib nil) ; Dribble output stream
(cmdord nil) ; Current command ordinal
(cmdstr nil) ; Current command string input
(cmdtkns nil) ; Tokens from command string input
(cmdtknc 0 :type fixnum) ; Count of tokens
(cmd-vec nil) ; Constant command string vector
(is-done nil) ; Exiting flag
(is-pass nil) ; Empty command input flag
(pgn nil) ; The current PGN; includes game object
(pse nil)) ; The current PSE
CPC initialization:
Code: Select all
(defun mk-cpc (my-cpid my-args)
"Return a new Command Processor Context object for the given command processor kind."
(declare (type fixnum my-cpid))
(let ((result (make-cpc)))
(setf (cpc-cpid result) my-cpid)
(setf (cpc-cpstr result) (svref as-cpid-vec my-cpid))
(setf (cpc-args result) my-args)
(setf (cpc-lfpstr result) (format nil "~A.log" (svref as-cpid-vec my-cpid)))
(setf (cpc-dfpstr result) (format nil "~A.drb" (svref as-cpid-vec my-cpid)))
(setf (cpc-stlog result) nil)
(setf (cpc-stdrib result) nil)
(setf (cpc-cmdord result) nil)
(setf (cpc-cmdstr result) "")
(setf (cpc-cmdtkns result) nil)
(setf (cpc-cmdtknc result) 0)
(setf (cpc-is-done result) nil)
(setf (cpc-is-pass result) nil)
(setf (cpc-pgn result) (mk-pgn))
(setf (cpc-pse result) (mk-pse))
(cond
((= my-cpid cpid-icp)
(setf (cpc-stin result) *terminal-io*)
(setf (cpc-stout result) *terminal-io*)
(setf (cpc-cmd-vec result) as-icp-cmd-vec))
((= my-cpid cpid-uci)
(setf (cpc-stin result) *standard-input*)
(setf (cpc-stout result) *standard-output*)
(setf (cpc-cmd-vec result) as-uci-cmd-vec))
((= my-cpid cpid-xboard)
(setf (cpc-stin result) *standard-input*)
(setf (cpc-stout result) *standard-output*)
(setf (cpc-cmd-vec result) as-xboard-cmd-vec))
(t (error "mk-cpc: cond fault")))
result))
The command dispatch that now handles logging and dribbling of the input command:
Code: Select all
(defun command-line-cpc (my-cpc)
"Set up the various command fields in a CPC based on a command line input string."
(let ((text-str (read-line (cpc-stin my-cpc) nil nil)) (stdrib (cpc-stdrib my-cpc)))
(setf (cpc-cmdord my-cpc) nil)
(setf (cpc-cmdstr my-cpc) text-str)
(setf (cpc-cmdtkns my-cpc) nil)
(setf (cpc-is-pass my-cpc) nil)
(if (null? text-str)
(setf (cpc-is-done my-cpc) t)
(progn
(cpc-log-line my-cpc (format nil "> ~A" text-str))
(when stdrib
(put-string stdrib (format nil "> ~A" text-str))
(newline stdrib)
(finish-output stdrib))
(setf (cpc-cmdtkns my-cpc) (tokenize text-str))
(setf (cpc-cmdtknc my-cpc) (length (cpc-cmdtkns my-cpc)))
(if (null? (cpc-cmdtkns my-cpc))
(setf (cpc-is-pass my-cpc) t)
(setf (cpc-cmdord my-cpc)
(locate-string (first (cpc-cmdtkns my-cpc)) (cpc-cmd-vec my-cpc))))))))
The logfile writer (note the use of timestamping):
Code: Select all
(defun cpc-log-line (my-cpc my-string)
"Output the given string plus a newline to the logging file."
(let ((stlog (cpc-stlog my-cpc)))
(when stlog
(encode-timemark stlog (calc-current-ltz-timemark))
(blank stlog)
(put-string stlog my-string)
(newline stlog)
(finish-output stlog)))
(values))
The CPC output text writer:
Code: Select all
(defun cpc-put-line (my-cpc my-string)
"Output the given string plus a newline."
(let ((stout (cpc-stout my-cpc)) (stdrib (cpc-stdrib my-cpc)))
(put-string stout my-string)
(newline stout)
(finish-output stout)
(when stdrib
(put-string stdrib my-string)
(newline stdrib)
(finish-output stdrib)))
(values))
The initialization and termination routines for the ICP (Interactive Command Processor; the UCI and xboard versions are similar):
Code: Select all
(defun icp-init (my-cpc)
"Initialize ICP operations."
(setf (cpc-stlog my-cpc) (open (cpc-lfpstr my-cpc) :direction :output))
(cpc-log-line my-cpc (format nil "~A: ~A logfile begin" prog-name (cpc-cpstr my-cpc)))
(cpc-log-line my-cpc (format nil "Program date: ~A" prog-date))
(values))
(defun icp-term (my-cpc)
"Terminate ICP operations."
(cpc-log-line my-cpc (format nil "~A: ~A logfile end" prog-name (cpc-cpstr my-cpc)))
(when (cpc-stdrib my-cpc)
(close (cpc-stdrib my-cpc)))
(when (cpc-stlog my-cpc)
(close (cpc-stlog my-cpc)))
(values))
The upgraded ICP main routine; the UCI and xboard versions are similar):
Code: Select all
(defun icp (&rest my-args)
"Interactive command processor for console operation of the Chess In Lisp Toolkit."
(let ((cpc (mk-cpc cpid-icp my-args)))
(icp-init cpc)
(icp-emit-greeting cpc)
(dountil (cpc-is-done cpc)
(icp-emit-prompt cpc)
(command-line-cpc cpc)
(unless (or (cpc-is-done cpc) (cpc-is-pass cpc))
(if (cpc-cmdord cpc)
(eval (list (svref icp-dispatch-vec (cpc-cmdord cpc)) cpc))
(icp-move-handler cpc)))
(finish-output (cpc-stout cpc)))
(icp-emit-farewell cpc)
(icp-term cpc))
(values))
And finally, a sample logfile:
Code: Select all
2008.09.25 14:25:05 Chess In Lisp Toolkit: ICP logfile begin
2008.09.25 14:25:05 Program date: 2008.09.25
2008.09.25 14:25:10 > db
2008.09.25 14:25:12 > dm
2008.09.25 14:25:14 > df
2008.09.25 14:25:15 >
2008.09.25 14:25:16 > exit
2008.09.25 14:25:16 Chess In Lisp Toolkit: ICP logfile end