diff options
-rw-r--r-- | Tools/bgen/bgen/bgen.py | 5 | ||||
-rw-r--r-- | Tools/bgen/bgen/bgenBuffer.py | 227 | ||||
-rw-r--r-- | Tools/bgen/bgen/bgenGenerator.py | 56 | ||||
-rw-r--r-- | Tools/bgen/bgen/bgenHeapBuffer.py | 108 | ||||
-rw-r--r-- | Tools/bgen/bgen/bgenModule.py | 19 | ||||
-rw-r--r-- | Tools/bgen/bgen/bgenObjectDefinition.py | 79 | ||||
-rw-r--r-- | Tools/bgen/bgen/bgenOutput.py | 136 | ||||
-rw-r--r-- | Tools/bgen/bgen/bgenStackBuffer.py | 59 | ||||
-rw-r--r-- | Tools/bgen/bgen/bgenStringBuffer.py | 64 | ||||
-rw-r--r-- | Tools/bgen/bgen/bgenType.py | 428 | ||||
-rw-r--r-- | Tools/bgen/bgen/bgenVariable.py | 88 | ||||
-rw-r--r-- | Tools/bgen/bgen/macsupport.py | 140 | ||||
-rw-r--r-- | Tools/bgen/bgen/scantools.py | 373 |
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() |