Refactoring of type parsing
authorRobert Bradshaw <robertwb@math.washington.edu>
Tue, 30 Sep 2008 02:08:45 +0000 (19:08 -0700)
committerRobert Bradshaw <robertwb@math.washington.edu>
Tue, 30 Sep 2008 02:08:45 +0000 (19:08 -0700)
This paves the way for "cimport *" cimporting types, as well as other simplifications.

Cython/Compiler/ExprNodes.py
Cython/Compiler/Parsing.py
Cython/Compiler/Symtab.py
tests/run/sizeof.pyx

index bcd29039a52fa9967ddb356130e5ad76f8586bd1..d7ea1d115fba348b8f041a4e0ec0da4dba6ac418 100644 (file)
@@ -289,6 +289,11 @@ class ExprNode(Node):
         # If this node can be interpreted as a reference to a
         # cimported module, return its scope, else None.
         return None
+        
+    def analyse_as_type(self, env):
+        # If this node can be interpreted as a reference to a
+        # type, return that type, else None.
+        return None
     
     def analyse_as_extension_type(self, env):
         # If this node can be interpreted as a reference to an
@@ -879,6 +884,15 @@ class NameNode(AtomicExprNode):
         if entry and entry.as_module:
             return entry.as_module
         return None
+        
+    def analyse_as_type(self, env):
+        entry = self.entry
+        if not entry:
+            entry = env.lookup(self.name)
+        if entry and entry.is_type:
+            return entry.type
+        else:
+            return None
     
     def analyse_as_extension_type(self, env):
         # Try to interpret this as a reference to an extension type.
@@ -1362,6 +1376,12 @@ class IndexNode(ExprNode):
     
     def analyse_target_declaration(self, env):
         pass
+        
+    def analyse_as_type(self, env):
+        base_type = self.base.analyse_as_type(env)
+        if base_type and not base_type.is_pyobject:
+            return PyrexTypes.CArrayType(base_type, int(self.index.compile_time_value(env)))
+        return None
     
     def analyse_types(self, env):
         self.analyse_base_and_index_types(env, getting = 1)
@@ -2182,6 +2202,14 @@ class AttributeNode(ExprNode):
                 self.mutate_into_name_node(env, ubcm_entry, None)
                 return 1
         return 0
+        
+    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 None
     
     def analyse_as_extension_type(self, env):
         # Try to interpret this as a reference to an extension type
@@ -3107,6 +3135,8 @@ class TypecastNode(ExprNode):
 
 class SizeofNode(ExprNode):
     #  Abstract base class for sizeof(x) expression nodes.
+    
+    type = PyrexTypes.c_int_type
 
     def check_const(self):
         pass
@@ -3126,7 +3156,7 @@ class SizeofTypeNode(SizeofNode):
     def analyse_types(self, env):
         # we may have incorrectly interpreted a dotted name as a type rather than an attribute
         # this could be better handled by more uniformly treating types as runtime-available objects
-        if self.base_type.module_path:
+        if self.base_type.module_path and 0:
             path = self.base_type.module_path
             obj = env.lookup(path[0])
             if obj.as_module is None:
@@ -3141,13 +3171,16 @@ class SizeofTypeNode(SizeofNode):
         base_type = self.base_type.analyse(env)
         _, arg_type = self.declarator.analyse(base_type, env)
         self.arg_type = arg_type
+        self.check_type()
+        
+    def check_type(self):
+        arg_type = self.arg_type
         if arg_type.is_pyobject and not arg_type.is_extension_type:
             error(self.pos, "Cannot take sizeof Python object")
         elif arg_type.is_void:
             error(self.pos, "Cannot take sizeof void")
         elif not arg_type.is_complete():
             error(self.pos, "Cannot take sizeof incomplete type '%s'" % arg_type)
-        self.type = PyrexTypes.c_int_type
         
     def calculate_result_code(self):
         if self.arg_type.is_extension_type:
@@ -3167,8 +3200,15 @@ class SizeofVarNode(SizeofNode):
     subexprs = ['operand']
     
     def analyse_types(self, env):
-        self.operand.analyse_types(env)
-        self.type = PyrexTypes.c_int_type
+        # We may actually be looking at a type rather than a variable...
+        # If we are, traditional analysis would fail...
+        operand_as_type = self.operand.analyse_as_type(env)
+        if operand_as_type:
+            self.arg_type = operand_as_type
+            self.__class__ = SizeofTypeNode
+            self.check_type()
+        else:
+            self.operand.analyse_types(env)
     
     def calculate_result_code(self):
         return "(sizeof(%s))" % self.operand.result()
index 415c29b9445215b181cd2561c128a85f66c1b3b0..d8e7f2e9d52d5823b8eaf096a4a97dbdab9d1c1f 100644 (file)
@@ -250,7 +250,10 @@ def p_sizeof(s):
     pos = s.position()
     s.next()
     s.expect('(')
