def preprocess_var_decls(tree):
    """Eliminate global variable declarations of the form
    
        S = Set()
        M = Map()
    
    and return a list of pairs of variables names and type names (i.e.,
    'Set' or 'Map').
    """
    assert isinstance(tree, P.Module)
    pat = P.Assign([P.Name(P.PatVar('_VAR'), P.Wildcard())],
                   P.Call(P.Name(P.PatVar('_KIND'), P.Load()), [], [], None,
                          None))

    decls = OrderedSet()
    body = []
    changed = False
    for stmt in tree.body:
        match = P.match(pat, stmt)
        if match is not None:
            var, kind = match['_VAR'], match['_KIND']
            if kind not in ['Set', 'Map']:
                raise L.ProgramError(
                    'Unknown declaration initializer {}'.format(kind))
            decls.add((var, kind))
            changed = True
        else:
            body.append(stmt)

    if changed:
        tree = tree._replace(body=body)
    return tree, list(decls)
    def visit_Attribute(self, node):
        # Note: As written, this will remove all aliases, not just the
        # prefix alias. That is, if A is an alias for the runtime in
        # quals, this will rewrite A.A...A.foo as foo. This shouldn't
        # be a problem because we shouldn't see aliases chained in this
        # manner.

        node = self.generic_visit(node)

        # Check prefix against each alias and the fully qualified path.
        # If none match, no change.
        for qual in self.quals:
            pat = P.Attribute(qual, P.PatVar('_ATTR'), P.Load())
            match = P.match(pat, node)
            if match is not None:
                break
        else:
            return node

        return P.Name(match['_ATTR'], P.Load())
    def visit_Import(self, node):
        pat = P.Import([P.alias('incoq.runtime', P.PatVar('_ALIAS'))])
        match = P.match(pat, node)
        if match is None:
            return node

        # Remove the import. Record the alias to remove its uses.
        if match['_ALIAS'] is not None:
            qual = P.Name(match['_ALIAS'], P.Load())
            self.quals.add(qual)
        return []
class QueryDirectiveRewriter(P.NodeTransformer):
    """Take QUERY(<source>, **<kargs>) directives and replace the
    first argument with the corresponding parsed Python AST.
    """

    # We'll use NodeTransformer rather than MacroProcessor because
    # MacroProcessor makes it hard to reconstruct the node.

    pat = P.Expr(
        P.Call(P.Name('QUERY', P.Load()), [P.Str(P.PatVar('_source'))],
               P.PatVar('_keywords'), None, None))

    def visit_Expr(self, node):
        match = P.match(self.pat, node)
        if match is not None:
            query = P.Parser.pe(node.value.args[0].s)
            call = node.value._replace(args=[query])
            node = node._replace(value=call)
        return node