I would like to take a large list (think faces in an emacs theme) and break it up into smaller lists in separate files. The problem I have is applying (backquote)
to the lists once I've read them in.
Here is the code I have been using to experiment with solutions:
(defvar x 23)
(defun read-from-file (file)
(with-temp-buffer
(insert-file-contents file)
(read (current-buffer))))
;;(defun apply-macro (macro arg-list)
;; (eval
;; `(,macro ,@(loop for arg in arg-list
;; collect `(quote ,arg)))))
(defvar parts (mapcar 'read-from-file (directory-files "./parts/" "parts/" "\.part$")))
;;(apply-macro 'backquote parts)
parts
This code relies on "data" files in a subdirectory called parts/
. Here are some samples:
parts/one.part
`( ("one" "two" "three") ("ten" ,x "twelve") )
NOTE: the,x
in this one. I want to have this evaluated after the expression is read from the file.parts/two.part
( ("four" "five" "six") (2 4 6) (9 87 6) )parts/three.part
(("seven" "eight" "nine"))
Reading the "part" files is no problem. The (defvar parts (mapcar ... )
expression works.
The problem is that once I have the lists in the parts
var, I cannot find a way to get the ,x
evaluated as it would be if the whole list was backquoted and not read from files.
I have tried a solution suggested in this question. You can see the apply-macro
function commented out in my code above. When I run it I get:
Debugger entered--Lisp error: (wrong-number-of-arguments #[(structure) "\301!A\207" [structure backquote-process] 2 1628852] 3)
#[(structure) "\301!A\207" [structure backquote-process] 2 1628852]((quote (\` (("one" "two" "three") ("ten" (\, x) "twelve")))) (quote (("seven" "eight" "nine"))) (quote (("four" "five" "six") (2 4 6) (9 87 6))))
(backquote (quote (\` (("one" "two" "three") ("ten" (\, x) "twelve")))) (quote (("seven" "eight" "nine"))) (quote (("four" "five" "six") (2 4 6) (9 87 6))))
eval((backquote (quote (\` (("one" "two" "three") ("ten" (\, x) "twelve")))) (quote (("seven" "eight" "nine"))) (quote (("four" "five" "six") (2 4 6) (9 87 6)))))
apply-macro(backquote ((\` (("one" "two" "three") ("ten" (\, x) "twelve"))) (("seven" "eight" "nine")) (("four" "five" "six") (2 4 6) (9 87 6))))
eval-region(648 678 t #[257 "\300\242b\210\301\207" [(678) (apply-macro (quote backquote) parts)] 2 "\n\n(fn IGNORE)"]) ; Reading at buffer position 651
eval-defun-2()
#[257 "\211\203
Backquote does interesting things. In Emacs lisp the returned value from reading a quasi-quoted list is a list of the following structure:
So, if you intend on using quasi-quoted lists, you might need to perform the replacement your self. For example, you can do:
PS. Common Lisp does something weird with comma characters, after reading the same list
,X
becomes an object of typeSB-IMPL::COMMA
(in SBCL): it's neither a symbol, nor a pair.PPS. Somehow those quasi-quotes and commas are treated specially by the reader-evaluator, to the point that the combo
(eval (read <...>))
does not produce the same result as internal evaluator.Something that works
While playing around with back-quotes and commas, I found that the following works, although it's quite a bit of hack.
First, don't back-quote your structures: it doesn't do any harm, but it wouldn't introduce anything either. Just have
(a b ,c)
.When you read it (either with
read
from file or withread-from-string
), it will be transformed into:Now, the piece of magic: there is macro
backquote
that does the substitution, but it accepts a structure: it does not evaluate its argument, so to make it act onx
have to do the following:As you can see
(\, c)
was replaced by local binding ofc
as wanted.PPPS. One would expect that reading from string
"
(a b ,c)"would produce
(backquote (a b ,c))` but it doesn't.I hope this provides the answer.