(defun split-components (forms) (loop :for cons :on forms :for form := (car cons) :if (symbolp (car form)) :collect form :into components :else :do (loop-finish) :finally (return (values components cons)))) (defun process-component (component) (destructuring-bind (type . options/args) component (let* ((options-p (listp (first options/args))) (args (if options-p (rest options/args) options/args)) (options (if options-p `(list ,@(first options/args)) '())) (accessors (loop :for (k v) :on args :by #'cddr :nconc (list k `(lambda () ,v)) ))) `(list ',type ,options ,@accessors)))) (defun process-entry (entry) (destructuring-bind ((name &rest options) &rest rest) entry (multiple-value-bind (components children) (split-components rest) `(list ',name ',options (list ,@(mapcar #'process-component components)) (list ,@(mapcar #'process-entry children)))))) (defmacro define-prefab (name options &body data) (let ((entries `(((,name ,@options) ,@data)))) `(list ,@(mapcar #'process-entry entries)))) (define-prefab foo () ((a :template xxx) (foo1 :bar 2) (bar1 :bar 2) ((b) (foo2 :foo2 3) (bar2 :bar2 4)))) ;; Expectations (list (list 'foo nil nil (list (list 'a '(:template xxx) (list (list 'foo1 'nil :bar (lambda () 2)) (list 'bar1 'nil :bar (lambda () 2))) (list (list 'b nil (list (list 'foo2 'nil :foo2 (lambda () 3)) (list 'bar2 'nil :bar2 (lambda () 4))) nil)))))) ;; Reality (LIST (LIST 'FOO (LIST) (LIST) (LIST (LIST 'A (LIST :TEMPLATE XXX) (LIST (LIST 'FOO1 NIL :BAR (LAMBDA () 2)) (LIST 'BAR1 NIL :BAR (LAMBDA () 2))) (LIST (LIST 'B (LIST) (LIST (LIST 'FOO2 NIL :FOO2 (LAMBDA () 3)) (LIST 'BAR2 NIL :BAR2 (LAMBDA () 4))) (LIST)))))))