There are lots of things one might want to replace within macros, from identifiers to punctuation, but I'd be willing to live with just two of them: expressions, and "series-of-statements" (that's almost the same as a block). There are only two places I'd want to be able to USE a macro: where an expression is called for, and where a series-of-statements is called for. In both cases, I'd be happy with a function-call like syntax for including the macro.
I have often wanted to replace (parts of) strings, either because I'm writing a wrapper or because I want a non-English version to be loadable without having to wrap strings in my own source code. This is best done as an import hook, but if I had read-write access to (a copy of) the source code, I would use it. I'm not sure I want that door opened, because if I start needing to parse regex substitions just to get a source code listing ... I won't be happy.
I do think macros should be prevented from "changing the level" of the code it replaces. Any suites/statements/expressions (including parentheses and strings) that are open before the macro must still be open afterwards, and any opened inside the macro must be closed inside the macro.
def foo(x): print x macro1(x) print x
might print different values for x on the two lines, but I would be less comfortable if it could result in any of the following:
def foo(x): print x while True: # An invisible loop, because of print x # Changing the indent level
def foo(x): print x return # and you thought it would print twice! (This one is iffy) print x
def foo(x) print x [(""" (unclosed string or paren eats up the rest of the file...) print x
def foo(x) "Hah! my backspaces and rubouts eliminated the print statements!"
def foo(x) print x def anotherfunc(x, y, z): print x # Hey, I didn't even mess with the indent!
And to be honest, even
def foo(x): macro1(x) stmt1() # syntax error, except for the macro, so not ambiguous
def foo(x) while x: print x # macro does not end on same indent level stmt1()
is ... not something I want to worry about when I'm reading.
(hint: the answer is that it ALL happens at runtime).
I have mixed feelings on this. It is more powerful that way, but it also limits future implementations -- and I'm not sure the extra power is entirely a good thing.
defmacro(): print x # Hey, x was in global scope at runtime when *I* tested
On The Other Hand, this certainly isn't the only piece of python that could *usually* be moved to compile-time, and I suppose it could piggyback on whatever extension is used for speeding up attribute lookup.