]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
Lots of new stuff again. Moved buffer types to some separate files.
authorGuido van Rossum <guido@python.org>
Wed, 25 Jan 1995 22:59:21 +0000 (22:59 +0000)
committerGuido van Rossum <guido@python.org>
Wed, 25 Jan 1995 22:59:21 +0000 (22:59 +0000)
Added some new-fangled features to bgenOutput.  Generate doc strings!

13 files changed:
Tools/bgen/bgen/bgen.py
Tools/bgen/bgen/bgenBuffer.py [new file with mode: 0644]
Tools/bgen/bgen/bgenGenerator.py
Tools/bgen/bgen/bgenHeapBuffer.py [new file with mode: 0644]
Tools/bgen/bgen/bgenModule.py
Tools/bgen/bgen/bgenObjectDefinition.py
Tools/bgen/bgen/bgenOutput.py
Tools/bgen/bgen/bgenStackBuffer.py [new file with mode: 0644]
Tools/bgen/bgen/bgenStringBuffer.py [new file with mode: 0644]
Tools/bgen/bgen/bgenType.py
Tools/bgen/bgen/bgenVariable.py [new file with mode: 0644]
Tools/bgen/bgen/macsupport.py [new file with mode: 0644]
Tools/bgen/bgen/scantools.py [new file with mode: 0644]

index aab35de31559c667f84f27b4b1e2112de328775c..db28b0a87d08633c73478b333d8fddb5e0c59a18 100644 (file)
@@ -1,6 +1,11 @@
 "Export everything in the various bgen submodules."
 
 from bgenType import *
+from bgenVariable import *
+from bgenBuffer import *
+from bgenStackBuffer import *
+from bgenHeapBuffer import *
+from bgenStringBuffer import *
 from bgenOutput import *
 from bgenGenerator import *
 from bgenModule import *
diff --git a/Tools/bgen/bgen/bgenBuffer.py b/Tools/bgen/bgen/bgenBuffer.py
new file mode 100644 (file)
index 0000000..925b937
--- /dev/null
@@ -0,0 +1,227 @@
+"""Buffers are character arrays that may contain null bytes.
+
+There are a number of variants depending on:
+- how the buffer is allocated (for output buffers), and
+- whether and how the size is passed into and/or out of the called function.
+"""
+
+
+from bgenType import Type, InputOnlyMixIn, OutputOnlyMixIn, InputOnlyType, OutputOnlyType
+from bgenOutput import *
+
+
+# Map common types to their format characters
+type2format = {
+       'long': 'l',
+       'int': 'i',
+       'short': 'h',
+       'char': 'b',
+       'unsigned long': 'l',
+       'unsigned int': 'i',
+       'unsigned short': 'h',
+       'unsigned char': 'b',
+}
+
+
+# ----- PART 1: Fixed character buffers -----
+
+
+class FixedInputOutputBufferType(InputOnlyType):
+       
+       """Fixed buffer -- passed as (inbuffer, outbuffer)."""
+
+       def __init__(self, size, datatype = 'char', sizetype = 'int', sizeformat = None):
+               self.typeName = "Buffer"
+               self.size = str(size)
+               self.datatype = datatype
+               self.sizetype = sizetype
+               self.sizeformat = sizeformat or type2format[sizetype]
+
+       def declare(self, name):
+               self.declareBuffer(name)
+               self.declareSize(name)
+       
+       def declareBuffer(self, name):
+               self.declareInputBuffer(name)
+               self.declareOutputBuffer(name)
+       
+       def declareInputBuffer(self, name):
+               Output("%s *%s__in__;", self.datatype, name)
+       
+       def declareOutputBuffer(self, name):
+               Output("%s %s__out__[%s];", self.datatype, name, self.size)
+
+       def declareSize(self, name):
+               Output("%s %s__len__;", self.sizetype, name)
+
+       def getargsFormat(self):
+               # XXX This only works if the size is int-sized!!!
+               return "s#"
+
+       def getargsArgs(self, name):
+               return "&%s__in__, &%s__len__" % (name, name)
+       
+       def getargsCheck(self, name):
+               Output("if (%s__len__ != %s)", name, self.size)
+               OutLbrace()
+               Output('PyErr_SetString(PyExc_TypeError, "buffer length should be %s");',
+                      self.size)
+               Output("goto %s__error__;", name)
+               OutRbrace()
+
+       def passOutput(self, name):
+               return "%s__in__, %s__out__" % (name, name)
+       
+       def mkvalueFormat(self):
+               return "s#"
+
+       def mkvalueArgs(self, name):
+               return "%s__out__, %s" % (name, self.size)
+       
+       def cleanup(self, name):
+               DedentLevel()
+               Output(" %s__error__: ;", name)
+               IndentLevel()
+
+
+class FixedCombinedInputOutputBufferType(FixedInputOutputBufferType):
+       
+       """Like fixed buffer -- but same parameter is input and output."""
+       
+       def passOutput(self, name):
+               return "(%s *)memcpy(%s__out__, %s__in__, %s)" % \
+                       (self.datatype, name,   name,     self.size)
+
+
+class InputOnlyBufferMixIn(InputOnlyMixIn):
+
+       def declareOutputBuffer(self, name):
+               pass
+
+
+class OutputOnlyBufferMixIn(OutputOnlyMixIn):
+
+       def declareInputBuffer(self, name):
+               pass
+
+
+class FixedInputBufferType(InputOnlyBufferMixIn, FixedInputOutputBufferType):
+
+       """Fixed size input buffer -- passed without size information.
+
+       Instantiate with the size as parameter.
+       """
+
+       def passInput(self, name):
+               return "%s__in__" % name
+
+
+class FixedOutputBufferType(OutputOnlyBufferMixIn, FixedInputOutputBufferType):
+
+       """Fixed size output buffer -- passed without size information.
+
+       Instantiate with the size as parameter.
+       """
+
+       def passOutput(self, name):
+               return "%s__out__" % name
+
+
+class VarInputBufferType(FixedInputBufferType):
+
+       """Variable size input buffer -- passed as (buffer, size).
+       
+       Instantiate without size parameter.
+       """
+       
+       def __init__(self, datatype = 'char', sizetype = 'int', sizeformat = None):
+               FixedInputBufferType.__init__(self, "0", datatype, sizetype, sizeformat)
+       
+       def getargsCheck(self, name):
+               pass
+       
+       def passInput(self, name):
+               return "%s__in__, %s__len__" % (name, name)
+
+
+# ----- PART 2: Structure buffers -----
+
+
+class StructInputOutputBufferType(FixedInputOutputBufferType):
+       
+       """Structure buffer -- passed as a structure pointer.
+
+       Instantiate with the struct type as parameter.
+       """
+       
+       def __init__(self, type):
+               FixedInputOutputBufferType.__init__(self, "sizeof(%s)" % type)
+               self.typeName = self.type = type
+       
+       def declareInputBuffer(self, name):
+               Output("%s *%s__in__;", self.type, name)
+       
+       def declareOutputBuffer(self, name):
+               Output("%s %s__out__;", self.type, name)
+       
+       def getargsArgs(self, name):
+               return "(char **)&%s__in__, &%s__len__" % (name, name)
+       
+       def passInput(self, name):
+               return "%s__in__" % name
+       
+       def passOutput(self, name):
+               return "%s__in__, &%s__out__" % (name, name)
+       
+       def mkvalueArgs(self, name):
+               return "(char *)&%s__out__, %s" % (name, self.size)
+
+
+class StructCombinedInputOutputBufferType(StructInputOutputBufferType):
+
+       """Like structure buffer -- but same parameter is input and output."""
+       
+       def passOutput(self, name):
+               return "(%s *)memcpy((char *)%s__out__, (char *)%s__in__, %s)" % \
+                       (self.type,          name,              name,     self.size)
+
+
+class StructInputBufferType(InputOnlyBufferMixIn, StructInputOutputBufferType):
+
+       """Fixed size input buffer -- passed as a pointer to a structure.
+
+       Instantiate with the struct type as parameter.
+       """
+
+
+class StructByValueBufferType(StructInputBufferType):
+
+       """Fixed size input buffer -- passed as a structure BY VALUE.
+
+       Instantiate with the struct type as parameter.
+       """
+
+       def passInput(self, name):
+               return "*%s__in__" % name
+
+
+class StructOutputBufferType(OutputOnlyBufferMixIn, StructInputOutputBufferType):
+
+       """Fixed size output buffer -- passed as a pointer to a structure.
+
+       Instantiate with the struct type as parameter.
+       """
+
+       def passOutput(self, name):
+               return "&%s__out__" % name
+
+
+class ArrayOutputBufferType(OutputOnlyBufferMixIn, StructInputOutputBufferType):
+
+       """Fixed size output buffer -- declared as a typedef, passed as an array.
+
+       Instantiate with the struct type as parameter.
+       """
+
+       def passOutput(self, name):
+               return "%s__out__" % name
index 62aab1c6f734fcea0bc22deea341d1cc776a6f54..4f57697f3acdd7525b7cab4192265df7a92437c9 100644 (file)
@@ -1,5 +1,6 @@
 from bgenOutput import *
 from bgenType import *
