More pure cython mode stuff.
authorRobert Bradshaw <robertwb@math.washington.edu>
Sat, 4 Oct 2008 11:34:40 +0000 (04:34 -0700)
committerRobert Bradshaw <robertwb@math.washington.edu>
Sat, 4 Oct 2008 11:34:40 +0000 (04:34 -0700)
Cython/Compiler/CythonScope.py
Cython/Compiler/ExprNodes.py
Cython/Compiler/Nodes.py
Cython/Compiler/ParseTreeTransforms.py
Cython/Compiler/PyrexTypes.py
Cython/Compiler/Symtab.py
Cython/Shadow.py

index d14d7c8074f41835b0af0d123139c57163487520..5a4a8c9d3570415bd50e992f8ff4753dfbc0d746 100644 (file)
@@ -15,6 +15,12 @@ class CythonScope(ModuleScope):
                                                   pos=None,
                                                   defining = 1,
                                                   cname='<error>')
+                                                  
+    def lookup_type(self, name):
+        # This function should go away when types are all first-level objects. 
+        type = parse_basic_type(name)
+        if type:
+            return type
 
 def create_cython_scope(context):
     return CythonScope(context)
index 6552b67b59cb23a10ce2096fc97d01e0da9c89bc..46323cd697602d8f37b411fee7f172b91cf33c7e 100644 (file)
@@ -563,6 +563,9 @@ class ExprNode(Node):
         #  a constant, local var, C global var, struct member
         #  reference, or temporary.
         return self.result_in_temp()
+        
+    def as_cython_attribute(self):
+        return None
 
 
 class AtomicExprNode(ExprNode):
@@ -715,6 +718,9 @@ class StringNode(ConstNode):
         self.entry = env.add_string_const(self.value)
         
     def analyse_as_type(self, env):
+        type = PyrexTypes.parse_basic_type(self.value)
+        if type is not None:    
+            return type
         from TreeFragment import TreeFragment
         pos = (self.pos[0], self.pos[1], self.pos[2]-7)
         declaration = TreeFragment(u"sizeof(%s)" % self.value, name=pos[0].filename, initial_pos=pos)
@@ -849,7 +855,8 @@ class NameNode(AtomicExprNode):
     #  entry           Entry     Symbol table entry
     #  interned_cname  string
     
-    is_name = 1
+    is_name = True
+    is_cython_module = False
     skip_assignment_decref = False
     entry = None
 
@@ -895,8 +902,9 @@ class NameNode(AtomicExprNode):
         return None
         
     def analyse_as_type(self, env):
-        if self.name in PyrexTypes.rank_to_type_name:
-            return PyrexTypes.simple_c_type(1, 0, self.name)
+        type = PyrexTypes.parse_basic_type(self.name)
+        if type:
+            return type
         entry = self.entry
         if not entry:
             entry = env.lookup(self.name)
@@ -1808,6 +1816,21 @@ class SimpleCallNode(CallNode):
             return function(*args)
         except Exception, e:
             self.compile_time_value_error(e)
+            
+    def analyse_as_type(self, env):
+        attr = self.function.as_cython_attribute()
+        if attr == 'pointer':
+            if len(self.args) != 1:
+                error(self.args.pos, "only one type allowed.")
+            else:
+                type = self.args[0].analyse_as_type(env)
+                if not type:
+                    error(self.args[0].pos, "Unknown type")
+                else:
+                    return PyrexTypes.CPtrType(type)
+
+    def explicit_args_kwds(self):
+        return self.args, None
 
     def analyse_types(self, env):
         function = self.function
@@ -2037,6 +2060,12 @@ class GeneralCallNode(CallNode):
             return function(*positional_args, **keyword_args)
         except Exception, e:
             self.compile_time_value_error(e)
+            
+    def explicit_args_kwds(self):
+        if self.starstar_arg or not isinstance(self.positional_args, TupleNode):
+            raise PostParseError(self.pos,
+                'Compile-time keyword arguments must be explicit.')
+        return self.positional_args.args, self.keyword_args
 
     def analyse_types(self, env):
         self.function.analyse_types(env)
@@ -2140,6 +2169,10 @@ class AttributeNode(ExprNode):
     is_called = 0
     needs_none_check = True
 
+    def as_cython_attribute(self):
+        if isinstance(self.obj, NameNode) and self.obj.is_cython_module:
+            return self.attribute
+
     def coerce_to(self, dst_type, env):
         #  If coercing to a generic pyobject and this is a cpdef function
         #  we can create the corresponding attribute
