def _resolve_AttributeNode(env, node): path = [] while isinstance(node, AttributeNode): path.insert(0, node.attribute) node = node.obj if isinstance(node, NameNode): path.insert(0, node.name) else: raise CompileError(node.pos, EXPR_ERR) modnames = path[:-1] # must be at least 1 module name, o/w not an AttributeNode. assert modnames scope = env for modname in modnames: mod = scope.lookup(modname) if not mod or not mod.as_module: raise CompileError(node.pos, "undeclared name not builtin: %s" % modname) scope = mod.as_module entry = scope.lookup(path[-1]) if not entry: raise CompileError(node.pos, "No such attribute '%s'" % path[-1]) return entry
def handle_scope(self, node, scope): # For all buffers, insert extra variables in the scope. # The variables are also accessible from the buffer_info # on the buffer entry bufvars = [entry for name, entry in scope.entries.iteritems() if entry.type.is_buffer] if len(bufvars) > 0: bufvars.sort(key=lambda entry: entry.name) self.buffers_exists = True memviewslicevars = [entry for name, entry in scope.entries.iteritems() if entry.type.is_memoryviewslice] if len(memviewslicevars) > 0: self.buffers_exists = True for (name, entry) in scope.entries.iteritems(): if name == 'memoryview' and isinstance(entry.utility_code_definition, CythonUtilityCode): self.using_memoryview = True break if isinstance(node, ModuleNode) and len(bufvars) > 0: # for now...note that pos is wrong raise CompileError(node.pos, "Buffer vars not allowed in module scope") for entry in bufvars: if entry.type.dtype.is_ptr: raise CompileError(node.pos, "Buffers with pointer types not yet supported.") name = entry.name buftype = entry.type if buftype.ndim > Options.buffer_max_dims: raise CompileError(node.pos, "Buffer ndims exceeds Options.buffer_max_dims = %d" % Options.buffer_max_dims) if buftype.ndim > self.max_ndim: self.max_ndim = buftype.ndim # Declare auxiliary vars def decvar(type, prefix): cname = scope.mangle(prefix, name) aux_var = scope.declare_var(name=None, cname=cname, type=type, pos=node.pos) if entry.is_arg: aux_var.used = True # otherwise, NameNode will mark whether it is used return aux_var auxvars = ((PyrexTypes.c_pyx_buffer_nd_type, Naming.pybuffernd_prefix), (PyrexTypes.c_pyx_buffer_type, Naming.pybufferstruct_prefix)) pybuffernd, rcbuffer = [decvar(type, prefix) for (type, prefix) in auxvars] entry.buffer_aux = Symtab.BufferAux(pybuffernd, rcbuffer) scope.buffer_entries = bufvars self.scope = scope
def handle_scope(self, node, scope): # For all buffers, insert extra variables in the scope. # The variables are also accessible from the buffer_info # on the buffer entry bufvars = [entry for name, entry in scope.entries.iteritems() if entry.type.is_buffer] if len(bufvars) > 0: self.buffers_exists = True if isinstance(node, ModuleNode) and len(bufvars) > 0: # for now...note that pos is wrong raise CompileError(node.pos, "Buffer vars not allowed in module scope") for entry in bufvars: if entry.type.dtype.is_ptr: raise CompileError(node.pos, "Buffers with pointer types not yet supported.") name = entry.name buftype = entry.type if buftype.ndim > self.max_ndim: self.max_ndim = buftype.ndim # Declare auxiliary vars cname = scope.mangle(Naming.bufstruct_prefix, name) bufinfo = scope.declare_var(name="$%s" % cname, cname=cname, type=PyrexTypes.c_py_buffer_type, pos=node.pos) if entry.is_arg: bufinfo.used = True # otherwise, NameNode will mark whether it is used def var(prefix, idx, initval): cname = scope.mangle(prefix, "%d_%s" % (idx, name)) result = scope.declare_var("$%s" % cname, PyrexTypes.c_py_ssize_t_type, node.pos, cname=cname, is_cdef=True) result.init = initval if entry.is_arg: result.used = True return result stridevars = [var(Naming.bufstride_prefix, i, "0") for i in range(entry.type.ndim)] shapevars = [var(Naming.bufshape_prefix, i, "0") for i in range(entry.type.ndim)] mode = entry.type.mode if mode == 'full': suboffsetvars = [var(Naming.bufsuboffset_prefix, i, "-1") for i in range(entry.type.ndim)] else: suboffsetvars = None entry.buffer_aux = Symtab.BufferAux(bufinfo, stridevars, shapevars, suboffsetvars) scope.buffer_entries = bufvars self.scope = scope
def _resolve_NameNode(env, node): try: resolved_name = env.lookup(node.name).name except AttributeError: raise CompileError(node.pos, INVALID_ERR) viewscope = env.global_scope().context.cython_scope.viewscope entry = viewscope.lookup(resolved_name) if entry is None: raise CompileError(node.pos, NOT_CIMPORTED_ERR) return entry
def interpret(node, ix): if ix in type_args: if type_env: type = node.analyse_as_type(type_env) if not type: raise CompileError(node.pos, "Invalid type.") return (type, node.pos) else: raise CompileError(node.pos, "Type not allowed here.") else: if (sys.version_info[0] >= 3 and isinstance(node, StringNode) and node.unicode_value is not None): return (node.unicode_value, node.pos) return (node.compile_time_value(empty_scope), node.pos)
def validate_axes_specs(positions, specs, is_c_contig, is_f_contig): packing_specs = ('contig', 'strided', 'follow') access_specs = ('direct', 'ptr', 'full') # is_c_contig, is_f_contig = is_cf_contig(specs) has_contig = has_follow = has_strided = has_generic_contig = False last_indirect_dimension = -1 for idx, (access, packing) in enumerate(specs): if access == 'ptr': last_indirect_dimension = idx for idx, pos, (access, packing) in zip(xrange(len(specs)), positions, specs): if not (access in access_specs and packing in packing_specs): raise CompileError(pos, "Invalid axes specification.") if packing == 'strided': has_strided = True elif packing == 'contig': if has_contig: raise CompileError( pos, "Only one direct contiguous " "axis may be specified.") valid_contig_dims = last_indirect_dimension + 1, len(specs) - 1 if idx not in valid_contig_dims and access != 'ptr': if last_indirect_dimension + 1 != len(specs) - 1: dims = "dimensions %d and %d" % valid_contig_dims else: dims = "dimension %d" % valid_contig_dims[0] raise CompileError( pos, "Only %s may be contiguous and direct" % dims) has_contig = access != 'ptr' elif packing == 'follow': if has_strided: raise CompileError( pos, "A memoryview cannot have both follow and strided axis specifiers." ) if not (is_c_contig or is_f_contig): raise CompileError(pos, "Invalid use of the follow specifier.") if access in ('ptr', 'full'): has_strided = False
def _resolve_NameNode(env, node): try: resolved_name = env.lookup(node.name).name except AttributeError: raise CompileError(node.pos, INVALID_ERR) viewscope = env.global_scope().context.cython_scope.viewscope return viewscope.lookup(resolved_name)
def _get_resolved_spec(env, spec): # spec must be a NameNode or an AttributeNode if isinstance(spec, NameNode): return _resolve_NameNode(env, spec) elif isinstance(spec, AttributeNode): return _resolve_AttributeNode(env, spec) else: raise CompileError(spec.pos, INVALID_ERR)
def interpret(node): if isinstance(node, CBaseTypeNode): if type_env: return (node.analyse(type_env), node.pos) else: raise CompileError(node.pos, "Type not allowed here.") else: return (node.compile_time_value(empty_scope), node.pos)
def extract_module_name(self, path): # Find fully_qualified module name from the full pathname # of a source file. dir, filename = os.path.split(path) module_name, _ = os.path.splitext(filename) if "." not in module_name: if module_name == "__init__": dir, module_name = os.path.split(dir) names = [module_name] while self.is_package_dir(dir): parent, package_name = os.path.split(dir) if parent == dir: break names.insert(0, package_name) dir = parent module_name = ".".join(names) if not module_name_pattern.match(module_name): raise CompileError((path, 0, 0), "'%s' is not a valid module name" % module_name) return module_name
def find_module(self, module_name, relative_to=None, pos=None, need_pxd=1, check_module_name=True): # Finds and returns the module scope corresponding to # the given relative or absolute module name. If this # is the first time the module has been requested, finds # the corresponding .pxd file and process it. # If relative_to is not None, it must be a module scope, # and the module will first be searched for relative to # that module, provided its name is not a dotted name. debug_find_module = 0 if debug_find_module: print( "Context.find_module: module_name = %s, relative_to = %s, pos = %s, need_pxd = %s" % (module_name, relative_to, pos, need_pxd)) scope = None pxd_pathname = None if check_module_name and not module_name_pattern.match(module_name): if pos is None: pos = (module_name, 0, 0) raise CompileError(pos, "'%s' is not a valid module name" % module_name) if "." not in module_name and relative_to: if debug_find_module: print("...trying relative import") scope = relative_to.lookup_submodule(module_name) if not scope: qualified_name = relative_to.qualify_name(module_name) pxd_pathname = self.find_pxd_file(qualified_name, pos) if pxd_pathname: scope = relative_to.find_submodule(module_name) if not scope: if debug_find_module: print("...trying absolute import") scope = self for name in module_name.split("."): scope = scope.find_submodule(name) if debug_find_module: print("...scope =", scope) if not scope.pxd_file_loaded: if debug_find_module: print("...pxd not loaded") scope.pxd_file_loaded = 1 if not pxd_pathname: if debug_find_module: print("...looking for pxd file") pxd_pathname = self.find_pxd_file(module_name, pos) if debug_find_module: print("......found ", pxd_pathname) if not pxd_pathname and need_pxd: package_pathname = self.search_include_directories( module_name, ".py", pos) if package_pathname and package_pathname.endswith( '__init__.py'): pass else: error(pos, "'%s.pxd' not found" % module_name) if pxd_pathname: try: if debug_find_module: print("Context.find_module: Parsing %s" % pxd_pathname) rel_path = module_name.replace( '.', os.sep) + os.path.splitext(pxd_pathname)[1] if not pxd_pathname.endswith(rel_path): rel_path = pxd_pathname # safety measure to prevent printing incorrect paths source_desc = FileSourceDescriptor(pxd_pathname, rel_path) err, result = self.process_pxd(source_desc, scope, module_name) if err: raise err (pxd_codenodes, pxd_scope) = result self.pxds[module_name] = (pxd_codenodes, pxd_scope) except CompileError: pass return scope
def assert_bool(name): x = options.get(name) if not isinstance(x, bool): raise CompileError(globalpos, ERR_BUF_BOOL % name)
def analyse_buffer_options(globalpos, env, posargs, dictargs, defaults=None, need_complete=True): """ Must be called during type analysis, as analyse is called on the dtype argument. posargs and dictargs should consist of a list and a dict of tuples (value, pos). Defaults should be a dict of values. Returns a dict containing all the options a buffer can have and its value (with the positions stripped). """ if defaults is None: defaults = buffer_defaults posargs, dictargs = Interpreter.interpret_compiletime_options(posargs, dictargs, type_env=env, type_args = (0,'dtype')) if len(posargs) > buffer_positional_options_count: raise CompileError(posargs[-1][1], ERR_BUF_TOO_MANY) options = {} for name, (value, pos) in dictargs.iteritems(): if not name in buffer_options: raise CompileError(pos, ERR_BUF_OPTION_UNKNOWN % name) options[name] = value for name, (value, pos) in zip(buffer_options, posargs): if not name in buffer_options: raise CompileError(pos, ERR_BUF_OPTION_UNKNOWN % name) if name in options: raise CompileError(pos, ERR_BUF_DUP % name) options[name] = value # Check that they are all there and copy defaults for name in buffer_options: if not name in options: try: options[name] = defaults[name] except KeyError: if need_complete: raise CompileError(globalpos, ERR_BUF_MISSING % name) dtype = options.get("dtype") if dtype and dtype.is_extension_type: raise CompileError(globalpos, ERR_BUF_DTYPE) ndim = options.get("ndim") if ndim and (not isinstance(ndim, int) or ndim < 0): raise CompileError(globalpos, ERR_BUF_NDIM) mode = options.get("mode") if mode and not (mode in ('full', 'strided', 'c', 'fortran')): raise CompileError(globalpos, ERR_BUF_MODE) def assert_bool(name): x = options.get(name) if not isinstance(x, bool): raise CompileError(globalpos, ERR_BUF_BOOL % name) assert_bool('negative_indices') assert_bool('cast') return options
def get_axes_specs(env, axes): ''' get_axes_specs(env, axes) -> list of (access, packing) specs for each axis. access is one of 'full', 'ptr' or 'direct' packing is one of 'contig', 'strided' or 'follow' ''' cythonscope = env.global_scope().context.cython_scope cythonscope.load_cythonscope() viewscope = cythonscope.viewscope access_specs = tuple( [viewscope.lookup(name) for name in ('full', 'direct', 'ptr')]) packing_specs = tuple( [viewscope.lookup(name) for name in ('contig', 'strided', 'follow')]) is_f_contig, is_c_contig = False, False default_access, default_packing = 'direct', 'strided' cf_access, cf_packing = default_access, 'follow' axes_specs = [] # analyse all axes. for idx, axis in enumerate(axes): if not axis.start.is_none: raise CompileError(axis.start.pos, START_ERR) if not axis.stop.is_none: raise CompileError(axis.stop.pos, STOP_ERR) if axis.step.is_none: axes_specs.append((default_access, default_packing)) elif isinstance(axis.step, IntNode): # the packing for the ::1 axis is contiguous, # all others are cf_packing. if axis.step.compile_time_value(env) != 1: raise CompileError(axis.step.pos, STEP_ERR) axes_specs.append((cf_access, 'cfcontig')) elif isinstance(axis.step, (NameNode, AttributeNode)): entry = _get_resolved_spec(env, axis.step) if entry.name in view_constant_to_access_packing: axes_specs.append(view_constant_to_access_packing[entry.name]) else: raise CompilerError(axis.step.pos, INVALID_ERR) else: raise CompileError(axis.step.pos, INVALID_ERR) # First, find out if we have a ::1 somewhere contig_dim = 0 is_contig = False for idx, (access, packing) in enumerate(axes_specs): if packing == 'cfcontig': if is_contig: raise CompileError(axis.step.pos, BOTH_CF_ERR) contig_dim = idx axes_specs[idx] = (access, 'contig') is_contig = True if is_contig: # We have a ::1 somewhere, see if we're C or Fortran contiguous if contig_dim == len(axes) - 1: is_c_contig = True else: is_f_contig = True if contig_dim and not axes_specs[contig_dim - 1][0] in ('full', 'ptr'): raise CompileError( axes[contig_dim].pos, "Fortran contiguous specifier must follow an indirect dimension" ) if is_c_contig: # Contiguous in the last dimension, find the last indirect dimension contig_dim = -1 for idx, (access, packing) in enumerate(reversed(axes_specs)): if access in ('ptr', 'full'): contig_dim = len(axes) - idx - 1 # Replace 'strided' with 'follow' for any dimension following the last # indirect dimension, the first dimension or the dimension following # the ::1. # int[::indirect, ::1, :, :] # ^ ^ # int[::indirect, :, :, ::1] # ^ ^ start = contig_dim + 1 stop = len(axes) - is_c_contig for idx, (access, packing) in enumerate(axes_specs[start:stop]): idx = contig_dim + 1 + idx if access != 'direct': raise CompileError( axes[idx].pos, "Indirect dimension may not follow " "Fortran contiguous dimension") if packing == 'contig': raise CompileError(axes[idx].pos, "Dimension may not be contiguous") axes_specs[idx] = (access, cf_packing) if is_c_contig: # For C contiguity, we need to fix the 'contig' dimension # after the loop a, p = axes_specs[-1] axes_specs[-1] = a, 'contig' validate_axes_specs([axis.start.pos for axis in axes], axes_specs, is_c_contig, is_f_contig) return axes_specs
class Context(object): # This class encapsulates the context needed for compiling # one or more Cython implementation files along with their # associated and imported declaration files. It includes # the root of the module import namespace and the list # of directories to search for include files. # # modules {string : ModuleScope} # include_directories [string] # future_directives [object] # language_level int currently 2 or 3 for Python 2/3 cython_scope = None def __init__(self, include_directories, compiler_directives, cpp=False, language_level=2, options=None, create_testscope=True): # cython_scope is a hack, set to False by subclasses, in order to break # an infinite loop. # Better code organization would fix it. import Builtin, CythonScope self.modules = {"__builtin__" : Builtin.builtin_scope} self.cython_scope = CythonScope.create_cython_scope(self) self.modules["cython"] = self.cython_scope self.include_directories = include_directories self.future_directives = set() self.compiler_directives = compiler_directives self.cpp = cpp self.options = options self.pxds = {} # full name -> node tree standard_include_path = os.path.abspath(os.path.normpath( os.path.join(os.path.dirname(__file__), os.path.pardir, 'Includes'))) self.include_directories = include_directories + [standard_include_path] self.set_language_level(language_level) self.gdb_debug_outputwriter = None def set_language_level(self, level): self.language_level = level if level >= 3: from Future import print_function, unicode_literals, absolute_import self.future_directives.update([print_function, unicode_literals, absolute_import]) self.modules['builtins'] = self.modules['__builtin__'] # pipeline creation functions can now be found in Pipeline.py def process_pxd(self, source_desc, scope, module_name): import Pipeline if isinstance(source_desc, FileSourceDescriptor) and source_desc._file_type == 'pyx': source = CompilationSource(source_desc, module_name, os.getcwd()) result_sink = create_default_resultobj(source, self.options) pipeline = Pipeline.create_pyx_as_pxd_pipeline(self, result_sink) result = Pipeline.run_pipeline(pipeline, source) else: pipeline = Pipeline.create_pxd_pipeline(self, scope, module_name) result = Pipeline.run_pipeline(pipeline, source_desc) return result def nonfatal_error(self, exc): return Errors.report_error(exc) def find_module(self, module_name, relative_to = None, pos = None, need_pxd = 1, check_module_name = True): # Finds and returns the module scope corresponding to # the given relative or absolute module name. If this # is the first time the module has been requested, finds # the corresponding .pxd file and process it. # If relative_to is not None, it must be a module scope, # and the module will first be searched for relative to # that module, provided its name is not a dotted name. debug_find_module = 0 if debug_find_module: print("Context.find_module: module_name = %s, relative_to = %s, pos = %s, need_pxd = %s" % ( module_name, relative_to, pos, need_pxd)) scope = None pxd_pathname = None if check_module_name and not module_name_pattern.match(module_name): if pos is None: pos = (module_name, 0, 0) raise CompileError(pos, "'%s' is not a valid module name" % module_name) if "." not in module_name and relative_to: if debug_find_module: print("...trying relative import") scope = relative_to.lookup_submodule(module_name) if not scope: qualified_name = relative_to.qualify_name(module_name) pxd_pathname = self.find_pxd_file(qualified_name, pos) if pxd_pathname: scope = relative_to.find_submodule(module_name) if not scope: if debug_find_module: print("...trying absolute import") scope = self for name in module_name.split("."): scope = scope.find_submodule(name) if debug_find_module: print("...scope =", scope) if not scope.pxd_file_loaded: if debug_find_module: print("...pxd not loaded") scope.pxd_file_loaded = 1 if not pxd_pathname: if debug_find_module: print("...looking for pxd file") pxd_pathname = self.find_pxd_file(module_name, pos) if debug_find_module: print("......found ", pxd_pathname) if not pxd_pathname and need_pxd: package_pathname = self.search_include_directories(module_name, ".py", pos) if package_pathname and package_pathname.endswith('__init__.py'): pass else: error(pos, "'%s.pxd' not found" % module_name) if pxd_pathname: try: if debug_find_module: print("Context.find_module: Parsing %s" % pxd_pathname) rel_path = module_name.replace('.', os.sep) + os.path.splitext(pxd_pathname)[1] if not pxd_pathname.endswith(rel_path): rel_path = pxd_pathname # safety measure to prevent printing incorrect paths source_desc = FileSourceDescriptor(pxd_pathname, rel_path) err, result = self.process_pxd(source_desc, scope, module_name) if err: raise err (pxd_codenodes, pxd_scope) = result self.pxds[module_name] = (pxd_codenodes, pxd_scope) except CompileError: pass return scope def find_pxd_file(self, qualified_name, pos): # Search include path for the .pxd file corresponding to the # given fully-qualified module name. # Will find either a dotted filename or a file in a # package directory. If a source file position is given, # the directory containing the source file is searched first # for a dotted filename, and its containing package root # directory is searched first for a non-dotted filename. pxd = self.search_include_directories(qualified_name, ".pxd", pos, sys_path=True) if pxd is None: # XXX Keep this until Includes/Deprecated is removed if (qualified_name.startswith('python') or qualified_name in ('stdlib', 'stdio', 'stl')): standard_include_path = os.path.abspath(os.path.normpath( os.path.join(os.path.dirname(__file__), os.path.pardir, 'Includes'))) deprecated_include_path = os.path.join(standard_include_path, 'Deprecated') self.include_directories.append(deprecated_include_path) try: pxd = self.search_include_directories(qualified_name, ".pxd", pos) finally: self.include_directories.pop() if pxd: name = qualified_name if name.startswith('python'): warning(pos, "'%s' is deprecated, use 'cpython'" % name, 1) elif name in ('stdlib', 'stdio'): warning(pos, "'%s' is deprecated, use 'libc.%s'" % (name, name), 1) elif name in ('stl'): warning(pos, "'%s' is deprecated, use 'libcpp.*.*'" % name, 1) if pxd is None and Options.cimport_from_pyx: return self.find_pyx_file(qualified_name, pos) return pxd def find_pyx_file(self, qualified_name, pos): # Search include path for the .pyx file corresponding to the # given fully-qualified module name, as for find_pxd_file(). return self.search_include_directories(qualified_name, ".pyx", pos) def find_include_file(self, filename, pos): # Search list of include directories for filename. # Reports an error and returns None if not found. path = self.search_include_directories(filename, "", pos, include=True) if not path: error(pos, "'%s' not found" % filename) return path def search_include_directories(self, qualified_name, suffix, pos, include=False, sys_path=False): return Utils.search_include_directories( tuple(self.include_directories), qualified_name, suffix, pos, include, sys_path) def find_root_package_dir(self, file_path): return Utils.find_root_package_dir(file_path) def check_package_dir(self, dir, package_names): return Utils.check_package_dir(dir, tuple(package_names)) def c_file_out_of_date(self, source_path): c_path = Utils.replace_suffix(source_path, ".c") if not os.path.exists(c_path): return 1 c_time = Utils.modification_time(c_path) if Utils.file_newer_than(source_path, c_time): return 1 pos = [source_path] pxd_path = Utils.replace_suffix(source_path, ".pxd") if os.path.exists(pxd_path) and Utils.file_newer_than(pxd_path, c_time): return 1 for kind, name in self.read_dependency_file(source_path): if kind == "cimport": dep_path = self.find_pxd_file(name, pos) elif kind == "include": dep_path = self.search_include_directories(name, pos) else: continue if dep_path and Utils.file_newer_than(dep_path, c_time): return 1 return 0 def find_cimported_module_names(self, source_path): return [ name for kind, name in self.read_dependency_file(source_path) if kind == "cimport" ] def is_package_dir(self, dir_path): return Utils.is_package_dir(dir_path) def read_dependency_file(self, source_path): dep_path = Utils.replace_suffix(source_path, ".dep") if os.path.exists(dep_path): f = open(dep_path, "rU") chunks = [ line.strip().split(" ", 1) for line in f.readlines() if " " in line.strip() ] f.close() return chunks else: return () def lookup_submodule(self, name): # Look up a top-level module. Returns None if not found. return self.modules.get(name, None) def find_submodule(self, name): # Find a top-level module, creating a new one if needed. scope = self.lookup_submodule(name) if not scope: scope = ModuleScope(name, parent_module = None, context = self) self.modules[name] = scope return scope def parse(self, source_desc, scope, pxd, full_module_name): if not isinstance(source_desc, FileSourceDescriptor): raise RuntimeError("Only file sources for code supported") source_filename = source_desc.filename scope.cpp = self.cpp # Parse the given source file and return a parse tree. try: f = Utils.open_source_file(source_filename, "rU") try: import Parsing s = PyrexScanner(f, source_desc, source_encoding = f.encoding, scope = scope, context = self) tree = Parsing.p_module(s, pxd, full_module_name) finally: f.close() except UnicodeDecodeError, e: #import traceback #traceback.print_exc() line = 1 column = 0 msg = e.args[-1] position = e.args[2] encoding = e.args[0] for idx, c in enumerate(open(source_filename, "rb").read()): if c in (ord('\n'), '\n'): line += 1 column = 0 if idx == position: break column += 1 error((source_desc, line, column), "Decoding error, missing or incorrect coding=<encoding-name> " "at top of source (cannot decode with encoding %r: %s)" % (encoding, msg)) if Errors.num_errors > 0: raise CompileError() return tree