c macros in python.

A.T.Hofkamp hat at se-126.se.wtb.tue.nl
Mon May 7 03:19:52 EDT 2007


On 2007-05-06, noagbodjivictor at gmail.com <noagbodjivictor at gmail.com> wrote:
> Hey,
>
> I'm writing a script to generate code. I'm a bit tired of typing
> outfile.write(....). Does python have a way to c-like macros? Every
> instance of o(...) in the code will be replaced by outfile.write(...)?

Just in case you don't know, you can write an arbitrary number of lines in one
write. Below is how I format a usage() message of a program in one write call:

def usage(fp):
    fp.write("Usage: convert options [infile] [outfile]\n"
            "with\n"
            "    options\n"
            "\t--prefix=<name-prefix>\t(obligatory)\n"
            "\t--write-size\t\tWrite a line containing the size\n"
            "\t--append-zero\t\tAppend a terminating 0 byte\n")

ie one multi-line write call. Pyhon concatenates two consequtive string
literals for us. Unfortunately, this gets less pretty when inserting variable
values.




In other code generation code, I normally use a list of lines. Rather than
writing everything directly to file, I make a list data structure containing
lines, then dump the list to file, as in:

lines = []
gencode_first(lines)
gencode_second(lines)
lines.append("the end")
write_lines(lines)

where write_lines() is

def write_lines(lines):
    for line in lines:
        outfile.write(line)
        outfile.write('\n')

(i left out the opening and closing of outfile).


I normally do not include the \n in the list but instead add it while writing
it to file. This makes life much easier since there are no special last-value
problems in the code generator itself.
The nice thing here is that 'lines' is a normal data structure which you can
manipulate if you like.




For more demanding code generators (ie C or C++ code) I use the concept
'sections'. At a global level, the generated code has an 'include',
'declarations', 'definitions', and 'main' section, each section is a list of
lines.
I use a dictionary for this, like

output = { 'incl': [], 'decl': [], 'def': [], 'main': [] }

then pass around this in the code generator.
Each part of the generator can write in each section, for example when defining
a C function, you can write the declaration in the decl section and the
definition in the def section at the same time.
For example

def write_c_function(output):
    output['decl'].append('int mycfunc(void);')
    output['def'].extend(['int myfunc(void)', '{' 'return 131;', }' ])

Reducing such a dictionary to a list is then something like

def make_lines(sec_list, output):
    lines = []
    for sec in sec_list:
        lines.extend(output[sec])
    return lines

And outputting the code is then something like

write_lines(make_lines(['incl', 'decl', 'def', 'main'], output))

In this way you can abstract away from the order of code as required by the
target language and instead generate code in a nicer order.
Note that this section trick can be done recursively. for example, a function
can be thought of as a number of sections like

funcoutput = { 'header': [], 'opening-bracket' : [], 'localvars':[], 'closing-bracket': [] }

so you can generate a function using sections as well, then at the end reduce
funcoutput to a list of lines, and insert that in a section of the global
'output'.




Last but not least, if you replace the lists by an object, you can do much
smarter things. For example, in general you don't want to have double #include
lines in the 'incl' section. Instead of worrying about generation of doubles,
just make an object that behaves like a list but silently drops doubles.
In the same way, you can create a list-like object that handles indenting for
you.

The possibilities here are endless!!


Good luck with your code generation problem, I hope I gave you some ideas of
alternative solutions that are available.

Albert




More information about the Python-list mailing list