diff options
Diffstat (limited to 'debian/pyrex/pyrex-0.9.9/Pyrex/Compiler/Symtab.py')
| -rw-r--r-- | debian/pyrex/pyrex-0.9.9/Pyrex/Compiler/Symtab.py | 1342 |
1 files changed, 1342 insertions, 0 deletions
diff --git a/debian/pyrex/pyrex-0.9.9/Pyrex/Compiler/Symtab.py b/debian/pyrex/pyrex-0.9.9/Pyrex/Compiler/Symtab.py new file mode 100644 index 00000000..b56b8531 --- /dev/null +++ b/debian/pyrex/pyrex-0.9.9/Pyrex/Compiler/Symtab.py @@ -0,0 +1,1342 @@ +# +# Pyrex - Symbol Table +# + +from Errors import warning, error, InternalError +import Options +import Naming +import PyrexTypes +from PyrexTypes import \ + py_object_type, py_type_type, \ + c_int_type, c_char_array_type, \ + CEnumType, CStructOrUnionType, PyExtensionType +from TypeSlots import \ + pyfunction_signature, pymethod_signature, \ + get_special_method_signature, get_property_accessor_signature + +class Entry: + # A symbol table entry in a Scope or ModuleNamespace. + # + # name string Python name of entity + # cname string C name of entity + # type PyrexType Type of entity + # ctype PyrexType Declared C type, if different from Pyrex type + # doc string Doc string + # init string Initial value + # visibility 'private' or 'public' or 'extern' + # is_builtin boolean Is an entry in the Python builtins dict + # is_cglobal boolean Is a C global variable + # is_pyglobal boolean Is a Python module-level variable or + # class attribute during class construction + # is_variable boolean Is a variable + # is_cfunction boolean Is a C function + # is_cmethod boolean Is a C method of an extension type + # is_builtin_method boolean Is a method corresponding to a Python/C API func + # is_type boolean Is a type definition + # is_const boolean Is a constant + # is_property boolean Is a property of an extension type: + # #doc_cname string or None C const holding the docstring + # getter_cname string C func for getting property + # setter_cname string C func for setting or deleting property + # is_self_arg boolean Is the "self" arg of an exttype method + # is_readonly boolean Can't be assigned to + # func_cname string C func implementing Python func + # pos position Source position where declared + # namespace_cname string If is_pyglobal, the C variable + # holding its home namespace + # pymethdef_cname string PyMethodDef structure + # signature Signature Arg & return types for Python func + # init_to_none boolean True if initial value should be None + # as_variable Entry Alternative interpretation of extension + # type name or builtin C function as a variable + # xdecref_cleanup boolean Use Py_XDECREF for error cleanup + # in_cinclude boolean Suppress C declaration code + # enum_values [Entry] For enum types, list of values + # qualified_name string "modname.funcname" or "modname.classname" + # or "modname.classname.funcname" + # is_declared_generic boolean Is declared as PyObject * even though its + # type is an extension type + # as_module None Module scope, if a cimported module + # is_inherited boolean Is an inherited attribute of an extension type + # #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 + # used boolean + # is_special boolean Is a special method or property accessor + # of an extension type + # defined_in_pxd boolean Is defined in a .pxd file (not just declared) + # api boolean Generate C API for C class or function + # utility_code string Utility code needed when this entry is used + + borrowed = 0 + init = "" + visibility = 'private' + ctype = None + is_builtin = 0 + is_cglobal = 0 + is_pyglobal = 0 + is_variable = 0 + is_cfunction = 0 + is_cmethod = 0 + is_builtin_method = 0 + is_type = 0 + is_const = 0 + is_property = 0 + doc_cname = None + getter_cname = None + setter_cname = None + is_self_arg = 0 + is_declared_generic = 0 + is_readonly = 0 + func_cname = None + doc = None + init_to_none = 0 + as_variable = None + xdecref_cleanup = 0 + in_cinclude = 0 + as_module = None + is_inherited = 0 + #interned_cname = None + pystring_cname = None + is_interned = 0 + used = 0 + is_special = 0 + defined_in_pxd = 0 + api = 0 + utility_code = None + + def __init__(self, name, cname, type, pos = None, init = None): + self.name = name + self.cname = cname + self.type = type + self.pos = pos + self.init = init + + def redeclared(self, pos): + error(pos, "'%s' does not match previous declaration" % self.name) + error(self.pos, "Previous declaration is here") + + +class Scope: + # name string Unqualified name + # outer_scope Scope or None Enclosing scope + # entries {string : Entry} Python name to entry, non-types + # const_entries [Entry] Constant entries + # type_entries [Entry] Struct/union/enum/typedef/exttype entries + # sue_entries [Entry] Struct/union/enum entries + # arg_entries [Entry] Function argument entries + # var_entries [Entry] User-defined variable entries + # pyfunc_entries [Entry] Python function entries + # cfunc_entries [Entry] C function entries + # c_class_entries [Entry] All extension type entries + # temp_entries [Entry] Temporary variable entries + # free_temp_entries [Entry] Temp variables currently unused + # temp_counter integer Counter for naming temp vars + # cname_to_entry {string : Entry} Temp cname to entry mapping + # pow_function_used boolean The C pow() function is used + # return_type PyrexType or None Return type of function owning scope + # is_py_class_scope boolean Is a Python class scope + # is_c_class_scope boolean Is an extension type scope + # scope_prefix string Disambiguator for C names + # in_cinclude boolean Suppress C declaration code + # qualified_name string "modname" or "modname.classname" + # #pystring_entries [Entry] String const entries newly used as + # # Python strings in this scope + # nogil boolean In a nogil section + # is_cplus boolean Is a C++ struct namespace + # reraise_used boolean Reraise statement encountered + + is_py_class_scope = 0 + is_c_class_scope = 0 + scope_prefix = "" + in_cinclude = 0 + nogil = 0 + return_type = None + reraise_used = 0 + + def __init__(self, name, outer_scope, parent_scope): + # The outer_scope is the next scope in the lookup chain. + # The parent_scope is used to derive the qualified name of this scope. + self.name = name + self.outer_scope = outer_scope + self.parent_scope = parent_scope + mangled_name = "%d%s_" % (len(name), name) + qual_scope = self.qualifying_scope() + if qual_scope: + self.qualified_name = qual_scope.qualify_name(name) + self.scope_prefix = qual_scope.scope_prefix + mangled_name + else: + self.qualified_name = name + self.scope_prefix = mangled_name + self.entries = {} + self.const_entries = [] + self.type_entries = [] + self.sue_entries = [] + self.arg_entries = [] + self.var_entries = [] + self.pyfunc_entries = [] + self.cfunc_entries = [] + self.c_class_entries = [] + self.defined_c_classes = [] + self.imported_c_classes = {} + self.temp_entries = [] + self.free_temp_entries = [] + self.temp_counter = 1 + self.cname_to_entry = {} + self.pow_function_used = 0 + #self.pystring_entries = [] + + def __str__(self): + return "<%s %s>" % (self.__class__.__name__, self.qualified_name) + +# def intern(self, name): +# return self.global_scope().intern(name) + + def qualifying_scope(self): + return self.parent_scope + + def mangle(self, prefix, name = None): + if name: + return "%s%s%s" % (prefix, self.scope_prefix, name) + else: + return self.parent_scope.mangle(prefix, self.name) + + def mangle_internal(self, name): + # Mangle an internal name so as not to clash with any + # user-defined name in this scope. + prefix = "%s%s_" % (Naming.pyrex_prefix, name) + return self.mangle(prefix) + #return self.parent_scope.mangle(prefix, self.name) + + def global_scope(self): + # Return the module-level scope containing this scope. + return self.outer_scope.global_scope() + + def declare(self, name, cname, type, pos): + # Create new entry, and add to dictionary if + # name is not None. Reports an error if already + # declared. + dict = self.entries + if name and dict.has_key(name): + error(pos, "'%s' already declared" % name) + entry = Entry(name, cname, type, pos = pos) + entry.in_cinclude = self.in_cinclude + if name: + entry.qualified_name = self.qualify_name(name) + dict[name] = entry + return entry + + def qualify_name(self, name): + return "%s.%s" % (self.qualified_name, name) + + def declare_const(self, name, type, value, pos, cname = None): + # Add an entry for a named constant. + if not cname: + if self.in_cinclude: + cname = name + else: + cname = self.mangle(Naming.enum_prefix, name) + entry = self.declare(name, cname, type, pos) + entry.is_const = 1 + entry.value = value + return entry + + def declare_type(self, name, type, pos, + cname = None, visibility = 'private', defining = 1): + # Add an entry for a type definition. + if not cname: + cname = name + entry = self.declare(name, cname, type, pos) + entry.visibility = visibility + entry.is_type = 1 + if defining: + self.type_entries.append(entry) + return entry + + def declare_typedef(self, name, base_type, pos, cname = None, + visibility = 'private'): + if not cname: + if self.in_cinclude or visibility == 'public': + cname = name + else: + cname = self.mangle(Naming.type_prefix, name) + type = PyrexTypes.CTypedefType(cname, base_type) + entry = self.declare_type(name, type, pos, cname, visibility) + type.qualified_name = entry.qualified_name + return entry + + def declare_struct_or_union(self, name, kind, scope, + typedef_flag, pos, cname = None, visibility = 'private'): + # Add an entry for a struct or union definition. + if not cname: + if self.in_cinclude or visibility == 'public': + cname = name + else: + cname = self.mangle(Naming.type_prefix, name) + entry = self.lookup_here(name) + if not entry: + type = CStructOrUnionType(name, kind, scope, typedef_flag, cname) + entry = self.declare_type(name, type, pos, cname, + visibility = visibility, defining = scope is not None) + self.sue_entries.append(entry) + else: + if not (entry.is_type and entry.type.is_struct_or_union + and entry.type.kind == kind): + entry.redeclared(pos) + elif scope and entry.type.scope: + error(pos, "'%s' already defined" % name) + else: + self.check_previous_typedef_flag(entry, typedef_flag, pos) + self.check_previous_visibility(entry, visibility, pos) + if scope: + entry.pos = pos + entry.type.set_scope(scope) + self.type_entries.append(entry) + if not scope and not entry.type.scope: + self.check_for_illegal_incomplete_ctypedef(typedef_flag, pos) + return entry + + def check_previous_typedef_flag(self, entry, typedef_flag, pos): + if typedef_flag <> entry.type.typedef_flag: + error(pos, "'%s' previously declared using '%s'" % ( + entry.name, ("cdef", "ctypedef")[entry.type.typedef_flag])) + + def check_previous_visibility(self, entry, visibility, pos): + if entry.visibility <> visibility: + error(pos, "'%s' previously declared as '%s'" % ( + entry.name, entry.visibility)) + + def declare_enum(self, name, pos, cname, typedef_flag, + visibility = 'private'): + if name: + if not cname: + if self.in_cinclude or visibility == 'public': + cname = name + else: + cname = self.mangle(Naming.type_prefix, name) + type = CEnumType(name, cname, typedef_flag) + else: + type = PyrexTypes.c_anon_enum_type + entry = self.declare_type(name, type, pos, cname = cname, + visibility = visibility) + entry.enum_values = [] + self.sue_entries.append(entry) + return entry + + def declare_var(self, name, type, pos, + cname = None, visibility = 'private', is_cdef = 0): + # Add an entry for a variable. + if not cname: + if visibility <> 'private': + cname = name + else: + cname = self.mangle(Naming.var_prefix, name) + entry = self.declare(name, cname, type, pos) + entry.is_variable = 1 + entry.visibility = visibility + return entry + + def declare_builtin(self, name, pos): + return self.outer_scope.declare_builtin(name, pos) + + def declare_pyfunction(self, name, pos): + # Add an entry for a Python function. + entry = self.declare_var(name, py_object_type, pos) + entry.signature = pyfunction_signature + self.pyfunc_entries.append(entry) + return entry + + def register_pyfunction(self, entry): + self.pyfunc_entries.append(entry) + + def declare_cfunction(self, name, type, pos, + cname = None, visibility = 'private', defining = 0, api = 0, in_pxd = 0): + # Add an entry for a C function. + entry = self.lookup_here(name) + if entry: + if visibility <> 'private' and visibility <> entry.visibility: + error(pos, "Function '%s' previously declared as '%s'" % ( + name, entry.visibility)) + if not entry.type.same_as(type): + error(pos, "Function signature does not match previous declaration") + else: + if not cname: + if api or visibility <> 'private': + cname = name + else: + cname = self.mangle(Naming.func_prefix, name) + entry = self.add_cfunction(name, type, pos, cname, visibility) + entry.func_cname = cname + if in_pxd and visibility <> 'extern': + entry.defined_in_pxd = 1 + if api: + entry.api = 1 + if not defining and not in_pxd and visibility <> 'extern': + error(pos, "Non-extern C function declared but not defined") + return entry + + def add_cfunction(self, name, type, pos, cname, visibility = 'private'): + # Add a C function entry without giving it a func_cname. + entry = self.declare(name, cname, type, pos) + entry.is_cfunction = 1 + entry.visibility = visibility + self.cfunc_entries.append(entry) + return entry + + def attach_var_entry_to_c_class(self, entry): + # The name of an extension class has to serve as both a type name and a + # variable name holding the type object. It is represented in the symbol + # table by a type entry with a variable entry attached to it. For the + # variable entry, we use a read-only C global variable whose name is an + # expression that refers to the type object. + var_entry = Entry(name = entry.name, + #type = py_object_type, + type = py_type_type, + pos = entry.pos, + #cname = "((PyObject*)%s)" % entry.type.typeptr_cname + cname = entry.type.typeptr_cname) + var_entry.is_variable = 1 + var_entry.is_cglobal = 1 + var_entry.is_readonly = 1 + entry.as_variable = var_entry + + def find(self, name, pos): + # Look up name, report error if not found. + entry = self.lookup(name) + if entry: + return entry + else: + error(pos, "'%s' is not declared" % name) + + def find_imported_module(self, path, pos): + # Look up qualified name, must be a module, report error if not found. + # Path is a list of names. + scope = self + for name in path: + entry = scope.find(name, pos) + if not entry: + return None + if entry.as_module: + scope = entry.as_module + else: + error(pos, "'%s' is not a cimported module" % scope.qualified_name) + return None + return scope + + def find_qualified_name(self, module_and_name, pos): + # Look up qualified name, report error if not found. + # module_and_name = [path, name] where path is a list of names. + module_path, name = module_and_name + scope = self.find_imported_module(module_path, pos) + if scope: + entry = scope.lookup_here(name) + if not entry: + mess = "'%s' is not declared" % name + if module_path: + mess = "%s in module '%s'" % (mess, ".".join(module_path)) + error(pos, mess) + return entry + + def lookup(self, name): + # Look up name in this scope or an enclosing one. + # Return None if not found. + return (self.lookup_here(name) + or (self.outer_scope and self.outer_scope.lookup(name)) + or None) + + def lookup_here(self, name): + # Look up in this scope only, return None if not found. + return self.entries.get(name, None) + + def lookup_target(self, name): + # Look up name in this scope only. Declare as Python + # variable if not found. + entry = self.lookup_here(name) + if not entry: + entry = self.declare_var(name, py_object_type, None) + return entry + +# def add_string_const(self, value): +# # Add an entry for a string constant. +# cname = self.new_const_cname() +# entry = Entry("", cname, c_char_array_type, init = value) +# entry.used = 1 +# self.const_entries.append(entry) +# return entry + +# def get_string_const(self, value): +# # Get entry for string constant. Returns an existing +# # one if possible, otherwise creates a new one. +# genv = self.global_scope() +# entry = genv.string_to_entry.get(value) +# if not entry: +# entry = self.add_string_const(value) +# genv.string_to_entry[value] = entry +# return entry + +# def add_py_string(self, entry): +# # If not already done, allocate a C name for a Python version of +# # a string literal, and add it to the list of Python strings to +# # be created at module init time. If the string resembles a +# # Python identifier, it will be interned. +# if not entry.pystring_cname: +# value = entry.init +# if identifier_pattern.match(value): +# entry.pystring_cname = self.intern(value) +# entry.is_interned = 1 +# else: +# entry.pystring_cname = entry.cname + "p" +# self.pystring_entries.append(entry) +# self.global_scope().all_pystring_entries.append(entry) + +# def new_const_cname(self): +# # Create a new globally-unique name for a constant. +# return self.global_scope().new_const_cname() + + def allocate_temp(self, type): + # Allocate a temporary variable of the given type from the + # free list if available, otherwise create a new one. + # Returns the cname of the variable. + for entry in self.free_temp_entries: + if entry.type == type: + self.free_temp_entries.remove(entry) + return entry.cname + n = self.temp_counter + self.temp_counter = n + 1 + cname = "%s%d" % (Naming.pyrex_prefix, n) + entry = Entry("", cname, type) + entry.used = 1 + if type.is_pyobject: + entry.init = "0" + self.cname_to_entry[entry.cname] = entry + self.temp_entries.append(entry) + return entry.cname + + def allocate_temp_pyobject(self): + # Allocate a temporary PyObject variable. + return self.allocate_temp(py_object_type) + + def release_temp(self, cname): + # Release a temporary variable for re-use. + if not cname: # can happen when type of an expr is void + return + entry = self.cname_to_entry[cname] + if entry in self.free_temp_entries: + raise InternalError("Temporary variable %s released more than once" + % cname) + self.free_temp_entries.append(entry) + + def temps_in_use(self): + # Return a new list of temp entries currently in use. + return [entry for entry in self.temp_entries + if entry not in self.free_temp_entries] + +# def use_utility_code(self, new_code): +# self.global_scope().use_utility_code(new_code) + + def generate_library_function_declarations(self, code): + # Generate extern decls for C library funcs used. + #if self.pow_function_used: + # code.putln("%s double pow(double, double);" % Naming.extern_c_macro) + pass + + def defines_any(self, names): + # Test whether any of the given names are + # defined in this scope. + for name in names: + if name in self.entries: + return 1 + return 0 + + +class BuiltinScope(Scope): + # The builtin namespace. + # + # type_names {string : 1} Set of type names (used during parsing) + + def __init__(self): + Scope.__init__(self, "__builtin__", None, None) + self.type_names = {} + + def declare_builtin(self, name, pos): + entry = self.declare(name, name, py_object_type, pos) + entry.is_builtin = 1 + return entry + + def declare_builtin_constant(self, name, type, cname, ctype = None): + entry = self.declare(name, cname, type, None) + if ctype: + entry.ctype = ctype + entry.is_variable = 1 + entry.is_cglobal = 1 + entry.is_readonly = 1 + return entry + + def declare_builtin_c_type(self, name, type): + entry = self.declare_type(name, type, pos = None) + self.type_names[name] = 1 + return entry + + def declare_builtin_cfunction(self, name, type, cname, python_equiv = None, + utility_code = None): + # If python_equiv == "*", the Python equivalent has the same name + # as the entry, otherwise it has the name specified by python_equiv. + entry = self.declare_cfunction(name, type, None, cname) + entry.utility_code = utility_code + if python_equiv: + if python_equiv == "*": + python_equiv = name + var_entry = Entry(python_equiv, python_equiv, py_object_type) + var_entry.is_variable = 1 + var_entry.is_builtin = 1 + entry.as_variable = var_entry + return entry + + def declare_builtin_class(self, name, objstruct_cname, typeobj_cname): + type = PyExtensionType(name, typedef_flag = 1, base_type = None) + type.module_name = "__builtin__" + type.typeptr_cname = "(&%s)" % typeobj_cname + type.objstruct_cname = objstruct_cname + type.is_builtin = 1 + scope = CClassScope(name = name, outer_scope = self, visibility = "extern") + type.set_scope(scope) + entry = self.declare_type(name, type, pos = None, visibility = "extern", + defining = 0) + self.attach_var_entry_to_c_class(entry) + self.type_names[name] = 1 + return entry + + def find_type(self, name): + # Used internally during initialisation, always succeeds + entry = self.lookup_here(name) + return entry.type + + +class ModuleScope(Scope): + # module_name string Python name of the module + # module_cname string C name of Python module object + # #module_dict_cname string C name of module dict object + # method_table_cname string C name of method table + # doc string Module doc string + # python_include_files [string] Standard Python headers to be included + # include_files [string] Other C headers to be included + # context Context + # pxd_file_loaded boolean Corresponding .pxd file has been processed + # cimported_modules [ModuleScope] Modules imported with cimport + # types_imported {PyrexType : 1} Set of types for which import code generated + # type_names {string : 1} Set of type names (used during parsing) + # pyrex_include_files [string] Pyrex sources included with 'include' + # gil_used boolean True if GIL is acquired/released anywhere + + gil_used = 0 + + def __init__(self, name, parent_module, context): + outer_scope = context.find_submodule("__builtin__") + Scope.__init__(self, name, outer_scope, parent_module) + self.module_name = name + self.context = context + self.module_cname = Naming.module_cname + self.module_dict_cname = Naming.moddict_cname + self.method_table_cname = Naming.methtable_cname + self.doc = "" + self.python_include_files = ["Python.h", "structmember.h"] + self.include_files = [] + self.type_names = self.outer_scope.type_names.copy() + self.pxd_file_loaded = 0 + self.cimported_modules = [] + self.types_imported = {} + self.pyrex_include_files = [] + +# def qualifying_scope(self): +# return self.parent_module + + def global_scope(self): + return self + + def declare_builtin(self, name, pos): + entry = Scope.declare_builtin(self, name, pos) + #entry.interned_cname = self.intern(name) + return entry + +# def intern(self, name): +# intern_map = self.intern_map +# cname = intern_map.get(name) +# if not cname: +# cname = Naming.interned_prefix + name +# intern_map[name] = cname +# self.interned_names.append(name) +# return cname + + def add_include_file(self, filename): + if filename not in self.python_include_files \ + and filename not in self.include_files: + self.include_files.append(filename) + + def add_imported_module(self, scope): + #print "add_imported_module:", scope, "to", self ### + if scope not in self.cimported_modules: + self.cimported_modules.append(scope) + + def add_imported_entry(self, name, entry, pos): + if entry not in self.entries: + self.entries[name] = entry + else: + error(pos, "'%s' already declared" % name) + + def declare_module(self, name, scope, pos): + # Declare a cimported module. This is represented as a + # Python module-level variable entry with a module + # scope attached to it. Reports an error and returns + # None if previously declared as something else. + entry = self.lookup_here(name) + if entry: + if entry.is_pyglobal and entry.as_module is scope: + return entry # Already declared as the same module + if not (entry.is_pyglobal and not entry.as_module): + #error(pos, "'%s' redeclared" % name) + entry.redeclared(pos) + return None + else: + entry = self.declare_var(name, py_object_type, pos) + #print "declare_module:", scope, "in", self ### + entry.as_module = scope + #self.cimported_modules.append(scope) + return entry + + def declare_var(self, name, type, pos, + cname = None, visibility = 'private', is_cdef = 0): + # Add an entry for a global variable. If it is a Python + # object type, and not declared with cdef, it will live + # in the module dictionary, otherwise it will be a C + # global variable. + entry = Scope.declare_var(self, name, type, pos, + cname, visibility, is_cdef) + if not visibility in ('private', 'public', 'extern'): + error(pos, "Module-level variable cannot be declared %s" % visibility) + if not is_cdef: + if not (type.is_pyobject and not type.is_extension_type): + raise InternalError( + "Non-cdef global variable is not a generic Python object") + entry.is_pyglobal = 1 + entry.namespace_cname = self.module_cname + #if Options.intern_names: + # entry.interned_cname = self.intern(name) + else: + entry.is_cglobal = 1 + self.var_entries.append(entry) + return entry + + def declare_global(self, name, pos): + entry = self.lookup_here(name) + if not entry: + self.declare_var(name, py_object_type, pos) + + def add_default_value(self, type): + # Add an entry for holding a function argument + # default value. + cname = "%s%d" % (Naming.default_prefix, self.default_counter) + self.default_counter += 1 + entry = Entry("", cname, type) + self.default_entries.append(entry) + return entry + +# def new_const_cname(self): +# # Create a new globally-unique name for a constant. +# n = self.const_counter +# self.const_counter = n + 1 +# return "%s%d" % (Naming.const_prefix, n) + +# def use_utility_code(self, new_code): +# # Add string to list of utility code to be included, +# # if not already there (tested using 'is'). +# for old_code in self.utility_code_used: +# if old_code is new_code: +# return +# self.utility_code_used.append(new_code) + + def declare_c_class(self, name, pos, defining = 0, implementing = 0, + module_name = None, base_type = None, visibility = 'private', + typedef_flag = 0, api = 0, options = None): + # + # Look for previous declaration as a type + # + #print "declare_c_class:", name, "in", self ### + entry = self.lookup_here(name) + if entry: + type = entry.type + if not (entry.is_type and type.is_extension_type): + entry = None # Will cause redeclaration and produce an error + else: + scope = type.scope + defined = scope and scope.defined + definitive = defining or (implementing and not defined) + self.check_previous_typedef_flag(entry, typedef_flag, pos) + if base_type or definitive: + if type.base_type and base_type is not type.base_type: + error(pos, "Base type does not match previous declaration") + type.base_type = base_type + # + # Make a new entry if needed + # + if not entry: + type = PyExtensionType(name, typedef_flag, base_type) + if visibility == 'extern': + type.module_name = module_name + else: + type.module_name = self.qualified_name + type.typeptr_cname = self.mangle(Naming.typeptr_prefix, name) + entry = self.declare_type(name, type, pos, visibility = visibility, + defining = 0) + if options and options.objstruct_cname: + type.objstruct_cname = options.objstruct_cname + elif not entry.in_cinclude: + type.objstruct_cname = self.mangle(Naming.objstruct_prefix, name) + else: + error(entry.pos, + "Object name required for 'public' or 'extern' C class") + self.attach_var_entry_to_c_class(entry) + self.c_class_entries.append(entry) + # + # Check for re-definition and create scope if needed + # + scope = type.scope + if not scope: + if defining or implementing: + scope = CClassScope(name = name, outer_scope = self, + visibility = visibility, no_gc = options.no_gc) + if base_type: + scope.declare_inherited_c_attributes(base_type.scope) + type.set_scope(scope) + self.type_entries.append(entry) + else: + self.check_for_illegal_incomplete_ctypedef(typedef_flag, pos) + else: + if defining and scope.defined: + error(pos, "C class '%s' already defined" % name) + elif implementing and scope.implemented: + error(pos, "C class '%s' already implemented" % name) + scope.outer_scope = self + # + # Fill in options, checking for compatibility with any previous declaration + # + if defining: + entry.defined_in_pxd = 1 + if implementing: # So that filenames in runtime exceptions refer to + entry.pos = pos # the .pyx file and not the .pxd file + if visibility <> 'private' and entry.visibility <> visibility: + error(pos, "Class '%s' previously declared as '%s'" + % (name, entry.visibility)) + if api: + entry.api = 1 + if options: + if options.objstruct_cname: + if type.objstruct_cname and type.objstruct_cname <> options.objstruct_cname: + error(pos, "Object struct name differs from previous declaration") + type.objstruct_cname = options.objstruct_cname + if options.typeobj_cname: + if type.typeobj_cname and type.typeobj_cname <> options.typeobj_cname: + error(pos, "Type object name differs from previous declaration") + type.typeobj_cname = options.typeobj_cname + # + # Return new or existing entry + # + return entry + + def check_for_illegal_incomplete_ctypedef(self, typedef_flag, pos): + if typedef_flag and not self.in_cinclude: + error(pos, "Forward-referenced type must use 'cdef', not 'ctypedef'") + + def allocate_vtable_names(self, entry): + # If extension type has a vtable, allocate vtable struct and + # slot names for it. + type = entry.type + if type.base_type and type.base_type.vtabslot_cname: + #print "...allocating vtabslot_cname because base type has one" ### + type.vtabslot_cname = "%s.%s" % ( + Naming.obj_base_cname, type.base_type.vtabslot_cname) + elif type.scope and type.scope.cfunc_entries: + #print "...allocating vtabslot_cname because there are C methods" ### + type.vtabslot_cname = Naming.vtabslot_cname + if type.vtabslot_cname: + #print "...allocating other vtable related cnames" ### + type.vtabstruct_cname = self.mangle(Naming.vtabstruct_prefix, entry.name) + type.vtabptr_cname = self.mangle(Naming.vtabptr_prefix, entry.name) + + def check_c_classes(self): + # Performs post-analysis checking and finishing up of extension types + # being implemented in this module. This is called only for the main + # .pyx file scope and its associated .pxd scope, not for cimported .pxd + # scopes. + # + # Checks all extension types declared in this scope to + # make sure that: + # + # * The extension type is implemented + # * All required object and type names have been specified or generated + # * All non-inherited C methods are implemented + # + # Also allocates a name for the vtable if needed. + # + debug_check_c_classes = 0 + if debug_check_c_classes: + print "Scope.check_c_classes: checking scope", self.qualified_name + for entry in self.c_class_entries: + if debug_check_c_classes: + print "...entry", entry.name, entry + print "......type =", entry.type + print "......visibility =", entry.visibility + type = entry.type + name = entry.name + visibility = entry.visibility + # Check defined + if not type.scope: + error(entry.pos, "C class '%s' is declared but not defined" % name) + # Generate typeobj_cname + if visibility <> 'extern' and not type.typeobj_cname: + type.typeobj_cname = self.mangle(Naming.typeobj_prefix, name) + ## Generate typeptr_cname + #type.typeptr_cname = self.mangle(Naming.typeptr_prefix, name) + # Check C methods defined + if type.scope: + for method_entry in type.scope.cfunc_entries: + if not method_entry.is_inherited and not method_entry.func_cname: + error(method_entry.pos, "C method '%s' is declared but not defined" % + method_entry.name) + # Allocate vtable name if necessary + if type.vtabslot_cname: + #print "ModuleScope.check_c_classes: allocating vtable cname for", self ### + type.vtable_cname = self.mangle(Naming.vtable_prefix, entry.name) + + +class DefinitionScope(ModuleScope): + # Scope for the definition part of a module (.pxd). + # + # parent_module Scope Parent in the import namespace + # module_entries {string : Entry} For cimport statements + + def __init__(self, name, parent_module, context): + ModuleScope.__init__(self, name, parent_module, context) + self.parent_module = parent_module + self.module_entries = {} + + def find_module(self, module_name, pos): + # Find a module in the import namespace, interpreting + # relative imports relative to this module's parent. + # Finds and parses the module's .pxd file if the module + # has not been referenced before. + return self.global_scope().context.find_module( + module_name, relative_to = self.parent_module, pos = pos) + + def find_submodule(self, name): + # Find and return the definition scope for a submodule of this module, + # creating a new empty one if necessary. Doesn't parse .pxd. + scope = self.lookup_submodule(name) + if not scope: + scope = DefinitionScope(name, + parent_module = self, context = self.context) + self.module_entries[name] = scope + return scope + + def lookup_submodule(self, name): + # Return scope for submodule of this module, or None. + return self.module_entries.get(name, None) + + +class ImplementationScope(ModuleScope): + # This scope is used to keep the names declared only in the implementation + # part of a module from being seen by other modules that cimport this + # module. Also holds information that is only relevant for the + # implementation part. When declaring or looking up a name, this scope + # behaves as though it and its corresponding definition_scope were a single + # scope. + # + # definition_scope ModuleScope Scope holding definitions from corresponding .pxd + # doc_cname string C name of module doc string + # default_counter string Counter for naming default values + # #const_counter integer Counter for naming constants + # #utility_code_used [string] Utility code to be included + # default_entries [Entry] Function argument default entries + # #string_to_entry {string : Entry} Map string const to entry + # #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 + + def __init__(self, def_scope): + ModuleScope.__init__(self, def_scope.name, def_scope.parent_scope, + def_scope.context) + self.definition_scope = def_scope + self.doc_cname = Naming.moddoc_cname + self.type_names = def_scope.type_names.copy() + self.default_counter = 1 + #self.const_counter = 1 + #self.utility_code_used = [] + self.default_entries = [] + #self.string_to_entry = {} + #self.intern_map = {} + #self.interned_names = [] + #self.all_pystring_entries = [] + + def lookup_here(self, name): + entry = Scope.lookup_here(self, name) + if not entry: + entry = self.definition_scope.lookup_here(name) + return entry + + def find_module(self, module_name, pos): + return self.definition_scope.find_module(module_name, pos) + + def check_c_classes(self): + self.definition_scope.check_c_classes() + ModuleScope.check_c_classes(self) + + +class LocalScope(Scope): + + def __init__(self, name, outer_scope): + Scope.__init__(self, name, outer_scope, outer_scope) + + def mangle(self, prefix, name): + return prefix + name + + def declare_arg(self, name, type, pos, readonly = 0): + # Add an entry for an argument of a function. + #print "LocalScope.declare_arg:", name, "readonly =", readonly ### + cname = self.mangle(Naming.var_prefix, name) + entry = self.declare(name, cname, type, pos) + entry.is_variable = 1 + entry.is_readonly = readonly + if type.is_pyobject: + entry.init = "0" + #entry.borrowed = 1 # Not using borrowed arg refs for now + self.arg_entries.append(entry) + return entry + + def declare_var(self, name, type, pos, + cname = None, visibility = 'private', is_cdef = 0): + # Add an entry for a local variable. + if visibility in ('public', 'readonly'): + error(pos, "Local variable cannot be declared %s" % visibility) + entry = Scope.declare_var(self, name, type, pos, + cname, visibility, is_cdef) + entry.init_to_none = type.is_pyobject + self.var_entries.append(entry) + return entry + + def declare_global(self, name, pos): + # Pull entry from global scope into local scope. + if self.lookup_here(name): + error(pos, "'%s' already declared") + else: + entry = self.global_scope().lookup_target(name) + self.entries[name] = entry + + +class StructOrUnionScope(Scope): + # Namespace of a C struct or union. + # + # cplus_constructors [CFuncType] C++ constructor signatures + + def __init__(self, is_cplus = False, base_scopes = []): + Scope.__init__(self, "?", None, None) + self.base_scopes = base_scopes + self.is_cplus = is_cplus + if is_cplus: + constructors = [] + for base in base_scopes: + constructors.extend(base.cplus_constructors) + self.cplus_constructors = constructors + + def lookup_here(self, name): + entry = Scope.lookup_here(self, name) + if not entry: + for base in self.base_scopes: + entry = base.lookup_here(name) + if entry: + break + return entry + + def declare_var(self, name, type, pos, + cname = None, visibility = 'private', **kwds): + # Add an entry for an attribute. + if not cname: + cname = name + entry = self.declare(name, cname, type, pos) + entry.is_variable = 1 + self.var_entries.append(entry) + if type.is_pyobject: + error(pos, + "C struct/union member cannot be a Python object") + if visibility <> 'private': + error(pos, + "C struct/union member cannot be declared %s" % visibility) + return entry + + def declare_cfunction(self, name, type, pos, **kwds): + #print "StructOrUnionScope.declare_cfunction:", name ### + if not self.is_cplus: + error(pos, "C struct/union member cannot be a function") + # Define it anyway to suppress further errors + elif name == "__init__": + type.pos = pos + self.cplus_constructors.append(type) + return + #kwds['defining'] = 1 + #Scope.declare_cfunction(self, name, type, pos, *args, **kwds) + self.declare_var(name, type, pos, **kwds) + + +class ClassScope(Scope): + # Abstract base class for namespace of + # Python class or extension type. + # + # class_name string Pyrex name of the class + # scope_prefix string Additional prefix for names + # declared in the class + # doc string or None Doc string + + def __init__(self, name, outer_scope): + Scope.__init__(self, name, outer_scope, outer_scope) + self.class_name = name + self.doc = None + + def add_string_const(self, value): + return self.outer_scope.add_string_const(value) + + +class PyClassScope(ClassScope): + # Namespace of a Python class. + # + # class_dict_cname string C variable holding class dict + # class_obj_cname string C variable holding class object + + is_py_class_scope = 1 + + def declare_var(self, name, type, pos, + cname = None, visibility = 'private', is_cdef = 0): + # Add an entry for a class attribute. + entry = Scope.declare_var(self, name, type, pos, + cname, visibility, is_cdef) + entry.is_pyglobal = 1 + entry.namespace_cname = self.class_obj_cname + #if Options.intern_names: + # entry.interned_cname = self.intern(name) + return entry + + def allocate_temp(self, type): + return self.outer_scope.allocate_temp(type) + + def release_temp(self, cname): + self.outer_scope.release_temp(cname) + + #def recycle_pending_temps(self): + # self.outer_scope.recycle_pending_temps() + + def add_default_value(self, type): + return self.outer_scope.add_default_value(type) + + +class CClassScope(ClassScope): + # Namespace of an extension type. + # + # parent_type CClassType + # #typeobj_cname string or None + # #objstruct_cname string + # method_table_cname string + # member_table_cname string + # getset_table_cname string + # has_pyobject_attrs boolean Any PyObject attributes? + # pyattr_entries [Entry] + # public_attr_entries boolean public/readonly attrs + # property_entries [Entry] + # defined boolean Defined in .pxd file + # implemented boolean Defined in .pyx file + # inherited_var_entries [Entry] Adapted var entries from base class + # no_gc boolean No GC even if there are Python attributes + + is_c_class_scope = 1 + + def __init__(self, name, outer_scope, visibility, no_gc = 0): + ClassScope.__init__(self, name, outer_scope) + if visibility <> 'extern': + self.method_table_cname = outer_scope.mangle(Naming.methtab_prefix, name) + self.member_table_cname = outer_scope.mangle(Naming.memtab_prefix, name) + self.getset_table_cname = outer_scope.mangle(Naming.gstab_prefix, name) + self.has_pyobject_attrs = 0 + self.pyattr_entries = [] + self.public_attr_entries = [] + self.property_entries = [] + self.inherited_var_entries = [] + self.defined = 0 + self.implemented = 0 + self.no_gc = no_gc + + def needs_gc(self): + # If the type or any of its base types have Python-valued + # C attributes, then it needs to participate in GC. + return self.has_pyobject_attrs or \ + (self.parent_type.base_type and \ + self.parent_type.base_type.scope.needs_gc()) + + def declare_builtin_var(self, name, type, cname): + entry = self.declare(name, cname or name, type, None) + entry.is_variable = 1 + return entry + + def declare_var(self, name, type, pos, + cname = None, visibility = 'private', is_cdef = 0): + # Add an entry for an attribute. + if self.defined: + error(pos, + "C attributes cannot be added in implementation part of" + " extension type") + if get_special_method_signature(name): + error(pos, + "The name '%s' is reserved for a special method." + % name) + if not cname: + cname = name + entry = self.declare(name, cname, type, pos) + entry.visibility = visibility + entry.is_variable = 1 + self.var_entries.append(entry) + if type.is_pyobject and name <> "__weakref__": + self.has_pyobject_attrs = 1 + self.pyattr_entries.append(entry) + if visibility not in ('private', 'public', 'readonly'): + error(pos, + "Attribute of extension type cannot be declared %s" % visibility) + if visibility in ('public', 'readonly'): + if type.pymemberdef_typecode: + self.public_attr_entries.append(entry) + if name == "__weakref__": + error(pos, "Special attribute __weakref__ cannot be exposed to Python") + else: + error(pos, + "C attribute of type '%s' cannot be accessed from Python" % type) + if visibility == 'public' and type.is_extension_type: + error(pos, + "Non-generic Python attribute cannot be exposed for writing from Python") + return entry + + def declare_pyfunction(self, name, pos): + # Add an entry for a method. + if name == "__new__": + error(pos, "__new__ method of extension type will change semantics " + "in a future version of Pyrex. Use __cinit__ instead.") + name = "__cinit__" + entry = self.lookup_here(name) + if entry and entry.is_builtin_method: + self.overriding_builtin_method(name, pos) + else: + entry = self.declare(name, name, py_object_type, pos) + special_sig = get_special_method_signature(name) + if special_sig: + entry.is_special = 1 + entry.signature = special_sig + # Special methods don't get put in the method table + else: + entry.signature = pymethod_signature + self.pyfunc_entries.append(entry) + return entry + + def overriding_builtin_method(self, name, pos): + error(pos, "Cannot override builtin method '%s' of class '%s'" % ( + name, self.parent_type.base_type.name)) + + def lookup_here(self, name): + if name == "__new__": + name = "__cinit__" + return ClassScope.lookup_here(self, name) + + def declare_builtin_method(self, name, type, cname): + entry = ClassScope.add_cfunction(self, name, type, None, cname) + entry.is_builtin_method = 1 + return entry + + def declare_cfunction(self, name, type, pos, + cname = None, visibility = 'private', defining = 0, api = 0, in_pxd = 0): + if get_special_method_signature(name): + error(pos, "Special methods must be declared with 'def', not 'cdef'") + args = type.args + if not args: + error(pos, "C method has no self argument") + elif not args[0].type.same_as(self.parent_type): + error(pos, "Self argument of C method does not match parent type") + entry = self.lookup_here(name) + if entry: + if not entry.is_cfunction: + entry.redeclared(pos) + elif entry.is_builtin_method: + self.overriding_builtin_method(name, pos) + else: + if defining and entry.func_cname: + error(pos, "'%s' already defined" % name) + if not entry.type.same_as(type, as_cmethod = 1): + error(pos, "Signature does not match previous declaration") + error(entry.pos, "Previous declaration is here") + else: + if self.defined: + error(pos, + "C method '%s' not previously declared in definition part of" + " extension type" % name) + entry = self.add_cfunction(name, type, pos, cname or name, visibility) + if defining: + entry.func_cname = self.mangle(Naming.func_prefix, name) + return entry + + def add_cfunction(self, name, type, pos, cname, visibility): + # Add a cfunction entry without giving it a func_cname. + entry = ClassScope.add_cfunction(self, name, type, pos, cname, visibility) + entry.is_cmethod = 1 + return entry + + def declare_property(self, name, doc, pos): + entry = self.declare(name, name, py_object_type, pos) + entry.is_property = 1 + entry.doc = doc + entry.scope = PropertyScope(name, + outer_scope = self.global_scope(), parent_scope = self) + entry.scope.parent_type = self.parent_type + self.property_entries.append(entry) + return entry + + def declare_inherited_c_attributes(self, base_scope): + # Declare entries for all the C attributes of an + # inherited type, with cnames modified appropriately + # to work with this type. + def adapt(cname): + return "%s.%s" % (Naming.obj_base_cname, base_entry.cname) + for base_entry in \ + base_scope.inherited_var_entries + base_scope.var_entries: + entry = self.declare(base_entry.name, adapt(base_entry.cname), + base_entry.type, None) + entry.is_variable = 1 + self.inherited_var_entries.append(entry) + for base_entry in base_scope.cfunc_entries: + cname = base_entry.cname + if base_entry.is_builtin_method: + self.entries[base_entry.name] = base_entry + else: + entry = self.add_cfunction(base_entry.name, base_entry.type, + base_entry.pos, adapt(base_entry.cname), base_entry.visibility) + entry.is_inherited = 1 + + +class PropertyScope(Scope): + # Scope holding the __get__, __set__ and __del__ methods for + # a property of an extension type. + # + # parent_type PyExtensionType The type to which the property belongs + + def declare_pyfunction(self, name, pos): + # Add an entry for a method. + entry = self.declare(name, name, py_object_type, pos) + signature = get_property_accessor_signature(name) + if signature: + entry.is_special = 1 + entry.signature = signature + else: + error(pos, "Only __get__, __set__ and __del__ methods allowed " + "in a property declaration") + entry.signature = pymethod_signature + return entry |
