Carlos Konstanski on 19 Aug 2005 03:20:58 -0000


[Date Prev] [Date Next] [Thread Prev] [Thread Next] [Date Index] [Thread Index]

[PLUG] Any lispers out there? Newbie needs booster shot


I am a software developer who has, up until now, used pay-dirt
languages exclusively (java, php, perl, other "hot" languages in
today's workaday world).

I have come to the realization that lisp is the only language that
matters.  I'm gobbling it up like a greedy child.  With any luck, I'll
find a way to use it at my paying job.

I have just written my first useful macro.  There is one small problem
with it that I do not have the lisp knowledge to solve.  I need to
know more about symbols vs. strings when used unquoted (preceded by
",") in a macro (or, really, in a backticked list).

Among other tasks, this macro writes a (defclass) and a (defmethod).
The name of the class I am creating is currently passed in as a symbol
(not a string).  Another argument of the macro is a URL (string).

I would like to be able to do away with the class name argument, since
the calling code does not need to know this bit of information.  I
would rather use the URL (a unique string) to derive a class name,
inside the macro definition.

Here's the relevant code (I'm using SBCL):

(defvar *document-root-url* nil
  "This is the public URL of the site.  Example: \"http://localhost:3000\".";)
(setf *document-root-url*
       (let ((fqdn #+sbcl(sb-bsd-sockets:host-ent-name
                             (sb-bsd-sockets:get-host-by-name
                             (machine-instance)))
                   #-sbcl (format nil "localhost")))
         (make-url :scheme "http"
                  : host fqdn
                  : port *tcp-port*)))

(defvar *website-url* nil)
(setf *website-url*
       (urlstring
         (merge-url *document-root-url* "/jewelery")))

(defvar *title* "Pippi's Jewelery")

(defun make-absolute-url(url)
  "Prepends *WEBSITE-URL* to the given URL and returns the full URL as
a string."
   (concatenate 'string *website-url* url))

(defmacro publish-page (handler-class-name url &rest body)
  "Macro to do the grunt work of creating and installing a subclass of
HANDLER for each page you wish to publish.

Publishing a page entails 3 actions:
     - defining a class for the page that is a subclass of HANDLER;
     - implementing the handle-request-response method on this class;
     - installing an instance of the handler into the listener.

This macro automates the production of all this code."
   `(progn
      (defclass ,handler-class-name (handler) ())
      (defmethod handle-request-response ((handler ,handler-class-name)
                                           method
                                           request)
        (request-send-headers request)
        (html-stream (request-stream request) ,@body))
      (install-handler (http-listener-handler *listener*)
                       (make-instance ',handler-class-name)
                       (make-absolute-url ,url)
                       t)))

(publish-page jewelery-home
               "/home"
               `(html
                 (head (title ,*title*))
                 (body ((h1 :align "center") "Hello Pip"))))


This is part of a simple website page publisher. It uses araneida as the web server API. Please assume that functions like MERGE-URL and URLSTRING are provided by the API and just work. *document-root-url*, by the way, is an object, not a string. We want a string for the call to INSTALL-HANDLER. That part works - I'm worried about something else in this code.

The code I didn't show you (because it works and has nothing to do
with the problem) supplies an instance of an araneida LISTENER class
in the constant *listener*.  *listener* has a slot to hold a single
DISPATCHING-HANDLER object, which is the container for the collection
of HANDLER instances.  This alist of HANDLER objects is keyed by URL
string.

A HANDLER is nothing more than a class with no slots and a single
method, HANDLE-REQUEST-RESPONSE.  The URL is used at request time to
look up the corresponding subcless of HANDLER, and calling the
HANDLE-REQUEST-RESPONSE method specialized on that subclass gets us to
the right function to render the page.  That is all done by the
araneida API.  To use the API, all we need to do is populate
DISPATCHING-HANDLER with HANDLER subclasses.

Another user-written lisp file is responsible for creating the
*listener* instance and putting the singeton root DISPATCHING-HANDLER
in it.  The code supplied above is what adds the HANDLER instances to
the DISPATCHING-HANDLER using INSTALL-HANDLER.

PUBLISH-PAGE is the macro that automates the writing of the code for
creating and installing a page handler.  It creates a subclass of
HANDLER.  Then it overrides the HANDLE-REQUEST-RESPONSE method, which
will get called by araneida when a request to the matching URL is
made.  Then it calls INSTALL-HANDLER to place the sublassed HANDLER
into the DISPATCHING-HANDLER.  This macro is where I need help.

There are 2 places where I run up against my limited lisp knowledge.
Both involve the class name:

      (defclass ,handler-class-name (handler) ())
      (defmethod handle-request-response ((handler ,handler-class-name)
                                           method
                                           request)

I shouldn't be passing in handler-class-name, since the code outside
this macro has no reason to care about the name of the class.  I
should be generating the class name somehow.  I can use the URL string
for this.  If my goal were to simply make another string that is based
off the URL, that would be easy.  I'm sure it will be the first step
of any solution to my problem.

Ultimately, I need the class name to be a symbol though, not a string.
The places where I put ",handler-class-name" need to be symbols.  That
is why it looks like this when I call the macro:

     (publish-page jewelery-home
                   "/home"
                   `(html
                     (head (title ,*title*))
                     (body ((h1 :align "center") "Hello Pip"))))

jewelery-home is not quoted or stringified.  It must be through some
magic of DEFMACRO argument lists that I am able to use this uninterned
symbol with no value in this way withoug a compiler error.

Is there a way to turn a string into a symbol, or get a symbol
representation of a string?  If so, I could quote jewelery-home and
treat it like a string.  Or better yet, I could transform the URL into
a class name using string handling functions.

I looked at INTERN and FIND-SYMBOL as a way of converting a string to
a symbol and back again.  I'm guessing, however, that there is a
common idiom for what I am trying to do, and I just need it to be
brought to my attention.

The question again, stated clearly this time:

In lisp, how do you convert a string into a symbol and place it,
unquoted, in a quoted list (as in a macro body)?

Carlos
___________________________________________________________________________
Philadelphia Linux Users Group         --        http://www.phillylinux.org
Announcements - http://lists.phillylinux.org/mailman/listinfo/plug-announce
General Discussion  --   http://lists.phillylinux.org/mailman/listinfo/plug