Home | History | Annotate | Download | only in bgen
      1 """Output primitives for the binding generator classes.
      2 
      3 This should really be a class, but then everybody would be passing
      4 the output object to each other.  I chose for the simpler approach
      5 of a module with a global variable.  Use SetOutputFile() or
      6 SetOutputFileName() to change the output file.
      7 """
      8 
      9 _NeedClose = 0
     10 
     11 def 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 
     31 def 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 
     41 SetOutputFile() # Initialize _File

     42 
     43 _Level = 0      # Indentation level

     44 
     45 def GetLevel():
     46     """Return the current indentation level."""
     47     return _Level
     48 
     49 def 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 
     57 def Output(format = "", *args):
     58     VaOutput(format, args)
     59 
     60 def 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 
     79 def 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 
     89 def 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 
     96 def 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 
    104 def 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 
    112 def 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 
    124 def OutRbrace():
    125     """Decrease the indentation level and output a '}' on a line by itself."""
    126     DedentLevel()
    127     Output("}")
    128 
    129 def 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 
    136 def OutHeader1(text):
    137     """Output a level 1 header comment (uses '=' dashes)."""
    138     OutHeader(text, "=")
    139 
    140 def OutHeader2(text):
    141     """Output a level 2 header comment (uses '-' dashes)."""
    142     OutHeader(text, "-")
    143 
    144 def 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 
    190 def _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 
    218 if __name__ == '__main__':
    219     _test()
    220