Then, you can only use bobs-crazy-macro in code wrapped in (bobs-crazy-dsl ...).
crazy-macrolet needs to implement a code walker to in order to expand those macros.
Macros can be context-dependent without having access to the literal forms to the left or right (or elsewhere).
In TXR Lisp, I implemented tagbody as a macro, providing a measure of CL compatibility. The go operators are local macros. They do not expand in a context-free way; they communicate with the surrounding tagbody.
So for instance if go is asked to jump to a nonexistent label, it errors:
1> (expand '(tagbody (go a) b))
** go: no a label visible
1> (expand '(tagbody (go a) a))
(let ((#:tb-id-0019
(gensym "tb-dyn-id-"))
(#:next-0020
0))
(sys:for-op ()
(#:next-0020)
((sys:setq #:next-0020
(block* #:tb-id-0019
(sys:switch #:next-0020
#(((return* #:tb-id-0019
1))
()))
())))))
So obviously, (go a) is behaving differently based on whether it can "see" that there is an a in its context, on the left or right side.
Comments
Regular macros can give you this in a disciplined way:
You make a macro called crazy-macrolet which is used like this:
Inside body, you use the local crazy macros. A little language made of these crazy macros can be wrapped up in a big macro. Then, you can only use bobs-crazy-macro in code wrapped in (bobs-crazy-dsl ...).crazy-macrolet needs to implement a code walker to in order to expand those macros.
Macros can be context-dependent without having access to the literal forms to the left or right (or elsewhere).
In TXR Lisp, I implemented tagbody as a macro, providing a measure of CL compatibility. The go operators are local macros. They do not expand in a context-free way; they communicate with the surrounding tagbody.
So for instance if go is asked to jump to a nonexistent label, it errors:
So obviously, (go a) is behaving differently based on whether it can "see" that there is an a in its context, on the left or right side.