[pypy-svn] r6291 - pypy/branch/pypy-genc/translator

arigo at codespeak.net arigo at codespeak.net
Mon Sep 6 14:37:24 CEST 2004


Author: arigo
Date: Mon Sep  6 14:37:23 2004
New Revision: 6291

Added:
   pypy/branch/pypy-genc/translator/genc_class.h
   pypy/branch/pypy-genc/translator/genc_typeset.py
Modified:
   pypy/branch/pypy-genc/translator/genc.h
   pypy/branch/pypy-genc/translator/genc.py
Log:
Translating simple classes to C structs and a PyTypeObject declaration.
Moved the CTypeSet class to its own file.


Modified: pypy/branch/pypy-genc/translator/genc.h
==============================================================================
--- pypy/branch/pypy-genc/translator/genc.h	(original)
+++ pypy/branch/pypy-genc/translator/genc.h	Mon Sep  6 14:37:23 2004
@@ -3,6 +3,7 @@
  /***  Generic C header section                            ***/
 
 #include <Python.h>
+#include <structmember.h>
 
 
 /*** operations on ints ***/
@@ -46,6 +47,7 @@
 #define OP_GETITEM_ooo(x,y,r,err)   if (!(r=PyObject_GetItem(x,y)))    goto err;
 #define OP_SETITEM_ooov(x,y,z,err)  if ((PyObject_SetItem(x,y,z))<0)   goto err;
 #define OP_GETATTR_ooo(x,y,r,err)   if (!(r=PyObject_GetAttr(x,y)))    goto err;
+#define OP_SETATTR_ooov(x,y,z,err)  if ((PyObject_SetAttr(x,y,z))<0)   goto err;
 #define OP_NEWSLICE_oooo(x,y,z,r,err)  if (!(r=PySlice_New(x,y,z)))    goto err;
 
 
@@ -92,6 +94,8 @@
 		}                                       \
 	}
 
+#define INSTANTIATE(cls, r, err)    if (!(r=cls##_new())) goto err;
+
 
 /* a few built-in functions */
 

