Commit 231240e1 authored by Robert Bradshaw's avatar Robert Bradshaw

Refactoring of type parsing

This paves the way for "cimport *" cimporting types, as well as other simplifications.
parent 3979e943
......@@ -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()
......
......@@ -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:
......
......@@ -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,
......
......@@ -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 (*)())
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment