Guest User

Untitled

a guest
Apr 26th, 2018
76
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lisp 2.94 KB | None | 0 0
  1. html> (defmacro defelement (tag-name)
  2.  
  3. #?{A Macro which defines another macro with name TAG-NAME which formats
  4. a string containing the HTML code (a tag with attributes and contents).
  5. Examples:
  6.  (deftag form)     ; => form
  7.  (deftag input)    ; => input
  8.  (form :action "/process-form.html" :method (if *use-get* 'get 'post)
  9.    (input :type 'checkbox
  10.           :disabled nil   ; Attribute will be ignored because have nil value
  11.           :selected t)    ; Will be transformed to "selected='selected'"
  12.    "Tags may contain any object including other tags."
  13.    :class 'login)
  14. =>
  15.  "<form action='/process-form.html' method='get' class='login'>
  16.    <input type='checkbox' selected='selected'>
  17.    Tags may contain any object including other tags."
  18. As you can see in the example above defined macros can accept any number of tag
  19. attributes and tag contents in any order. Attribute values and contens may have
  20. any type and can be computed at the runtime. Attribute names must be a static keyword.
  21. Also there is a support for boolean attributes (which has value T or NIL).}
  22.  
  23.   ;; body for (defelement (tag-name)) here
  24.   `(defmacro ,tag-name (&rest contents-or-attributes)
  25.      ;; Firstly walk through arguments and separate them into two lists.
  26.      ;; The first will contain the code which formats HTML element contents.
  27.      ;; And the second list will contain a code which formats tag attributes.
  28.      (let ((contents '(list))
  29.        (attributes '(list)))
  30.        (push nil contents-or-attributes)
  31.        (loop for (current next) on contents-or-attributes do
  32.         ;; Every element which not a keyword and not stands after a keyword
  33.         ;; is tag contents
  34.         (when (and (not (keywordp current))
  35.                (not (keywordp next)))
  36.           (push next contents))
  37.  
  38.         ;; Every pair (a keyword + not a keyword) is an attribute.
  39.         (when (and (keywordp current)
  40.                (not (keywordp next)))
  41.           (push `',current attributes)
  42.           (push              
  43.            (cond ((eq t next) current)  ; boolean attribute checks
  44.              ((and (listp next) (not (eq 'quote (car next))))
  45.               `(let ((name ,current) (value ,next))
  46.              (if (eq t value) name value)))
  47.              (t next))
  48.            attributes))
  49.  
  50.       ;; reversing lists because elements was pushed in reverse order
  51.       finally (setf contents (nreverse contents)
  52.             attributes (nreverse attributes)))
  53.  
  54.        ;; Now we ready to generate actual code which formats a string
  55.        ;; containing the HTML code (a tag with attributes and contents).
  56.        `(format nil
  57.         ,(format nil "<~a~a>~a</~3:*~a>"
  58.             ',tag-name
  59.             "~{~*~@[ ~:*~a='~a'~]~}"
  60.             "~{~@[~a~]~}")
  61.         ,attributes
  62.         ,contents))))
  63. defelement
  64. html> (defelement a)
  65. a
  66. html> (macroexpand '(a :href 1 1))
  67. (format nil "<a~{~*~@[ ~:*~a='~a'~]~}>~{~@[~a~]~}</a>" (list ':href 1)
  68.         (list 1 nil))
  69. t
  70. html> (macroexpand '(a :href 1 1))
  71. (format nil "<a~{~*~@[ ~:*~a='~a'~]~}>~{~@[~a~]~}</a>" (1 ':href list ':href 1)
  72.         (nil 1 list 1 nil))
  73. t
  74. html>
Add Comment
Please, Sign In to add comment