[mcclim-devel] specialized command-parsers

Christophe Rhodes csr21 at cam.ac.uk
Fri Apr 21 14:21:34 UTC 2006


Hi,

I'm trying to write a specialized command parser for use in
Emacs-Style Applications, where the input pane is a one-line
minibuffer.  Given this constraint, the default partial-command-parser
(which constructs an accepting-values dialog) is undesireable, as is
the rightwards march for multiple arguments.

At present, ESA implements the desired behaviour by having no commands
with arguments at all, instead using calls to ACCEPT within the bodies
of its commands to get arguments.  This is unacceptable (haha) in my
view because it does not allow for scriptability: the ability of other
applications to call 
  (execute-frame-command *climacs-frame* 
                         `(climacs-gui::com-find-file "/tmp/foo"))
is potentially one of the selling points of the as-yet mythical CLIM
environment.

My current attempt at writing a command parser (no attempt at partial
parsing or unparsing yet) is

(defun esa-command-parser (command-table stream)
  (let ((command-name nil)
        (command-args nil))
    (with-delimiter-gestures (*command-name-delimiters* :override t)
      ;; While reading the command name we want use the history of the
      ;; (accept 'command ...) that's calling this function.
      (setq command-name (accept `(command-name :command-table ,command-table)
                                  :stream stream :prompt nil :history nil))
      (let ((delimiter (read-gesture :stream stream :peek-p t)))
      ;; Let argument parsing function see activation gestures.
      (when (and delimiter (delimiter-gesture-p delimiter))
        (read-gesture :stream stream))))
    (with-delimiter-gestures (*command-argument-delimiters* :override t)
      (let* ((info (gethash command-name climi::*command-parser-table*))
             (required-args (climi::required-args info))
             (keyword-args (climi::keyword-args info)))
        (let (result)
          ;; only required args for now.
          (dolist (arg required-args (cons command-name (nreverse result)))
            (destructuring-bind (name ptype &rest args) arg
              (declare (ignore name))
              ;; clear the stream somehow
              #+nil
              (setf (stream-cursor-position stream) (values 0 0))
              #+nil
              (replace-input stream "")
              (push (apply #'accept 
                           (eval ptype) :stream stream 
                           ;; FIXME: probably wrong evaluation for ARGS
                           args)
                    result))))))))

Quite apart from the apparent impossibility of writing a command
parser portable across CLIM implementations, this doesn't work in
several ways.  It doesn't handle keyword arguments, the evaluation of
the arguments in the call to ACCEPT for arguments is wrong (see the
spec for DEFINE-COMMAND for the evaluation rules...), and, most
importantly, I find myself unable to clear the stream.  Setting the
stream cursor position doesn't work, because stream is an input
stream; I don't know why REPLACE-INPUT doesn't work, but it gives me
an error.  ERASE-INPUT-BUFFER is unimplemented.

The pieces of the implementation of this behaviour in ESA, currently,
include a method on STREAM-ACCEPT:

  (defmethod stream-accept :before ((pane minibuffer-pane) type &rest args)
    (declare (ignore type args))
    (window-clear pane))

which clears the minibuffer window at every call to ACCEPT, which is
what we want.  However, inside my ESA-COMMAND-PARSER, ACCEPT is called
on an input-editing-stream, not a minibuffer-pane.

How am I meant to do this?  Any ideas?

Cheers,

Christophe



More information about the mcclim-devel mailing list