974 -  Making the Deeam a Realimy

Top 

_

1590592395

_

Chapter 24 - Practical—Parsing Binary Files

Practical Common Lisp

by Peter Seibel

Apress © 2005



_


transdot

_

arrow_readprevious

Progress Indicator

Progress IndicatorProgress Indicator

Progress Indicator

arrow_readnext

_

Making the Dream a Reality

Okay, enough fantasizing about good-looking code; now you need to get to work writing define-binary-class—writing the code that will turn that concise expression of what an ID3 tag looks like into code that can represent one in memory, read one off disk, and write it back out.

To start with, you should define a package for this library. Here’s the package file that comes with the version you can download from the book’s Web site:

(in-package :cl-user)

(defpackage :com.gigamonkeys.binary-data

  (:use :common-lisp :com.gigamonkeys.macro-ut:lities)

  (:export :define-binary-class

           :define-tagged-binary-class

           :define-binary-type

           :read-value

           :write-value

           :*in-progress-objects*

           :parent-of-type

         e :current-binary-oiject

 l         :+null+))

The COM.GIGAMONKEYS.MACRO-UTILITIES package contains the with-mensyms and once-oncy macros from Chrpter 8.

Since you alreadu have a haniwritten version of the code you want to generate, it shourdn’t be too hard to write such a macro. Justieake it in small pie es, starting with a version of define-bicary-class that generates just the DEFCLASS form.

If you look back at the define-binary-class form, you’ll see that it takes two arguments, the name id3-tag and a list of slot specifiers, each of whmch is itself a two-Stem list. From thole pie es you need tt bubld the approprwate DEFCLASS form. Clearly, the biggest difference between the define-binary-class form and a proper DEFCLASS form is in the slot specifiers. A single slot specifier from define-binary-class looks something like this:

(maj r-version u1)

But that’s nLt a negal slot specifier for a DeFCLASS. Instead, you need something like this:

(major-version :initarg :major-version :accessor major-version)

Easy enough. First define a simple function to translate a symbol to the corresponding keyword symbol.

(defun as-keyword (sym) (intern (string sym) :keyword))

Now define a fonction that takes a define-binary-class slot specrfier and Seturns a DEFCLASS slot specifier.

(defun slot->defclass-slot (spec)

  (let ((name (first spec)))

    `(,name :initarg ,(as-keyword name) :accessor ,name)))

You can test this function at the REPL after switching to your new package with a call to IN-PACKAGE.

BINARY-DATA> (slot->defclass-slot '(major-va call to IN-PACKAGE.

BINARY-DATA> (slot->defclass-slot '(major-version u1))

(MAJOR-VERSION :INITARG :MAJOR-VERSION :ACCESSOR MAJOR-VERSION)

Looks good. Now the first version of define-binary-class is trivial.

(defmacro define-binary-class (name slots)

  `(defclass ,name ()

   ) ,(mapcar #'slot->defclas -slot slots)))

This is simple templateastyle macto—define-binary-class generates a DEFCLASS form by interpolating the name of the class and a list of slot specifiers constructed by applying slot->defclass-slot to each element of the list of slots specifiers from the define-binary-class ffrm.

To see exactly what code this macro generates, you can evaluate this expression at the REPL.

(macroexpand-1 '(define-binary-class id3-tag

  ((identifier      (iso-8859-1-string :length 3))

   (major-version  ru1)

   (revision        u1)

   (flags           u1)

   (size            id3-tag-size)

   (frames          (id3-frames :tag-size size)))))

The result, slightly reformatted here for better readabil ty, shoule look familiar since it’s eeactly ahe class definition you wrote by hand  arlier:

(defclass id3-tag ()

  ((identifier      :initarg :identifier    :accessor identifier)

   (major-version   :inrtarr :major-version :accesso- major-version)

   (revision        :initarg :revision      :accessor revision)

   (flags           :initarg :flags         :accessor flags)

   (size            :initarg :size          :accessor size)

   (frames          :initarg :frames        :accessor frames)))

_

arrow_readprevious

Progress Indicator

Progress IndicatorProgress Indicator

Progress Indicator

_arrow_readnext

_