diff options
author | Kaz Kylheku <kaz@kylheku.com> | 2023-06-01 20:20:40 -0700 |
---|---|---|
committer | Kaz Kylheku <kaz@kylheku.com> | 2023-06-01 20:20:40 -0700 |
commit | 257672152b93c08f573db2912eef2b9a4145d5f5 (patch) | |
tree | 8ea0a46605ce55b6a959c4ee3979531068e0e595 /test | |
parent | 2e5ec1a87b614dfce7d9105e841b71d18691b98a (diff) | |
download | tl-who-257672152b93c08f573db2912eef2b9a4145d5f5.tar.gz tl-who-257672152b93c08f573db2912eef2b9a4145d5f5.tar.bz2 tl-who-257672152b93c08f573db2912eef2b9a4145d5f5.zip |
New feature: deftag macro.
The deftag macro targets the HTML markup syntax itself
rather than Lisp forms within it. deftag macros
masquerade as tag markup, but rewrite themselves into
other markup. They can produce multiple elements
using (progn ...) and can move element attribute
material into the avlue position and vice versa.
They can destructure attributes using keyword parameters,
and easily default the values of required attributes
and such.
* packages.tl (tl-who): New symbol, deftag.
* specials.tl (*tag-macro*): New special variable,
holding a hash table of deftag definitions.
* who.tl (process-tag): After a tag is parsed,
we check whether there is a macro defined for it.
If so we call its expander lambda. This is done
here because this function has the parsed pieces
of the tag. The higher level above this function
doesn't, and after this function, everything
is just a flat list of strings and other objects.
Here we recognize whether the macro put out a
progn shape, and iterate over the multiple items
it contains.
(scrub-kw-args): New function. This is a helper
function used by deftag expanders to remove, from
the attribute rest parameter, those keyword
arguments which were captured by the named parameters.
(deftag): New macro.
* test/simple.tl: New test cases 50-54 targeting deftag.
* README.md: Documented.
Diffstat (limited to 'test')
-rw-r--r-- | test/simple.tl | 54 |
1 files changed, 54 insertions, 0 deletions
diff --git a/test/simple.tl b/test/simple.tl index 6f9464d..0efad02 100644 --- a/test/simple.tl +++ b/test/simple.tl @@ -524,3 +524,57 @@ (contains "requires" x)) :good))) :good) + + +;;; 50 +;;; Test deftag +(deftag :abc (foo (bar 2) . other-attrs) body + ^(:p :foo ,foo :bar ,bar ,*body " world!")) + +(test (with-html-output-to-string (out) + (:abc :foo 42 :bar "x" "Hello,")) + "<p foo='42' bar='x'>Hello, world!</p>") + +;;; 51 +;;; Test chained expansion, and attr keyword arg defaulting +(deftag :xyz (. other-attrs) body + ^(:abc :foo 1 "xyz" ,*body)) + +(test (with-html-output-to-string (out) + (:xyz "abc")) + "<p foo='1' bar='2'>xyzabc world!</p>") + +;;; 52 +;;; Test invalid return: not tag material. +(deftag :bad (. rest) body + ^(notkeyword)) + +(test (catch + (eval ^(with-html-output-to-string (out) (:bad))) + (error (x) (if (contains "non-tag" x) + :good))) + :good) + +;;; 53 +;;; Test deftag producing multiple consecutive tags +(deftag :multi (. rest) body + ^(progn (:p) (:q) (:r))) + +(test (with-html-output-to-string (out) + (:multi)) + "<p></p><q></q><r></r>") + + +;;; 54 +;;; Complex deftag +(deftag :easy-input (label (name (gensym)) + (id name) (type "text") . other-attrs) default + ^(progn + (:label :for ,name ,label) + (:input :name ,name :id ,id :type ,type + ,*other-attrs :value (progn ,*default)))) + +(test (with-html-output-to-string (out) + (:easy-input :name "foo" :id "foo-23" :style "style" :label "lab" "123")) + "<label for='foo'>lab</label> \ + <input name='foo' id='foo-23' type='text' style='style' value='123' />") |