+from bgenVariable import *
 
 
 Error = "bgenGenerator.Error"
@@ -14,6 +15,7 @@ INOUT = IN_OUT = "in-out"
 class FunctionGenerator:
 
        def __init__(self, returntype, name, *argumentList):
+               print "<--", name
                self.returntype = returntype
                self.name = name
                self.argumentList = []
@@ -51,8 +53,42 @@ class FunctionGenerator:
        def reference(self, name = None):
                if name is None:
                        name = self.name
-               Output("{\"%s\", (PyCFunction)%s_%s, 1},",
-                      name, self.prefix, self.name)
+               docstring = self.docstring()
+               Output("{\"%s\", (PyCFunction)%s_%s, 1,", name, self.prefix, self.name)
+               Output(" %s},", stringify(docstring))
+       
+       def docstring(self):
+               import string
+               input = []
+               output = []
+               for arg in self.argumentList:
+                       if arg.flags == ErrorMode or arg.flags == SelfMode:
+                               continue
+                       if arg.type == None:
+                               str = 'void'
+                       else:
+                               if hasattr(arg.type, 'typeName'):
+                                       typeName = arg.type.typeName
+                                       if typeName is None: # Suppressed type
+                                               continue
+                               else:
+                                       typeName = "?"
+                                       print "Nameless type", arg.type
+                                       
+                               str = typeName + ' ' + arg.name
+                       if arg.mode in (InMode, InOutMode):
+                               input.append(str)
+                       if arg.mode in (InOutMode, OutMode):
+                               output.append(str)
+               if not input:
+                       instr = "()"
+               else:
+                       instr = "(%s)" % string.joinfields(input, ", ")
+               if not output or output == ["void"]:
+                       outstr = "None"
+               else:
+                       outstr = "(%s)" % string.joinfields(output, ", ")
+               return instr + " -> " + outstr
 
        def generate(self):
                print "-->", self.name
@@ -143,9 +179,7 @@ class FunctionGenerator:
                tmp.reverse()
                for arg in tmp:
                        if not arg: continue
-                       if arg.flags == ErrorMode: continue
-                       if arg.mode in (OutMode, InOutMode):
-                               arg.mkvalueCleanup()
+                       arg.cleanup()
                Output("return _res;")
 
        def functiontrailer(self):
@@ -174,6 +208,18 @@ class ManualGenerator(FunctionGenerator):
                Output("%s", self.body)
                self.functiontrailer()
 
+_stringify_map = {'\n': '\\n', '\t': '\\t', '\r': '\\r', '\b': '\\b',
+                  '\e': '\\e', '\a': '\\a', '\f': '\\f', '"': '\\"'}
+def stringify(str):
+       if str is None: return "None"
+       res = '"'
+       map = _stringify_map
+       for c in str:
+               if map.has_key(c): res = res + map[c]
+               elif ' ' <= c <= '~': res = res + c
+               else: res = res + '\\%03o' % ord(c)
+       res = res + '"'
+       return res
 
 def _test():
        void = None
diff --git a/Tools/bgen/bgen/bgenHeapBuffer.py b/Tools/bgen/bgen/bgenHeapBuffer.py
new file mode 100644 (file)
index 0000000..2c51695
--- /dev/null
@@ -0,0 +1,108 @@
+# Buffers allocated on the heap
+
+from bgenOutput import *
+from bgenType import OutputOnlyMixIn
+from bgenBuffer import FixedInputOutputBufferType
+
+
+class HeapInputOutputBufferType(FixedInputOutputBufferType):
+
+       """Input-output buffer allocated on the heap -- passed as (inbuffer, outbuffer, size).
+
+       Instantiate without parameters.
+       Call from Python with input buffer.
+       """
+
+       def __init__(self, datatype = 'char', sizetype = 'int', sizeformat = None):
+               FixedInputOutputBufferType.__init__(self, "0", datatype, sizetype, sizeformat)
+
+       def declareOutputBuffer(self, name):
+               Output("%s *%s__out__;", self.datatype, name)
+
+       def getargsCheck(self, name):
+               Output("if ((%s__out__ = malloc(%s__len__)) == NULL)", name, name)
+               OutLbrace()
+               Output('PyErr_NoMemory();')
+               Output("goto %s__error__;", name)
+               OutRbrace()
+
+       def passOutput(self, name):
+               return "%s__in__, %s__out__, %s__len__" % (name, name, name)
+
+       def mkvalueArgs(self, name):
+               return "%s__out__, %s__len__" % (name, name)
+
+       def cleanup(self, name):
+               Output("free(%s__out__);", name)
+               FixedInputOutputBufferType.cleanup(self, name)
+
+
+class VarHeapInputOutputBufferType(HeapInputOutputBufferType):
+
+       """same as base class, but passed as (inbuffer, outbuffer, &size)"""
+       
+       def passOutput(self, name):
+               return "%s__in__, %s__out__, &%s__len__" % (name, name, name)
+
+
+class HeapCombinedInputOutputBufferType(HeapInputOutputBufferType):
+
+       """same as base class, but passed as (inoutbuffer, size)"""
+       
+       def passOutput(self, name):
+               return "(%s *)memcpy(%s__out__, %s__in__, %s__len__)" % \
+                       (self.datatype, name,   name,     name)
+
+
+class VarHeapCombinedInputOutputBufferType(HeapInputOutputBufferType):
+
+       """same as base class, but passed as (inoutbuffer, &size)"""
+       
+       def passOutput(self, name):
+               return "(%s *)memcpy(%s__out__, %s__in__, &%s__len__)" % \
+                       (self.datatype, name,   name,      name)
+
+
+class HeapOutputBufferType(OutputOnlyMixIn, HeapInputOutputBufferType):
+
+       """Output buffer allocated on the heap -- passed as (buffer, size).
+
+       Instantiate without parameters.
+       Call from Python with buffer size.
+       """
+       
+       def declareInputBuffer(self, name):
+               pass
+       
+       def getargsFormat(self):
+               return self.sizeformat
+       
+       def getargsArgs(self, name):
+               return "&%s__len__" % name
+       
+       def passOutput(self, name):
+               return "%s__out__, %s__len__" % (name, name)
+
+
+class VarHeapOutputBufferType(HeapOutputBufferType):
+
+       """Output buffer allocated on the heap -- passed as (buffer, &size).
+
+       Instantiate without parameters.
+       Call from Python with buffer size.
+       """
+
+       def passOutput(self, name):
+               return "%s__out__, &%s__len__" % (name, name)
+
+
+class VarVarHeapOutputBufferType(VarHeapOutputBufferType):
+
+       """Output buffer allocated on the heap -- passed as (buffer, size, &size).
+
+       Instantiate without parameters.
+       Call from Python with buffer size.
+       """
+
+       def passOutput(self, name):
+               return "%s__out__, %s__len__, &%s__len__" % (name, name, name)
index 8824ee948a78e3107025cc0c826f54cd4015d0b1..eda1a02750b551137544bbd4dce76d34c12b4192 100644 (file)
@@ -5,13 +5,13 @@ class Module(GeneratorGroup):
 
        def __init__(self, name, prefix = None,
                     includestuff = None,
-                    initstuff = None,
-                    preinitstuff = None):
+                    finalstuff = None,
+                    initstuff = None):
                GeneratorGroup.__init__(self, prefix or name)
                self.name = name
                self.includestuff = includestuff
                self.initstuff = initstuff
