moderninterpreters / markup

MARKUP provides a reader-macro to read HTML tags inside of Common Lisp code
Apache License 2.0
70 stars 8 forks source link

Deftag Macro #8

Open charJe opened 3 years ago

charJe commented 3 years ago

Currently deftag defines a function. Would it be possible to have something like deftag* that defines a macro. I mainly want this so I can require certain tags to have certain attributes (and error at compile time if the attributes are missing).

tdrhq commented 3 years ago

Could you give me a more concrete example? I recently tried using a macro, and then found it to be error prone because it was strongly dependent on file loading order. But I'm sure we could add a separate deftag*, but give me an example so I understand its use case better.

charJe commented 3 years ago

Say I want all tab tags to have a title attribute.

(deftag tab (children &key (title (error "tab must have a title")))
  <:tab title=title >
    ,@children
  </:tab>)

Provides good run time checking, but I would prefer to catch it before that.

tdrhq commented 3 years ago

Got it, I think just making tag into a macro would not achieve what you're looking for. Markup doesn't use the #'tag (the function stored in tag) when you call <tag>, instead it calls the function stored in (get 'tag 'markup-fn). #'tag was designed for convenience, and probably isn't as performant (it let's you create the same tag with s-expressions (tag :title foo (child-tag) (child-tag)).

<tag> expands to (make-xml-tag 'tag ...). In order to build what you're asking for, we could make make-xml-tag into a macro and build some kind of extension mechanism.

Alternatively, I already have a define-compiler-macro for make-xml-tag: https://github.com/moderninterpreters/markup/blob/master/markup.lisp#L484. We could create an define-markup-compiler-macro that provides an extension point to this, so that you can create custom compile time validations.

charJe commented 3 years ago

Using a compiler macro is what I originally tried, but I guess it didn't work because the tab tag isn't actually using the #'tab function.

tdrhq commented 3 years ago

Another scenario where defmacrotag would be useful would be for i18n. Imagine a tag which basically wraps strings. At compile time, it would be nice to get a list of all the constant strings so that you can send it to a translation service, but the compile time aspect would require a macro.