summaryrefslogtreecommitdiffstats
path: root/Tools/bgen
diff options
context:
space:
mode:
authorGuido van Rossum <guido@python.org>1995-01-25 22:59:21 (GMT)
committerGuido van Rossum <guido@python.org>1995-01-25 22:59:21 (GMT)
commit01f5a81d11b935b3aa9ed3347d41a52daef07a3c (patch)
treec3c324264e2e0427e41a62ea811a16cbd06e72d4 /Tools/bgen
parent5679e56bd1614a3b78653008e216fa9f7b5eb215 (diff)
downloadcpython-01f5a81d11b935b3aa9ed3347d41a52daef07a3c.zip
cpython-01f5a81d11b935b3aa9ed3347d41a52daef07a3c.tar.gz
cpython-01f5a81d11b935b3aa9ed3347d41a52daef07a3c.tar.bz2
Lots of new stuff again. Moved buffer types to some separate files.
Added some new-fangled features to bgenOutput. Generate doc strings!
Diffstat (limited to 'Tools/bgen')
-rw-r--r--Tools/bgen/bgen/bgen.py5
-rw-r--r--Tools/bgen/bgen/bgenBuffer.py227
-rw-r--r--Tools/bgen/bgen/bgenGenerator.py56
-rw-r--r--Tools/bgen/bgen/bgenHeapBuffer.py108
-rw-r--r--Tools/bgen/bgen/bgenModule.py19
-rw-r--r--Tools/bgen/bgen/bgenObjectDefinition.py79
-rw-r--r--Tools/bgen/bgen/bgenOutput.py136
-rw-r--r--Tools/bgen/bgen/bgenStackBuffer.py59
-rw-r--r--Tools/bgen/bgen/bgenStringBuffer.py64
-rw-r--r--Tools/bgen/bgen/bgenType.py428
-rw-r--r--Tools/bgen/bgen/bgenVariable.py88
-rw-r--r--Tools/bgen/bgen/macsupport.py140
-rw-r--r--Tools/bgen/bgen/scantools.py373
13 files changed, 1327 insertions, 455 deletions
diff --git a/Tools/bgen/bgen/bgen.py b/Tools/bgen/bgen/bgen.py
index aab35de..db28b0a 100644
--- a/Tools/bgen/bgen/bgen.py
+++ b/Tools/bgen/bgen/bgen.py
@@ -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
index 0000000..925b937
--- /dev/null
+++ b/Tools/bgen/bgen/bgenBuffer.py
@@ -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
diff --git a/Tools/bgen/bgen/bgenGenerator.py b/Tools/bgen/bgen/bgenGenerator.py
index 62aab1c..4f57697 100644
--- a/Tools/bgen/bgen/bgenGenerator.py
+++ b/Tools/bgen/bgen/bgenGenerator.py
@@ -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
index 0000000..2c51695
--- /dev/null
+++ b/Tools/bgen/bgen/bgenHeapBuffer.py
@@ -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)
diff --git a/Tools/bgen/bgen/bgenModule.py b/Tools/bgen/bgen/bgenModule.py
index 8824ee9..eda1a02 100644
--- a/Tools/bgen/bgen/bgenModule.py
+++ b/Tools/bgen/bgen/bgenModule.py
@@ -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():
diff --git a/Tools/bgen/bgen/bgenObjectDefinition.py b/Tools/bgen/bgen/bgenObjectDefinition.py
index 10a4468..e7fa146 100644
--- a/Tools/bgen/bgen/bgenObjectDefinition.py
+++ b/Tools/bgen/bgen/bgenObjectDefinition.py
@@ -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 = ""
diff --git a/Tools/bgen/bgen/bgenOutput.py b/Tools/bgen/bgen/bgenOutput.py
index 06ca3ed..1a512e5 100644
--- a/Tools/bgen/bgen/bgenOutput.py
+++ b/Tools/bgen/bgen/bgenOutput.py
@@ -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
index 0000000..b7df5bd
--- /dev/null
+++ b/Tools/bgen/bgen/bgenStackBuffer.py
@@ -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
index 0000000..7d9c77e
--- /dev/null
+++ b/Tools/bgen/bgen/bgenStringBuffer.py
@@ -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.
+ """
diff --git a/Tools/bgen/bgen/bgenType.py b/Tools/bgen/bgen/bgenType.py
index 6518509..ba9a53e 100644
--- a/Tools/bgen/bgen/bgenType.py
+++ b/Tools/bgen/bgen/bgenType.py
@@ -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
index 0000000..310cc8f
--- /dev/null
+++ b/Tools/bgen/bgen/bgenVariable.py
@@ -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
index 0000000..d2269b1
--- /dev/null
+++ b/Tools/bgen/bgen/macsupport.py
@@ -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
index 0000000..d6ee88c
--- /dev/null
+++ b/Tools/bgen/bgen/scantools.py
@@ -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()