Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- html> (defmacro defelement (tag-name)
- #?{A Macro which defines another macro with name TAG-NAME which formats
- a string containing the HTML code (a tag with attributes and contents).
- Examples:
- (deftag form) ; => form
- (deftag input) ; => input
- (form :action "/process-form.html" :method (if *use-get* 'get 'post)
- (input :type 'checkbox
- :disabled nil ; Attribute will be ignored because have nil value
- :selected t) ; Will be transformed to "selected='selected'"
- "Tags may contain any object including other tags."
- :class 'login)
- =>
- "<form action='/process-form.html' method='get' class='login'>
- <input type='checkbox' selected='selected'>
- Tags may contain any object including other tags."
- As you can see in the example above defined macros can accept any number of tag
- attributes and tag contents in any order. Attribute values and contens may have
- any type and can be computed at the runtime. Attribute names must be a static keyword.
- Also there is a support for boolean attributes (which has value T or NIL).}
- ;; body for (defelement (tag-name)) here
- `(defmacro ,tag-name (&rest contents-or-attributes)
- ;; Firstly walk through arguments and separate them into two lists.
- ;; The first will contain the code which formats HTML element contents.
- ;; And the second list will contain a code which formats tag attributes.
- (let ((contents '(list))
- (attributes '(list)))
- (push nil contents-or-attributes)
- (loop for (current next) on contents-or-attributes do
- ;; Every element which not a keyword and not stands after a keyword
- ;; is tag contents
- (when (and (not (keywordp current))
- (not (keywordp next)))
- (push next contents))
- ;; Every pair (a keyword + not a keyword) is an attribute.
- (when (and (keywordp current)
- (not (keywordp next)))
- (push `',current attributes)
- (push
- (cond ((eq t next) current) ; boolean attribute checks
- ((and (listp next) (not (eq 'quote (car next))))
- `(let ((name ,current) (value ,next))
- (if (eq t value) name value)))
- (t next))
- attributes))
- ;; reversing lists because elements was pushed in reverse order
- finally (setf contents (nreverse contents)
- attributes (nreverse attributes)))
- ;; Now we ready to generate actual code which formats a string
- ;; containing the HTML code (a tag with attributes and contents).
- `(format nil
- ,(format nil "<~a~a>~a</~3:*~a>"
- ',tag-name
- "~{~*~@[ ~:*~a='~a'~]~}"
- "~{~@[~a~]~}")
- ,attributes
- ,contents))))
- defelement
- html> (defelement a)
- a
- html> (macroexpand '(a :href 1 1))
- (format nil "<a~{~*~@[ ~:*~a='~a'~]~}>~{~@[~a~]~}</a>" (list ':href 1)
- (list 1 nil))
- t
- html> (macroexpand '(a :href 1 1))
- (format nil "<a~{~*~@[ ~:*~a='~a'~]~}>~{~@[~a~]~}</a>" (1 ':href list ':href 1)
- (nil 1 list 1 nil))
- t
- html>
Add Comment
Please, Sign In to add comment