[cig-commits] r6224 - cs/babel/trunk/spike/Spike/Compiler
leif at geodynamics.org
leif at geodynamics.org
Sun Mar 11 09:17:08 PDT 2007
Author: leif
Date: 2007-03-11 09:17:07 -0700 (Sun, 11 Mar 2007)
New Revision: 6224
Modified:
cs/babel/trunk/spike/Spike/Compiler/ExprNodes.py
cs/babel/trunk/spike/Spike/Compiler/Nodes.py
cs/babel/trunk/spike/Spike/Compiler/Parsing.py
cs/babel/trunk/spike/Spike/Compiler/SpikeTypes.py
cs/babel/trunk/spike/Spike/Compiler/Symtab.py
Log:
Implemented a hack to address the chief limitation of Pyrex: the
inability to parse C include files.
In Pyrex, to use stuff from "spam.h", one writes:
cdef extern from "spam.h":
int spam_counter
void order_spam(int tons)
>From the documentation: "It's important to understand that Pyrex does
not itself read the C header file, so you still need to provide Pyrex
versions of any declarations from it that you use."
If you only need a handful of declarations, this isn't so bad. But if
you are wrapping an entire library, it quickly becomes tedious. (Matt
told me that someone created a Python interface to PETSc by
*generating* Pyrex code from the C header files. Yuck!)
Spike will be able to parse C header files soon. But in the interim,
here is an interesting and ugly hack. Even after the C parser is
fully functional, this hack may prove useful for external code that
Spike is unwilling or unable to parse (namely C++, although Elsa looks
promising). In any case, I was simply curious whether this hack would
work.
In a .pyx file, one can now write:
cimport "mpi.h" as cmpi
One can then immediately start using names from 'cmpi':
cdef cmpi.MPI_Comm comm
cdef int error, rank
error = cmpi.MPI_Comm_rank(comm, &rank)
Spike treats all names originating from "cmpi" as having "unknown"
type. This means that any errors (including typos) are passed through
to the underlying C compiler. (Since Spike emits #line directives,
the errors will sometimes point back to the original .pyx file.)
Since Spike doesn't know the type of anything from an include file,
casts to and from Python types will not work. (Currently, these
result in generated code which doesn't compile or link, but in the
future there could be an error message.) In simple cases, one can
work-around this with the use of a temporary:
cdef int temp
temp = ccode.xyz
return temp
In Pyrex code, the "." operator is used for both "." and "->".
Therefore, for an unknown type, "." is ambiguous. So, Spike assumes
everything is a struct: "x.y" always generates "x.y" for an unknown
'x'. But, one can declare a pointer to an unknown type, and all is
well:
cdef unknown.X *x
x.field = 42 # generates "x->field = 42"
This hack hasn't been tested thoroughly, but it is good enough for
Pythia's "_mpi.pyx".
Modified: cs/babel/trunk/spike/Spike/Compiler/ExprNodes.py
===================================================================
--- cs/babel/trunk/spike/Spike/Compiler/ExprNodes.py 2007-03-11 05:27:50 UTC (rev 6223)
+++ cs/babel/trunk/spike/Spike/Compiler/ExprNodes.py 2007-03-11 16:17:07 UTC (rev 6224)
@@ -762,7 +762,8 @@
def check_identifier_kind(self):
entry = self.entry
if not (entry.is_const or entry.is_variable
- or entry.is_builtin or entry.is_cfunction or entry.is_spikefunction):
+ or entry.is_builtin or entry.is_cfunction or entry.is_spikefunction
+ or entry.is_unknown):
if self.entry.as_variable:
self.entry = self.entry.as_variable
else:
@@ -1288,6 +1289,9 @@
def analyse_c_function_call(self, env):
func_type = self.function_type()
# Check function type
+ if func_type.is_unknown:
+ self.type = func_type
+ return
if not func_type.is_cfunction:
if not func_type.is_error:
error(self.pos, "Calling non-function type '%s'" %
@@ -1331,11 +1335,19 @@
return self.c_call_code()
def c_call_code(self):
+ if self.args is None:
+ return "<error>"
+ arg_list_code = []
func_type = self.function_type()
- if self.args is None or not func_type.is_cfunction:
+ if func_type.is_unknown:
+ for actual_arg in self.args:
+ arg_list_code.append(actual_arg.result_code)
+ result = "%s(%s)" % (self.function.result_code,
+ join(arg_list_code, ","))
+ return result
+ if not func_type.is_cfunction:
return "<error>"
formal_args = func_type.args
- arg_list_code = []
for (formal_arg, actual_arg) in \
zip(formal_args, self.args):
arg_code = actual_arg.result_as(formal_arg.type)
@@ -1512,7 +1524,8 @@
entry = module_scope.lookup_here(self.attribute)
if entry and (
entry.is_cglobal or entry.is_cfunction
- or entry.is_type or entry.is_const):
+ or entry.is_type or entry.is_const
+ or entry.is_unknown):
self.mutate_into_name_node(env, entry, target)
return 1
return 0
@@ -1628,7 +1641,7 @@
if Options.intern_names:
self.interned_attr_cname = env.intern(self.attribute)
else:
- if not obj_type.is_error:
+ if not (obj_type.is_error or obj_type.is_unknown):
error(self.pos,
"Object of type '%s' has no attribute '%s'" %
(obj_type, self.attribute))
@@ -2679,6 +2692,8 @@
type2 = operand2.type
if type1.is_error or type2.is_error:
return 1
+ if type1.is_unknown or type2.is_unknown:
+ return 1
if type1.is_pyobject: # type2 will be, too
return 1
elif type1.is_ptr:
Modified: cs/babel/trunk/spike/Spike/Compiler/Nodes.py
===================================================================
--- cs/babel/trunk/spike/Spike/Compiler/Nodes.py 2007-03-11 05:27:50 UTC (rev 6223)
+++ cs/babel/trunk/spike/Spike/Compiler/Nodes.py 2007-03-11 16:17:07 UTC (rev 6224)
@@ -1546,6 +1546,8 @@
entry = scope.find(self.name, self.pos)
if entry and entry.is_type:
type = entry.type
+ elif entry and entry.is_unknown:
+ type = entry.type
else:
error(self.pos, "'%s' is not a type identifier" % self.name)
if type:
@@ -3684,6 +3686,25 @@
pass
+class CImportIncludeStatNode(StatNode):
+ # 'cimport "header.h" as name' statement
+ #
+ # include_file_name string Qualified name of module being imported
+ # as_name string Name specified in "as" clause
+
+ def analyse_declarations(self, env):
+ module_scope = env.find_submodule(self.as_name)
+ entry = env.declare_module(self.as_name, module_scope, self.pos)
+ entry.as_module.is_entry_into_the_unknown = 1
+ entry.as_module.add_include_file(self.include_file_name)
+
+ def analyse_expressions(self, env):
+ pass
+
+ def generate_execution_code(self, code):
+ pass
+
+
class FromCImportStatNode(StatNode):
# from ... cimport statement
#
Modified: cs/babel/trunk/spike/Spike/Compiler/Parsing.py
===================================================================
--- cs/babel/trunk/spike/Spike/Compiler/Parsing.py 2007-03-11 05:27:50 UTC (rev 6223)
+++ cs/babel/trunk/spike/Spike/Compiler/Parsing.py 2007-03-11 16:17:07 UTC (rev 6224)
@@ -785,6 +785,18 @@
pos = s.position()
kind = s.sy
s.next()
+ if kind == 'cimport':
+ literal = p_opt_string_literal(s)
+ if literal:
+ _, include_file_name = literal
+ as_name = p_as_name(s)
+ if as_name is None:
+ s.error("Expected 'as'")
+ stat = Nodes.CImportIncludeStatNode(
+ pos,
+ include_file_name = include_file_name,
+ as_name = as_name)
+ return Nodes.StatListNode(pos, stats = [stat])
items = [p_dotted_name(s, as_allowed = 1)]
while s.sy == ',':
s.next()
Modified: cs/babel/trunk/spike/Spike/Compiler/SpikeTypes.py
===================================================================
--- cs/babel/trunk/spike/Spike/Compiler/SpikeTypes.py 2007-03-11 05:27:50 UTC (rev 6223)
+++ cs/babel/trunk/spike/Spike/Compiler/SpikeTypes.py 2007-03-11 16:17:07 UTC (rev 6224)
@@ -71,6 +71,7 @@
is_string = 0
is_returncode = 0
is_error = 0
+ is_unknown = 0
has_attributes = 0
default_value = ""
parsetuple_format = ""
@@ -260,7 +261,12 @@
to_py_function = None
from_py_function = None
+ def assignable_from(self, src_type):
+ if src_type.is_unknown:
+ return 1
+ return SpikeType.assignable_from(self, src_type)
+
class CSimpleType(CType):
#
# Base class for all unstructured C types.
@@ -283,6 +289,24 @@
return 0
+class CUnknownType(CSimpleType):
+ is_unknown = 1
+
+ def __init__(self, name):
+ self.name = name
+
+ def __repr__(self):
+ return "<CUnknownType>"
+
+ def declaration_code(self, entity_code,
+ for_display = 0, dll_linkage = None, pyrex = 0):
+ base = public_decl(self.name, dll_linkage)
+ return "%s %s" % (base, entity_code)
+
+ def assignable_from(self, src_type):
+ return isinstance(src_type, CType)
+
+
class CNumericType(CType):
#
# Base class for all C numeric types.
Modified: cs/babel/trunk/spike/Spike/Compiler/Symtab.py
===================================================================
--- cs/babel/trunk/spike/Spike/Compiler/Symtab.py 2007-03-11 05:27:50 UTC (rev 6223)
+++ cs/babel/trunk/spike/Spike/Compiler/Symtab.py 2007-03-11 16:17:07 UTC (rev 6224)
@@ -9,7 +9,8 @@
from SpikeTypes import c_int_type, \
py_object_type, c_char_array_type, \
spike_function_type, \
- CEnumType, CStructOrUnionType, PyExtensionType
+ CEnumType, CStructOrUnionType, PyExtensionType, \
+ CUnknownType
from TypeSlots import \
pyfunction_signature, pymethod_signature, \
get_special_method_signature, get_property_accessor_signature
@@ -63,6 +64,7 @@
# interned_cname string C name of interned name string
# pystring_cname string C name of Python version of string literal
# is_interned boolean For string const entries, value is interned
+ # is_unknown boolean Is from an included C header file
borrowed = 0
init = ""
@@ -94,6 +96,7 @@
interned_cname = None
pystring_cname = None
is_interned = 0
+ is_unknown = 0
def __init__(self, name, cname, type, pos = None, init = None):
self.name = name
@@ -133,6 +136,7 @@
is_c_class_scope = 0
scope_prefix = ""
in_cinclude = 0
+ is_entry_into_the_unknown = 0
def __init__(self, name, outer_scope, parent_scope):
# The outer_scope is the next scope in the lookup chain.
@@ -210,6 +214,11 @@
def qualify_name(self, name):
return "%s.%s" % (self.qualified_name, name)
+
+ def declare_unknown(self, name):
+ entry = self.declare(name, name, CUnknownType(name), ('<unknown>', 0, 0))
+ entry.is_unknown = 1
+ return entry
def declare_const(self, name, type, value, pos, cname = None):
# Add an entry for a named constant.
@@ -351,7 +360,10 @@
def lookup_here(self, name):
# Look up in this scope only, return None if not found.
- return self.entries.get(name, None)
+ entry = self.entries.get(name, None)
+ if entry is None and self.is_entry_into_the_unknown:
+ entry = self.declare_unknown(name)
+ return entry
def lookup_target(self, name):
# Look up name in this scope only. Declare as Python
@@ -490,6 +502,7 @@
# intern_map {string : string} Mapping from Python names to interned strs
# interned_names [string] Interned names pending generation of declarations
# all_pystring_entries [Entry] Python string consts from all scopes
+ # is_entry_into_the_unknown boolean Contains any name asked for
def __init__(self, name, parent_module, context):
self.parent_module = parent_module
@@ -514,6 +527,7 @@
self.intern_map = {}
self.interned_names = []
self.all_pystring_entries = []
+ self.is_entry_into_the_unknown = 0
def qualifying_scope(self):
return self.parent_module
More information about the cig-commits
mailing list