-    if looking_at_type(s) or looking_at_dotted_name(s):
+    # Here we decide if we are looking at an expression or type
+    # If it is actually a type, but parsable as an expression, 
+    # we treat it as an expression here. 
+    if looking_at_type(s):
         base_type = p_c_base_type(s)
         declarator = p_c_declarator(s, empty = 1)
         node = ExprNodes.SizeofTypeNode(pos, 
@@ -1030,7 +1033,13 @@ def p_from_import_statement(s, first_statement = 0):
     elif kind == 'cimport':
         for (name_pos, name, as_name, kind) in imported_names:
             local_name = as_name or name
-            s.add_type_name(local_name)
+            if local_name == "*" and False:
+                print s.__dict__.keys()
+                module = s.context.find_module(dotted_name)
+                for type in module.type_entries:
+                    s.add_type_name(type.name)
+            else:
+                s.add_type_name(local_name)
         return Nodes.FromCImportStatNode(pos,
             module_name = dotted_name,
             imported_names = imported_names)
@@ -1619,7 +1628,15 @@ def p_c_simple_base_type(s, self_flag, nonempty):
             s.next()
         else:
             name = 'int'
-    elif s.looking_at_type_name():
+    elif looking_at_dotted_name(s):
+        #print "p_c_simple_base_type: looking_at_type_name at", s.position()
+        name = s.systring
+        s.next()
+        while s.sy == '.':
+            module_path.append(name)
+            s.next()
+            name = p_ident(s)
+    elif s.looking_at_type_name(): # looking_at_type(s):
         name = s.systring
         s.next()
         if nonempty and s.sy != 'IDENT':
@@ -1636,14 +1653,6 @@ def p_c_simple_base_type(s, self_flag, nonempty):
             elif s.sy not in ('*', '**', '['):
                 s.put_back('IDENT', name)
                 name = None
-    elif looking_at_dotted_name(s):
-        #print "p_c_simple_base_type: looking_at_type_name at", s.position()
-        name = s.systring
-        s.next()
-        while s.sy == '.':
-            module_path.append(name)
-            s.next()
-            name = p_ident(s)
     else:
         #print "p_c_simple_base_type: not looking at type at", s.position()
         name = None
@@ -1687,8 +1696,45 @@ def p_buffer_access(s, base_type_node):
     return result
     
 
+def looking_at_possible_type(s):
+    return s.sy == 'IDENT' and not s.systring in calling_convention_words
+#    return looking_at_base_type(s) or s.looking_at_type_name()
+
 def looking_at_type(s):
-    return looking_at_base_type(s) or s.looking_at_type_name()
+    if s.systring in base_type_start_words:
+        return True
+    elif s.sy == 'IDENT':
+        is_type = False
+        name = s.systring
+        dotted_path = []
+        s.next()
+        while s.sy == '.':
+            s.next()
+            dotted_path.append(s.systring)
+            s.expect('IDENT')
+        saved = s.sy, s.systring
+        if s.sy == '*' or s.sy == '**':
+            s.next()
+            is_type = s.sy == ')'
+            s.put_back(*saved)
+        elif s.sy == '(':
+            s.next()
+            is_type = s.sy == '*'
+            s.put_back(*saved)
+        elif s.sy == '[':
+            s.next()
+            is_type = s.sy == ']'
+            s.put_back(*saved)
+        elif s.sy == 'IDENT':
+            is_type = True
+        dotted_path.reverse()
+        for p in dotted_path:
+            s.put_back('IDENT', p)
+            s.put_back('.', '.')
+        s.put_back('IDENT', name)
+        return is_type
+    else:
+        return False
 
 def looking_at_base_type(s):
     #print "looking_at_base_type?", s.sy, s.systring, s.position()
@@ -1703,7 +1749,7 @@ def looking_at_dotted_name(s):
         return result
     else:
         return 0
-
+        
 basic_c_type_names = ("void", "char", "int", "float", "double", "Py_ssize_t", "bint")
 
 sign_and_longness_words = ("short", "long", "signed", "unsigned")
@@ -1744,7 +1790,7 @@ def p_c_declarator(s, ctx = Ctx(), empty = 0, is_type = 0, cmethod_flag = 0,
     pos = s.position()
     if s.sy == '(':
         s.next()
-        if s.sy == ')' or looking_at_type(s):
+        if s.sy == ')' or looking_at_possible_type(s):
             base = Nodes.CNameDeclaratorNode(pos, name = EncodedString(u""), cname = None)
             result = p_c_func_declarator(s, pos, ctx, base, cmethod_flag)
         else:
index 35ce1c170c1d390cea2853db5d74266ea085bd3a..ec41d83d64c1682a3350b7371f60945517483690 100644 (file)
@@ -313,6 +313,7 @@ class Scope:
         entry.is_type = 1
         if defining:
             self.type_entries.append(entry)
+        # here we would set as_variable to an object representing this type
         return entry
     
     def declare_typedef(self, name, base_type, pos, cname = None,
index f2832cd71c35c4bf17c3f6f482d99426072e56f6..3360273f8f5e4a54aa47729172c980909d27eec6 100644 (file)
@@ -11,4 +11,9 @@ def f():
     i = sizeof(p)
     i = sizeof(j + k)
     i = sizeof(int)
+    i = sizeof(long int)
+    i = sizeof(void*)
     i = sizeof(Spam)
+    i = sizeof(Spam*)
+    i = sizeof(Spam[5])
+    i = sizeof(Spam (*)())