1"""Output primitives for the binding generator classes. 2 3This should really be a class, but then everybody would be passing 4the output object to each other. I chose for the simpler approach 5of a module with a global variable. Use SetOutputFile() or 6SetOutputFileName() to change the output file. 7""" 8 9_NeedClose = 0 10 11def SetOutputFile(file = None, needclose = 0): 12 """Call this with an open file object to make it the output file. 13 14 Call it without arguments to close the current file (if necessary) 15 and reset it to sys.stdout. 16 If the second argument is true, the new file will be explicitly closed 17 on a subsequence call. 18 """ 19 global _File, _NeedClose 20 if _NeedClose: 21 tmp = _File 22 _NeedClose = 0 23 _File = None 24 tmp.close() 25 if file is None: 26 import sys 27 file = sys.stdout 28 _File = file 29 _NeedClose = file and needclose 30 31def SetOutputFileName(filename = None): 32 """Call this with a filename to make it the output file. 33 34 Call it without arguments to close the current file (if necessary) 35 and reset it to sys.stdout. 36 """ 37 SetOutputFile() 38 if filename: 39 SetOutputFile(open(filename, 'w'), 1) 40 41SetOutputFile() # Initialize _File 42 43_Level = 0 # Indentation level 44 45def GetLevel(): 46 """Return the current indentation level.""" 47 return _Level 48 49def SetLevel(level): 50 """Set the current indentation level. 51 52 This does no type or range checking -- use at own risk. 53 """ 54 global _Level 55 _Level = level 56 57def Output(format = "", *args): 58 VaOutput(format, args) 59 60def VaOutput(format, args): 61 """Call this with a format string and argument tuple for the format. 62 63 A newline is always added. Each line in the output is indented 64 to the proper indentation level -- even if the result of the 65 format expansion contains embedded newlines. Exception: lines 66 beginning with '#' are not indented -- these are assumed to be 67 C preprprocessor lines. 68 """ 69 text = format % args 70 if _Level > 0: 71 indent = '\t' * _Level 72 lines = text.split('\n') 73 for i in range(len(lines)): 74 if lines[i] and lines[i][0] != '#': 75 lines[i] = indent + lines[i] 76 text = '\n'.join(lines) 77 _File.write(text + '\n') 78 79def IndentLevel(by = 1): 80 """Increment the indentation level by one. 81 82 When called with an argument, adds it to the indentation level. 83 """ 84 global _Level 85 if _Level+by < 0: 86 raise Error, "indentation underflow (internal error)" 87 _Level = _Level + by 88 89def DedentLevel(by = 1): 90 """Decrement the indentation level by one. 91 92 When called with an argument, subtracts it from the indentation level. 93 """ 94 IndentLevel(-by) 95 96def OutIndent(format = "", *args): 97 """Combine Output() followed by IndentLevel(). 98 99 If no text is given, acts like lone IndentLevel(). 100 """ 101 if format: VaOutput(format, args) 102 IndentLevel() 103 104def OutDedent(format = "", *args): 105 """Combine Output() followed by DedentLevel(). 106 107 If no text is given, acts like loneDedentLevel(). 108 """ 109 if format: VaOutput(format, args) 110 DedentLevel() 111 112def OutLbrace(format = "", *args): 113 """Like Output, but add a '{' and increase the indentation level. 114 115 If no text is given a lone '{' is output. 116 """ 117 if format: 118 format = format + " {" 119 else: 120 format = "{" 121 VaOutput(format, args) 122 IndentLevel() 123 124def OutRbrace(): 125 """Decrease the indentation level and output a '}' on a line by itself.""" 126 DedentLevel() 127 Output("}") 128 129def OutHeader(text, dash): 130 """Output a header comment using a given dash character.""" 131 n = 64 - len(text) 132 Output() 133 Output("/* %s %s %s */", dash * (n/2), text, dash * (n - n/2)) 134 Output() 135 136def OutHeader1(text): 137 """Output a level 1 header comment (uses '=' dashes).""" 138 OutHeader(text, "=") 139 140def OutHeader2(text): 141 """Output a level 2 header comment (uses '-' dashes).""" 142 OutHeader(text, "-") 143 144def Out(text): 145 """Output multiline text that's internally indented. 146 147 Pass this a multiline character string. The whitespace before the 148 first nonblank line of the string will be subtracted from all lines. 149 The lines are then output using Output(), but without interpretation 150 of formatting (if you need formatting you can do it before the call). 151 Recommended use: 152 153 Out(''' 154 int main(argc, argv) 155 int argc; 156 char *argv; 157 { 158 printf("Hello, world\\n"); 159 exit(0); 160 } 161 ''') 162 163 Caveat: the indentation must be consistent -- if you use three tabs 164 in the first line, (up to) three tabs are removed from following lines, 165 but a line beginning with 24 spaces is not trimmed at all. Don't use 166 this as a feature. 167 """ 168 # (Don't you love using triple quotes *inside* triple quotes? :-) 169 170 lines = text.split('\n') 171 indent = "" 172 for line in lines: 173 if line.strip(): 174 for c in line: 175 if not c.isspace(): 176 break 177 indent = indent + c 178 break 179 n = len(indent) 180 for line in lines: 181 if line[:n] == indent: 182 line = line[n:] 183 else: 184 for c in indent: 185 if line[:1] <> c: break 186 line = line[1:] 187 VaOutput("%s", line) 188 189 190def _test(): 191 """Test program. Run when the module is run as a script.""" 192 OutHeader1("test bgenOutput") 193 Out(""" 194 #include <Python.h> 195 #include <stdio.h> 196 197 main(argc, argv) 198 int argc; 199 char **argv; 200 { 201 int i; 202 """) 203 IndentLevel() 204 Output("""\ 205/* Here are a few comment lines. 206 Just to test indenting multiple lines. 207 208 End of the comment lines. */ 209""") 210 Output("for (i = 0; i < argc; i++)") 211 OutLbrace() 212 Output('printf("argv[%%d] = %%s\\n", i, argv[i]);') 213 OutRbrace() 214 Output("exit(0)") 215 OutRbrace() 216 OutHeader2("end test") 217 218if __name__ == '__main__': 219 _test() 220