| """Output primitives for the binding generator classes. |
| |
| This should really be a class, but then everybody would be passing |
| the output object to each other. I chose for the simpler approach |
| of a module with a global variable. Use SetOutputFile() or |
| SetOutputFileName() to change the output file. |
| """ |
| |
| _NeedClose = 0 |
| |
| def SetOutputFile(file = None, needclose = 0): |
| """Call this with an open file object to make it the output file. |
| |
| Call it without arguments to close the current file (if necessary) |
| and reset it to sys.stdout. |
| If the second argument is true, the new file will be explicitly closed |
| on a subsequence call. |
| """ |
| global _File, _NeedClose |
| if _NeedClose: |
| tmp = _File |
| _NeedClose = 0 |
| _File = None |
| tmp.close() |
| if file is None: |
| import sys |
| file = sys.stdout |
| _File = file |
| _NeedClose = file and needclose |
| |
| def SetOutputFileName(filename = None): |
| """Call this with a filename to make it the output file. |
| |
| Call it without arguments to close the current file (if necessary) |
| and reset it to sys.stdout. |
| """ |
| SetOutputFile() |
| if filename: |
| SetOutputFile(open(filename, 'w'), 1) |
| |
| SetOutputFile() # Initialize _File |
| |
| _Level = 0 # Indentation level |
| |
| def GetLevel(): |
| """"Return the current indentation level.""" |
| return _Level |
| |
| def SetLevel(level): |
| """Set the current indentation level. |
| |
| This does no type or range checking -- use at own risk. |
| """ |
| global _Level |
| _Level = level |
| |
| def Output(format = "", *args): |
| VaOutput(format, args) |
| |
| def VaOutput(format, args): |
| """Call this with a format string and and argument tuple for the format. |
| |
| A newline is always added. Each line in the output is indented |
| to the proper indentation level -- even if the result of the |
| format expansion contains embedded newlines. Exception: lines |
| beginning with '#' are not indented -- these are assumed to be |
| C preprprocessor lines. |
| """ |
| text = format % args |
| if _Level > 0: |
| indent = '\t' * _Level |
| import string |
| lines = string.splitfields(text, '\n') |
| for i in range(len(lines)): |
| if lines[i] and lines[i][0] != '#': |
| lines[i] = indent + lines[i] |
| text = string.joinfields(lines, '\n') |
| _File.write(text + '\n') |
| |
| def IndentLevel(by = 1): |
| """Increment the indentation level by one. |
| |
| When called with an argument, adds it to the indentation level. |
| """ |
| global _Level |
| if _Level+by < 0: |
| raise Error, "indentation underflow (internal error)" |
| _Level = _Level + by |
| |
| def DedentLevel(by = 1): |
| """Decrement the indentation level by one. |
| |
| When called with an argument, subtracts it from the indentation level. |
| """ |
| IndentLevel(-by) |
| |
| def OutIndent(format = "", *args): |
| """Combine Output() followed by IndentLevel(). |
| |
| If no text is given, acts like lone IndentLevel(). |
| """ |
| if format: VaOutput(format, args) |
| IndentLevel() |
| |
| def OutDedent(format = "", *args): |
| """Combine Output() followed by DedentLevel(). |
| |
| If no text is given, acts like loneDedentLevel(). |
| """ |
| if format: VaOutput(format, args) |
| DedentLevel() |
| |
| def OutLbrace(format = "", *args): |
| """Like Output, but add a '{' and increase the indentation level. |
| |
| If no text is given a lone '{' is output. |
| """ |
| if format: |
| format = format + " {" |
| else: |
| format = "{" |
| VaOutput(format, args) |
| IndentLevel() |
| |
| def OutRbrace(): |
| """Decrease the indentation level and output a '}' on a line by itself.""" |
| DedentLevel() |
| Output("}") |
| |
| def OutHeader(text, dash): |
| """Output a header comment using a given dash character.""" |
| n = 64 - len(text) |
| Output() |
| Output("/* %s %s %s */", dash * (n/2), text, dash * (n - n/2)) |
| Output() |
| |
| def OutHeader1(text): |
| """Output a level 1 header comment (uses '=' dashes).""" |
| OutHeader(text, "=") |
| |
| def OutHeader2(text): |
| """Output a level 2 header comment (uses '-' dashes).""" |
| OutHeader(text, "-") |
| |
| def Out(text): |
| """Output multiline text that's internally indented. |
| |
| Pass this a multiline character string. The whitespace before the |
| first nonblank line of the string will be subtracted from all lines. |
| The lines are then output using Output(), but without interpretation |
| of formatting (if you need formatting you can do it before the call). |
| Recommended use: |
| |
| Out(''' |
| int main(argc, argv) |
| int argc; |
| char *argv; |
| { |
| printf("Hello, world\\n"); |
| exit(0); |
| } |
| ''') |
| |
| Caveat: the indentation must be consistent -- if you use three tabs |
| in the first line, (up to) three tabs are removed from following lines, |
| but a line beginning with 24 spaces is not trimmed at all. Don't use |
| this as a feature. |
| """ |
| # (Don't you love using triple quotes *inside* triple quotes? :-) |
| |
| import string |
| lines = string.splitfields(text, '\n') |
| indent = "" |
| for line in lines: |
| if string.strip(line): |
| for c in line: |
| if c not in string.whitespace: |
| break |
| indent = indent + c |
| break |
| n = len(indent) |
| for line in lines: |
| if line[:n] == indent: |
| line = line[n:] |
| else: |
| for c in indent: |
| if line[:1] <> c: break |
| line = line[1:] |
| VaOutput("%s", line) |
| |
| |
| def _test(): |
| """Test program. Run when the module is run as a script.""" |
| OutHeader1("test bgenOutput") |
| Out(""" |
| #include <Python.h> |
| #include <stdio.h> |
| |
| main(argc, argv) |
| int argc; |
| char **argv; |
| { |
| int i; |
| """) |
| IndentLevel() |
| Output("""\ |
| /* Here are a few comment lines. |
| Just to test indenting multiple lines. |
| |
| End of the comment lines. */ |
| """) |
| Output("for (i = 0; i < argc; i++)") |
| OutLbrace() |
| Output('printf("argv[%%d] = %%s\\n", i, argv[i]);') |
| OutRbrace() |
| Output("exit(0)") |
| OutRbrace() |
| OutHeader2("end test") |
| |
| if __name__ == '__main__': |
| _test() |