2024-10-03 02:00:19 +00:00
|
|
|
(defpackage #:hsx/dsl
|
2024-05-25 16:26:26 +00:00
|
|
|
(:use #:cl)
|
2024-06-06 05:59:46 +00:00
|
|
|
(:import-from #:alexandria
|
|
|
|
#:make-keyword
|
|
|
|
#:symbolicate)
|
|
|
|
(:import-from #:hsx/element
|
|
|
|
#:create-element)
|
|
|
|
(:export #:hsx
|
|
|
|
#:deftag
|
|
|
|
#:defcomp))
|
2024-10-03 02:00:19 +00:00
|
|
|
(in-package #:hsx/dsl)
|
2024-05-25 16:26:26 +00:00
|
|
|
|
2024-06-06 05:59:46 +00:00
|
|
|
;;;; hsx macro
|
|
|
|
|
2024-05-31 22:22:01 +00:00
|
|
|
(defmacro hsx (form)
|
2024-06-11 10:18:47 +00:00
|
|
|
"Detect built-in HSX elements and automatically import them."
|
2024-05-31 22:22:01 +00:00
|
|
|
(find-builtin-symbols form))
|
2024-05-26 10:48:09 +00:00
|
|
|
|
2024-12-12 03:57:05 +00:00
|
|
|
(defun get-builtin-symbol (sym)
|
|
|
|
(multiple-value-bind (builtin-sym kind)
|
|
|
|
(find-symbol (string sym) :hsx/builtin)
|
|
|
|
(and (eq kind :external) builtin-sym)))
|
|
|
|
|
|
|
|
(defun find-builtin-symbols (form)
|
|
|
|
(check-type form cons)
|
|
|
|
(let* ((head (first form))
|
|
|
|
(tail (rest form))
|
|
|
|
(well-formed-p (listp tail))
|
|
|
|
(builtin-sym (and (symbolp head)
|
|
|
|
(not (keywordp head))
|
|
|
|
(get-builtin-symbol head))))
|
|
|
|
(if (and well-formed-p builtin-sym)
|
|
|
|
(cons builtin-sym
|
|
|
|
(mapcar (lambda (sub-form)
|
|
|
|
(if (consp sub-form)
|
|
|
|
(find-builtin-symbols sub-form)
|
|
|
|
sub-form))
|
|
|
|
tail))
|
|
|
|
form)))
|
2024-06-06 05:59:46 +00:00
|
|
|
|
|
|
|
;;;; defhsx macro
|
|
|
|
|
|
|
|
(defmacro defhsx (name element-type)
|
|
|
|
`(defmacro ,name (&body body)
|
|
|
|
`(%create-element ,',element-type ,@body)))
|
|
|
|
|
|
|
|
(defun %create-element (type &rest body)
|
|
|
|
(multiple-value-bind (props children)
|
|
|
|
(parse-body body)
|
|
|
|
(create-element type props children)))
|
|
|
|
|
|
|
|
(defun parse-body (body)
|
|
|
|
(cond ((and (listp (first body))
|
|
|
|
(keywordp (first (first body))))
|
|
|
|
(values (first body) (rest body)))
|
|
|
|
((keywordp (first body))
|
|
|
|
(loop :for thing :on body :by #'cddr
|
|
|
|
:for (k v) := thing
|
|
|
|
:when (and (keywordp k) v)
|
|
|
|
:append (list k v) :into props
|
|
|
|
:when (not (keywordp k))
|
|
|
|
:return (values props thing)
|
|
|
|
:finally (return (values props nil))))
|
|
|
|
(t (values nil body))))
|
|
|
|
|
|
|
|
(defmacro deftag (name)
|
|
|
|
`(eval-when (:compile-toplevel :load-toplevel :execute)
|
|
|
|
(defhsx ,name ,(make-keyword name))))
|
|
|
|
|
|
|
|
(defmacro defcomp (name props &body body)
|
2024-06-11 10:18:47 +00:00
|
|
|
"Define a function component for use in HSX.
|
|
|
|
The props must be declared with either &key or &rest (or both).
|
|
|
|
The body must return an HSX element."
|
2024-06-06 05:59:46 +00:00
|
|
|
(unless (or (null props)
|
|
|
|
(member '&key props)
|
|
|
|
(member '&rest props))
|
|
|
|
(error "Component properties must be declared with either &key, &rest, or both."))
|
|
|
|
(let ((%name (symbolicate '% name)))
|
|
|
|
`(eval-when (:compile-toplevel :load-toplevel :execute)
|
|
|
|
(defun ,%name ,props
|
|
|
|
,@body)
|
|
|
|
(defhsx ,name (fdefinition ',%name)))))
|