def visit_For(self, node): """ Discover loop variables. TODO: this is incomplete; we just assume that loop variables are all classical. We don't attempt to infer anything about the iterator. """ for subnode in ast.walk(node.target): if isinstance(subnode, ast.Attribute): # This is a fatal error and we don't want to confuse # ourselves by trying to process the ast.Name # nodes beneath # name_text = pyqgl2.importer.collapse_name(subnode) NodeError.fatal_msg(subnode, ('loop var [%s] is not local' % name_text)) elif isinstance(subnode, ast.Name): name = subnode.id # Warn the user if they're doing something that's # likely to provoke an error # if self.name_is_in_lscope(name): NodeError.warning_msg( subnode, ('loop var [%s] hides sym in outer scope' % name)) DebugMsg.log('FOR (%s)' % name) self.add_type_binding(subnode, name, QGL2.CLASSICAL) self.visit_body(node.body) self.visit_body(node.orelse)
def read_import_str(self, text, path='<stdin>', module_name='__main__'): ptree = ast.parse(text, mode='exec') self.path2ast[path] = ptree # label each node with the name of the input file; # this will make error messages that reference these # notes much more readable # for node in ast.walk(ptree): node.qgl_fname = path node.qgl_modname = module_name # The preprocessor will ignore any imports that are not # at the "top level" (imports that happen conditionally, # or when a function is executed for the first time, etc) # because it can't figure out if/when these imports would # occur, and it only understands imports that occur before # the execution of any other statements of the program. # # Therefore warn the programmer that any such detected # imports will be ignored. # # TODO: we don't make any attempt to find calls to # __import__() or importlib.import_module(). The # preprocessor always ignores these, without warning. # for node in ast.walk(ptree): if ((isinstance(node, ast.Import) or isinstance(node, ast.ImportFrom)) and (node.col_offset != 0)): NodeError.warning_msg( node, ('conditional/runtime import [%s] ignored by pyqgl2' % pyqgl2.ast_util.ast2str(node).strip())) # Populate the namespace # namespace = NameSpace(path, ptree=ptree) self.path2namespace[path] = namespace for stmnt in ptree.body: if isinstance(stmnt, ast.FunctionDef): self.add_function(namespace, stmnt.name, stmnt) elif isinstance(stmnt, ast.Import): # print('NN ADDING import %s' % ast.dump(stmnt)) self.add_import_as(namespace, stmnt) elif isinstance(stmnt, ast.ImportFrom): # print('NN ADDING import-from %s' % ast.dump(stmnt)) self.add_from_as(namespace, stmnt.module, stmnt) # We're not doing module-level variables right now; no globals """ elif isinstance(stmnt, ast.Assign): print('NN ASSIGN %s' % ast.dump(stmnt)) """ # print('NN NAMESPACE %s' % str(self.path2namespace)) return self.path2ast[path]
def visit_Assign(self, node): # FIXME: can't handle nested tuples properly # For now we're not even going to try. if not isinstance(node.targets[0], ast.Name): NodeError.warning_msg(node, 'tuple returns not supported yet') self.generic_visit(node) return target = node.targets[0] print('CP target0: %s' % ast.dump(target)) value = node.value name = target.id # TODO: what if the lval is an array or dict expression? # Need to sort out what's referenced by the lval. if isinstance(value, ast.Str): print('CP looks like a str assignment %s' % ast.dump(node)) elif isinstance(value, ast.Num): print('CP looks like a num assignment %s' % ast.dump(node)) elif isinstance(value, ast.List): print('CP looks like a list assignment %s' % ast.dump(node)) elif isinstance(value, ast.Call): print('CP looks like a call assignment %s' % ast.dump(node)) self.generic_visit(node)
def add_local_func(self, name, ptree): if not self.check_dups(name, 'local-function'): NodeError.warning_msg(ptree, 'redefinition of function [%s]' % name) else: self.order_added.append(('D', ptree)) self.local_defs[name] = ptree
def add_local_var(self, name, ptree): if not self.check_dups(name, 'local-variable'): NodeError.warning_msg(ptree, 'redefinition of variable [%s]' % name) else: self.order_added.append(('V', ptree)) self.local_vars[name] = ptree
def add_import_as(self, namespace, stmnt): namespace.native_import(pyqgl2.ast_util.ast2str(stmnt), stmnt) namespace.add_import_as_stmnt(stmnt) for imp in stmnt.names: subpath = resolve_path(imp.name) if not subpath: NodeError.warning_msg(stmnt, 'path to [%s] not found' % imp.name) elif is_system_file(subpath): continue else: namespace.add_import_as(imp.name, imp.asname) self.read_import(subpath)
def find_sequences(self, node): ''' Input AST node is the main function definition. Strips out QRegister creation statements and builds a list of corresponding Qubit creation statements. Converts calls on QRegisters into calls on Qubits.''' if not isinstance(node, ast.FunctionDef): NodeError.fatal_msg(node, 'not a function definition') return False self.qbits = self.qbits_from_qregs(self.allocated_qregs) for q in self.qbits: # Using QubitFactory here means the qubit must already exist # If did Channels.Qubit, we'd be creating it new; but it wouldn't be in the ChannelLibrary stmnt = ast.parse("QBIT_{0} = QubitFactory('q{0}')".format(q)) self.qbit_creates.append(stmnt) lineNo = -1 while lineNo + 1 < len(node.body): lineNo += 1 # print("Line %d of %d" % (lineNo+1, len(node.body))) stmnt = node.body[lineNo] # print("Looking at stmnt %s" % stmnt) if is_qbit_create(stmnt): # drop it continue elif isinstance(stmnt, ast.Expr): # expand calls on QRegisters into calls on Qubits if (hasattr(stmnt, 'qgl2_type') and (stmnt.qgl2_type == 'stub' or stmnt.qgl2_type == 'measurement')): new_stmnts = self.expand_qreg_call(stmnt) self.sequence.extend(new_stmnts) else: self.sequence.append(stmnt) else: NodeError.error_msg(stmnt, 'orphan statement %s' % ast.dump(stmnt)) # print("Seqs: %s" % self.sequences) if not self.sequence: NodeError.warning_msg(node, "No qubit operations discovered") return False return True
def do_lval(self, lval): assigned_names, _dotted, _arrays = self.name_finder.find_names(lval) for name in assigned_names: if name not in self.local_names: self.local_names[name] = self.nesting_depth elif self.local_names[name] > self.nesting_depth: # we permit the nest depth to decrease, but # not increase # self.local_names[name] = self.nesting_depth elif self.local_names[name] == CheckScoping.BUILTIN_SCOPE: # If the symbol is a builtin, then warn that # it's being reassigned # NodeError.warning_msg( lval, 'reassignment of a builtin symbol [%s]' % name)
def collapse_name(node): """ Given the AST for a symbol reference, collapse it back into the original reference string Example, instead of the AST Attribute(Name(id='x'), addr='y') return 'x.y' """ if isinstance(node, ast.Name): return node.id elif isinstance(node, ast.Attribute): return collapse_name(node.value) + '.' + node.attr else: # TODO: handle this more gracefully NodeError.warning_msg( node, 'unexpected failure to resolve [%s]' % ast.dump(node)) return None
def visit_FunctionDef(self, node): """ The usual entry point: insert the names used by the formal parameters, and then process the body """ for arg in node.args.args: name = arg.arg if name not in self.local_names: self.local_names[name] = CheckScoping.PARAM_SCOPE elif self.local_names[name] == CheckScoping.MODULE_SCOPE: NodeError.warning_msg( node, 'formal parameter masks a module symbol [%s]' % name) else: NodeError.warning_msg( node, 'formal parameter masks a builtin symbol [%s]' % name) self.do_body(node.body)
def visit_Name(self, node): """ Process an ast.Name node for a symbol reference (not a symbol assignment, which should be done in do_lval). If we find a name that doesn't have a binding in the current scope, or that was defined at a higher nesting level than we're currently in, then warn the user that this might be an error. (these aren't always errors, but they're strange enough that they're worth calling out) """ name = node.id if name not in self.local_names: NodeError.warning_msg(node, 'potentially undefined symbol [%s]' % name) elif self.local_names[name] > self.nesting_depth: NodeError.warning_msg( node, 'symbol [%s] referenced outside defining block' % name)
def __init__(self, path, qglmain_name=None, text=None): # map from path to AST # self.path2ast = dict() # map from path to NameSpace # self.path2namespace = dict() # The error/warning messages are clearer if we always # use the relpath # if not path or path == '<stdin>': self.base_fname = '<stdin>' else: self.base_fname = os.path.relpath(path) # Reference to the main function; initially None because # we haven't read it in yet # self.qglmain = None if text: self.read_import_str(text, self.base_fname) else: self.read_import(self.base_fname) # TODO: if the user asks for a specific main, then go # back and use it. Don't gripe if the user has already defined # one. Resolve the name with respect to the namespace # of base_fname if qglmain_name: qglmain_def = self.resolve_sym(self.base_fname, qglmain_name) if not qglmain_def: NodeError.error_msg( None, 'no definition for qglmain [%s]' % qglmain_name) elif not qglmain_def.qgl_func: NodeError.error_msg( None, 'qglmain [%s] not declared QGL' % qglmain_name) else: self.qglmain = qglmain_def qglmain_def.qgl_main = True if self.qglmain: NodeError.diag_msg(None, 'using [%s] as qglmain' % self.qglmain.name) else: NodeError.warning_msg(None, 'warning: no qglmain declared or chosen') # This is a hack to make sure that the base file # is read in as a "native import". Since there isn't # an explicit "import" of this file anywhere, we don't # have an AST node that contains the code for this import. # We can't use None, because this importer uses this as # a sentinel value, so we use self.qgl2main. This is # bogus -- we should make a fake node for this purpose # FIXME fin = open(self.base_fname, 'r') text = fin.read() fin.close() namespace = self.path2namespace[self.base_fname] namespace.native_import(text, self.qglmain)
def add_from_as(self, namespace, module_name, stmnt): """ Process a "from import as" statement (where the "as" is optional). The rules for how this works in Python are complicated, not particularly well specified (from the docs I can find), and more than we're attempting to do. Here's what we do: Given a statement with one of the following forms: from X import A as Z from X import A, B from X import * X is referred to here as the module name, but it is not required (as of Python 3.4) to refer to a module; it may refer to a package, or (as of 3.4) a directory containing other packages or modules but is not itself a package. Note: it is not legal for A to be a compound thing, i.e. "from os import path.sep" is invalid syntax. After converting X from Python notation (including relative paths) into a file system path XP, we check to see whether it resolves to a module (with name XP + ".py"), or a package (with name XP + '/__init__.py') or a directory (with name XP). In Python 3.4/3.5, there is a new feature of being able to do a from-import of a module from a directory, i.e. from X import A where A is a module, rather than a symbol inside module or package X. We DO NOT support this feature yet. """ # setup the namespace for this module # NOTE: this is incomplete: it only sets up the specific # name, and may do so repeatedly. # TODO: We should only do this once # TODO: and we should import the entire namespace so that # local functions can access local definitions and # functions that are otherwise private # namespace.native_import(pyqgl2.ast_util.ast2str(stmnt), stmnt) namespace.add_from_as_stmnt(stmnt) # print('NX orig statement [%s]' % ast.dump(stmnt)) # print('NX orig statement [%s]' % # pyqgl2.ast_util.ast2str(stmnt).strip()) # placeholder subpath = None if stmnt.level > 0: # Deal with relative imports: these have a level of 1 # or higher # # Find the directory by peeling the last component off # of stmnt.qgl_fname and keeping the rest. # # Then append the right number of '..' components (level - 1) # to either look in the same directory, or a parent directory. # The resulting path is hideous and non-canonical, but we'll # fix that later. # # Finally, add the relative component (after translating it # from Python notation to path notation, and adding the # suffix). # dir_name = stmnt.qgl_fname.rpartition(os.sep)[0] # If the relative path is for a parent directory, add # the proper number of '..' components. A single '.', # however, represents this directory. # if stmnt.level > 1: dir_name += os.sep + os.sep.join(['..'] * (stmnt.level - 1)) # We're going to convert the entire path to a relative path # later, but doing it for the directory prefix makes things # more legible while debugging # dir_name = os.path.relpath(dir_name) # if there's a module name, prepare to test whether it's # a file or a directory. If there's not then the dir_name # is the dpath, and there is no fpath # if module_name: mod_path = os.sep.join(module_name.split('.')) from_path = os.path.join(dir_name, mod_path) else: from_path = dir_name # Now figure out what kind of thing is at the end of that # path: a module, a package, or a directory: module_path = from_path + '.py' package_path = os.path.join(from_path, '__init__.py') dir_path = from_path if os.path.isfile(module_path): subpath = module_path elif os.path.isfile(package_path): subpath = package_path elif os.path.isdir(dir_path): subpath = from_path # Since we don't know what our own module name is, # we can't figure out the "full" name of the relatively # imported module. FIXME # full_module_name = None NodeError.warning_msg( stmnt, ('cannot evaluate exprs in a relative import [%s]' % module_name)) else: # use normal resolution to find the location of module # subpath = resolve_path(module_name) full_module_name = module_name # There are a lot of reasons why we might not be able # to resolve a module name; it could be in a binary file # or a zip file, or obscured in some other way, so that # the ordinary Python interpreter can find it but we cannot. # So we can't treat this as an error, even though it might # be one. # if subpath is None: NodeError.diag_msg(stmnt, ('path to [%s%s] not found' % ('.' * stmnt.level, module_name))) elif is_system_file(subpath): NodeError.diag_msg(stmnt, ('import of [%s%s] ignored' % ('.' * stmnt.level, module_name))) else: self.read_import(subpath) for imp in stmnt.names: if imp.name == '*': NodeError.warning_msg( stmnt, ('deprecated wildcard import from [%s]' % module_name)) self.add_from_wildcard(namespace, subpath, module_name) if full_module_name: namespace.native_import( ('from %s import *' % full_module_name), stmnt) else: namespace.add_from_as_path(subpath, imp.name, imp.asname) if full_module_name: symname = imp.name if imp.asname: symname += ' %s' % imp.asname namespace.native_import(('from %s import %s' % (full_module_name, symname)), stmnt)
def visit_Assign(self, node): # FIXME: can't handle nested tuples properly # For now we're not even going to try. if not isinstance(node.targets[0], ast.Name): NodeError.warning_msg(node, 'tuple returns not supported yet') self.generic_visit(node) return node target = node.targets[0] print('VA target0: %s' % ast.dump(target)) value = node.value name = target.id if not isinstance(target, ast.Name): # should this be considered an error? # it's not an error in Python, but it's hard for us to handle. return node if self.is_qbit_parameter(name): msg = 'reassignment of qbit parameter \'%s\' forbidden' % name NodeError.error_msg(node, msg) return node if self.is_qbit_local(name): msg = 'reassignment of qbit \'%s\' forbidden' % name NodeError.error_msg(node, msg) return node if isinstance(value, ast.Name): if not self.name_is_in_lscope(value.id): NodeError.error_msg(node, 'unknown symbol \'%s\'' % value.id) return node if self.is_qbit_parameter(name): self.warning_msg( node, 'aliasing qbit parameter \'%s\' as \'%s\'' % (value.id, name)) self.add_type_binding(value, name, QGL2.QBIT) target.qgl_is_qbit = True elif self.is_qbit_local(name): self.warning_msg( node, 'aliasing local qbit \'%s\' as \'%s\'' % (value.id, name)) self.add_type_binding(value, name, QGL2.QBIT) target.qgl_is_qbit = True else: self.add_type_binding(value, name, QGL2.CLASSICAL) target.qgl_is_qbit = False elif isinstance(value, ast.Call): func_name = pyqgl2.importer.collapse_name(value.func) func_def = self.importer.resolve_sym(value.qgl_fname, func_name) # FIXME: for debugging only! new_scope = FindTypes.find_lscope(self.importer, func_def, value, self) # FIXME: end debugging # If we can't find the function definition, or it's not declared # to be QGL, then we can't handle it. Return immediately. # if not func_def: NodeError.warning_msg(value, 'function [%s] not found' % func_name) self.add_type_binding(value, name, 'unknown') return node if func_def.returns: rtype = func_def.returns if (isinstance(rtype, ast.Name) and rtype.id == QGL2.QBIT): # Not sure what happens if we get here: we might # have a wandering variable that we know is a qbit, # but we never know which one. # DebugMsg.log('extending local (%s)' % name) self.add_type_binding(value, name, QGL2.QBIT) target.qgl_is_qbit = True if not func_def.qgl_func: # TODO: this seems bogus. We should be able to call # out to non-QGL functions # NodeError.error_msg( value, 'function [%s] not declared to be QGL2' % func_name) return node # When we're figuring out whether something is a call to # the Qbit assignment function, we look at the name of the # function as it is defined (i.e, as func_def), not as it # is imported (i.e., as func_name). # # This makes the assumption that ANYTHING named 'Qubit' or 'QubitFactory' # is a Qbit assignment function, which is lame and should # be more carefully parameterized. Things to think about: # looking more deeply at its signature and making certain # that it looks like the 'right' function and not something # someone mistakenly named 'Qubit' in an unrelated context. # if isinstance(value, ast.Call) and (func_def.name == QGL2.QBIT_ALLOC or func_def.name == QGL2.QBIT_ALLOC2): self.add_type_binding(value, name, QGL2.QBIT) return node
def process_params(self, func_def, call=None, call_scope=None): # The formal parameters are an AST object. # The way they are represented is a little awkward; # all parameters (positional and keyword) are in a # positional list (because Python can handle keyword # parameters as positional parameters) and then the # keyword default values are in a separate positional # list.) type_bindings = dict() val_bindings = dict() all_arg_names = list() # First, pretend all the parameters are positional # for arg in func_def.args.args: arg_name = arg.arg arg_type = arg.annotation if arg_type and isinstance(arg_type, ast.Name): arg_type_name = arg_type.id else: arg_type_name = 'unknown' if arg_name in all_arg_names: NodeError.error_msg( arg, 'repeated parameter name \'%s\'' % arg_name) # if arg_type_name not in [QGL2.CLASSICAL, QGL2.QBIT, 'unknown', QGL2.CONTROL, QGL2.PULSE, QGL2.SEQUENCE, QGL2.QBIT_LIST]: if arg_type_name not in [QGL2.CLASSICAL, QGL2.QBIT, 'unknown']: NodeError.warning_msg( arg, ('parameter type \'%s\' is not supported' % arg_type_name)) all_arg_names.append(arg_name) type_bindings[arg_name] = arg_type_name val_bindings[arg_name] = None # Then process any defaults that were provided # default_vals = func_def.args.defaults if default_vals: default_names = all_arg_names[:-len(default_vals)] for ind in range(len(default_vals)): val_bindings[default_names[ind]] = default_vals[ind] # TODO: we need to make sure that the default # values actually match the declared type, if any # # NOTE that the default value is an AST, which could be # almost any expression. Many expressions are going to # be a headache for us, so maybe we should disallow # many of them. # Now replace the default values with whatever is in the # actuals, if any actuals are provided. if call: seen_args = set() print('CALL %s' % ast.dump(call)) if call.args: for ind in range(len(call.args)): seen_args.add(all_arg_names[ind]) val_bindings[all_arg_names[ind]] = call.args[ind] # TODO: If there were fewer args than required, then # gripe. TODO: if there were unexpected arguments, gripe for kwarg in call.keywords: name = kwarg.arg if name in seen_args: NodeError( call, 'more than one value for parameter \'%s\'' % name) seen_args.add(name) val_bindings[name] = kwarg.value print('CALL %s' % str(val_bindings)) # TODO: if provided a surrounding scope and a call, then try to # infer types from actual parameters. For example, if one of # the actual parameters is 'x', and we know the type of 'x', then # propogate it. # # Right now we don't try to statically determine values. # # # TODO: this is incomplete if call and call_scope: # Create a dictionary of known types from the given # call_scope. Note that we are only interested in # known types, so omit any "unknown" types # scope_types = dict() for name in call_scope.parameter_names: name_type = call_scope.parameter_names[name] if name_type != 'unknown': scope_types[name] = name_type for name in call_scope.local_names: name_type = call_scope.local_names[name] if name_type != 'unknown': scope_types[name] = name_type # Now look at each actual parameter, and try # to infer what type it has. If it's a number or # string, it's classical. If it's the value of # a variable, look in scope_types to see what we # know about that variable (if anything). If it's # a method call, look at the definition of the # method to see whether it has a declared type. # for name in type_bindings: actual_val = val_bindings[name] if isinstance(actual_val, ast.Num): type_bindings[name] = QGL2.CLASSICAL elif isinstance(actual_val, ast.Str): type_bindings[name] = QGL2.CLASSICAL elif isinstance(actual_val, ast.NameConstant): type_bindings[name] = QGL2.CLASSICAL elif isinstance(actual_val, ast.Name): if actual_val.id in scope_types: type_bindings[name] = scope_types[actual_val.id] elif isinstance(actual_val, ast.Call): called_func_name = pyqgl2.importer.collapse_name( actual_val.func) called_func = self.importer.resolve_sym( actual_val.qgl_fname, func_name) if not called_func: NodeError.warning_msg( value, 'function [%s] not found' % called_func_name) continue elif called_func.returns: rtype = called_func_def.returns if isinstance(rtype, ast.Name): rtype_name = rtype.id # if rtype_name not in [QGL2.CLASSICAL, QGL2.QBIT, 'unknown', QGL2.SEQUENCE, QGL2.PULSE, QGL2.CONTROL, QGL2.QBIT_LIST]: if rtype_name not in [ QGL2.CLASSICAL, QGL2.QBIT, 'unknown' ]: NodeError.warning_msg( arg, ('parameter type \'%s\' is not supported' % arg_type_name)) type_bindings[name] = rtype_name return val_bindings, type_bindings
def find_type_decl(self, node): """ Copied from check_qbit. Both need to be refactored. """ q_args = list() q_return = None if node is None: NodeError.warning_msg(node, 'unexpected None node') return None if not isinstance(node, ast.FunctionDef): NodeError.warning_msg( node, 'expected a FunctionDef, got [%s]' % ast.dump(node)) return None if node.returns: ret = node.returns if isinstance(ret, ast.Name): if ret.id == QGL2.QBIT: q_return = QGL2.QBIT elif ret.id == QGL2.CLASSICAL: q_return = QGL2.CLASSICAL # elif ret.id == QGL2.QBIT_LIST: # q_return = QGL2.QBIT_LIST elif ret.id == QGL2.PULSE: q_return = QGL2.PULSE elif ret.id == QGL2.CONTROL: q_return = QGL2.CONTROL elif ret.id == QGL2.SEQUENCE: q_return = QGL2.SEQUENCE else: NodeError.error_msg( node, 'unsupported return type [%s]' % ret.id) # FIXME: What about kwonlyargs? or storing the defaults? if node.args.args: for arg in node.args.args: # print('>> %s' % ast.dump(arg)) name = arg.arg annotation = arg.annotation if not annotation: q_args.append('%s:%s' % (name, QGL2.CLASSICAL)) elif isinstance(annotation, ast.Name): if annotation.id == QGL2.QBIT: q_args.append('%s:%s' % (name, QGL2.QBIT)) elif annotation.id == QGL2.CLASSICAL: q_args.append('%s:%s' % (name, QGL2.CLASSICAL)) # elif annotation.id == QGL2.QBIT_LIST: # q_args.append('%s:%s' % (name, QGL2.QBIT_LIST)) elif annotation.id == QGL2.PULSE: q_args.append('%s:%s' % (name, QGL2.PULSE)) elif annotation.id == QGL2.CONTROL: q_args.append('%s:%s' % (name, QGL2.CONTROL)) elif annotation.id == QGL2.SEQUENCE: q_args.append('%s:%s' % (name, QGL2.SEQUENCE)) else: NodeError.error_msg( node, ('unsupported parameter annotation [%s]' % annotation.id)) else: NodeError.error_msg( node, 'unsupported parameter annotation [%s]' % ast.dump(annotation)) # print('NN NAME %s (%s) -> %s' % # (node.name, str(q_args), str(q_return))) return (q_args, q_return)
def add_func_decorators(self, module_name, node): # print('NNN module_name %s ofname %s' % (module_name, self.base_fname)) qglmain = False qglfunc = False other_decorator = False qglstub = False # A stub for a QGL1 function; check args but do not inline qglstub_import = False qglmeas = False # A QGL measurement if node.decorator_list: for dec in node.decorator_list: # qglmain implies qglfunc, but it's permitted to # have both # if isinstance(dec, ast.Name) and (dec.id == QGL2.QMAIN): qglfunc = True qglmain = True elif isinstance(dec, ast.Name) and (dec.id == QGL2.QSTUB): # A stub for a QGL1 function; check args but do not inline qglfunc = True qglstub = True NodeError.warning_msg( node, ('old-style stub for [%s]: no import info' % node.name)) elif (isinstance(dec, ast.Call) and isinstance(dec.func, ast.Name) and dec.func.id == QGL2.QSTUB): qglfunc = True qglstub = True qglstub_import = self.find_stub_import(dec, node.name) elif (isinstance(dec, ast.Call) and isinstance(dec.func, ast.Name) and dec.func.id == QGL2.QMEAS): qglfunc = True qglstub = True qglmeas = True qglstub_import = self.find_stub_import(dec, node.name) elif isinstance(dec, ast.Name) and (dec.id == QGL2.QDECL): qglfunc = True else: other_decorator = True if qglmain and other_decorator: NodeError.warning_msg( node, 'unrecognized decorator with %s' % QGL2.QMAIN) elif qglfunc and other_decorator: NodeError.warning_msg( node, 'unrecognized decorator with %s' % QGL2.QDECL) node.qgl_func = qglfunc # A stub for a QGL1 function; check args but do not inline node.qgl_stub = qglstub node.qgl_meas = qglmeas node.qgl_main = qglmain node.qgl_stub_import = qglstub_import # Only assign the qglmain at the root of the namespace # if we're in the base file # if qglmain and (module_name == self.base_fname): if self.qglmain: omain = self.qglmain # This is not an error; optimized versions of # the qglmain can be added without error # NodeError.diag_msg(node, 'more than one %s function' % QGL2.QMAIN) NodeError.diag_msg( node, 'previously defined %s:%d:%d' % (omain.qgl_fname, omain.lineno, omain.col_offset)) else: NodeError.diag_msg( node, '%s declared as %s' % (node.name, QGL2.QMAIN)) self.qglmain = node