I've been continuing learning/using Common Lisp.  I'm actually getting to
the point where I'm writing real code, and very much enjoying the language.

I can speak highly of Paul Graham's "ANSI Common Lisp" book.  It's a good
tutorial for someone familiar with programming concepts, but is also
exhaustive (he does dismiss "loop").

Speaking of "loop", I've been using "iterate"
<http://common-lisp.net/project/iterate/> instead.  It gives most of the
expressiveness of loop, but fits better into the syntax of lisp (which
means that the editor indents it better).  The nice thing is that it's just
a normal lisp package, and works with whatever implementation I have.

As a (somewhat perverse) example, I wrote this macro last night.

(defmacro encode-packet ((sftp id) &body contents)
  (multiple-value-bind (length-constant lets length-terms)
      (iter (for (type form) :in contents)
            (let ((sym (gensym "FIELD")))
              (collect `(,sym ,form) :into lets)
              (ecase type
                (byte (sum 1 :into length-constant))
                (uint32 (sum 4 :into length-constant))
                ((string byte-string)
                 (sum 4 :into length-constant)
                 (collect `(length ,sym) :into length-terms))
                (packed-attributes
                 (collect `(packed-attributes-length ,sym)
                   :into length-terms))))
            (finally (return (values length-constant lets length-terms))))
    (let ((encoder (gensym "ENCODER"))
          (length (gensym "LENGTH")))
      `(let* ((,id (next-packet-id))
              ,@lets
              (,length (+ ,(+ 4 length-constant) ,@length-terms))
              (,encoder (make-encoder ,length)))
         ,@(iter (for (type form) :in contents)
                 (for (sym _) :in lets)
                 (let* ((type-name (symbol-name type))
                        (encoder-name (concatenate 'string "ENCODE-" 
type-name)))
                   (collect `(,(intern encoder-name) ,encoder ,sym))))
         (send-packet ,sftp ,encoder)
         ,id))))

which takes an expression like:

  (encode-packet (sftp id)
    (byte +fxp-open+)
    (uint32 id)
    (string filename)
    (uint32 pflags)
    (packed-attributes (pack-attributes atts)))

and turns it into:

(LET* ((ID (NEXT-PACKET-ID))
       (#:FIELD2107 +FXP-OPEN+)
       (#:FIELD2108 ID)
       (#:FIELD2109 FILENAME)
       (#:FIELD2110 PFLAGS)
       (#:FIELD2111 (PACK-ATTRIBUTES ATTS))
       (#:LENGTH2113
        (+ 17 (LENGTH #:FIELD2109) (PACKED-ATTRIBUTES-LENGTH #:FIELD2111)))
       (#:ENCODER2112 (MAKE-ENCODER #:LENGTH2113)))
  (ENCODE-BYTE #:ENCODER2112 #:FIELD2107)
  (ENCODE-UINT32 #:ENCODER2112 #:FIELD2108)
  (ENCODE-STRING #:ENCODER2112 #:FIELD2109)
  (ENCODE-UINT32 #:ENCODER2112 #:FIELD2110)
  (ENCODE-PACKED-ATTRIBUTES #:ENCODER2112 #:FIELD2111)
  (SEND-PACKET SFTP #:ENCODER2112)
  ID)

It's complicated because the resulting code needs to pre-compute the length
of the packet for the encoder, and then emit various pieces of code to
encode the data in the packets.  The SFTP packets contain variable length
fields.

I'm using this as part of a package I'm writing to talk to an SFTP server.

David

--
[email protected]
http://www.kernel-panic.org/cgi-bin/mailman/listinfo/kplug-lpsg

Reply via email to