hsx/src/dsl.lisp

93 lines
3 KiB
Common Lisp
Raw Normal View History

2024-10-03 02:00:19 +00:00
(defpackage #:hsx/dsl
2024-05-25 16:26:26 +00:00
(:use #:cl)
(: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
;;;; hsx macro
2024-05-31 22:22:01 +00:00
(defmacro hsx (form)
"Detect HSX elements and automatically import them."
(detect-elements form))
2024-05-26 10:48:09 +00:00
2024-12-17 07:14:46 +00:00
(defun detect-builtin-symbol (sym)
2024-12-12 03:57:05 +00:00
(multiple-value-bind (builtin-sym kind)
(find-symbol (string sym) :hsx/builtin)
(and (eq kind :external) builtin-sym)))
(defun start-with-tilde-p (sym)
(string= "~" (subseq (string sym) 0 1)))
2024-12-17 07:14:46 +00:00
(defun detect-component-symbol (sym)
(and (start-with-tilde-p sym) sym))
(defun detect-elements (form)
2024-12-12 03:57:05 +00:00
(check-type form cons)
(let* ((head (first form))
(tail (rest form))
(well-formed-p (listp tail))
(detected-sym (and (symbolp head)
(not (keywordp head))
2024-12-17 07:14:46 +00:00
(or (detect-builtin-symbol head)
(detect-component-symbol head)))))
(if (and well-formed-p detected-sym)
(cons detected-sym
2024-12-12 03:57:05 +00:00
(mapcar (lambda (sub-form)
(if (consp sub-form)
(detect-elements sub-form)
2024-12-12 03:57:05 +00:00
sub-form))
tail))
form)))
;;;; 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-12-17 07:14:46 +00:00
"Define an HSX function component.
The component name must start with a tilde (~).
2024-12-17 07:14:46 +00:00
Component properties must be declared using &key, &rest, or both.
The body of the component must produce a valid HSX element."
(unless (start-with-tilde-p ~name)
(error "The component name must start with a tilde (~~)."))
(unless (or (null props)
(member '&key props)
(member '&rest props))
2024-12-17 07:14:46 +00:00
(error "Component properties must be declared using &key, &rest, or both."))
(let ((%name (symbolicate '% ~name)))
`(eval-when (:compile-toplevel :load-toplevel :execute)
(defun ,%name ,props
,@body)
(defhsx ,~name (fdefinition ',%name)))))