Please read on for my plea for help formalizing the semantics of Parenscript identifiers.
John Fremlin wrote:
...
Perhaps compiler macros (or something more powerful like SBCL's deftransform) could be included?
I am not intimately familiar with either of these features, though I would appreciate an explanation.
I am certainly interested in opening up and advancing the compilation architecture. Since I am not exactly a seasoned compiler programmer I do not know exactly how to do this effectively. I have found a few articles about the subject:
"An Architecture for an Open Compiler:" http://www2.parc.com/spl/groups/eca/pubs/papers/Lamping-IMSA92/for-web.pdf "What a Metaobject Protocol Based Compiler Can Do For Lisp:" http://www2.parc.com/csl/groups/sda/publications/papers/Kiczales-MOPs-for-Li...
I'd quite like the ability to delay the emission of JavaScript code, so that definitions can be emitted only when the call tree explicitly draws in a particular function or variable. I guess the information for these delayed definitions would belong in the compilation environment. (Maybe a later version ;-)
This certainly seems like a reasonable feature to include in future versions.
;; enter the user package ; changes current package in compilation environment (in-package :parenscript-user)
As Henrik said, some people might want to consider a ParenScript package to be JavaScript object. So that here one might emit
with (parenscript-user) { ... }
(Just throwing out random ideas with complicated ramifications. Personally I prefer the prefixed name approach but some JavaScript megalibraries do stash everything in one object.)
[...]
with() opens up a can of worms, but placing function/variable definitions into a single object is in the game plan. Right now, while I try to get something working, I am using a strategy that prefixes variable names.
ATTN: I would like to request some help figuring out how exactly Parenscript identifiers will work. A Parenscript identifier is exactly a lisp symbol. Lisp symbols have associated Lisp packages. Likewise, identifiers should have an associated Parenscript package.
There are two problems right now that make it unclear how to implement identifiers: (1) issues with serialization and (2) complications trying to map lisp symbols to Parenscript identifiers.
1. Serialization-wise, it is unclear what the best semantics are for Parenscript identifiers sometimes. Consider the following code:
(in-package :parenscript-user) (slot-value thing 'property)
If identifiers are to be prefixed before compilation, then this may compile to something like:
thing.parenscriptUser_property
I think this is the correct behavior, but it is confusing. A solution is to introduce a "global" package that compiles without identifier prefixes:
(in-package :parenscript-user) (slot-value thing 'global::property) ; => thing.property
Some syntax sugar might make this more manageable. For example, using keywords instead of global symbols:
(in-package :parenscript-user) (slot-value thing :property) ; => thing.property
If we go the other way and have quoted identifiers serialize without prefixes, we might end up with other confusing semantics:
(setf (slot-value maple 'tree:root) "sprawling.") (setf (slot-value maple 'etymology:root) "Old English.") (alert (slot-value maple 'tree-root)) ; expected: "sprawling"
This would be a confusing compilation:
maple.root = "sprawling" maple.root = "Old English." alert (maple.root)
2. Determining the Parenscript package associated with a given Lisp symbol seems difficult. This is as a result of the ability of Lisp packages to import symbols. Here is the basic functionality for determining the Script package of a Lisp symbol:
(defun *lisp-to-paren-package-table* (make-hash-table) "Maps a lisp package to a script package.")
(defun lisp-to-paren-package (lisp-package) "Gets a script package corresponding to the given Lisp package." (gethash lisp-package *lisp-to-paren-package-table*))
(defun symbol-paren-package (symbol) "Gets the Parenscript package associated with a Lisp symbol." (lisp-to-paren-package (symbol-package symbol))
To me this could introduce some confusing circumstances when a Script package's Lisp package uses symbols from another package. The most common are conflicts from the common-lisp package. Imagine a Parenscript package "paren-psos" has a (defclass) macro--a symbol imported from common-lisp. The standard "parenscript" package has many forms associated with (e.g. defvar, defun)
;; parenscript code: package definitions (defpackage parenscript (:lisp-package :parenscript) (:second-lisp-packages :common-lisp))
(defpackage paren-psos (:lisp-package :paren-psos))
;; lisp code: (in-package :parenscript) (defjsmacro defhappyclass (name slots &rest options) `(progn (paren-psos:defclass ,name ,slots ,@options) (parenscript:setf (slot-value happy-classes ,(string name)) ,name)))
The compiler will need to resolve the script package of the paren-psos:defclass symbol, but will run into problems if that lisp symbol is imported from the common-lisp package. (In lisp, (symbol-package 'paren-psos:defclass) returns COMMON-LISP.) It will end up thinking that defclass is an identifier from the "parenscript" script package. This is confusing. One way to get around it is to make paren-psos shadow some symbols from the packages it uses. This might be sufficient, especially if we add compiler warnings when a parenscript macro is defined and the lisp package of its symbol is different from the current package.
Package resolution is not a problem using a separate Parenscript reader (in fact I have already done it), but using when defining Parenscript macros in Lisp we are confined to the standard Lisp reader.
Any thoughts on these two problems would be appreciated. I am sort of stuck.
Red