-               self.preinitstuff = preinitstuff
+               self.finalstuff = finalstuff
 
        def addobject(self, od):
                self.generators.append(od)
@@ -29,9 +29,9 @@ class Module(GeneratorGroup):
 
                GeneratorGroup.generate(self)
                
-               if self.preinitstuff:
+               if self.finalstuff:
                        Output()
-                       Output("%s", self.preinitstuff)
+                       Output("%s", self.finalstuff)
 
                Output()
                Output("void init%s()", self.name)
@@ -56,12 +56,17 @@ class Module(GeneratorGroup):
                Output("static PyObject *%s;", self.errorname)
 
        def createModuleVariables(self):
-               Output("""if ((%s = PyString_FromString("%s.Error")) == NULL ||""",
-                              self.errorname,           self.name)
+               Output("""%s = %s;""", self.errorname, self.exceptionInitializer())
+               Output("""if (%s == NULL ||""", self.errorname)
                Output("""    PyDict_SetItemString(d, "Error", %s) != 0)""",
                                                               self.errorname)
+               IndentLevel()
                Output("""Py_FatalError("can't initialize %s.Error");""",
                                                           self.name)
+               DedentLevel()
+
+       def exceptionInitializer(self):
+               return """PyString_FromString("%s.Error")""" % self.name
 
 
 def _test():
index 10a4468a0699834622d65c77b90303cae2019a6f..e7fa146abe022780ef7591b66be5bb576ecf4a2a 100644 (file)
@@ -2,15 +2,25 @@ from bgenOutput import *
 from bgenGeneratorGroup import GeneratorGroup
 
 class ObjectDefinition(GeneratorGroup):
+       "Spit out code that together defines a new Python object type"
 
        def __init__(self, name, prefix, itselftype):
-               import string
+               """ObjectDefinition constructor.  May be extended, but do not override.
+               
+               - name: the object's official name, e.g. 'SndChannel'.
+               - prefix: the prefix used for the object's functions and data, e.g. 'SndCh'.
+               - itselftype: the C type actually contained in the object, e.g. 'SndChannelPtr'.
+               
+               XXX For official Python data types, rules for the 'Py' prefix are a problem.
+               """
+               
                GeneratorGroup.__init__(self, prefix or name)
                self.name = name
                self.itselftype = itselftype
                self.objecttype = name + 'Object'
                self.typename = name + '_Type'
                self.argref = ""        # set to "*" if arg to <type>_New should be pointer
+               self.static = "static " # set to "" to make <type>_New and <type>_Convert public
 
        def add(self, g):
                g.setselftype(self.objecttype, self.itselftype)
@@ -25,17 +35,18 @@ class ObjectDefinition(GeneratorGroup):
 
                OutHeader2("Object type " + self.name)
 
-               Output("staticforward PyTypeObject %s;", self.typename)
+               sf = self.static and "staticforward "
+               Output("%sPyTypeObject %s;", sf, self.typename)
                Output()
 
                Output("#define %s_Check(x) ((x)->ob_type == &%s)",
                       self.prefix, self.typename)
                Output()
 
-               Output("typedef struct {")
+               Output("typedef struct %s {", self.objecttype)
                IndentLevel()
                Output("PyObject_HEAD")
-               Output("%s ob_itself;", self.itselftype)
+               self.outputStructMembers()
                DedentLevel()
                Output("} %s;", self.objecttype)
                Output()
@@ -56,8 +67,11 @@ class ObjectDefinition(GeneratorGroup):
 
                OutHeader2("End object type " + self.name)
 
+       def outputStructMembers(self):
+               Output("%s ob_itself;", self.itselftype)
+
        def outputNew(self):
-               Output("static PyObject *%s_New(itself)", self.prefix)
+               Output("%sPyObject *%s_New(itself)", self.static, self.prefix)
                IndentLevel()
                Output("const %s %sitself;", self.itselftype, self.argref)
                DedentLevel()
@@ -66,28 +80,36 @@ class ObjectDefinition(GeneratorGroup):
                self.outputCheckNewArg()
                Output("it = PyObject_NEW(%s, &%s);", self.objecttype, self.typename)
                Output("if (it == NULL) return NULL;")
-               Output("it->ob_itself = %sitself;", self.argref)
+               self.outputInitStructMembers()
                Output("return (PyObject *)it;")
                OutRbrace()
                Output()
+
+       def outputInitStructMembers(self):
+               Output("it->ob_itself = %sitself;", self.argref)
        
        def outputCheckNewArg(self):
-               pass
-
+                       "Override this method to apply additional checks/conversions"
+       
        def outputConvert(self):
-               Output("""\
-static int %(prefix)s_Convert(v, p_itself)
-       PyObject *v;
-       %(itselftype)s *p_itself;
-{
-       if (v == NULL || !%(prefix)s_Check(v)) {
-               PyErr_SetString(PyExc_TypeError, "%(name)s required");
-               return 0;
-       }
-       *p_itself = ((%(objecttype)s *)v)->ob_itself;
-       return 1;
-}
-""" % self.__dict__)
+               Output("%s%s_Convert(v, p_itself)", self.static, self.prefix)
+               IndentLevel()
+               Output("PyObject *v;")
+               Output("%s *p_itself;", self.itselftype)
+               DedentLevel()
+               OutLbrace()
+               self.outputCheckConvertArg()
+               Output("if (!%s_Check(v))", self.prefix)
+               OutLbrace()
+               Output('PyErr_SetString(PyExc_TypeError, "%s required");', self.name)
+               Output("return 0;")
+               OutRbrace()
+               Output("*p_itself = ((%s *)v)->ob_itself;", self.objecttype)
+               Output("return 1;")
+               OutRbrace()
+
+       def outputCheckConvertArg(self):
+               "Override this method to apply additional conversions"
 
        def outputDealloc(self):
                Output("static void %s_dealloc(self)", self.prefix)
@@ -95,11 +117,14 @@ static int %(prefix)s_Convert(v, p_itself)
                Output("%s *self;", self.objecttype)
                DedentLevel()
                OutLbrace()
-               self.outputFreeIt("self->ob_itself")
+               self.outputCleanupStructMembers()
                Output("PyMem_DEL(self);")
                OutRbrace()
                Output()
 
+       def outputCleanupStructMembers(self):
+               self.outputFreeIt("self->ob_itself")
+
        def outputFreeIt(self, name):
                Output("/* Cleanup of %s goes here */", name)
 
@@ -126,7 +151,7 @@ static int %(prefix)s_Convert(v, p_itself)
                Output()
 
        def outputTypeObject(self):
-               Output("static PyTypeObject %s = {", self.typename)
+               Output("%sPyTypeObject %s = {", self.static, self.typename)
                IndentLevel()
                Output("PyObject_HEAD_INIT(&PyType_Type)")
                Output("0, /*ob_size*/")
@@ -140,3 +165,11 @@ static int %(prefix)s_Convert(v, p_itself)
                Output("(setattrfunc) %s_setattr, /*tp_setattr*/", self.prefix)
                DedentLevel()
                Output("};")
+
+
+class GlobalObjectDefinition(ObjectDefinition):
+       "Same as ObjectDefinition but exports its New and Create methods"
+
+       def __init__(self, name, prefix = None, itselftype = None):
+               ObjectDefinition.__init__(self, name, prefix or name, itselftype or name)
+               self.static = ""
index 06ca3ed0276cbbda79ecec5ed345cef06f6adb29..1a512e5c8b3b064743410ebb0efff9d3a37b26ee 100644 (file)
@@ -2,20 +2,41 @@
 
 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() to change
-the output file.
+of a module with a global variable.  Use SetOutputFile() or
+SetOutputFileName() to change the output file.
 """
 
-def SetOutputFile(file = None):
-       """Call this with an open file object to make that the output file.
+_NeedClose = 0
 
-       Call it without arguments to reset the output file to sys.stdout.
+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
+       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
 
@@ -34,7 +55,10 @@ def SetLevel(level):
        _Level = level
 
 def Output(format = "", *args):
-       """Call this with a format string and arguments for the format.
+       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
@@ -64,15 +88,38 @@ def IndentLevel(by = 1):
        _Level = _Level + by
 
 def DedentLevel(by = 1):
-       """Decfrement the indentation level by one.
+       """Decrement the indentation level by one.
 
        When called with an argument, subtracts it from the indentation level.
        """
        IndentLevel(-by)
 
