def match(self, expr) -> MatchDict:
        """Match the given expression (recursively)

        Returns a :class:`MatchDict` instance that maps any wildcard names to
        the expressions that the corresponding wildcard pattern matches. For
        (sub-)pattern that have a `mode` attribute other than `Pattern.single`,
        the wildcard name is mapped to a list of all matched expression.

        If the match is successful, the resulting :class:`MatchDict` instance
        will evaluate to True in a boolean context. If the match is not
        successful, it will evaluate as False, and the reason for failure is
        available in the `reason` attribute of the :class:`MatchDict` object.
        """
        res = MatchDict()
        if self._has_non_single_arg:
            if self._non_single_arg_on_left:
                res.merge_lists = 1
            else:
                res.merge_lists = -1
        if self.head is not None:
            if not isinstance(expr, self.head):
                res.reason = ("%s is not an instance of %s"
                              % (repr(expr), self._repr_head()))
                res.success = False
                return res
        for i_cond, condition in enumerate(self.conditions):
            if not condition(expr):
                res.reason = ("%s does not meet condition %d"
                              % (repr(expr), i_cond+1))
                res.success = False
                return res
        try:
            if self.args is not None:
                arg_pattern = self.extended_arg_patterns()
                for arg in self._arg_iterator(expr.args):
                    current_arg_pattern = next(arg_pattern)
                    res.update(match_pattern(current_arg_pattern, arg))
                    if not res.success:
                        return res
                # ensure that we have matched all arg patterns
                try:
                    last_arg_pattern = next(arg_pattern)
                    res.update(self._check_last_arg_pattern(
                                    current_arg_pattern, last_arg_pattern))
                except StopIteration:
                    pass  # expected, if current_arg_pattern was the last one
            if self.kwargs is not None:
                for key, arg_pattern in self.kwargs.items():
                    res.update(match_pattern(arg_pattern, expr.kwargs[key]))
                    if not res.success:
                        return res
        except AttributeError as exc_info:
            res.reason = ("%s is a scalar, not an Expression: %s"
                          % (repr(expr), str(exc_info)))
            res.success = False
        except ValueError as exc_info:
            res.reason = "%s: %s" % (repr(expr), str(exc_info))
            res.success = False
        except StopIteration:
            res.reason = ("%s has an too many positional arguments"
                          % repr(expr))
            res.success = False
        except KeyError as exc_info:
            if "has already been set" in str(exc_info):
                res.reason = "Double wildcard: %s" % str(exc_info)
            else:
                res.reason = ("%s has no keyword argument %s"
                              % (repr(expr), str(exc_info)))
            res.success = False
        if res.success:
            if self.wc_name is not None:
                try:
                    if self.mode > self.single:
                        res[self.wc_name] = [expr, ]
                    else:
                        res[self.wc_name] = expr
                except KeyError as exc_info:
                    res.reason = "Double wildcard: %s" % str(exc_info)
                    res.success = False
        return res