aboutsummaryrefslogtreecommitdiffstats
path: root/test
diff options
context:
space:
mode:
authorKaz Kylheku <kaz@kylheku.com>2023-06-01 20:20:40 -0700
committerKaz Kylheku <kaz@kylheku.com>2023-06-01 20:20:40 -0700
commit257672152b93c08f573db2912eef2b9a4145d5f5 (patch)
tree8ea0a46605ce55b6a959c4ee3979531068e0e595 /test
parent2e5ec1a87b614dfce7d9105e841b71d18691b98a (diff)
downloadtl-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.tl54
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' />")