-def OutLbrace():
-       """Output a '{' on a line by itself and increase the indentation level."""
-       Output("{")
+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():
@@ -95,22 +142,67 @@ 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")
-       Output("""
-#include <Python.h>
-#include <stdio.h>
-""")
-       Output("main(argc, argv)")
+       Out("""
+               #include <Python.h>
+               #include <stdio.h>
+       
+               main(argc, argv)
+                       int argc;
+                       char **argv;
+               {
+                       int i;
+       """)
        IndentLevel()
-       Output("int argc;")
-       Output("char **argv;")
-       DedentLevel()
-       OutLbrace()
-       Output("int i;")
-       Output()
        Output("""\
 /* Here are a few comment lines.
    Just to test indenting multiple lines.
diff --git a/Tools/bgen/bgen/bgenStackBuffer.py b/Tools/bgen/bgen/bgenStackBuffer.py
new file mode 100644 (file)
index 0000000..b7df5bd
--- /dev/null
@@ -0,0 +1,59 @@
+"""Buffers allocated on the stack."""
+
+
+from bgenBuffer import FixedInputBufferType, FixedOutputBufferType
+
+
+class StackOutputBufferType(FixedOutputBufferType):
+
+       """Fixed output buffer allocated on the stack -- passed as (buffer, size).
+
+       Instantiate with the buffer size as parameter.
+       """
+
+       def passOutput(self, name):
+               return "%s__out__, %s" % (name, self.size)
+
+
+class VarStackOutputBufferType(StackOutputBufferType):
+
+       """Output buffer allocated on the stack -- passed as (buffer, &size).
+
+       Instantiate with the buffer size as parameter.
+       """
+
+       def declareSize(self, name):
+               Output("int %s__len__ = %s;", name, self.size)
+
+       def passOutput(self, name):
+               return "%s__out__, &%s__len__" % (name, name)
+
+       def mkvalueArgs(self, name):
+               return "%s__out__, %s__len__" % (name, name)
+
+
+class VarVarStackOutputBufferType(VarStackOutputBufferType):
+
+       """Output buffer allocated on the stack -- passed as (buffer, size, &size).
+
+       Instantiate with the buffer size as parameter.
+       """
+
+       def passOutput(self, name):
+               return "%s__out__, %s__len__, &%s__len__" % (name, name, name)
+
+
+class ReturnVarStackOutputBufferType(VarStackOutputBufferType):
+
+       """Output buffer allocated on the stack -- passed as (buffer, size) -> size.
+
+       Instantiate with the buffer size as parameter.
+       The function's return value is the size.
+       (XXX Should have a way to suppress returning it separately, too.)
+       """
+
+       def passOutput(self, name):
+               return "%s__out__, %s__len__" % (name, name)
+
+       def mkvalueArgs(self, name):
+               return "%s__out__, _rv" % name
diff --git a/Tools/bgen/bgen/bgenStringBuffer.py b/Tools/bgen/bgen/bgenStringBuffer.py
new file mode 100644 (file)
index 0000000..7d9c77e
--- /dev/null
@@ -0,0 +1,64 @@
+"""Buffers used to hold null-terminated strings."""
+
+
+from bgenBuffer import FixedOutputBufferType
+from bgenStackBuffer import StackOutputBufferType
+from bgenHeapBuffer import HeapOutputBufferType
+
+
+class StringBufferMixIn:
+
+       """Mix-in class to create various string buffer types.
+
+       Strings are character arrays terminated by a null byte.
+       (For input, this is also covered by stringptr.)
+       For output, there are again three variants:
+       - Fixed: size is a constant given in the documentation; or
+       - Stack: size is passed to the C function but we decide on a size at
+         code generation time so we can still allocate on the heap); or
+       - Heap: size is passed to the C function and we let the Python caller
+         pass a size.
+       (Note that this doesn't cover output parameters in which a string
+       pointer is returned.  These are actually easier (no allocation) but far
+       less common.  I'll write the classes when there is demand.)
+       """
+       
+       def declareSize(self, name):
+               pass
+       
+       def getargsFormat(self):
+               return "s"
+       
+       def getargsArgs(self, name):
+               return "&%s__in__" % name
+
+       def mkvalueFormat(self):
+               return "s"
+
+       def mkvalueArgs(self, name):
+               return "%s__out__" % name
+
+
+class FixedOutputStringType(StringBufferMixIn, FixedOutputBufferType):
+
+       """Null-terminated output string -- passed without size.
+
+       Instantiate with buffer size as parameter.
+       """
+
+
+class StackOutputStringType(StringBufferMixIn, StackOutputBufferType):
+
+       """Null-terminated output string -- passed as (buffer, size).
+
+       Instantiate with buffer size as parameter.
+       """
+
+
+class HeapOutputStringType(StringBufferMixIn, HeapOutputBufferType):
+
+       """Null-terminated output string -- passed as (buffer, size).
+
+       Instantiate without parameters.
+       Call from Python with buffer size.
+       """
index 65185094bea5d5b9f11c98953856159fdf940ddb..ba9a53e4304872046f04e192468323f205d5f422 100644 (file)
@@ -1,20 +1,7 @@
-"""Type and Variable classes and a modest collection of standard types."""
-
-from bgenOutput import *
-
-
-# Values to represent argument transfer modes
-InMode    = 1 # input-only argument
-OutMode   = 2 # output-only argument
-InOutMode = 3 # input-output argument
-ModeMask  = 3 # bits to keep for mode
+"""Type classes and a modest collection of standard types."""
 
 
-# Special cases for mode/flags argument
-# XXX This is still a mess!
-SelfMode   =  4+InMode  # this is 'self' -- don't declare it
-ReturnMode =  8+OutMode # this is the function return value
-ErrorMode  = 16+OutMode # this is an error status -- turn it into an exception
+from bgenOutput import *
 
 
 class Type:
@@ -106,8 +93,8 @@ class Type:
                """
                return name
 
-       def mkvalueCleanup(self, name):
-               """Clean up if necessary after mkvalue().
+       def cleanup(self, name):
+               """Clean up if necessary.
 
                This is normally empty; it may deallocate buffers etc.
                """
@@ -120,7 +107,7 @@ class InputOnlyMixIn:
 
        "Mix-in class to boobytrap passOutput"
 
-       def passOutput(self):
+       def passOutput(self, name):
                raise RuntimeError, "this type can only be used for input parameters"
 
 class InputOnlyType(InputOnlyMixIn, Type):
@@ -131,7 +118,7 @@ class OutputOnlyMixIn:
 
        "Mix-in class to boobytrap passInput"
 
-       def passInput(self):
+       def passInput(self, name):
                raise RuntimeError, "this type can only be used for output parameters"
 
 class OutputOnlyType(OutputOnlyMixIn, Type):
@@ -145,13 +132,14 @@ char = Type("char", "c")
 short = Type("short", "h")
 int = Type("int", "i")
 long = Type("long", "l")
+unsigned_long = Type("unsigned long", "l")
 float = Type("float", "f")
 double = Type("double", "d")
 
 
 # The most common use of character pointers is a null-terminated string.
 # For input, this is easy.  For output, and for other uses of char *,
-# see the various Buffer types below.
+# see the module bgenBuffer.
 stringptr = InputOnlyType("char*", "s")
 
 
@@ -170,6 +158,7 @@ class FakeType(InputOnlyType):
 
        def __init__(self, substitute):
                self.substitute = substitute
+               self.typeName = None    # Don't show this argument in __doc__ string
 
        def declare(self, name):
                pass
@@ -184,333 +173,49 @@ class FakeType(InputOnlyType):
                return self.substitute
 
 
-class AbstractBufferType:
-       """Buffers are character arrays that may contain null bytes.
-
-       There are a number of variants depending on:
-       - how the buffer is allocated (for output buffers), and
-       - whether and how the size is passed into and/or out of the called function.
-
-       The AbstractbufferType only serves as a placeholder for this doc string.
-       """
-
-       def declare(self, name):
-               self.declareBuffer(name)
-               self.declareSize(name)
-
-
-class FixedBufferType(AbstractBufferType):
-
-       """Fixed size buffer -- passed without size information.
-
-       Instantiate with the size as parameter.
-       THIS IS STILL AN ABSTRACT BASE CLASS -- DO NOT INSTANTIATE.
-       """
-
-       def __init__(self, size):
-               self.size = str(size)
-
-       def declareSize(self, name):
-               Output("int %s__len__;", name)
-
-
-class FixedInputBufferType(InputOnlyMixIn, FixedBufferType):
-
-       """Fixed size input buffer -- passed without size information.
-
-       Instantiate with the size as parameter.
-       """
-
-       def declareBuffer(self, name):
-               Output("char *%s;", name)
-
-       def getargsFormat(self):
-               return "s#"
-
-       def getargsArgs(self, name):
-               return "&%s, &%s__len__" % (name, name)
-
-       def getargsCheck(self, name):
-               Output("if (%s__len__ != %s)", name, self.size)
-               OutLbrace()
-               Output('PyErr_SetString(PyExc_TypeError, "buffer length should be %s");',
-                      self.size)
-               Output('return NULL;') # XXX should do a goto
-               OutRbrace()
-
-       def passInput(self, name):
-               return name
-
-
-class FixedOutputBufferType(OutputOnlyMixIn, FixedBufferType):
-
-       """Fixed size output buffer -- passed without size information.
-
-       Instantiate with the size as parameter.
-       """
-
-       def declareBuffer(self, name):
-               Output("char %s[%s];", name, self.size)
-
-       def passOutput(self, name):
-               return name
-
-       def mkvalueFormat(self):
-               return "s#"
-
-       def mkvalueArgs(self):
-               return "%s, %s" % (name, self.size)
-
-
-class StructBufferType(FixedBufferType):
-
-       """Structure buffer -- passed as a structure pointer.
-
-       Instantiate with the struct type as parameter.
-       """
-
-       def __init__(self, type):
-               FixedBufferType.__init__(self, "sizeof(%s)" % type)
-               self.type = type
-
-
-class StructInputBufferType(StructBufferType, FixedInputBufferType):
-
-       """Fixed size input buffer -- passed as a pointer to a structure.
-
-       Instantiate with the struct type as parameter.
-       """
-
-       def declareBuffer(self, name):
-               Output("%s *%s;", self.type, name)
-
-
-class StructByValueBufferType(StructInputBufferType):
-
-       """Fixed size input buffer -- passed as a structure BY VALUE.
-
-       Instantiate with the struct type as parameter.
-       """
-
-       def passInput(self, name):
-               return "*%s" % name
-
-
-class StructOutputBufferType(StructBufferType, FixedOutputBufferType):
-
-       """Fixed size output buffer -- passed as a pointer to a structure.
-
-       Instantiate with the struct type as parameter.
-       """
-
-       def declareBuffer(self, name):
-               Output("%s %s;", self.type, name)
-
-       def passOutput(self, name):
-               return "&%s" % name
-
-       def mkvalueArgs(self, name):
-               return "(char *)&%s" % name
-
-
-class VarInputBufferType(InputOnlyMixIn, FixedInputBufferType):
-
-       """Input buffer -- passed as (buffer, size).
-
-       Instantiate without parameters.
-       """
-
-       def __init__(self):
-               pass
-
-       def getargsCheck(self, name):
-               pass
-
-       def passInput(self, name):
-               return "%s, %s__len__" % (name, name)
-
-
-class StackOutputBufferType(OutputOnlyMixIn, FixedOutputBufferType):
-
-       """Output buffer -- passed as (buffer, size).
-
-       Instantiate with the buffer size as parameter.
-       """
-
-       def passOutput(self, name):
-               return "%s, %s" % (name, self.size)
-
-
-class VarStackOutputBufferType(StackOutputBufferType):
-
-       """Output buffer allocated on the stack -- passed as (buffer, &size).
-
-       Instantiate with the buffer size as parameter.
-       """
-
-       def declareSize(self, name):
-               Output("int %s__len__ = %s;", name, self.size)
-
-       def passOutput(self, name):
-               return "%s, &%s__len__" % (name, name)
-
-       def mkvalueArgs(self, name):
-               return "%s, %s__len__" % (name, name)
-
-
-class VarVarStackOutputBufferType(VarStackOutputBufferType):
-
-       """Output buffer allocated on the stack -- passed as (buffer, size, &size).
-
-       Instantiate with the buffer size as parameter.
-       """
-
-       def passOutput(self, name):
-               return "%s, %s__len__, &%s__len__" % (name, name, name)
-
-
-class HeapOutputBufferType(FixedOutputBufferType):
-
-       """Output buffer allocated on the heap -- passed as (buffer, size).
-
-       Instantiate without parameters.
-       Call from Python with buffer size.
-       """
-
-       def __init__(self):
-               pass
-
-       def declareBuffer(self, name):
-               Output("char *%s;", name)
-
-       def getargsFormat(self):
-               return "i"
-
-       def getargsArgs(self, name):
-               return "&%s__len__" % name
-
-       def getargsCheck(self, name):
-               Output("if ((%s = malloc(%s__len__)) == NULL) goto %s__error__;",
-                            name,       name,                     name)
-
-       def passOutput(self, name):
-               return "%s, %s__len__" % (name, name)
-
-       def mkvalueArgs(self, name):
-               return "%s, %s__len__" % (name, name)
-
-       def mkvalueCleanup(self, name):
-               Output("free(%s);", name)
-               DedentLevel()
-               Output(" %s__error__: ;", name);
-               IndentLevel()
-
-
-class VarHeapOutputBufferType(HeapOutputBufferType):
-
-       """Output buffer allocated on the heap -- passed as (buffer, &size).
-
-       Instantiate without parameters.
-       Call from Python with buffer size.
-       """
-
-       def passOutput(self, name):
-               return "%s, &%s__len__" % (name, name)
-
-
-class VarVarHeapOutputBufferType(VarHeapOutputBufferType):
-
-       """Output buffer allocated on the heap -- passed as (buffer, size, &size).
-
-       Instantiate without parameters.
-       Call from Python with buffer size.
-       """
-
-       def passOutput(self, name):
-               return "%s, %s__len__, &%s__len__" % (name, name, name)
-
-
-class StringBufferType:
-
-       """Mix-in class to create various string buffer types.
-
-       Strings are character arrays terminated by a null byte.
-       For input, this is already covered by stringptr.
-       For output, there are again three variants:
-       - Fixed (size is a constant given in the documentation),
-       - Stack (size is passed to the C function but we decide on a size at
-         code generation time so we can still allocate on the heap), or
-       - Heap (size is passed to the C function and we let the Python caller
-         pass a size.
-       (Note that this doesn't cover output parameters in which a string
-       pointer is returned.  These are actually easier (no allocation) but far
-       less common.  I'll write the classes when there is demand.)
-       """
-
-       def mkvalueFormat(self):
-               return "s"
-
-       def mkvalueArgs(self, name):
-               return name
-
-
-class FixedOutputStringType(StringBufferType, FixedOutputBufferType):
-
-       """Null-terminated output string -- passed without size.
-
-       Instantiate with buffer size as parameter.
-       """
-
-
-class StackOutputStringType(StringBufferType, StackOutputBufferType):
-
-       """Null-terminated output string -- passed as (buffer, size).
-
-       Instantiate with buffer size as parameter.
-       """
-
-class HeapOutputStringType(StringBufferType, HeapOutputBufferType):
-
-       """Null-terminated output string -- passed as (buffer, size).
-
-       Instantiate without parameters.
-       Call from Python with buffer size.
-       """
-
-
 class OpaqueType(Type):
 
        """A type represented by an opaque object type, always passed by address.
 
-       Instantiate with the type name, and optional an object type name whose
-       New/Convert functions will be used.
+       Instantiate with the type name and the names of the new and convert procs.
+       If fewer than three arguments are passed, the second argument is used
+       to derive the new and convert procs by appending _New and _Convert; it
+       defaults to the first argument.
        """
 
-       def __init__(self, name, sameAs = None):
+       def __init__(self, name, arg = None, extra = None):
                self.typeName = name
-               self.sameAs = sameAs or name
+               if extra is None:
+                        # Two arguments (name, usetype) or one (name)
+                       arg = arg or name
+                       self.new = arg + '_New'
+                       self.convert = arg + '_Convert'
+               else:
+                       # Three arguments (name, new, convert)
+                       self.new = arg 
+                       self.convert = extra 
 
        def getargsFormat(self):
-               return 'O&'
+               return "O&"
 
        def getargsArgs(self, name):
-               return "%s_Convert, &%s" % (self.sameAs, name)
+               return "%s, &%s" % (self.convert, name)
 
        def passInput(self, name):
                return "&%s" % name
 
        def mkvalueFormat(self):
-               return 'O&'
+               return "O&"
 
        def mkvalueArgs(self, name):
-               return "%s_New, &%s" % (self.sameAs, name)
+               return "%s, &%s" % (self.new, name)
 
 
 class OpaqueByValueType(OpaqueType):
 
        """A type represented by an opaque object type, on input passed BY VALUE.
 
-       Instantiate with the type name, and optional an object type name whose
+       Instantiate with the type name, and optionally an object type name whose
        New/Convert functions will be used.
        """
 
@@ -518,7 +223,7 @@ class OpaqueByValueType(OpaqueType):
                return name
 
        def mkvalueArgs(self, name):
-               return "%s_New, %s" % (self.sameAs, name)
+               return "%s, %s" % (self.new, name)
 
 
 class OpaqueArrayType(OpaqueByValueType):
@@ -530,80 +235,7 @@ class OpaqueArrayType(OpaqueByValueType):
        """
 
        def getargsArgs(self, name):
-               return "%s_Convert, &%s" % (self.sameAs, name)
+               return "%s, %s" % (self.convert, name)
 
        def passOutput(self, name):
                return name
-
-
-class Variable:
-
-       """A Variable holds a type, a name, a transfer mode and flags.
-
-       Most of its methods call the correponding type method with the
-       variable name.
-       """
-
-       def __init__(self, type, name = None, flags = InMode):
-               """Call with a type, a name and flags.
-
-               If name is None, it muse be set later.
-               flags defaults to InMode.
-               """
-               self.type = type
-               self.name = name
-               self.flags = flags
-               self.mode = flags & ModeMask
-
-       def declare(self):
-               """Declare the variable if necessary.
-
-               If it is "self", it is not declared.
-               """
-               if self.flags != SelfMode:
-                       self.type.declare(self.name)
-
-       def getargsFormat(self):
-               """Call the type's getargsFormatmethod."""
-               return self.type.getargsFormat()
-
-       def getargsArgs(self):
-               """Call the type's getargsArgsmethod."""
-               return self.type.getargsArgs(self.name)
-
-       def getargsCheck(self):
-               return self.type.getargsCheck(self.name)
-
-       def passArgument(self):
-               """Return the string required to pass the variable as argument.
-
-               For "in" arguments, return the variable name.
-               For "out" and "in out" arguments,
-               return its name prefixed with "&".
-               """
-               if self.mode == InMode:
-                       return self.type.passInput(self.name)
-               if self.mode in (OutMode, InOutMode):
-                       return self.type.passOutput(self.name)
-               # XXX Shouldn't get here
-               return "/*mode?*/" + self.type.passInput(self.name)
-
-       def errorCheck(self):
-               """Check for an error if necessary.
-
-               This only generates code if the variable's mode is ErrorMode.
-               """
-               if self.flags == ErrorMode:
-                       self.type.errorCheck(self.name)
-
-       def mkvalueFormat (self):
-               """Call the type's mkvalueFormatmethod."""
-               return self.type.mkvalueFormat()
-
-       def mkvalueArgs(self):
-               """Call the type's mkvalueArgs method."""
-               return self.type.mkvalueArgs(self.name)
-
-       def mkvalueCleanup(self):
-               """Call the type's mkvalueCleanup method."""
-               return self.type.mkvalueCleanup(self.name)
diff --git a/Tools/bgen/bgen/bgenVariable.py b/Tools/bgen/bgen/bgenVariable.py
new file mode 100644 (file)
index 0000000..310cc8f
--- /dev/null
@@ -0,0 +1,88 @@
+"""Variables, arguments and argument transfer modes etc."""
+
+
+# Values to represent argument transfer modes
+InMode    = 1 # input-only argument
+OutMode   = 2 # output-only argument
+InOutMode = 3 # input-output argument
+ModeMask  = 3 # bits to keep for mode
+
+
+# Special cases for mode/flags argument
+# XXX This is still a mess!
+SelfMode   =  4+InMode  # this is 'self' -- don't declare it
+ReturnMode =  8+OutMode # this is the function return value
+ErrorMode  = 16+OutMode # this is an error status -- turn it into an exception
+
+
+class Variable:
+
+       """A Variable holds a type, a name, a transfer mode and flags.
+
+       Most of its methods call the correponding type method with the
+       variable name.
+       """
+
+       def __init__(self, type, name = None, flags = InMode):
+               """Call with a type, a name and flags.
+
+               If name is None, it muse be set later.
+               flags defaults to InMode.
+               """
+               self.type = type
+               self.name = name
+               self.flags = flags
+               self.mode = flags & ModeMask
+
+       def declare(self):
+               """Declare the variable if necessary.
+
+               If it is "self", it is not declared.
+               """
+               if self.flags != SelfMode:
+                       self.type.declare(self.name)
+
+       def getargsFormat(self):
+               """Call the type's getargsFormatmethod."""
+               return self.type.getargsFormat()
+
+       def getargsArgs(self):
+               """Call the type's getargsArgsmethod."""
+               return self.type.getargsArgs(self.name)
+
+       def getargsCheck(self):
+               return self.type.getargsCheck(self.name)
+
+       def passArgument(self):
+               """Return the string required to pass the variable as argument.
+
+               For "in" arguments, return the variable name.
+               For "out" and "in out" arguments,
+               return its name prefixed with "&".
+               """
+               if self.mode == InMode:
+                       return self.type.passInput(self.name)
+               if self.mode in (OutMode, InOutMode):
+                       return self.type.passOutput(self.name)
+               # XXX Shouldn't get here
+               return "/*mode?*/" + self.type.passInput(self.name)
+
+       def errorCheck(self):
+               """Check for an error if necessary.
+
+               This only generates code if the variable's mode is ErrorMode.
+               """
+               if self.flags == ErrorMode:
+                       self.type.errorCheck(self.name)
+
+       def mkvalueFormat (self):
+               """Call the type's mkvalueFormat method."""
+               return self.type.mkvalueFormat()
+
+       def mkvalueArgs(self):
+               """Call the type's mkvalueArgs method."""
+               return self.type.mkvalueArgs(self.name)
+
+       def cleanup(self):
+               """Call the type's cleanup method."""
+               return self.type.cleanup(self.name)
diff --git a/Tools/bgen/bgen/macsupport.py b/Tools/bgen/bgen/macsupport.py
new file mode 100644 (file)
index 0000000..d2269b1
--- /dev/null
@@ -0,0 +1,140 @@
+"""\
+Augment the "bgen" package with definitions that are useful on the Apple Macintosh.
+
+Intended usage is "from macsupport import *" -- this implies all bgen's goodies.
+"""
+
+
+# Import everything from bgen (for ourselves as well as for re-export)
+from bgen import *
+
+
+# Simple types
+Boolean = Type("Boolean", "b")
+SignedByte = Type("SignedByte", "b")
+ScriptCode = Type("ScriptCode", "h")
+Size = Type("Size", "l")
+Style = Type("Style", "b")
+
+# Pascal strings
+ConstStr255Param = OpaqueArrayType("Str255", "PyMac_BuildStr255", "PyMac_GetStr255")
+Str255 = OpaqueArrayType("Str255", "PyMac_BuildStr255", "PyMac_GetStr255")
+
+# File System Specifications
+FSSpec_ptr = OpaqueType("FSSpec", "PyMac_BuildFSSpec", "PyMac_GetFSSpec")
+
+# OSType and ResType: 4-byte character strings
+def OSTypeType(typename):
+       return OpaqueByValueType(typename, "PyMac_BuildOSType", "PyMac_GetOSType")
+OSType = OSTypeType("OSType")
+ResType = OSTypeType("ResType")
+
+# Handles (always resources in our case)
+Handle = OpaqueByValueType("Handle", "ResObj")
+MenuHandle = OpaqueByValueType("MenuHandle", "MenuObj")
+ControlHandle = OpaqueByValueType("ControlHandle", "CtlObj")
+
+# Windows and Dialogs
+WindowPtr = OpaqueByValueType("WindowPtr", "WinObj")
+DialogPtr = OpaqueByValueType("DialogPtr", "DlgObj")
+
+# NULL pointer passed in as optional storage -- not present in Python version
+NullStorage = FakeType("(void *)0")
+
+# Quickdraw data types
+Rect = Rect_ptr = OpaqueType("Rect", "PyMac_BuildRect", "PyMac_GetRect")
+Point = OpaqueByValueType("Point", "PyMac_BuildPoint", "PyMac_GetPoint")
+
+# Event records
+EventRecord = OpaqueType("EventRecord", "PyMac_BuildEventRecord", "PyMac_GetEventRecord")
+EventRecord_ptr = EventRecord
+
+# OSErr is special because it is turned into an exception
+# (Could do this with less code using a variant of mkvalue("O&")?)
+class OSErrType(Type):
+       def errorCheck(self, name):
+               Output("if (%s != noErr) return PyMac_Error(%s);", name, name)
+               self.used = 1
+OSErr = OSErrType("OSErr", 'h')
+
+
+# Various buffer types
+
+InBuffer = VarInputBufferType('char', 'long', 'l')             # (buf, len)
+
+InOutBuffer = HeapInputOutputBufferType('char', 'long', 'l')   # (inbuf, outbuf, len)
+VarInOutBuffer = VarHeapInputOutputBufferType('char', 'long', 'l') # (inbuf, outbuf, &len)
+
+OutBuffer = HeapOutputBufferType('char', 'long', 'l')          # (buf, len)
+VarOutBuffer = VarHeapOutputBufferType('char', 'long', 'l')    # (buf, &len)
+VarVarOutBuffer = VarVarHeapOutputBufferType('char', 'long', 'l') # (buf, len, &len)
+
+
+# Predefine various pieces of program text to be passed to Module() later:
+
+# Stuff added immediately after the system include files
+includestuff = """
+#define SystemSevenOrLater 1
+
+#include "macglue.h"
+#include <Memory.h>
+#include <Dialogs.h>
+#include <Menus.h>
+#include <Controls.h>
+
+extern PyObject *ResObj_New(Handle);
+extern int ResObj_Convert(PyObject *, Handle *);
+
+extern PyObject *WinObj_New(WindowPtr);
+extern int WinObj_Convert(PyObject *, WindowPtr *);
+
+extern PyObject *DlgObj_New(DialogPtr);
+extern int DlgObj_Convert(PyObject *, DialogPtr *);
+extern PyTypeObject Dialog_Type;
+#define DlgObj_Check(x) ((x)->ob_type == &Dialog_Type)
+
+extern PyObject *MenuObj_New(MenuHandle);
+extern int MenuObj_Convert(PyObject *, MenuHandle *);
+
+extern PyObject *CtlObj_New(ControlHandle);
+extern int CtlObj_Convert(PyObject *, ControlHandle *);
+"""
+
+# Stuff added just before the module's init function
+finalstuff = """
+"""
+
+# Stuff added inside the module's init function
+initstuff = """
+"""
+
+
+# Generator classes with a twist -- if the function returns OSErr,
+# its mode is manipulated so that it turns into an exception or disappears
+# (and its name is changed to _err, for documentation purposes).
+# This requires that the OSErr type (defined above) has a non-trivial
+# errorCheck method.
+class OSErrMixIn:
+       "Mix-in class to treat OSErr return values special"
+       def makereturnvar(self):
+               if self.returntype is OSErr:
+                       return Variable(self.returntype, "_err", ErrorMode)
+               else:
+                       return Variable(self.returntype, "_rv", OutMode)
+
+class OSErrFunctionGenerator(OSErrMixIn, FunctionGenerator): pass
+class OSErrMethodGenerator(OSErrMixIn, MethodGenerator): pass
+
+
+class MacModule(Module):
+       "Subclass which gets the exception initializer from macglue.c"
+       def exceptionInitializer(self):
+               return "PyMac_GetOSErrException()"
+
+_SetOutputFileName = SetOutputFileName # Save original
+def SetOutputFileName(file = None):
+       "Set the output file name and set its creator&type to KAHL&TEXT"
+       _SetOutputFileName(file)
+       if file:
+               import MacOS
+               MacOS.SetCreatorAndType(file, 'KAHL', 'TEXT')
diff --git a/Tools/bgen/bgen/scantools.py b/Tools/bgen/bgen/scantools.py
new file mode 100644 (file)
index 0000000..d6ee88c
--- /dev/null
@@ -0,0 +1,373 @@
+"""\
+Tools for scanning header files in search of function prototypes.
+
+Often, the function prototypes in header files contain enough information
+to automatically generate (or reverse-engineer) interface specifications
+from them.  The conventions used are very vendor specific, but once you've
+figured out what they are they are often a great help, and it sure beats
+manually entering the interface specifications.  (These are needed to generate
+the glue used to access the functions from Python.)
+
+In order to make this class useful, almost every component can be overridden.
+The defaults are (currently) tuned to scanning Apple Macintosh header files,
+although most Mac specific details are contained in header-specific subclasses.
+"""
+
+import regex
+import regsub
+import string
+import sys
+import os
+import fnmatch
+from types import *
+try:
+       import MacOS
+except ImportError:
+       MacOS = None
+
+# Default preferences
+CREATOR = 'KAHL'               # My favorite text editor on the Mac
+INCLUDEDIR = "D:Development:THINK C:Mac #includes:Apple #includes:"
+
+
+Error = "scantools.Error"
+
+class Scanner:
+
+       def __init__(self, input = None, output = None, defsoutput = None):
+               self.initsilent()
+               self.initblacklists()
+               self.initrepairinstructions()
+               self.initpaths()
+               self.initfiles()
+               self.initpatterns()
+               self.compilepatterns()
+               self.initosspecifics()
+               if output:
+                       self.setoutput(output, defsoutput)
+               if input:
+                       self.setinput(input)
+
+       def initsilent(self):
+               self.silent = 0
+
+       def error(self, format, *args):
+               if self.silent >= 0:
+                       print format%args
+
+       def report(self, format, *args):
+               if not self.silent:
+                       print format%args
+
+       def initblacklists(self):
+               self.blacklistnames = self.makeblacklistnames()
+               self.blacklisttypes = ["unknown"] + self.makeblacklisttypes()
+
+       def makeblacklistnames(self):
+               return []
+
+       def makeblacklisttypes(self):
+               return []
+
+       def initrepairinstructions(self):
+               self.repairinstructions = self.makerepairinstructions()
+
+       def makerepairinstructions(self):
+               return []
+
+       def initfiles(self):
+               self.specmine = 0
+               self.defsmine = 0
+               self.scanmine = 0
+               self.specfile = sys.stdout
+               self.defsfile = None
+               self.scanfile = sys.stdin
+               self.lineno = 0
+               self.line = ""
+
+       def initpaths(self):
+               self.includepath = [':', INCLUDEDIR]
+
+       def initpatterns(self):
+               self.head_pat = "^pascal[ \t]+" # XXX Mac specific!
+               self.tail_pat = "[);]"
+               self.whole_pat = "\(<type>[a-zA-Z0-9_]+\)[ \t\n]+" + \
+                                "\(<name>[a-zA-Z0-9_]+\)[ \t\n]*(\(<args>[^()]*\))"
+               self.sym_pat = "^[ \t]*\(<name>[a-zA-Z0-9_]+\)[ \t]*=" + \
+                              "[ \t]*\(<defn>[-0-9'\"][^\t\n,]*\),?"
+               self.asplit_pat = "^\(<type>.*[^a-zA-Z0-9_]\)\(<name>[a-zA-Z0-9_]+\)$"
+
+       def compilepatterns(self):
+               for name in dir(self):
+                       if name[-4:] == "_pat":
+                               pat = getattr(self, name)
+                               prog = regex.symcomp(pat)
+                               setattr(self, name[:-4], prog)
+
+       def initosspecifics(self):
+               if MacOS:
+                       self.filetype = 'TEXT'
+                       self.filecreator = CREATOR
+               else:
+                       self.filetype = self.filecreator = None
+
+       def setfiletype(self, filename):
+               if MacOS and (self.filecreator or self.filetype):
+                       creator, type = MacOS.GetCreatorAndType(filename)
+                       if self.filecreator: creator = self.filecreator
+                       if self.filetype: type = self.filetype
+                       MacOS.SetCreatorAndType(filename, creator, type)
+
+       def close(self):
+               self.closefiles()
+
+       def closefiles(self):
+               self.closespec()
+               self.closedefs()
+               self.closescan()
+
+       def closespec(self):
+               tmp = self.specmine and self.specfile
+               self.specfile = None
+               if tmp: tmp.close()
+
+       def closedefs(self):
+               tmp = self.defsmine and self.defsfile
+               self.defsfile = None
+               if tmp: tmp.close()
+
+       def closescan(self):
+               tmp = self.scanmine and self.scanfile
+               self.scanfile = None
+               if tmp: tmp.close()
+
+       def setoutput(self, spec, defs = None):
+               self.closespec()
+               self.closedefs()
+               if spec:
+                       if type(spec) == StringType:
+                               file = self.openoutput(spec)
+                               mine = 1
+                       else:
+                               file = spec
+                               mine = 0
+                       self.specfile = file
+                       self.specmine = mine
+               if defs:
+                       if type(defs) == StringType:
+                               file = self.openoutput(defs)
+                               mine = 1
+                       else:
+                               file = defs
+                               mine = 0
+                       self.defsfile = file
+                       self.defsmine = mine
+
+       def openoutput(self, filename):
+               file = open(filename, 'w')
+               self.setfiletype(filename)
+               return file
+
+       def setinput(self, scan = sys.stdin):
+               self.closescan()
+               if scan:
+                       if type(scan) == StringType:
+                               file = self.openinput(scan)
+                               mine = 1
+                       else:
+                               file = scan
+                               mine = 0
+                       self.scanfile = file
+                       self.scanmine = mine
+               self.lineno = 0
+
+       def openinput(self, filename):
+               if not os.path.isabs(filename):
+                       for dir in self.includepath:
+                               fullname = os.path.join(dir, filename)
+                               #self.report("trying full name %s", `fullname`)
+                               try:
+                                       return open(fullname, 'r')
+                               except IOError:
+                                       pass
+               # If not on the path, or absolute, try default open()
+               return open(filename, 'r')
+
+       def getline(self):
+               if not self.scanfile:
+                       raise Error, "input file not set"
+               self.line = self.scanfile.readline()
+               if not self.line:
+                       raise EOFError
+               self.lineno = self.lineno + 1
+               return self.line
+
+       def scan(self):
+               if not self.scanfile:
+                       self.error("No input file has been specified")
+                       return
+               inputname = self.scanfile.name
+               self.report("scanfile = %s", `inputname`)
+               if not self.specfile:
+                       self.report("(No interface specifications will be written)")
+               else:
+                       self.report("specfile = %s", `self.specfile.name`)
+                       self.specfile.write("# Generated from %s\n" % `inputname`)
+               if not self.defsfile:
+                       self.report("(No symbol definitions will be written)")
+               else:
+                       self.report("defsfile = %s", `self.defsfile.name`)
+                       self.defsfile.write("# Generated from %s\n" % `inputname`)
+               self.alreadydone = []
+               try:
+                       while 1:
+                               try: line = self.getline()
+                               except EOFError: break
+                               if self.defsfile and self.sym.match(line) >= 0:
+                                       self.dosymdef()
+                                       continue
+                               if self.head.match(line) >= 0:
+                                       self.dofuncspec()
+                                       continue
+               except EOFError:
+                       self.error("Uncaught EOF error")
+
+       def dosymdef(self):
+               name, defn = self.sym.group('name', 'defn')
+               self.defsfile.write("%s = %s\n" % (name, defn))
+
+       def dofuncspec(self):
+               raw = self.line[len(self.head.group(0)):]
+               while self.tail.search(raw) < 0:
+                       line = self.getline()
+                       raw = raw + line
+               self.processrawspec(raw)
+
+       def processrawspec(self, raw):
+               if self.whole.search(raw) < 0:
+                       self.report("Bad raw spec: %s", `raw`)
+                       return
+               type, name, args = self.whole.group('type', 'name', 'args')
+               if name in self.alreadydone:
+                       self.report("Name has already been defined: %s", `name`)
+                       return
+               self.report("==> %s %s <==", type, name)
+               if self.blacklisted(type, name):
+                       self.error("*** %s %s blacklisted", type, name)
+                       return
+               arglist = self.extractarglist(args)
+               arglist = self.repairarglist(arglist)
+               if self.unmanageable(type, name, arglist):
+                       ##for arg in arglist:
+                       ##      self.report("    %s", `arg`)
+                       self.error("*** %s %s unmanageable", type, name)
+                       return
+               self.alreadydone.append(name)
+               self.generate(type, name, arglist)
+
+       def extractarglist(self, args):
+               args = string.strip(args)
+               if not args or args == "void":
+                       return []
+               parts = map(string.strip, string.splitfields(args, ","))
+               arglist = []
+               for part in parts:
+                       arg = self.extractarg(part)
+                       arglist.append(arg)
+               return arglist
+
+       def extractarg(self, part):
+               mode = "InMode"
+               if self.asplit.match(part) < 0:
+                       self.error("Indecipherable argument: %s", `part`)
+                       return ("unknown", part, mode)
+               type, name = self.asplit.group('type', 'name')
+               type = regsub.gsub("\*", " ptr ", type)
+               type = string.strip(type)
+               type = regsub.gsub("[ \t]+", "_", type)
+               if type[:6] == "const_":
+                       type = type[6:]
+               elif type[-4:] == "_ptr":
+                       type = type[:-4]
+                       mode = "OutMode"
+               return type, name, mode
+
+       def repairarglist(self, arglist):
+               arglist = arglist[:]
+               i = 0
+               while i < len(arglist):
+                       for pattern, replacement in self.repairinstructions:
+                               n = len(pattern)
+                               if i+n > len(arglist): continue
+                               current = arglist[i:i+n]
+                               for j in range(n):
+                                       if not self.matcharg(pattern[j], current[j]):
+                                               break
+                               else: # All items of the pattern match
+                                       new = self.substituteargs(
+                                                       pattern, replacement, current)
+                                       if new is not None:
+                                               arglist[i:i+n] = new
+                                               i = i+len(new) # No recursive substitutions
+                                               break
+                       else: # No patterns match
+                               i = i+1
+               return arglist
+       
+       def matcharg(self, patarg, arg):
+               return len(filter(None, map(fnmatch.fnmatch, arg, patarg))) == 3
+
+       def substituteargs(self, pattern, replacement, old):
+               new = []
+               for k in range(len(replacement)):
+                       item = replacement[k]
+                       newitem = [item[0], item[1], item[2]]
+                       for i in range(3):
+                               if item[i] == '*':
+                                       newitem[i] = old[k][i]
+                               elif item[i][:1] == '$':
+                                       index = string.atoi(item[i][1:]) - 1
+                                       newitem[i] = old[index][i]
+                       new.append(tuple(newitem))
+               ##self.report("old: %s", `old`)
+               ##self.report("new: %s", `new`)
+               return new
+
+       def generate(self, type, name, arglist):
+               classname, listname = self.destination(type, name, arglist)
+               if not self.specfile: return
+               self.specfile.write("\nf = %s(%s, %s,\n" % (classname, type, `name`))
+               for atype, aname, amode in arglist:
+                       self.specfile.write("    (%s, %s, %s),\n" %
+                                           (atype, `aname`, amode))
+               self.specfile.write(")\n")
+               self.specfile.write("%s.append(f)\n" % listname)
+
+       def destination(self, type, name, arglist):
+               return "FunctionGenerator", "functions"
+
+       def blacklisted(self, type, name):
+               if type in self.blacklisttypes:
+                       ##self.report("return type %s is blacklisted", type)
+                       return 1
+               if name in self.blacklistnames:
+                       ##self.report("function name %s is blacklisted", name)
+                       return 1
+               return 0
+
+       def unmanageable(self, type, name, arglist):
+               for atype, aname, amode in arglist:
+                       if atype in self.blacklisttypes:
+                               self.report("argument type %s is blacklisted", atype)
+                               return 1
+               return 0
+
+def test():
+       input = "D:Development:THINK C:Mac #includes:Apple #includes:AppleEvents.h"
+       output = "@aespecs.py"
+       defsoutput = "@aedefs.py"
+       s = Scanner(input, output, defsoutput)
+       s.scan()
+
+if __name__ == '__main__':
+       test()