Page 1 of 1
``unpacking''
Posted: Tue May 31, 2011 7:36 am
by mtl^urass
I'm trying to create a macro or something that will run a body with all the keys in a hashtable passed to it bound to their values in a local environment. I.e if I have a hashtable *foo* which has the key 'a bound to 1 and 'b bound to 2, I would want something like this:
My first attempt was this:
Code: Select all
(defmacro with-unpacked-table (hashtable &body body)
`(let ,(loop for key being the hash-keys of (eval hashtable)
using (hash-value value)
collecting `(,key ,value))
,@body))
This obviously doesn't work because the hashtable has to be present at macro expansion time. This goes for most similar solution. The options are ugly. I could define a package and defparameter all of them inside it, etc, but that's sort of stupid. The only thing I could think of was the scheme eval where you can pass it an environment. Then I could easily write a function to convert from a hash table to whatever format the environment needs to have, and do something like this:
Code: Select all
(defmacro with-unpacked-table (hashtable &body body)
`(eval (progn ,@body)
(hash-to-env ,hashtable)))
Is there any equivalent technique in Common Lisp?
Re: ``unpacking''
Posted: Tue May 31, 2011 8:28 am
by ramarren
mtl^urass wrote:I'm trying to create a macro or something that will run a body with all the keys in a hashtable passed to it bound to their values in a local environment.
Why would you want to do that? The code must necessarily know what variables it is using if it is referring to them by name, so the obvious way would be to separate the body into a function taking those values as arguments, and then call that function with appropriate values from the hashtable.
Still, there are ways to achieve this directly. One option is to use
PROGV, but then you would have to declare all those variables used as
special. This is because names of lexical variables are compiled away.
The use of EVAL is a possibility of course. In CL you can pass a lexical environment, but you can trivially establish it by surrounding the form to be executed with a generated LET form.
Code: Select all
(defun eval-in-context (hashtable form)
(eval (list 'let
(loop for key being the hash-key of hashtable using (hash-value value)
collect (list key value))
form)))
This doesn't really seem like a good idea. For most applications you would either use the approach above or write custom interpreter. EVAL is obviously fully general CL evaluator, and hence relatively slow if you want to evaluate some constrained language.
Re: ``unpacking''
Posted: Tue May 31, 2011 8:50 am
by mtl^urass
I'm open to different ways of implementing this other than the unpacking way. It's just the first I thought of. The reason I want it is because I'm screwing around with a template language, and I want to define some primitives to evaluate variables passed to the template compiler(it compiles into XHTML). I need the names to name them in the template, and the values for obvious reasons.
Yay, PROGV works:
Code: Select all
(defmacro with-unpacked-alist (alist &body body)
`(progv (map 'list #'car ,alist) (map 'list #'cdr ,alist)
,@body))
(defun hashtable-to-alist (hashtable)
(loop for key being the hash-keys of hashtable
using (hash-value value)
collecting (cons key value)))
(defmacro with-unpacked-hashtable (hashtable &body body)
`(with-unpacked-alist (hashtable-to-alist ,hashtable)
,@body))
Thanks a lot. I didn't actually know about PROGV. I'm still open for suggestions though.
Re: ``unpacking''
Posted: Tue May 31, 2011 9:40 am
by ramarren
The approach used by
HTML-TEMPLATE is to use constrained template language, where the variables only appear directly and can be just referenced by name by the template compiler. If you want arbitrary Lisp code in the template then it might be better to declare used variables in a header of some sort, which could be used to construct and verify appropriate environment. It is hard to be more specific without knowing more about your architecture.
Re: ``unpacking''
Posted: Tue May 31, 2011 10:04 am
by mtl^urass
https://github.com/mtlupurass/cl-IB/
You can disregard all of the tag-eval code. It's getting completely changed(which is why I needed some help). As you can see, I was aiming for templates with Lisp syntax, as I find it superior to typical HTML. Anyway, I want "var" to be eval, with the passed environment unpacked. Loop is fine once I have var working(again, the current definition is incorrect). I'm probably gonna end up removing const, seeing as constants and variables have the same naming syntax(the primitives were originally inspired by a perl template system I've used. Perl has different syntax). If will be the equivalent of WHEN in Lisp.