1

Here's what I'm trying to do:

(defun test-macrolet ()
  (macrolet
      ((%test 
        (x)
        (message "%%test is called")
        `(message "x: %s" ,x))
       (%aref (array place)
              `(aref ,array ,place)))
    ;; it doesn't expand to (message "x: %s" 100)
    (message "expanded: %s" (macroexpand-all '(%test 100)))
    (%test 100)
    (%aref (%test 100) 0)))

I'm calling this function from inside another macro, where I'd like to expand all inner macros where some had to be defined locally. To, perhaps, give you a better idea, I want this:

(iter (for i from 0 to 10)
      (when (oddp i) (collect i)))

to expand in environment where macros for and collect are defined, but I don't want these macros to be defined globally (obviously, not the best names to put into global name space).

I hoped that by defining a local macro with macrolet I could expand the form containing macros defined locally, and then return the expansion, but macroexpand and macroexpand-all act as if they don't recognize locally defined macros (and don't expand them).

Well, hope I'm making myself clear...

EDIT

Thanks to an answer the respondent decided to remove, I've started to search for ways of obtaining lexical environment in Emacs Lisp. I found this example function:

(defun byte-compile-make-lambda-lexenv (form)
  "Return a new lexical environment for a lambda expression FORM."
  ;; See if this is a closure or not
  (let ((args (byte-compile-arglist-vars (cadr form))))
    (let ((lexenv nil))
      ;; Fill in the initial stack contents
      (let ((stackpos 0))
    ;; Add entries for each argument
    (dolist (arg args)
      (push (cons arg stackpos) lexenv)
      (setq stackpos (1+ stackpos)))
    ;; Return the new lexical environment
    lexenv))))

in bytecomp.el. Which on the surface of it means that the environment is just a alist made of function (macros in my case) names and their "position on the stack", I don't quite understand the second part, but will experiment and we'll see...

EDIT 2

OK, this seems like it would work:

(defun test-macrolet ()
  (macrolet
      ((%test 
        (x)
        (message "%%test is called")
        `(message "x: %s" ,x))
       (%aref (array place)
              `(aref ,array ,place)))
    (%test 100)
    (%aref (%test 100) 0)
    (message "expanded: %S" 
             (macroexpand
              '(%test 100)
              (cons `(,@(list (intern "%test")) . 
                      (lambda (y)
                        (message "Called from environment '%s'" y)
                        `(message "Final expansion '%s'" ,y)))
                    macroexpand-all-environment)))))

(test-macrolet)
"expanded: (message \"Final expansion '%s'\" 100)"

Sorry for the sloppy code. Some more info: macroexpand-all-environment is the variable where compiler / interpreter stores current environment. The environment object contains (expander-name . expander-function) pairs, where the car of the pair is the symbol which was encountered at this step of macro expansion and cdr is the function which should handle the expansion.

EDIT 3

As this seems to be very confusing to readers, here's why I don't want to return a form containing a macrolet, but rather expand everything before generating the code.

While processing the top-level form, I'm creating a bunch of objects, well, I'll call it an AST (abstract syntax tree), but it's not really that, it's just some complex model of the code I'm going to generate from the macro. If macros are expanded repeatedly, the AST will be lost in subsequent expansions, besides, I will not be able to properly generate the next step of expansion and will not be able to do certain manipulations on the code, as for example, some forms rely on their stray logical parts will be present deeper inside the "foreign" expressions. It is really easier for me to generate the code then to rely on the macro expansion mechanism in many cases. So, for example, answering to @6502:

what should (for...) expand to given that the for form is closed before the body?

It is not expanded in the regular sense of the word. The form is parsed, the parts of it are extracted into a special object, that is being later used in generating the final code. For example:

(iter (for var in '(1 2 3 4))
      (when (oddp var) (collect (next var)))

Will first create an object, that registers two variables var and an autogenerated variable, where the result is stored, let's call it --0 (because that's how I indeed generate the names). I can only find out that I need --0 variable only after parsing the second expression inside iter. So, if I expanded for very early into some let expression, and only after that first expansion had I discovered that I need to add one more variable - that would be too late. I.e. the expansion of the above should look something like this:

(let (--0 var (--1 '(1 2 3 4)))
  (while --1
     (setq var (car --1) --1 (cdr --1))
     (if (oddp var) (setq --0 (cons (cadr --l) --0))))
  --0)

If I had to generate a macrolet with half-expanded previous expression, I would've not known which variable do I have to collect into, how to generate the next expression and so on.

Also why does collect need to be a macro and not just a local function?

There's no requirement it be either, I just need it to signal during macro expansion process to me so that I could generate appropriate code. It, technically, doesn't expand into anything (hypothetically, it could be even ignored during expansion, if some code analysis will show it creates unreachable code, for example).

EDIT 4

This is getting really long, sorry. Besides the schema and the state of expansion that I must preserve during the entire expansion process, there another reason. Consider two following examples:

(iter (for (key . value) in some-hash-table)
      (message "key: %s => value: %s" key value))

This code is not expected to generate a (while ...) loop, because it is inefficient to do so, and it is better to use a (maphash ...) here. However:

(iter (for (key . value) in some-hash-table)
      (for (key-2 . value-2) in some-other-hash-table)
      (message "key: %s => value: %s" (list key key-2) (list value value-2)))

Will require generating an additional form for collecting the keys of the second hash table (or the firs one, whichever is smaller) and quite a bit of code will have to go into the section preceding the first (maphash ...) expression that could've been generated earlier. So, if I generated a macrolet here, I would be stuck in a situation, where I have to generate the code, which has already been generated, i.e. I'd had stale expression, that I would need to go back and reparse (if I'm lucky and I can somehow still reach there).

4

2 回答 2

1

我仍然不确定您要做什么。但是您编写的代码macroexpand-all不是在宏扩展时运行,而是在运行时运行,这就是为什么 %text 在那个时候不存在(与第二个相反(%test 100),应该宏扩展就好了)。

我认为您想要的内容如下:

(defmacro iter (&rest elems)
  `(macrolet ((for (..) ...)
              (collect (..) ...))
     ,@elems))

for如果你说你真的需要在和collect宏的各种扩展期间保持一些本地状态,那么你应该完全放弃macrolet并使用更像;

(defmacro iter (&rest elems)
  (let (..some.local.state..)
    (let ((body (macroexpand-all `(progn elems)
                                 `((for  . ,(lambda (..) ...))
                                   (loop . ,(lambda (..) ...))
                                   ,@macroexpand-all-environment))))
     ...)))

它只是复制macrolet内部所做的事情。

于 2012-11-23T18:32:12.697 回答
0

您应该做的只是从包含macrolet. 从宏返回的内容已经再次进行宏扩展,因此很少需要自己进行显式宏扩展。

macrolet并且symbol-macrolet在大多数情况下使您免于代码遍历,因为编译器会为您正确处理宏隐藏进行遍历。

我记得唯一需要显式扩展宏的情况是宏必须在外部级别生成不同的代码,具体取决于是否使用了另一个本地宏。

你的例子对iter我来说没有多大意义......(for...)鉴于for表格在正文之前关闭,应该扩展什么?如果列表只是一个语法要求(例如 or 发生了什么dolist) ,dotimes那么for它不是一个宏,而是由iter显式处理的东西,因为这是唯一可以接受的位置。

宏仅在代码位置进行检查,并且仅当它们可以放置在其有效性上下文中的任何代码位置时才应使用 IMO 。它们是抽象的,而不是随机的 s 表达式块。

另外为什么collect需要一个宏而不仅仅是一个本地函数?

于 2012-11-24T09:42:39.783 回答