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