Parenscipt 没有编译有效的表达式?
Parenscipt not compiling valid expression?
我有这个 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
和 args 并重新编译处理程序,它编译没有问题。
有什么想法吗?
好吧,看来我不(仍然不)理解 defpsmacro
是如何工作的。用包裹在 ps
形式中的普通 defmacro
改变它解决了这个问题。
所以宏代码现在是:
(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 专家可以解释哪里出了问题,我将不胜感激。
如果您在同一个文件中同时使用 defpsmacro
和定义的宏,就会发生这种情况。我没有时间深入研究 parenscript 代码以了解评估顺序究竟出了什么问题,但最终结果是,在编译文件时,宏定义在 'compilation' ps
形式。
作为解决方案,将您的 parenscript 宏移动到一个单独的文件中,并使您的其他代码文件依赖于它。
附带说明,:open
关键字参数上的 (ps ...)
形式是不必要的 - 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))
.
我有这个 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
和 args 并重新编译处理程序,它编译没有问题。
有什么想法吗?
好吧,看来我不(仍然不)理解 defpsmacro
是如何工作的。用包裹在 ps
形式中的普通 defmacro
改变它解决了这个问题。
所以宏代码现在是:
(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 专家可以解释哪里出了问题,我将不胜感激。
如果您在同一个文件中同时使用 defpsmacro
和定义的宏,就会发生这种情况。我没有时间深入研究 parenscript 代码以了解评估顺序究竟出了什么问题,但最终结果是,在编译文件时,宏定义在 'compilation' ps
形式。
作为解决方案,将您的 parenscript 宏移动到一个单独的文件中,并使您的其他代码文件依赖于它。
附带说明,:open
关键字参数上的 (ps ...)
形式是不必要的 - 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))
.