@@ -2217,9 +2250,7 @@ class AttributeNode(ExprNode):
     def analyse_as_type(self, env):
         module_scope = self.obj.analyse_as_module(env)
         if module_scope:
-            entry = module_scope.lookup_here(self.attribute)
-            if entry and entry.is_type:
-                return entry.type
+            return module_scope.lookup_type(self.attribute)
         return None
     
     def analyse_as_extension_type(self, env):
index 846ab5b6729e5569d9be672b289c0cc784fdcf66..136dbedf23ccc8d3c3cd09f2d1f01fab0f0185ae 100644 (file)
@@ -2597,9 +2597,68 @@ class SingleAssignmentNode(AssignmentNode):
     
     child_attrs = ["lhs", "rhs"]
     first = False
+    declaration_only = False
 
     def analyse_declarations(self, env):
-        self.lhs.analyse_target_declaration(env)
+        import ExprNodes
+        
+        # handle declarations of the form x = cython.foo()
+        if isinstance(self.rhs, ExprNodes.CallNode):
+            func_name = self.rhs.function.as_cython_attribute()
+            if func_name:
+                args, kwds = self.rhs.explicit_args_kwds()
+                
+                if func_name in ['declare', 'typedef']:
+                    self.declaration_only = True
+                    if len(args) != 1 or kwds is not None:
+                        error(rhs.pos, "Can only declare one type at a time.")
+                        return
+                    type = args[0].analyse_as_type(env)
+                    if type is None:
+                        error(args[0].pos, "Unknown type")
+                        return
+                    lhs = self.lhs
+                    if func_name == 'declare':
+                        if isinstance(lhs, ExprNodes.NameNode):
+                            vars = [(lhs.name, lhs.pos)]
+                        elif isinstance(lhs, ExprNodes.TupleNode):
+                            vars = [(var.name, var.pos) for var in lhs.args]
+                        else:
+                            error(lhs.pos, "Invalid declaration")
+                            return
+                        for var, pos in vars:
+                            env.declare_var(var, type, pos, is_cdef = True)
+                    else:
+                        if not isinstance(lhs, ExprNodes.NameNode):
+                            error(lhs.pos, "Invalid declaration.")
+                        env.declare_typedef(lhs.name, type, self.pos, 'private')
+                    
+                elif func_name in ['struct', 'union']:
+                    self.declaration_only = True
+                    if len(args) > 0 or kwds is None:
+                        error(rhs.pos, "Struct or union members must be given by name.")
+                        return
+                    members = []
+                    for member, type_node in kwds.key_value_pairs:
+                        type = type_node.analyse_as_type(env)
+                        if type is None:
+                            error(type_node.pos, "Unknown type")
+                        else:
+                            members.append((member.value, type, member.pos))
+                    if len(members) < len(kwds.key_value_pairs):
+                        return
+                    if not isinstance(self.lhs, ExprNodes.NameNode):
+                        error(self.lhs.pos, "Invalid declaration.")
+                    name = self.lhs.name
+                    scope = StructOrUnionScope(name)
+                    env.declare_struct_or_union(name, func_name, scope, False, self.rhs.pos)
+                    for member, type, pos in members:
+                        scope.declare_var(member, type, pos)
+                    
+        if self.declaration_only:
+            return
+        else:
+            self.lhs.analyse_target_declaration(env)
     
     def analyse_types(self, env, use_temp = 0):
         self.rhs.analyse_types(env)
index 1c3ab75e7f235e454c0b85d2ad26ec49bd4d204b..c04b2e22e4f2608bde607b63d9defa70ef7a5d60 100644 (file)
@@ -313,10 +313,18 @@ class InterpretCompilerDirectives(CythonTransform):
     def visit_SingleAssignmentNode(self, node):
         if (isinstance(node.rhs, ImportNode) and
                 node.rhs.module_name.value == u'cython'):
-            self.cython_module_names.add(node.lhs.name)
+            node = CImportStatNode(node.pos, 
+                                   module_name = u'cython',
+                                   as_name = node.lhs.name)
+            self.visit_CImportStatNode(node)
         else:
             self.visitchildren(node)
-            return node
+        return node
+            
+    def visit_NameNode(self, node):
+        if node.name in self.cython_module_names:
+            node.is_cython_module = True
+        return node
 
     def visit_Node(self, node):
         self.visitchildren(node)
@@ -339,15 +347,7 @@ class InterpretCompilerDirectives(CythonTransform):
         if optname:
             optiontype = Options.option_types.get(optname)
             if optiontype:
