Parenscipt не компилирует правильное выражение?
У меня есть этот макрос parenscript:
;;; Parenscript macro for showModal() and close() methods for pop-up dialogs.
;;;Takes the dialog's id, button for opening the dialog's id, and closing button's id.
(defpsmacro open-close-modal-dialog (dialog-id element-id-1 element-id-2 &key open close open-args close-args)
(let ((dialog (ps-gensym)))
`(progn
(setf ,dialog (chain document (get-element-by-id ,dialog-id)))
(setf (chain document (get-element-by-id ,element-id-1) onclick)
(lambda (,@open-args)
(progn
,@open
(funcall (chain ,dialog show-modal)))))
(setf (chain document (get-element-by-id ,element-id-2) onclick)
(lambda (,@close-args)
(progn
,@close
(funcall (chain ,dialog close))))))))
И я использую его в обработчике Hunchentoot следующим образом:
(define-easy-handler (student-name :uri "/student-info") (name)
(let ((student (student-from-name name)))
(standard-page (:title "Ashtanga Yoga Osaka | Student Page"
:script (ps
(defun init ()
(open-close-modal-dialog "editPassDialog" "getPass" "submitPass"
;; This is the pop-up dialog
:open (ps(defvar frm (chain document (get-element-by-id "editPass"))))))
(setf (chain window onload) init)))
;; Main form
(:form :action "/edit-student" :method "post" :id "editStudent"
(:p "Name" (:input :type "text" :name "name" :class "txt" :value (format nil "~A" (name student))))
(:P "Email" (:input :type "email" :name "email" :class "txt" :value (format nil "~A" (email student))))
(:p "Passes" (:select :name "passlist"
(dolist (pass (pass student))
(htm
(:option :id "pass" :value (pass->json pass)
(fmt "~A ~A" (print-month (getf pass :date))
(print-year (getf pass :date)))))))
(:button :type "button" :id "getPass" :class "btn" "Get Pass"))
(:input :type "hidden" :name "previous" :value nil) ; previous value of the pass
(:input :type "hidden" :name "old-name" :value name) ; old name of student, used for retrieving the correct instance
(:p (:input :type "submit" :value "Edit Info" :class "btn")))
;; Pop-up dialog for editing passes
(:dialog :id "editPassDialog"
(:h1 "Edit Pass")
(:form :action "#" :method "post" :id "editPass"
(:p "Date bought" (:input :type "text" :name "date" :class "txt"))
(:p "Type" (:input :type "text" :name "type" :class "txt"))
(:p "Amount Paid" (:input :type "text" :name "amt"))
(:p (:button :type "button" :class "btn" :id "submitPass" "Edit Pass")))))))
Теперь, когда я загружаю систему через Quicklisp, я получаю эту ошибку:
; caught ERROR:
; during macroexpansion of
; (PS
; (DEFUN INIT # ...)
; (SETF #)).
; Use *BREAK-ON-SIGNALS* to intercept.
;
; The Parenscript form (DEFVAR FRM
; (CHAIN DOCUMENT
; (GET-ELEMENT-BY-ID
; editPass))) cannot be compiled into an expression.
Что странно, потому что я могу определить эту форму в REPL:
SHALA-SYS> (macroexpand-1 '(ps (defvar frm (chain document (GET-ELEMENT-BY-ID "editPass")))))
"var frm = document.getElementById('editPass');"
И если я уберу :open
и это аргументы, система загружается, потом добавляю :open
и возвращает обратно и перекомпилирует обработчик, и он без проблем компилируется.
Какие-нибудь мысли?
2 ответа
Это произойдет, если у вас есть оба defpsmacro
и использование определенного макроса в том же файле. У меня не было времени углубиться в код паранскрипта достаточно глубоко, чтобы понять, что именно не так с порядком оценки, но конечный результат заключается в том, что при компиляции файла определение макроса не существует во время ' сборник ps
форма.
В качестве решения переместите свои макросы parenscript в отдельный файл и сделайте так, чтобы другие файлы кода зависели от него.
Как примечание стороны, (ps ...)
форма на :open
аргумент ключевого слова не нужен - open
параметр уже раскрыт внутри кода parenscript в вашем макросе. Дополнительно, ,@
неверно при расширении open
а также - и эти две ошибки отменяют друг друга.
(progn
,@open
(funcall (chain ,dialog show-modal)))
;; with :open (ps (foo)) expands to
"ps; foo(); dialog.showModal();"
;; and with :open (foo) expands to
"foo; dialog.showModal();"
(progn
,open
(funcall (chain ,dialog show-modal)))
;; would expand :open (ps (foo)) to
"ps(foo()); dialog.showModal();"
;; and :open (foo) to
"foo(); dialog.showModal();"
;; which is what you intended.
Также, funcall
в этом куске кода нет необходимости; Вы могли бы просто использовать (chain ,dialog (show-modal))
,
Итак, кажется, я не (до сих пор не понимаю), как defpsmacro
работает. Меняя это с нормальным defmacro
завернутый в ps
Форма решила проблему.
Итак, код макроса теперь:
(ps (defmacro open-close-modal-dialog (dialog-id element-id-1 element-id-2 &key open close open-args close-args)
(let ((dialog (ps-gensym)))
`(progn
(setf ,dialog (chain document (get-element-by-id ,dialog-id)))
(setf (chain document (get-element-by-id ,element-id-1) onclick)
(lambda (,@open-args)
(progn
,@open
(funcall (chain ,dialog show-modal)))))
(setf (chain document (get-element-by-id ,element-id-2) onclick)
(lambda (,@close-args)
(progn
,@close
(funcall (chain ,dialog close)))))))))
Если какой- нибудь эксперт ParenScript сможет объяснить, что случилось, я был бы очень благодарен.