On Jan 5, 2008 9:26 AM, Gustavo <[EMAIL PROTECTED]> wrote:

> 2008/1/5, Justin Heyes-Jones <[EMAIL PROTECTED]>:
>
> > As it happens I've been playing with lispbuilder-sdl writing a
> > component based engine. [...]
> >
> > What's nice is you can stop the game, add or remove objects and so on,
> > and then restart the engine. When I'm using lispworks I can do that
> > without stopping the engine using the repl in lispworks and starting
> > the engine in emacs. Presumably theres some way to do that in slime
> > and have to repl's on the same lisp?
>
>
> Maybe you can use multiple threads? Threading is implementation dependent,
> but there are some libraries that provide portable multi-threading (like
> http://common-lisp.net/project/bordeaux-threads/)
>

Unfortunately, CLISP does not have working threads and SBCL's threads only
work on Unixy platforms as far as I know. In fact, I am not sure any of the
major free implementations have threads working on all platforms (except
ABCL and ECL, which I don't believe work with lispbuilder-sdl). I would use
lispworks except I want to be able to create executables, and the
professional edition is exceeding expensive.

I am trying to find a workaround that would let me do both the game loop and
REPL at the same time (in sequence that is). It should be theoretically
possible in the main event loop to update the game, then read all available
input without hanging, eval it if amounts to a complete form, for the entire
duration of the game.

After a bit of coding I managed to write a couple of functions to take input
from the keyboard without hanging, and evaluate that input if it forms a
complete expression. It's a little buggy, in that SBCL will freeze
occasionally until the user presses enter in the repl (it seems to freeze
whenever the user clicks the mouse or moves the mouse over the command
prompt). In CLISP, the text entered into the repl won't show up until the
user presses enter, and CLISP will only pick up one character per enter
keystroke (so if one types "hello\n\n\n\n\n" CLISP reads "h\ne\nl\nl\no\n").
I can't get lispworks to load lispbuilder-sdl so I can't tell you how it
works in lispworks.

Comments and suggestions are welcome. Thanks for your time if you managed to
get through reading all this.

Here is my code (more or less):

;; Banner, prompt, and input line for REPL.
(defvar *banner* "Welcome to the REPL.")

(defvar *input-prompt* "~&[~a]> ")

(defvar *input-line* 0)

;; Used to catch end-of-file conditions (e.g. when calling (read) on "(+ 2
2"; used to allow multiline input).
(defun trap-eof-handler (condition)
  (throw 'trap-eofs (values nil nil)))

(defmacro trap-eofs (&rest forms)
  `(catch 'trap-eofs
    (handler-bind ((end-of-file #'trap-eof-handler))
      ,@forms)))

;; Wrap read and parse functions in closures.
(let ((input-string ""))
  ;; Read without hanging, put input into input-string.
  (defun read-repl (&optional input-stream (eof-error-p t) eof-value
recursive-p)
    (setf input-string
      (do* ((char
             (read-char-no-hang input-stream eof-error-p eof-value
recursive-p)
             (read-char-no-hang input-stream eof-error-p eof-value
recursive-p))
            (input (if char (cons char nil) nil)
                   (if char (cons char input) input)))
           ((not char) (concatenate 'string input-string (nreverse
input))))))

  ;; Parse contents of input-string if they form a complete input.
  ;; The second return value signals whether or not a complete input was
recieved, to differentiate between user entered nil and no input.
  (defun parse-repl ()
    (with-input-from-string (input-stream input-string)
      (trap-eofs
       (values
        (prog1
          (read input-stream)
          (clear-repl)
          (read-repl input-stream nil))
        t))))

  ;; Clear input-string.
  (defun clear-repl ()
    (setf input-string "")))

;; Print banner and first prompt and exit.
(defun enter-repl ()
  (let ((package *package*))
;    (in-package :bt) ; Normally used to control eval environment
    (unwind-protect
      (progn
        (format t "~&~a~&" *banner*)
        (incf *input-line*)
        (format t *input-prompt* *input-line*)
        (force-output))
      (setf *package* package))))

;; Eval input and reprint prompt if complete, otherwise return immediately.
(defun update-repl ()
  (let ((package *package*))
;    (in-package :bt) ; Normally used to control eval environment
    (unwind-protect
      (progn
        (read-repl)
        (multiple-value-bind (input exists) (parse-repl)
          (when exists
            (trap-errors
              (format t "~&~s~&" (eval input)))
            (incf *input-line*)
            (format t *input-prompt* *input-line*)
            (force-output))))
      (setf *package* package))))

So now my main function looks like this:

(defvar *running* t)

(defun main ()
  (sdl:with-init (sdl:sdl-init-video)
    ; init code...
    (enter-repl)
    (sdl:with-events ()
      (:key-down-event (:key key)
        (cond
          ((sdl:key= key :sdl-key-backquote)
           (setf *running* (not *running*)))))
      (:idle ()
        (when *running*
          ; render, update, etc...
          )
        (update-repl)))))

-- 
Elliott Slaughter

"Any road followed precisely to its end leads precisely nowhere." - Frank
Herbert
_______________________________________________
application-builder mailing list
application-builder@lispniks.com
http://www.lispniks.com/mailman/listinfo/application-builder

Reply via email to