Example #1
0
    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)
Example #2
0
    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]
Example #3
0
    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)
Example #4
0
    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
Example #5
0
    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
Example #6
0
    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)
Example #7
0
    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
Example #8
0
    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)
Example #9
0
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
Example #10
0
    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)
Example #11
0
    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)
Example #12
0
    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)
Example #13
0
    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)
Example #14
0
    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
Example #15
0
    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
Example #16
0
    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)
Example #17
0
    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