From: Robert Bradshaw Date: Sat, 4 Oct 2008 11:34:40 +0000 (-0700) Subject: More pure cython mode stuff. X-Git-Tag: 0.9.9.2.beta~69 X-Git-Url: http://git.tremily.us/?a=commitdiff_plain;h=b016353fb182f9eddc2dd8fd94af985234c5edd7;p=cython.git More pure cython mode stuff. --- diff --git a/Cython/Compiler/CythonScope.py b/Cython/Compiler/CythonScope.py index d14d7c80..5a4a8c9d 100644 --- a/Cython/Compiler/CythonScope.py +++ b/Cython/Compiler/CythonScope.py @@ -15,6 +15,12 @@ class CythonScope(ModuleScope): pos=None, defining = 1, cname='') + + 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) diff --git a/Cython/Compiler/ExprNodes.py b/Cython/Compiler/ExprNodes.py index 6552b67b..46323cd6 100644 --- a/Cython/Compiler/ExprNodes.py +++ b/Cython/Compiler/ExprNodes.py @@ -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): diff --git a/Cython/Compiler/Nodes.py b/Cython/Compiler/Nodes.py index 846ab5b6..136dbedf 100644 --- a/Cython/Compiler/Nodes.py +++ b/Cython/Compiler/Nodes.py @@ -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) diff --git a/Cython/Compiler/ParseTreeTransforms.py b/Cython/Compiler/ParseTreeTransforms.py index 1c3ab75e..c04b2e22 100644 --- a/Cython/Compiler/ParseTreeTransforms.py +++ b/Cython/Compiler/ParseTreeTransforms.py @@ -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) diff --git a/Cython/Compiler/PyrexTypes.py b/Cython/Compiler/PyrexTypes.py index f0027b6b..b23a626a 100644 --- a/Cython/Compiler/PyrexTypes.py +++ b/Cython/Compiler/PyrexTypes.py @@ -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. diff --git a/Cython/Compiler/Symtab.py b/Cython/Compiler/Symtab.py index ac144ee7..eca416a7 100644 --- a/Cython/Compiler/Symtab.py +++ b/Cython/Compiler/Symtab.py @@ -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. diff --git a/Cython/Shadow.py b/Cython/Shadow.py index cc1f07ad..b0cba9e9 100644 --- a/Cython/Shadow.py +++ b/Cython/Shadow.py @@ -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