In a current thread, people have claimed that generating properly indented nested blocks is a pain because of the need to keep track of indent levels. Someone countered with the now rather ancient

http://effbot.org/zone/python-code-generator.htm

The usage example

c = CodeGeneratorBackend()
c.begin(tab="    ")
c.write("for i in range(1000):\n")
c.indent()
c.write("print 'code generation is trivial'")
c.dedent()

illustrates three problems with the CodeGeneratorBackend class. 1) it requires explicit \n on all lines (which the second omits, though it is non-fatal since it is also the last) 2) the user still has to manually match indents and dedents, and 3) the user *cannot* indent lines that produce indented code.

The relatively new with statement and associated context managers are designed, among other things, for this situation, where one needs to alter and restore a global context. So here is my updated (3.1) proof-of-concept version.

class PyCodeGen:
    def __init__(self, tab="    "):
        self.code = []
        self.tab = tab
        self.level = 0
        # all attributes should be treated as read-only
    def end(self):
        return '\n'.join(self.code)
    def line(self, string): # new line
        self.code.append(self.tab * self.level + string)

class For:
    def __init__(self, target, in_expression):
        target.line('for ' + in_expression + ':')
        self.target = target
    def __enter__(self):
        self.target.level += 1
    def __exit__(self, t, v, tb):
        self.target.level -= 1

c = PyCodeGen()

with For(c, 'i in range(1000)'):
    c.line('print("Code gen is easy")')
c.line('# done')

print(c.end())

# prints

for i in range(1000):
    print("Code gen is easy")
# done

Note that the absence of .indent and .dedent is intentional. In a fleshed out system, there would be a context manager for each compound statement and these would handle all indents and dedents.

If one really preferred to write, for instance, 'c.For(s); instead of 'For(c,s)' in the with statement, one could add a wrapper method like
    def For(self, s): return For(self, s)
for each context manager. I left that out.

Similar methods can be used to auto-match C braces and other open/close pairs.

Terry Jan Reedy



--
http://mail.python.org/mailman/listinfo/python-list

Reply via email to