-                if isinstance(node, SimpleCallNode):
-                    args = node.args
-                    kwds = None
-                else:
-                    if node.starstar_arg or not isinstance(node.positional_args, TupleNode):
-                        raise PostParseError(dec.function.pos,
-                            'Compile-time keyword arguments must be explicit.' % optname)
-                    args = node.positional_args.args
-                    kwds = node.keyword_args
+                args, kwds = node.explicit_args_kwds()
                 if optiontype is bool:
                     if kwds is not None or len(args) != 1 or not isinstance(args[0], BoolNode):
                         raise PostParseError(dec.function.pos,
@@ -516,7 +516,7 @@ property NAME:
         node.analyse_declarations(self.env_stack[-1])
         self.visitchildren(node)
         return node
-
+        
     def visit_FuncDefNode(self, node):
         lenv = node.create_local_scope(self.env_stack[-1])
         node.body.analyse_control_flow(lenv) # this will be totally refactored
@@ -686,24 +686,20 @@ class EnvTransform(CythonTransform):
 
 class TransformBuiltinMethods(EnvTransform):
 
-    def cython_attribute(self, node):
-        if (isinstance(node, AttributeNode) and 
-            isinstance(node.obj, NameNode) and
-            node.obj.name in self.cython_module_names):
-            return node.attribute
+    def visit_SingleAssignmentNode(self, node):
+        if node.declaration_only:
+            return None
+        else:
+            self.visitchildren(node)
+            return node
     
-    def visit_ModuleNode(self, node):
-        self.cython_module_names = node.cython_module_names
-        self.visitchildren(node)
-        return node
-        
     def visit_AttributeNode(self, node):
-        attribute = self.cython_attribute(node)
+        attribute = node.as_cython_attribute()
         if attribute:
             if attribute == u'compiled':
                 node = BoolNode(node.pos, value=True)
             else:
-                error(node.function.pos, u"'%s' not a valid cython attribute" % function)
+                error(node.pos, u"'%s' not a valid cython attribute" % attribute)
         return node
 
     def visit_SimpleCallNode(self, node):
@@ -719,7 +715,7 @@ class TransformBuiltinMethods(EnvTransform):
                 return ExprNodes.DictNode(pos, key_value_pairs=items)
 
         # cython.foo
-        function = self.cython_attribute(node.function)
+        function = node.function.as_cython_attribute()
         if function:
             if function == u'cast':
                 if len(node.args) != 2:
@@ -739,6 +735,11 @@ class TransformBuiltinMethods(EnvTransform):
                         node = SizeofTypeNode(node.function.pos, arg_type=type)
                     else:
                         node = SizeofVarNode(node.function.pos, operand=node.args[0])
+            elif function == 'address':
+                if len(node.args) != 1:
+                    error(node.function.pos, u"sizeof takes exactly one argument" % function)
+                else:
+                    node = AmpersandNode(node.function.pos, operand=node.args[0])
             else:
                 error(node.function.pos, u"'%s' not a valid cython language construct" % function)
         
index f0027b6b302091004e2313428541ce2deabfce9f..b23a626a1ffd855706c90479a0c425d3532e521e 100644 (file)
@@ -1182,6 +1182,7 @@ modifiers_and_name_to_type = {
     (2, 0, "Py_ssize_t"): c_py_ssize_t_type,
     
     (1, 0, "long"): c_long_type,
+    (1, 0, "longlong"): c_longlong_type,
     (1, 0, "bint"): c_bint_type,
 }
 
@@ -1205,6 +1206,21 @@ def simple_c_type(signed, longness, name):
     # Find type descriptor for simple type given name and modifiers.
     # Returns None if arguments don't make sense.
     return modifiers_and_name_to_type.get((signed, longness, name))
+    
+def parse_basic_type(name):
+    base = None
+    if name.startswith('p_'):
+        base = parse_basic_type(name[2:])
+    elif name.startswith('p'):
+        base = parse_basic_type(name[1:])
+    elif name.endswith('*'):
+        base = parse_basic_type(name[:-1])
+    if base:
+        return CPtrType(base)
+    elif name.startswith('u'):
+        return simple_c_type(0, 0, name[1:])
+    else:
+        return simple_c_type(1, 0, name)
 
 def c_array_type(base_type, size):
     # Construct a C array type.
index ac144ee750e47c77fb8af422c6690c29693be4dd..eca416a76c37f9bb8076c8c23f55e4e481b7e5ae 100644 (file)
@@ -502,6 +502,11 @@ class Scope:
         if not entry:
             entry = self.declare_var(name, py_object_type, None)
         return entry
+        
+    def lookup_type(self, name):
+        entry = self.lookup(name)
+        if entry and entry.is_type:
+            return entry.type
 
     def add_string_const(self, value, identifier = False):
         # Add an entry for a string constant.
index cc1f07adaa37a38cc28020ea8315c19b4cdf6711..b0cba9e9732afd73670713277532c92877682169 100644 (file)
@@ -6,17 +6,157 @@ def empty_decorator(x):
 def locals(**arg_types):
     return empty_decorator
 
+
+# Emulated language constructs
+
 def cast(type, arg):
-    # can/should we emulate anything here?
-    return arg
+    if callable(type):
+        return type(arg)
+    else:
+        return arg
 
 def sizeof(arg):
-    # can/should we emulate anything here?
     return 1
+    
+def address(arg):
+    return pointer(type(arg))([arg])
+    
+def declare(type):
+    if callable(type):
+        return type()
+    else:
+        return None
+
+# Emulated types
+
+class CythonType(object):
+
+    def _pointer(self, n=1):
+        for i in range(n):
+            self = pointer(self)
+        return self
+
+    def __getitem__(self, ix):
+        return array(self, ix)
+
+
+class PointerType(CythonType):
+
+    def __init__(self, value=None):
+        if isinstance(value, ArrayType):
+            self._items = [cast(self._basetype, a) for a in value._items]
+        elif isinstance(value, list):
+            self._items = [cast(self._basetype, a) for a in value]
+        elif value is None:
+            self._items = []
+        else:
+            raise ValueError
+            
+    def __getitem__(self, ix):
+        if ix < 0:
+            raise IndexError, "negative indexing not allowed in C"
+        return self._items[ix]
+        
+    def __setitem__(self, ix, value):
+        if ix < 0:
+            raise IndexError, "negative indexing not allowed in C"
+        self._items[ix] = cast(self._basetype, value)
+        
+class ArrayType(PointerType):
+    
+    def __init__(self):
+        self._items = [None] * self._n
+
+
+class StructType(CythonType):
+    
+    def __init__(self, **data):
+        for key, value in data.items():
+            setattr(self, key, value)
+            
+    def __setattr__(self, key, value):
+        if key in self._members:
+            self.__dict__[key] = cast(self._members[key], value)
+        else:
+            raise AttributeError, "Struct has no member '%s'" % key
+    
+
+class UnionType(CythonType):
+
+    def __init__(self, **data):
+        if len(data) > 0:
+            raise AttributeError, "Union can only store one field at a time."
+        for key, value in data.items():
+            setattr(self, key, value)
+            
+    def __setattr__(self, key, value):
+        if key in '__dict__':
+            CythonType.__setattr__(self, key, value)
+        elif key in self._members:
+            self.__dict__ = {key: cast(self._members[key], value)}
+        else:
+            raise AttributeError, "Union has no member '%s'" % key
+
+def pointer(basetype):
+    class PointerInstance(PointerType):
+        _basetype = basetype
+    return PointerInstance
+
+def array(basetype, n):
+    class ArrayInstance(ArrayType):
+        _basetype = basetype
+        _n = n
+    return ArrayInstance
+
+def struct(**members):
+    class StructInstance(StructType):
+        _members = members
+    for key in members.keys():
+        setattr(StructInstance, key, None)
+    return StructInstance
+
+def union(**members):
+    class UnionInstance(UnionType):
+        _members = members
+    for key in members.keys():
+        setattr(UnionInstance, key, None)
+    return UnionInstance
+
+class typedef(CythonType):
+
+    def __init__(self, type):
+        self._basetype = type
+    
+    def __call__(self, value=None):
+        if value is not None:
+            value = cast(self._basetype, value)
+        return value
+        
+
 
 py_int = int
 py_long = long
 py_float = float
 
-# They just have to exist...
-int = long = char = bint = uint = ulong = longlong = ulonglong = Py_ssize_t = float = double = None
+
+# Predefined types
+
+int_types = ['char', 'short', 'int', 'long', 'longlong', 'Py_ssize_t'] 
+float_types = ['double', 'float']
+other_types = ['bint', 'Py_ssize_t', 'void']
+gs = globals()
+
+for name in int_types:
+    gs[name] = typedef(py_int)
+    gs['u'+name] = typedef(py_int)
+    
+double = float = typedef(py_float)
+bint = typedef(bool)
+void = typedef(int)
+
+for t in int_types + float_types + other_types:
+    for i in range(1, 4):
+        gs["%s_%s" % ('p'*i, t)] = globals()[t]._pointer(i)
+
+void = typedef(None)
+NULL = None