November 2005 Archives

Wed Nov 16 15:28:05 CST 2005

Auto-generating ASDF defsystems given a bunch of .lisp files

I hacked together a really simple ASDF defsystem generator. What it does is scan through a set of source files for any DEF* form, assuming that when it sees (defblargh foo) it's defining a "blargh" named foo, and anytime it subsequently sees "foo" it knows which .lisp file "foo" was defined in, and thus adds it as a :depends-on clause to the .lisp file which used "foo".

It has a lot of shortcomings. Firstly, it simply uses the Lisp reader, which tries to intern symbols it sees - but into which package? If it sees, for instance, clsql:connect, then it will bork unless CLSQL has been loaded first. Which is why the first thing make-asdf does is load all the systems your new system depends on, which should really be ASDF's job. By the same token, if your system uses multiple packages then it won't work at all because the defpackage forms will never be evaluated before they're needed.

Anyway, here is the first rudimentary version of the code.

Posted by a1k0n | Permanent Link | Categories: Lisp

Fri Nov 4 18:00:44 CST 2005

Using SLIME over an SSH tunnel

If you'd like to use emacs on one computer (i.e. your windows box at home) and use SLIME to connect to a Lisp process on a remote computer (i.e. your server at work), here's how I do it.

First, create a startup file for your favorite Lisp implementation.

lisp startup file

(require 'asdf)
(asdf:oos 'asdf:load-op 'swank)
 
; start swank
(setf swank:*use-dedicated-output-stream* nil)
(setf swank:*communication-style* :fd-handler)
(swank:create-server :dont-close t)
Now edit your ~/.emacs so that you've got something like the following in it:

.emacs

(require 'slime)
(require 'tramp)
 
(add-hook 'lisp-mode-hook (lambda () (slime-mode t)))
(add-hook 'inferior-lisp-mode-hook (lambda () (inferior-slime-mode t)))
 
(setq lisp-indent-function 'common-lisp-indent-function
      slime-complete-symbol-function 'slime-fuzzy-complete-symbol)
 
(slime-setup)
 
;;; If you want to tunnel through an intermediate host, such as your
;;; work firewall, use the following couple lines.  If you're using a
;;; Windows emacs, use 'plink' as below, otherwise substitute 'ssh'.
(add-to-list
 'tramp-default-proxies-alist
 '("\\.work-domain\\.com" nil "/plink:fwuserid@firewall.work-domain.com:/"))
(add-to-list
 'tramp-default-proxies-alist
 '("firewall\\.work-domain\\.com" nil nil))
 
(defvar *my-box-tramp-path*
  "/ssh:me@my-box.work-domain.com:")
 
(defvar *current-tramp-path* nil)
(defun connect-to-host (path)
  (setq *current-tramp-path* path)
  (setq slime-translate-from-lisp-filename-function
    (lambda (f)
      (concat *current-tramp-path* f)))
  (setq slime-translate-to-lisp-filename-function
    (lambda (f)
      (substring f (length *current-tramp-path*))))
  (slime-connect "localhost" 4005))
 
(defun my-box-slime ()
  (interactive)
  (connect-to-host *my-box-tramp-path*))
 
(defun my-box-homedir ()
  (interactive)
  (find-file (concat *zarniwoop-tramp-path* "/home/me/")))


Now, load up the startup file you created on your host Lisp to start the swank server. Then, create an ssh tunnel, i.e. ssh -L 4005:localhost:4005 me@my-work.com.

Now you can M-x my-box-slime to connect through your SSH tunnel to your work box; SLIME's M-. command will also correctly open up the file containing the defun of whatever's under your cursor, and C-c C-k works correctly, etc. If you want to open up some lisp file, M-x my-box-homedir is a convenient shortcut.

For Windows users

If you're using Windows and want to also use a multi-hop tramp method (i.e. ssh into your work firewall, and then ssh from there to your server at work), be aware that tramp 2.1.4 and prior has a bug; it's fixed in CVS and probably 2.1.5, which is not out yet. Information and a patch is available here.

You'll also want to use plink from the PuTTY distribution in lieu of ssh. If you're doing multi-hop tramp, though, you need to use plink for the first hop (Windows box -> "firewall" box) and ssh thereafter ("firewall" -> "server").

Posted by a1k0n | Permanent Link | Categories: Lisp

Fri Nov 4 17:36:48 CST 2005

Lisp REPL in Vendetta Online

Vendetta Online has a Lisp environment (using SBCL) which controls much of its NPC behavior and will soon be in charge of generating player and NPC missions. Partly in order to get around some thread-safety issues, and partly for convenience we built an REPL into a secret chat channel. (it only responds to developer accounts)





It also handles errors by printing a quick and dirty stack trace:



Here's the code for the stack dump handler, for interested readers:

(defun trap-and-log-error-handler (condition)
  (format t "Error signalled: ~A~%" condition)
  ;; this (write-string (with-output-to-string (s) ...) thing is odd,
  ;; but seemingly necessary because print-frame-call seems
  ;; uncooperative when using t for a stream argument.
  (write-string (with-output-to-string (s)
		  (do ((frame (sb-di:top-frame) (sb-di:frame-down frame))
		       (i 0 (1+ i)))
		      ((null frame))
		    (format s "~a: " i)
		    (sb-debug::print-frame-call frame s)
		    (format s "~%")))))


(defmacro trap-and-log-errors (&body body) `(ignore-errors (handler-bind ((error #'trap-and-log-error-handler)) ,@body)))



and here's the REPL code (trap-and-log-errors is somewhere upstream from where this is called)

;;; this is what interprets "eval" requests from developers; we have a
;;; simple color code tag involving the non-printable character 
;;; (code-char 127), so return values show up in red (like in SLIME)


(define-server-function eval (&rest cmd) (flet ((format-return-value (return-val) (with-output-to-string (os) (with-input-from-string (s (format nil "~s~&" return-val)) (loop for line = (read-line s nil) while line do (format os "~aff0000~a~%" (code-char 127) line)))))) (let ((return-val (multiple-value-list (eval `(progn (in-package :com.guildsoftware.deliverator) ,@cmd))))) (format t "~&") (dolist (val return-val) (write-string (format-return-value val))))))





Why didn't I just use sb-debug:backtrace? I didn't know about it. I just looked at how SLIME did it.

Posted by a1k0n | Permanent Link | Categories: Vendetta, Lisp

Fri Nov 4 15:46:27 CST 2005

An introductory note.

My uncle Ron asked me, "Do you know what a blog is? I have a blog, and I don't even know what the hell it is."

I feigned ignorance.

But at last, I broke down and made one of my own. Will it have any purpose? Who knows! I think it might help me flesh out new ideas if I actually decide to write them down. It might also take lots of time away from me, as I write about pointless tangents when I'm supposed to be working.

Alas, this, my first entry, shall contain no new ideas. But what I will do is convert some of the old stuff on my site into retroactive entries, which is sort of cheating but whatever.

So anything below this post was written prior to this post, but reformatted for the blog afterwards.

Posted by a1k0n | Permanent Link | Categories: Personal