Modified: pypy/branch/pypy-genc/translator/genc.py
==============================================================================
--- pypy/branch/pypy-genc/translator/genc.py	(original)
+++ pypy/branch/pypy-genc/translator/genc.py	Mon Sep  6 14:37:23 2004
@@ -2,251 +2,17 @@
 Generate a C source file from the flowmodel.
 
 """
-import autopath, os, re, types, __builtin__
-from pypy.objspace.flow.model import Variable, Constant, UndefinedConstant
-from pypy.objspace.flow.model import Block, Link, traverse
-from pypy.translator.typer import LLFunction, LLOp, LLConst, TypingError
-from pypy.annotation import model as annmodel
-
-
-class CRepr:
-    "A possible representation of a flow-graph variable as C-level variables."
-
-    def __init__(self, impl, err_check=None, parse_code=None):
-        self.impl = impl   # list [(C type, prefix for variable name)]
-        self.err_check = err_check  # condition to check for error return value
-        self.parse_code = parse_code  # character(s) for PyArg_ParseTuple()
-
-    def __repr__(self):
-        if hasattr(self, 'const'):
-            return '<C:= %r>' % (self.const,)
-        else:
-            return '<C: %s>' % ' + '.join(self.impl)
-
-
-class CTypeSet:
-    "A (small) set of C types that typer.LLFunction can manipulate."
-
-    R_VOID   = CRepr([])
-    R_INT    = CRepr(['int'],       err_check='< 0',     parse_code='i')
-    R_OBJECT = CRepr(['PyObject*'], err_check='== NULL', parse_code='O')
-
-    REPR_BY_CODE = {
-        'v': R_VOID,
-        'i': R_INT,
-        'o': R_OBJECT,
-        }
-
-    def __init__(self, genc, bindings):
-        self.genc = genc
-        self.bindings = bindings
-        self.r_constants = {}
-        self.lloperations = {'convert': {}, 'release': {}}
-        self.parse_operation_templates()
-
-    # __________ methods required by LLFunction __________
-    #
-    # Here, we assume that every high-level type has a canonical representation
-    # so a high-level type is just a CRepr.
-
-    def gethltype(self, var):
-        if isinstance(var, Variable):
-            s_value = self.bindings.get(var) or annmodel.SomeObject()
-            if s_value.is_constant():
-                return self.constant_representation(s_value.const)
-            if issubclass(s_value.knowntype, int):
-                return self.R_INT
-            # fall-back
-            return self.R_OBJECT
-        if isinstance(var, Constant):
-            return self.constant_representation(var.value)
-        raise TypeError, var
-
-    def represent(self, hltype):
-        return hltype.impl
-
-    def typingerror(self, opname, hltypes):
-        # build operations with a variable number of arguments on demand
-        if opname == 'OP_NEWLIST':
-            opnewlist = self.lloperations.setdefault('OP_NEWLIST', {})
-            sig = (self.R_OBJECT,) * len(hltypes)
-            if sig in opnewlist:
-                return False
-            def writer(*stuff):
-                content = stuff[:-2]
-                result = stuff[-2]
-                err = stuff[-1]
-                ls = ['if (!(%s = PyList_New(%d))) goto %s;' % (
-                    result, len(content), err)]
-                for i in range(len(content)):
-                    ls.append('PyList_SET_ITEM(%s, %d, %s); Py_INCREF(%s);' % (
-                        result, i, content[i], content[i]))
-                return '\n'.join(ls)
-            opnewlist[sig] = writer, True
-            return True   # retry
-        if opname == 'OP_SIMPLE_CALL' and hltypes:
-            opsimplecall = self.lloperations.setdefault('OP_SIMPLE_CALL', {})
-            sig = (self.R_OBJECT,) * len(hltypes)
-            if sig in opsimplecall:
-                return False
-            def writer(func, *stuff):
-                args = stuff[:-2]
-                result = stuff[-2]
-                err = stuff[-1]
-                format = '"' + 'O' * len(args) + '"'
-                args = (func, format) + args
-                return ('if (!(%s = PyObject_CallFunction(%s)))'
-                        ' goto %s;' % (result, ', '.join(args), err))
-            opsimplecall[sig] = writer, True
-            return True   # retry
-        return False
-
-    def knownanswer(self, llname):
-        return getattr(llname, 'known_answer', None)
-
-    # ____________________________________________________________
-
-    def constant_representation(self, value):
-        key = type(value), value   # to avoid mixing for example 0 and 0.0
-        try:
-            return self.r_constants[key]
-        except KeyError:
-            # a constant doesn't need any C variable to be encoded
-            r = self.r_constants[key] = CRepr([])
-            r.const = value
-            # returning a constant
-            def writer():
-                return 'return 0;'
-            self.lloperations['return'][r,] = writer, False
-            def writer():
-                return 'return -1;'
-            self.lloperations['returnerr'][r,] = writer, False
-            
-            # but to convert it to something more general like an int or
-            # a PyObject* we need to revive its value, which is done by
-            # new conversion operations that we define now
-            conv = self.lloperations['convert']
-            if isinstance(value, int):
-                # can convert the constant to a C int
-                def writer(z):
-                    return '%s = %d;' % (z, value)
-                conv[r, self.R_INT] = writer, False
-                writer.known_answer = [LLConst(self.R_INT, '%d' % value)]
-                # can convert the constant to a PyObject*
-                if value >= 0:
-                    name = 'g_IntObj_%d' % value
-                else:
-                    name = 'g_IntObj_minus%d' % abs(value)
-                self.can_convert_to_pyobj(r, 'PyInt_FromLong(%d)' % value,
-                                          name)
-            elif isinstance(value, str):
-                # can convert the constant to a PyObject*
-                self.can_convert_to_pyobj(r,
-                    'PyString_FromStringAndSize(%s, %d)' % (c_str(value),
-                                                            len(value)),
-                    'g_StrObj_%s' % manglestr(value))
-            elif value is None:
-                # can convert the constant to Py_None
-                self.can_convert_to_pyobj(r, None, 'Py_None')
-            elif callable(value) and value in self.genc.llfunctions:
-                # another Python function: can be called with OP_SIMPLE_CALL
-                llfunc = self.genc.llfunctions[value]
-                ops = self.lloperations.setdefault('OP_SIMPLE_CALL', {})
-                sig = [r]
-                for v in llfunc.graph.getargs():
-                    sig.append(self.gethltype(v))
-                hltype = self.gethltype(llfunc.graph.getreturnvar())
-                sig.append(hltype)
-                if len(hltype.impl) == 0:   # no return value
-                    def writer(*stuff):
-                        args = stuff[:-1]
-                        err = stuff[-1]
-                        return 'if (%s(%s) < 0) goto %s;' % (
-                            llfunc.name, ', '.join(args), err)
-                elif len(hltype.impl) == 1:  # one LLVar for the return value
-                    def writer(*stuff):
-                        args = stuff[:-2]
-                        result = stuff[-2]
-                        err = stuff[-1]
-                        return ('if ((%s = %s(%s)) %s) goto %s;' % (
-                            result, llfunc.name, ', '.join(args),
-                            hltype.err_check, err))
-                else:
-                    XXX("to do")
-                ops[tuple(sig)] = writer, True
-            elif (isinstance(value, types.BuiltinFunctionType) and
-                  value is getattr(__builtin__, value.__name__, None)):
-                # a function from __builtin__: can convert to PyObject*
-                self.can_convert_to_pyobj(r,
-                    'PyMapping_GetItemString(PyEval_GetBuiltins(), %s)' % (
-                    c_str(value.__name__)),
-                    'g_Builtin_%s' % manglestr(value.__name__))
-                # if the function is defined in genc.h, import its definition
-                # by copying the operation CALL_xxx to OP_SIMPLE_CALL with
-                # a first argument which is the constant function xxx.
-                opname = 'CALL_' + value.__name__
-                if opname in self.lloperations:
-                    ops = self.lloperations.setdefault('OP_SIMPLE_CALL', {})
-                    for sig, ll in self.lloperations[opname].items():
-                        sig = (r,) + sig
-                        ops[sig] = ll
-            else:
-                print "// XXX not implemented: constant", key
-            return r
-
-    def can_convert_to_pyobj(self, r, initexpr, globalname):
-        conv = self.lloperations['convert']
-        def writer(z):
-            return '%s = %s; Py_INCREF(%s);' % (z, globalname, z)
-        conv[r, self.R_OBJECT] = writer, False
-        llconst = LLConst('PyObject*', globalname, initexpr,
-                          to_declare = bool(initexpr))
-        writer.known_answer = [llconst]
-
-    def parse_operation_templates(self):
-        # parse the genc.h header to figure out which macros are implemented
-        codes = ''.join(self.REPR_BY_CODE.keys())
-        pattern = r"#define ([A-Za-z_][0-9A-Za-z_]*)_([%s]*)[(](.*?)[)]" % codes
-        rexp = re.compile(pattern)
-        for line in C_HEADER.split('\n'):
-            match = rexp.match(line)
-            if match:
-                self.register_operation_template(*match.groups())
-
-    def register_operation_template(self, opname, typecodes, formalargs):
-        llname = '%s_%s' % (opname, typecodes)
-        sig = tuple([self.REPR_BY_CODE[code] for code in typecodes])
-        can_fail = formalargs.replace(' ','').endswith(',err')
-        ops = self.lloperations.setdefault(opname, {})
-        assert sig not in ops, llname
-        # the operation's low-level name is a callable that will
-        # produce the correct macro call
-        def writer(*args):
-            return llname + '(' + ', '.join(args) + ')'
-        ops.setdefault(sig, (writer, can_fail))
-
-def c_str(s):
-    "Return the C expression for the string 's'."
-    s = repr(s)
-    if s.startswith("'"):
-        s = '"' + s[1:-1].replace('"', r'\"') + '"'
-    return s
-
-def manglestr(s):
-    "Return an identifier name unique for the string 's'."
-    l = []
-    for c in s:
-        if not ('a' <= c <= 'z' or 'A' <= c <= 'Z' or '0' <= c <= '9'):
-            if c == '_':
-                c = '__'
-            else:
-                c = '_%02x' % ord(c)
-        l.append(c)
-    return ''.join(l)
+import autopath, os
+from pypy.translator.typer import LLFunction, LLOp, LLConst
+from pypy.translator import genc_typeset
+
+class CClass:
+    def __init__(self, name, cdef):
+        self.name = name
+        self.cdef = cdef    # instance of pypy.annotator.factory.ClassDef
 
 # ____________________________________________________________
 
-
 class GenC:
 
     def __init__(self, f, translator, modname=None):
@@ -264,9 +30,11 @@
                 pass
         else:
             bindings = {}
-        self.typeset = CTypeSet(self, bindings)
+        self.typeset = genc_typeset.CTypeSet(self, bindings)
         self.initializationcode = []
+        self.cclasses = {}
         self.llfunctions = {}
+        self.build_cclasses()
         self.build_llfunctions()
         self.gen_source()
 
@@ -277,7 +45,7 @@
             'exported': self.translator.functions[0].__name__,
             }
         # header
-        print >> f, C_HEADER
+        print >> f, self.C_HEADER
 
         # forward declarations
         print >> f, '/* forward declarations */'
@@ -285,26 +53,44 @@
             print >> f, 'static %s;' % self.cfunction_header(func)
         print >> f
 
+        # class declarations
+        if self.cclasses:
+            # keep original order, so don't iterate over the dict self.cclasses
+            for cdef in self.translator.annotator.getuserclassdefinitions():
+                self.gen_cclass(self.cclasses[cdef.cls])
+
         # function implementation
-        print >> f, C_SEP
+        print >> f, self.C_SEP
         print >> f
         for func in self.translator.functions:
             self.gen_cfunction(func)
             print >> f
 
         # entry point
-        print >> f, C_SEP
-        print >> f, C_ENTRYPOINT_HEADER % info
+        print >> f, self.C_SEP
+        print >> f, self.C_ENTRYPOINT_HEADER % info
         self.gen_entrypoint(self.translator.functions[0])
-        print >> f, C_ENTRYPOINT_FOOTER % info
+        print >> f, self.C_ENTRYPOINT_FOOTER % info
 
         # footer
-        print >> f, C_METHOD_TABLE % info
-        print >> f, C_INIT_HEADER % info
+        print >> f, self.C_METHOD_TABLE % info
+        print >> f, self.C_INIT_HEADER % info
         for codeline in self.initializationcode:
             print >> f, '\t' + codeline
-        print >> f, C_INIT_FOOTER % info
+        print >> f, self.C_INIT_FOOTER % info
+
 
+    def build_cclasses(self):
+        if not self.translator.annotator:
+            return
+        n = 0
+        for cdef in self.translator.annotator.getuserclassdefinitions():
+            cls = cdef.cls
+            self.cclasses[cls] = CClass(
+                name = '%s__%d' % (cls.__name__, n),
+                cdef = cdef,
+                )
+            n += 1
 
     def build_llfunctions(self):
         n = 0
@@ -403,7 +189,7 @@
                 # operation that comes from typer.py.
                 writer = line.name   
                 if isinstance(writer, str):
-                    writer = LL_ONLY_OPERATIONS[writer]
+                    writer = self.LL_ONLY_OPERATIONS[writer]
                 code = writer(*args)
                 for codeline in code.split('\n'):
                     print >> f, '\t' + codeline
@@ -414,35 +200,43 @@
                 print >> f
         print >> f, '}'
 
+    LL_ONLY_OPERATIONS = {
+        'move': lambda x,y: '%s = %s;' % (y,x),
+        'goto': lambda err: 'goto %s;' % err,
+        }
 
-LL_ONLY_OPERATIONS = {
-    'move': lambda x,y: '%s = %s;' % (y,x),
-    'goto': lambda err: 'goto %s;' % err,
-    }
+    def gen_cclass(self, cclass):
+        f = self.f
+        info = {
+            'name': cclass.name,
+            'module': cclass.cdef.cls.__module__,
+            }
+        print >> f, self.C_CLASS % info
 
 # ____________________________________________________________
 
-C_HEADER = open(os.path.join(autopath.this_dir, 'genc.h')).read()
+    C_HEADER = open(os.path.join(autopath.this_dir, 'genc.h')).read()
+    C_CLASS = open(os.path.join(autopath.this_dir, 'genc_class.h')).read()
 
-C_SEP = "/************************************************************/"
+    C_SEP = "/************************************************************/"
 
-C_ENTRYPOINT_HEADER = '''
+    C_ENTRYPOINT_HEADER = '''
 static PyObject* c_%(exported)s(PyObject* self, PyObject* args)
 {'''
 
-C_ENTRYPOINT_FOOTER = '''}'''
+    C_ENTRYPOINT_FOOTER = '''}'''
 
-C_METHOD_TABLE = '''
+    C_METHOD_TABLE = '''
 static PyMethodDef %(modname)sMethods[] = {
 \t{"%(exported)s", (PyCFunction)c_%(exported)s, METH_VARARGS},
 \t{NULL, NULL}
 };'''
 
-C_INIT_HEADER = '''
+    C_INIT_HEADER = '''
 void init%(modname)s(void)
 {
 \tPy_InitModule("%(modname)s", %(modname)sMethods);'''
 
-C_INIT_FOOTER = '''}'''
+    C_INIT_FOOTER = '''}'''
 
 # ____________________________________________________________

Added: pypy/branch/pypy-genc/translator/genc_class.h
==============================================================================
--- (empty file)
+++ pypy/branch/pypy-genc/translator/genc_class.h	Mon Sep  6 14:37:23 2004
@@ -0,0 +1,83 @@
+/************************************************************/
+/* Definition of class %(module)s.%(name)s */
+
+typedef struct {
+	PyObject_HEAD
+	PyObject* dict;
+} %(name)s_Object;
+staticforward PyTypeObject %(name)s_Type;
+
+static PyObject* %(name)s_new(void)
+{
+	%(name)s_Object* op = PyObject_GC_New(%(name)s_Object, &%(name)s_Type);
+	if (op)
+		PyObject_GC_Track(op);
+	return (PyObject*) op;
+}
+
+static void %(name)s_dealloc(%(name)s_Object* op)
+{
+	PyObject_GC_UnTrack(op);
+	Py_XDECREF(op->dict);
+	op->ob_type->tp_free((PyObject *)op);
+}
+
+static int %(name)s_traverse(%(name)s_Object* op, visitproc visit, void* arg)
+{
+	if (op->dict != NULL)
+		return visit(op->dict, arg);
+	else
+		return 0;
+}
+
+static int %(name)s_clear(%(name)s_Object* op)
+{
+	PyObject* d = op->dict;
+	op->dict = NULL;
+	Py_XDECREF(d);
+	return 0;
+}
+
+statichere PyTypeObject %(name)s_Type = {
+	PyObject_HEAD_INIT(&PyType_Type)
+	0,
+	"%(name)s",
+	sizeof(%(name)s_Object),
+	0,
+	(destructor)%(name)s_dealloc,		/* tp_dealloc */
+	0,					/* tp_print */
+	0,					/* tp_getattr */
+	0,					/* tp_setattr */
+	0,					/* tp_compare */
+	0,					/* tp_repr */
+	0,					/* tp_as_number */
+	0,					/* tp_as_sequence */
+	0,					/* tp_as_mapping */
+	0,					/* tp_hash */
+	0,					/* tp_call */
+	0,					/* tp_str */
+	PyObject_GenericGetAttr,		/* tp_getattro */
+	PyObject_GenericSetAttr,		/* tp_setattro */
+	0,					/* tp_as_buffer */
+	Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC |
+		Py_TPFLAGS_BASETYPE,		/* tp_flags */
+ 	0,					/* tp_doc */
+ 	(traverseproc)%(name)s_traverse,	/* tp_traverse */
+ 	(inquiry)%(name)s_clear,		/* tp_clear */
+	0,					/* tp_richcompare */
+	0,					/* tp_weaklistoffset */
+	0,					/* tp_iter */
+	0,					/* tp_iternext */
+	0,					/* tp_methods */
+	0,					/* tp_members */
+	0,					/* tp_getset */
+	0,					/* tp_base */
+	0,					/* tp_dict */
+	0,					/* tp_descr_get */
+	0,					/* tp_descr_set */
+	offsetof(%(name)s_Object, dict),	/* tp_dictoffset */
+	0,					/* tp_init */
+	PyType_GenericAlloc,			/* tp_alloc */
+	PyType_GenericNew,			/* tp_new */
+	PyObject_GC_Del,			/* tp_free */
+};

Added: pypy/branch/pypy-genc/translator/genc_typeset.py
==============================================================================
--- (empty file)
+++ pypy/branch/pypy-genc/translator/genc_typeset.py	Mon Sep  6 14:37:23 2004
@@ -0,0 +1,250 @@
+import re, types, __builtin__
+from pypy.objspace.flow.model import Variable, Constant
+from pypy.annotation import model as annmodel
+from pypy.translator.typer import LLConst
+
+
+class CRepr:
+    "A possible representation of a flow-graph variable as C-level variables."
+
+    def __init__(self, impl, err_check=None, parse_code=None):
+        self.impl = impl   # list [(C type, prefix for variable name)]
+        self.err_check = err_check  # condition to check for error return value
+        self.parse_code = parse_code  # character(s) for PyArg_ParseTuple()
+
+    def __repr__(self):
+        if hasattr(self, 'const'):
+            return '<C:= %r>' % (self.const,)
+        else:
+            return '<C: %s>' % ' + '.join(self.impl)
+
+
+class CTypeSet:
+    "A (small) set of C types that typer.LLFunction can manipulate."
+
+    R_VOID   = CRepr([])
+    R_INT    = CRepr(['int'],       err_check='< 0',     parse_code='i')
+    R_OBJECT = CRepr(['PyObject*'], err_check='== NULL', parse_code='O')
+
+    REPR_BY_CODE = {
+        'v': R_VOID,
+        'i': R_INT,
+        'o': R_OBJECT,
+        }
+
+    def __init__(self, genc, bindings):
+        self.genc = genc
+        self.bindings = bindings
+        self.r_constants = {}
+        self.lloperations = {'convert': {}, 'release': {}}
+        self.parse_operation_templates()
+
+    # __________ methods required by LLFunction __________
+    #
+    # Here, we assume that every high-level type has a canonical representation
+    # so a high-level type is just a CRepr.
+
+    def gethltype(self, var):
+        if isinstance(var, Variable):
+            s_value = self.bindings.get(var) or annmodel.SomeObject()
+            if s_value.is_constant():
+                return self.constant_representation(s_value.const)
+            if issubclass(s_value.knowntype, int):
+                return self.R_INT
+            # fall-back
+            return self.R_OBJECT
+        if isinstance(var, Constant):
+            return self.constant_representation(var.value)
+        raise TypeError, var
+
+    def represent(self, hltype):
+        return hltype.impl
+
+    def typingerror(self, opname, hltypes):
+        # build operations with a variable number of arguments on demand
+        if opname == 'OP_NEWLIST':
+            opnewlist = self.lloperations.setdefault('OP_NEWLIST', {})
+            sig = (self.R_OBJECT,) * len(hltypes)
+            if sig in opnewlist:
+                return False
+            def writer(*stuff):
+                content = stuff[:-2]
+                result = stuff[-2]
+                err = stuff[-1]
+                ls = ['if (!(%s = PyList_New(%d))) goto %s;' % (
+                    result, len(content), err)]
+                for i in range(len(content)):
+                    ls.append('PyList_SET_ITEM(%s, %d, %s); Py_INCREF(%s);' % (
+                        result, i, content[i], content[i]))
+                return '\n'.join(ls)
+            opnewlist[sig] = writer, True
+            return True   # retry
+        if opname == 'OP_SIMPLE_CALL' and hltypes:
+            opsimplecall = self.lloperations.setdefault('OP_SIMPLE_CALL', {})
+            sig = (self.R_OBJECT,) * len(hltypes)
+            if sig in opsimplecall:
+                return False
+            def writer(func, *stuff):
+                args = stuff[:-2]
+                result = stuff[-2]
+                err = stuff[-1]
+                format = '"' + 'O' * len(args) + '"'
+                args = (func, format) + args
+                return ('if (!(%s = PyObject_CallFunction(%s)))'
+                        ' goto %s;' % (result, ', '.join(args), err))
+            opsimplecall[sig] = writer, True
+            return True   # retry
+        return False
+
+    def knownanswer(self, llname):
+        return getattr(llname, 'known_answer', None)
+
+    # ____________________________________________________________
+
+    def constant_representation(self, value):
+        key = type(value), value   # to avoid mixing for example 0 and 0.0
+        try:
+            return self.r_constants[key]
+        except KeyError:
+            # a constant doesn't need any C variable to be encoded
+            r = self.r_constants[key] = CRepr([])
+            r.const = value
+            # returning a constant
+            def writer():
+                return 'return 0;'
+            self.lloperations['return'][r,] = writer, False
+            def writer():
+                return 'return -1;'
+            self.lloperations['returnerr'][r,] = writer, False
+            
+            # but to convert it to something more general like an int or
+            # a PyObject* we need to revive its value, which is done by
+            # new conversion operations that we define now
+            conv = self.lloperations['convert']
+            if isinstance(value, int):
+                # can convert the constant to a C int
+                def writer(z):
+                    return '%s = %d;' % (z, value)
+                conv[r, self.R_INT] = writer, False
+                writer.known_answer = [LLConst(self.R_INT, '%d' % value)]
+                # can convert the constant to a PyObject*
+                if value >= 0:
+                    name = 'g_IntObj_%d' % value
+                else:
+                    name = 'g_IntObj_minus%d' % abs(value)
+                self.can_convert_to_pyobj(r, 'PyInt_FromLong(%d)' % value,
+                                          name)
+            elif isinstance(value, str):
+                # can convert the constant to a PyObject*
+                self.can_convert_to_pyobj(r,
+                    'PyString_FromStringAndSize(%s, %d)' % (c_str(value),
+                                                            len(value)),
+                    'g_StrObj_%s' % manglestr(value))
+            elif value is None:
+                # can convert the constant to Py_None
+                self.can_convert_to_pyobj(r, None, 'Py_None')
+            elif callable(value) and value in self.genc.llfunctions:
+                # another Python function: can be called with OP_SIMPLE_CALL
+                llfunc = self.genc.llfunctions[value]
+                ops = self.lloperations.setdefault('OP_SIMPLE_CALL', {})
+                sig = [r]
+                for v in llfunc.graph.getargs():
+                    sig.append(self.gethltype(v))
+                hltype = self.gethltype(llfunc.graph.getreturnvar())
+                sig.append(hltype)
+                if len(hltype.impl) == 0:   # no return value
+                    def writer(*stuff):
+                        args = stuff[:-1]
+                        err = stuff[-1]
+                        return 'if (%s(%s) < 0) goto %s;' % (
+                            llfunc.name, ', '.join(args), err)
+                elif len(hltype.impl) == 1:  # one LLVar for the return value
+                    def writer(*stuff):
+                        args = stuff[:-2]
+                        result = stuff[-2]
+                        err = stuff[-1]
+                        return ('if ((%s = %s(%s)) %s) goto %s;' % (
+                            result, llfunc.name, ', '.join(args),
+                            hltype.err_check, err))
+                else:
+                    XXX("to do")
+                ops[tuple(sig)] = writer, True
+            elif (isinstance(value, types.BuiltinFunctionType) and
+                  value is getattr(__builtin__, value.__name__, None)):
+                # a function from __builtin__: can convert to PyObject*
+                self.can_convert_to_pyobj(r,
+                    'PyMapping_GetItemString(PyEval_GetBuiltins(), %s)' % (
+                    c_str(value.__name__)),
+                    'g_Builtin_%s' % manglestr(value.__name__))
+                # if the function is defined in genc.h, import its definition
+                # by copying the operation CALL_xxx to OP_SIMPLE_CALL with
+                # a first argument which is the constant function xxx.
+                opname = 'CALL_' + value.__name__
+                if opname in self.lloperations:
+                    ops = self.lloperations.setdefault('OP_SIMPLE_CALL', {})
+                    for sig, ll in self.lloperations[opname].items():
+                        sig = (r,) + sig
+                        ops[sig] = ll
+            elif (isinstance(value, (type, types.ClassType)) and
+                  value in self.genc.cclasses):
+                # a user-defined class
+                ops = self.lloperations.setdefault('OP_SIMPLE_CALL', {})
+                # XXX do __init__
+                sig = (r, self.R_OBJECT)
+                def writer(res, err):
+                    return 'INSTANTIATE(%s, %s, %s)' % (
+                        self.genc.cclasses[value].name, res, err)
+                ops[sig] = writer, True
+            else:
+                print "// XXX not implemented: constant", key
+            return r
+
+    def can_convert_to_pyobj(self, r, initexpr, globalname):
+        conv = self.lloperations['convert']
+        def writer(z):
+            return '%s = %s; Py_INCREF(%s);' % (z, globalname, z)
+        conv[r, self.R_OBJECT] = writer, False
+        llconst = LLConst('PyObject*', globalname, initexpr,
+                          to_declare = bool(initexpr))
+        writer.known_answer = [llconst]
+
+    def parse_operation_templates(self):
+        # parse the genc.h header to figure out which macros are implemented
+        codes = ''.join(self.REPR_BY_CODE.keys())
+        pattern = r"#define ([A-Za-z_][0-9A-Za-z_]*)_([%s]*)[(](.*?)[)]" % codes
+        rexp = re.compile(pattern)
+        for line in self.genc.C_HEADER.split('\n'):
+            match = rexp.match(line)
+            if match:
+                self.register_operation_template(*match.groups())
+
+    def register_operation_template(self, opname, typecodes, formalargs):
+        llname = '%s_%s' % (opname, typecodes)
+        sig = tuple([self.REPR_BY_CODE[code] for code in typecodes])
+        can_fail = formalargs.replace(' ','').endswith(',err')
+        ops = self.lloperations.setdefault(opname, {})
+        assert sig not in ops, llname
+        # the operation's low-level name is a callable that will
+        # produce the correct macro call
+        def writer(*args):
+            return llname + '(' + ', '.join(args) + ')'
+        ops.setdefault(sig, (writer, can_fail))
+
+def c_str(s):
+    "Return the C expression for the string 's'."
+    s = repr(s)
+    if s.startswith("'"):
+        s = '"' + s[1:-1].replace('"', r'\"') + '"'
+    return s
+
+def manglestr(s):
+    "Return an identifier name unique for the string 's'."
+    l = []
+    for c in s:
+        if not ('a' <= c <= 'z' or 'A' <= c <= 'Z' or '0' <= c <= '9'):
+            if c == '_':
+                c = '__'
+            else:
+                c = '_%02x' % ord(c)
+        l.append(c)
+    return ''.join(l)



More information about the Pypy-commit mailing list