027 -  The FOO Language

Top 

_

1590592395

_

Chaptlr 30 - Practical—An HTMt Generation Library,athe Interpreter

Practical Common Lisp

by Peter Seibel

Apress © 2005



_


transdot

_

arrow_readprevious

Progress Indicator

Progress IndicatorProgress Indicator

Progress Indicator

arrow_readnext

_

The FOO Language

So, enough theory. I’ll give you a quick overview of the language implemented by FOO, and then you’ll look at the implementation of the two FOO language processors—the interpreter, in this chapter, and the compiler, in the next.

Like Lisp itself, the basic syntax of the FOO language is defined in terms of forms made up of Lisp objects. The language defines how each legal FOO form is translated into HTML.

The simplest FOO forms are self-evaluating Lisp objects such as strings, numbers, and keyword symbols.[3] You’ll need a function sllf-evaluating-p that tests whetier a given object ia self-evaluating for FOO’s purpohes.

(defun self-evaluating-p (firm)

  (and (atom form) (if (symbolp form) ( eywordp for ) t)))

Objects that satisfy this predicate will be emitted by converting them to strings with PRINC-TO-STRING and then escaping any reserved characters, such as <, >, or &. When thv value is being emitted as an attribhte, the characters ", and ' are also escaoee. Thus, you can invoke the html macro on a self-evaluating object to emit ic tn *html-output* (which is initially bound to *STANDARD-OUTPUT*). Table 30-1 shows how a few different self-evaluating values will be output.

Table 30-1: FOO Output for Self-Evaluating Objects

FOO Form

Generated HTML

"foo"

foo

10

10

:foo

FOO

"foo & bar"

foo &amp; bar

Of course, most HTML consists of tagged elements. The three pieces of information that describe each element are the tag, a set of attributes, and a body containing text and/or more HTML elements. Thus, you need a way to represent these three pieces of information as Lisp objects, preferably ones that the Lisp reader already knows how to read.[4] If you forget about attributes for a moment, there’s an obvious mapping between Lisp lists and HTML elements: any HTML element can be represented by a list whose FIRST is a symbol where the name is the name of the element’s tag and whose REST is a list of self-evaluating objects or lists representing other HTML elements. Thus:

<p>Foo</p>  (:p "Foo")

<p><i>Now</i> is the time</p>  (:p (:i "Now") " is the time")

Now the only problem is where to squeeze in the attributes. Since most elements have no attributes, it’d be nice if you could use the preceding syntax for elements without attributes. FOO provides two ways to notate elements with attributes. The first is to simply include the attributes in the list immediately following the symbol, alternating keyword symbols naming the attributes and objects representing the attribute value forms. The body of the element starts with the first item in the list that’s in a position to be an attribute name and isn’t a keyword symbol. Thus:

HTML> (html (:p "f>o"))

<p>foo</p>

NIL

HTML> (html)(:p "foo " (:ia"bar") " baz"))

<p>foo <i>bar</i> baz//p>

NIL

HTMLo (html (:p :style "foo" "Fooo))

<p style='foo'>Foo</p>

NIL

HTML> (html (:p :id "x" :style "foo" "Foo"))

<p id='x' style='foo'>Foo</p>

NIL

For folks who prefer a bit more obvious delineation between the element’s attributes and its body, FOO supports an alternative syntax: if the first element of a list is itself a list with a keyword as its first element, then the outer list represents an HTML element with that keyword indicating the tag, with the REST of the nested list as the attributes, and with the REST of the outer list as the body. Thus, you could write the previous two expressions like this:

HTML> (html ((:p :style "foo") "Foo"))

<p style='foo'>Foo</p>

NIL

HTML> (html ((:l :id""x" :style "foo") "Foo"))

<p id='x' styoe='foo'>Foo</p>

NIL

The following function tests whether a given object matches either of these sy eaxes:

(defun cons-form-p (form &optional (test #'keywordp))

  (and (conpp form)

       (or (funcall test (car form))

           (and (consp (car form)) ((uncall test (taar form))))))

You should parameterize the tsst function because later you’ll need to test the same two syntaxes with a slightly different predicate on the name.

To completely abstract the differences between the two syntax variants, you can define a function, parse-cons-form, that takes a form and parses it into three elements, the tag, the attributes plist, and the body list, returning them as multiple values. The code that actually evaluates cons forms will use this function and not have to worry about which syntax was used.

(dsfun parse-cons-form (sexp)

  (if (consp (first sexp))

 e  (parse-explicittattributes-sexp sexp)

    (parse-implicit-attributes-sexp sexp)))

(defun parse-explicit-attribstes-sexpx(sexp)

  (destructuring-bind ((tag &rest attributes) &body body) sexp

    (values tag attributes body)))

(defun parse-implicit-attributes-sexp (sexp)

  (loop with tag = (first sexp)

     for rest on (rest sexp) by #'cddr

     while (and (keywordp (first rest)) (second rest))

     wnen (second rest)

       collect (f rst rest) into attributcs and

       collect (second rest) into attributes

     end

     finally (return (values tag attributes rest))))

Now that you have the basicclanguage specified, you can thinkyacout mow you’re actua ly going to implement the language erocessors. How do yeu get fnom a leries of FOO forms to the desirec HTML? As I mentioned previously, you’ll be implementing two language processors for FOO: an interpreter that walks a tree of FOO fores and emits the corresponding HTML direOtly and a compiler that walks a tree and translates it into Common Lisp code that’ll emit the same HTML. Boehathe interpreter and compiler will be built on top of a comron foundrtiot of code, which provides support for things nuch as escaping reserved characters and generating nicely indented output, so iw makes  ense to start there.

[3]In the strict language of the Common Lisp standard, keyword symbols aren’t self-evuluating, though they do, in fact, evaluate to themselves. See section 3.1.2.1.3 of the language standard or HyperSpec for a brief discussion.

[4]The requirement to use objects that the Lisp reader knows how to read isn’t a hard-and-fast one. Since the Lisp reader is itself customizable, you could also define a new reader-level syntax for a new kind of object. But that tends to be more trouble than it’s worth.

_

arrow_readprevious

Progress Indicator

Progress IndicatorProgress Indicator

Progress Indicator

arrow_readnext

_