Ejemplo n.º 1
0
    def apply(self, expr, evaluation):
        'Unset[expr_]'

        name = expr.get_head_name()
        if name in system_symbols('OwnValues', 'DownValues', 'SubValues',
                                  'UpValues', 'NValues', 'Options', 'Messages'):
            if len(expr.leaves) != 1:
                evaluation.message_args(name, len(expr.leaves), 1)
                return Symbol('$Failed')
            symbol = expr.leaves[0].get_name()
            if not symbol:
                evaluation.message(name, 'fnsym', expr)
                return Symbol('$Failed')
            if name == 'System`Options':
                empty = {}
            else:
                empty = []
            evaluation.definitions.set_values(symbol, name, empty)
            return Symbol('Null')
        name = expr.get_lookup_name()
        if not name:
            evaluation.message('Unset', 'usraw', expr)
            return Symbol('$Failed')
        if not evaluation.definitions.unset(name, expr):
            if not expr.is_atom():
                evaluation.message('Unset', 'norep', expr, Symbol(name))
                return Symbol('$Failed')
        return Symbol('Null')
Ejemplo n.º 2
0
    def apply(self, expr, evaluation):
        'Unset[expr_]'

        name = expr.get_head_name()
        if name in system_symbols('OwnValues', 'DownValues', 'SubValues',
                                  'UpValues', 'NValues', 'Options', 'Messages'):
            if len(expr.leaves) != 1:
                evaluation.message_args(name, len(expr.leaves), 1)
                return Symbol('$Failed')
            symbol = expr.leaves[0].get_name()
            if not symbol:
                evaluation.message(name, 'fnsym', expr)
                return Symbol('$Failed')
            if name == 'System`Options':
                empty = {}
            else:
                empty = []
            evaluation.definitions.set_values(symbol, name, empty)
            return Symbol('Null')
        name = expr.get_lookup_name()
        if not name:
            evaluation.message('Unset', 'usraw', expr)
            return Symbol('$Failed')
        if not evaluation.definitions.unset(name, expr):
            if not expr.is_atom():
                evaluation.message('Unset', 'norep', expr, Symbol(name))
                return Symbol('$Failed')
        return Symbol('Null')
Ejemplo n.º 3
0
    def assign_elementary(self, lhs, rhs, evaluation, tags=None, upset=False):
        name = lhs.get_head_name()

        if name in system_symbols('OwnValues', 'DownValues', 'SubValues',
                                  'UpValues', 'NValues', 'Options',
                                  'DefaultValues', 'Attributes', 'Messages'):
            if len(lhs.leaves) != 1:
                evaluation.message_args(name, len(lhs.leaves), 1)
                return False
            tag = lhs.leaves[0].get_name()
            if not tag:
                evaluation.message(name, 'sym', lhs.leaves[0], 1)
                return False
            if tags is not None and tags != [tag]:
                evaluation.message(name, 'tag', Symbol(name), Symbol(tag))
                return False

            if (name != 'System`Attributes' and 'System`Protected'    # noqa
                in evaluation.definitions.get_attributes(tag)):
                evaluation.message(name, 'wrsym', Symbol(tag))
                return False
            if name == 'System`Options':
                option_values = rhs.get_option_values(evaluation)
                if option_values is None:
                    evaluation.message(name, 'options', rhs)
                    return False
                evaluation.definitions.set_options(tag, option_values)
            elif name == 'System`Attributes':
                attributes = get_symbol_list(
                    rhs, lambda item: evaluation.message(name, 'sym', item, 1))
                if attributes is None:
                    return False
                if 'System`Locked' in evaluation.definitions.get_attributes(tag):
                    evaluation.message(name, 'locked', Symbol(tag))
                    return False
                evaluation.definitions.set_attributes(tag, attributes)
            else:
                rules = rhs.get_rules_list()
                if rules is None:
                    evaluation.message(name, 'vrule', lhs, rhs)
                    return False
                evaluation.definitions.set_values(tag, name, rules)
            return True

        form = ''
        nprec = None
        default = False
        message = False

        allow_custom_tag = False

        focus = lhs

        if name == 'System`N':
            if len(lhs.leaves) not in (1, 2):
                evaluation.message_args('N', len(lhs.leaves), 1, 2)
                return False
            if len(lhs.leaves) == 1:
                nprec = Symbol('MachinePrecision')
            else:
                nprec = lhs.leaves[1]
            focus = lhs.leaves[0]
            lhs = Expression('N', focus, nprec)
        elif name == 'System`MessageName':
            if len(lhs.leaves) != 2:
                evaluation.message_args('MessageName', len(lhs.leaves), 2)
                return False
            focus = lhs.leaves[0]
            message = True
        elif name == 'System`Default':
            if len(lhs.leaves) not in (1, 2, 3):
                evaluation.message_args('Default', len(lhs.leaves), 1, 2, 3)
                return False
            focus = lhs.leaves[0]
            default = True
        elif name == 'System`Format':
            if len(lhs.leaves) not in (1, 2):
                evaluation.message_args('Format', len(lhs.leaves), 1, 2)
                return False
            if len(lhs.leaves) == 2:
                form = lhs.leaves[1].get_name()
                if not form:
                    evaluation.message('Format', 'fttp', lhs.leaves[1])
                    return False
            else:
                form = system_symbols(
                    'StandardForm', 'TraditionalForm', 'OutputForm',
                    'TeXForm', 'MathMLForm')
            lhs = focus = lhs.leaves[0]
        else:
            allow_custom_tag = True

        focus = focus.evaluate_leaves(evaluation)

        if tags is None and not upset:
            name = focus.get_lookup_name()
            if not name:
                evaluation.message(self.get_name(), 'setraw', focus)
                return False
            tags = [name]
        elif upset:
            if allow_custom_tag:
                tags = []
                if focus.is_atom():
                    evaluation.message(self.get_name(), 'normal')
                    return False
                for leaf in focus.leaves:
                    name = leaf.get_lookup_name()
                    tags.append(name)
            else:
                tags = [focus.get_lookup_name()]
        else:
            allowed_names = [focus.get_lookup_name()]
            if allow_custom_tag:
                for leaf in focus.get_leaves():
                    allowed_names.append(leaf.get_lookup_name())
            for name in tags:
                if name not in allowed_names:
                    evaluation.message(self.get_name(), 'tagnfd', Symbol(name))
                    return False

        ignore_protection = False
        rhs_int_value = rhs.get_int_value()
        lhs_name = lhs.get_name()
        if lhs_name == 'System`$RecursionLimit':
            # if (not rhs_int_value or rhs_int_value < 20) and not
            # rhs.get_name() == 'System`Infinity':
            if (not rhs_int_value or rhs_int_value < 20 or
                rhs_int_value > settings.MAX_RECURSION_DEPTH):  # nopep8

                evaluation.message('$RecursionLimit', 'limset', rhs)
                return False
            try:
                set_recursionlimit(rhs_int_value)
            except OverflowError:
                # TODO: Message
                return False
            ignore_protection = True
        elif lhs_name == 'System`$ModuleNumber':
            if not rhs_int_value or rhs_int_value <= 0:
                evaluation.message('$ModuleNumber', 'set', rhs)
                return False
            ignore_protection = True
        elif lhs_name in ('System`$Line', 'System`$HistoryLength'):
            if rhs_int_value is None or rhs_int_value < 0:
                evaluation.message(lhs_name, 'intnn', rhs)
                return False
            ignore_protection = True
        elif lhs_name == 'System`$RandomState':
            # TODO: allow setting of legal random states!
            # (but consider pickle's insecurity!)
            evaluation.message('$RandomState', 'rndst', rhs)
            return False
        elif lhs_name == 'System`$Context':
            new_context = rhs.get_string_value()
            if new_context is None or not valid_context_name(
                    new_context, allow_initial_backquote=True):
                evaluation.message(lhs_name, 'cxset', rhs)
                return False

            # With $Context in Mathematica you can do some strange
            # things: e.g. with $Context set to Global`, something
            # like:
            #    $Context = "`test`"; newsym
            # is accepted and creates Global`test`newsym.
            # Implement this behaviour by interpreting
            #    $Context = "`test`"
            # as
            #    $Context = $Context <> "test`"
            #
            if new_context.startswith('`'):
                new_context = (evaluation.definitions.get_current_context() +
                               new_context.lstrip('`'))

            evaluation.definitions.set_current_context(new_context)
            ignore_protection = True
            return True
        elif lhs_name == 'System`$ContextPath':
            context_path = [s.get_string_value() for s in rhs.get_leaves()]
            if rhs.has_form('List', None) and all(valid_context_name(s) for s in context_path):
                evaluation.definitions.set_context_path(context_path)
                ignore_protection = True
                return True
            else:
                evaluation.message(lhs_name, 'cxlist', rhs)
                return False
        elif lhs_name == 'System`$MinPrecision':
            # $MinPrecision = Infinity is not allowed
            if rhs_int_value is not None and rhs_int_value >= 0:
                ignore_protection = True
                max_prec = evaluation.definitions.get_config_value('$MaxPrecision')
                if max_prec is not None and max_prec < rhs_int_value:
                    evaluation.message('$MinPrecision', 'preccon', Symbol('$MinPrecision'))
                    return True
            else:
                evaluation.message(lhs_name, 'precset', lhs, rhs)
                return False
        elif lhs_name == 'System`$MaxPrecision':
            if rhs.has_form('DirectedInfinity', 1) and rhs.leaves[0].get_int_value() == 1:
                ignore_protection = True
            elif rhs_int_value is not None and rhs_int_value > 0:
                ignore_protection = True
                min_prec = evaluation.definitions.get_config_value('$MinPrecision')
                if min_prec is not None and rhs_int_value < min_prec:
                    evaluation.message('$MaxPrecision', 'preccon', Symbol('$MaxPrecision'))
                    ignore_protection = True
                    return True
            else:
                evaluation.message(lhs_name, 'precset', lhs, rhs)
                return False

        rhs_name = rhs.get_head_name()
        if rhs_name == 'System`Condition':
            if len(rhs.leaves) != 2:
                evaluation.message_args('Condition', len(rhs.leaves), 2)
                return False
            else:
                lhs = Expression('Condition', lhs, rhs.leaves[1])
                rhs = rhs.leaves[0]

        rule = Rule(lhs, rhs)
        count = 0
        defs = evaluation.definitions
        for tag in tags:
            if (not ignore_protection and 'System`Protected'   # noqa
                in evaluation.definitions.get_attributes(tag)):
                if lhs.get_name() == tag:
                    evaluation.message(self.get_name(), 'wrsym', Symbol(tag))
                else:
                    evaluation.message(self.get_name(), 'write', Symbol(tag), lhs)
                continue
            count += 1
            if form:
                defs.add_format(tag, rule, form)
            elif nprec:
                defs.add_nvalue(tag, rule)
            elif default:
                defs.add_default(tag, rule)
            elif message:
                defs.add_message(tag, rule)
            else:
                if upset:
                    defs.add_rule(tag, rule, position='up')
                else:
                    defs.add_rule(tag, rule)
        if count == 0:
            return False

        return True
Ejemplo n.º 4
0
    def assign_elementary(self, lhs, rhs, evaluation, tags=None, upset=False):
        name = lhs.get_head_name()

        if name in system_symbols('OwnValues', 'DownValues', 'SubValues',
                                  'UpValues', 'NValues', 'Options',
                                  'DefaultValues', 'Attributes', 'Messages'):
            if len(lhs.leaves) != 1:
                evaluation.message_args(name, len(lhs.leaves), 1)
                return False
            tag = lhs.leaves[0].get_name()
            if not tag:
                evaluation.message(name, 'sym', lhs.leaves[0], 1)
                return False
            if tags is not None and tags != [tag]:
                evaluation.message(name, 'tag', Symbol(name), Symbol(tag))
                return False

            if (name != 'System`Attributes' and 'System`Protected'    # noqa
                in evaluation.definitions.get_attributes(tag)):
                evaluation.message(name, 'wrsym', Symbol(tag))
                return False
            if name == 'System`Options':
                option_values = rhs.get_option_values(evaluation)
                if option_values is None:
                    evaluation.message(name, 'options', rhs)
                    return False
                evaluation.definitions.set_options(tag, option_values)
            elif name == 'System`Attributes':
                attributes = get_symbol_list(
                    rhs, lambda item: evaluation.message(name, 'sym', item, 1))
                if attributes is None:
                    return False
                if 'System`Locked' in evaluation.definitions.get_attributes(tag):
                    evaluation.message(name, 'locked', Symbol(tag))
                    return False
                evaluation.definitions.set_attributes(tag, attributes)
            else:
                rules = rhs.get_rules_list()
                if rules is None:
                    evaluation.message(name, 'vrule', lhs, rhs)
                    return False
                evaluation.definitions.set_values(tag, name, rules)
            return True

        form = ''
        nprec = None
        default = False
        message = False

        allow_custom_tag = False

        focus = lhs

        if name == 'System`N':
            if len(lhs.leaves) not in (1, 2):
                evaluation.message_args('N', len(lhs.leaves), 1, 2)
                return False
            if len(lhs.leaves) == 1:
                nprec = Symbol('MachinePrecision')
            else:
                nprec = lhs.leaves[1]
            focus = lhs.leaves[0]
            lhs = Expression('N', focus, nprec)
        elif name == 'System`MessageName':
            if len(lhs.leaves) != 2:
                evaluation.message_args('MessageName', len(lhs.leaves), 2)
                return False
            focus = lhs.leaves[0]
            message = True
        elif name == 'System`Default':
            if len(lhs.leaves) not in (1, 2, 3):
                evaluation.message_args('Default', len(lhs.leaves), 1, 2, 3)
                return False
            focus = lhs.leaves[0]
            default = True
        elif name == 'System`Format':
            if len(lhs.leaves) not in (1, 2):
                evaluation.message_args('Format', len(lhs.leaves), 1, 2)
                return False
            if len(lhs.leaves) == 2:
                form = lhs.leaves[1].get_name()
                if not form:
                    evaluation.message('Format', 'fttp', lhs.leaves[1])
                    return False
            else:
                form = system_symbols(
                    'StandardForm', 'TraditionalForm', 'OutputForm',
                    'TeXForm', 'MathMLForm')
            lhs = focus = lhs.leaves[0]
        else:
            allow_custom_tag = True

        focus = focus.evaluate_leaves(evaluation)

        if tags is None and not upset:
            name = focus.get_lookup_name()
            if not name:
                evaluation.message(self.get_name(), 'setraw', focus)
                return False
            tags = [name]
        elif upset:
            if allow_custom_tag:
                tags = []
                if focus.is_atom():
                    evaluation.message(self.get_name(), 'normal')
                    return False
                for leaf in focus.leaves:
                    name = leaf.get_lookup_name()
                    tags.append(name)
            else:
                tags = [focus.get_lookup_name()]
        else:
            allowed_names = [focus.get_lookup_name()]
            if allow_custom_tag:
                for leaf in focus.get_leaves():
                    allowed_names.append(leaf.get_lookup_name())
            for name in tags:
                if name not in allowed_names:
                    evaluation.message(self.get_name(), 'tagnfd', Symbol(name))
                    return False

        ignore_protection = False
        rhs_int_value = rhs.get_int_value()
        lhs_name = lhs.get_name()
        if lhs_name == 'System`$RecursionLimit':
            # if (not rhs_int_value or rhs_int_value < 20) and not
            # rhs.get_name() == 'System`Infinity':
            if (not rhs_int_value or rhs_int_value < 20 or
                rhs_int_value > settings.MAX_RECURSION_DEPTH):  # nopep8

                evaluation.message('$RecursionLimit', 'limset', rhs)
                return False
            try:
                set_recursionlimit(rhs_int_value)
            except OverflowError:
                # TODO: Message
                return False
            ignore_protection = True
        elif lhs_name == 'System`$ModuleNumber':
            if not rhs_int_value or rhs_int_value <= 0:
                evaluation.message('$ModuleNumber', 'set', rhs)
                return False
            ignore_protection = True
        elif lhs_name in ('System`$Line', 'System`$HistoryLength'):
            if rhs_int_value is None or rhs_int_value < 0:
                evaluation.message(lhs_name, 'intnn', rhs)
                return False
            ignore_protection = True
        elif lhs_name == 'System`$RandomState':
            # TODO: allow setting of legal random states!
            # (but consider pickle's insecurity!)
            evaluation.message('$RandomState', 'rndst', rhs)
            return False
        elif lhs_name == 'System`$Context':
            new_context = rhs.get_string_value()
            if new_context is None or not valid_context_name(
                    new_context, allow_initial_backquote=True):
                evaluation.message(lhs_name, 'cxset', rhs)
                return False

            # With $Context in Mathematica you can do some strange
            # things: e.g. with $Context set to Global`, something
            # like:
            #    $Context = "`test`"; newsym
            # is accepted and creates Global`test`newsym.
            # Implement this behaviour by interpreting
            #    $Context = "`test`"
            # as
            #    $Context = $Context <> "test`"
            #
            if new_context.startswith('`'):
                new_context = (evaluation.definitions.get_current_context() +
                               new_context.lstrip('`'))

            evaluation.definitions.set_current_context(new_context)
            ignore_protection = True
            return True
        elif lhs_name == 'System`$ContextPath':
            context_path = [s.get_string_value() for s in rhs.get_leaves()]
            if rhs.has_form('List', None) and all(valid_context_name(s) for s in context_path):
                evaluation.definitions.set_context_path(context_path)
                ignore_protection = True
                return True
            else:
                evaluation.message(lhs_name, 'cxlist', rhs)
                return False
        elif lhs_name == 'System`$MinPrecision':
            # $MinPrecision = Infinity is not allowed
            if rhs_int_value is not None and rhs_int_value >= 0:
                ignore_protection = True
                max_prec = evaluation.definitions.get_config_value('$MaxPrecision')
                if max_prec is not None and max_prec < rhs_int_value:
                    evaluation.message('$MinPrecision', 'preccon', Symbol('$MinPrecision'))
                    return True
            else:
                evaluation.message(lhs_name, 'precset', lhs, rhs)
                return False
        elif lhs_name == 'System`$MaxPrecision':
            if rhs.has_form('DirectedInfinity', 1) and rhs.leaves[0].get_int_value() == 1:
                ignore_protection = True
            elif rhs_int_value is not None and rhs_int_value > 0:
                ignore_protection = True
                min_prec = evaluation.definitions.get_config_value('$MinPrecision')
                if min_prec is not None and rhs_int_value < min_prec:
                    evaluation.message('$MaxPrecision', 'preccon', Symbol('$MaxPrecision'))
                    ignore_protection = True
                    return True
            else:
                evaluation.message(lhs_name, 'precset', lhs, rhs)
                return False

        rhs_name = rhs.get_head_name()
        if rhs_name == 'System`Condition':
            if len(rhs.leaves) != 2:
                evaluation.message_args('Condition', len(rhs.leaves), 2)
                return False
            else:
                lhs = Expression('Condition', lhs, rhs.leaves[1])
                rhs = rhs.leaves[0]

        rule = Rule(lhs, rhs)
        count = 0
        defs = evaluation.definitions
        for tag in tags:
            if (not ignore_protection and 'System`Protected'   # noqa
                in evaluation.definitions.get_attributes(tag)):
                if lhs.get_name() == tag:
                    evaluation.message(self.get_name(), 'wrsym', Symbol(tag))
                else:
                    evaluation.message(self.get_name(), 'write', Symbol(tag), lhs)
                continue
            count += 1
            if form:
                defs.add_format(tag, rule, form)
            elif nprec:
                defs.add_nvalue(tag, rule)
            elif default:
                defs.add_default(tag, rule)
            elif message:
                defs.add_message(tag, rule)
            else:
                if upset:
                    defs.add_rule(tag, rule, position='up')
                else:
                    defs.add_rule(tag, rule)
        if count == 0:
            return False

        return True
Ejemplo n.º 5
0
    <dt>'ImageSize' -> 'Large'
        <dd>produces a large image.
    </dl>
    """


element_heads = frozenset(
    system_symbols(
        "Arrow",
        "BezierCurve",
        "Circle",
        "Cylinder",
        "Disk",
        "FilledCurve",
        "Inset",
        "Line",
        "Point",
        "Polygon",
        "Rectangle",
        "RegularPolygon",
        "Sphere",
        "Style",
        "Text",
    )
)

styles = system_symbols_dict(
    {
        "RGBColor": RGBColor,
        "XYZColor": XYZColor,
        "LABColor": LABColor,
Ejemplo n.º 6
0
    def match_leaf(self, yield_func, leaf, rest_leaves, rest_expression, vars,
                   expression, attributes, evaluation, leaf_index=1,
                   leaf_count=None, first=False, fully=True, depth=1,
                   wrap_oneid=True):

        if rest_expression is None:
            rest_expression = ([], [])

        evaluation.check_stopped()

        match_count = leaf.get_match_count(vars)
        leaf_candidates = leaf.get_match_candidates(
            rest_expression[1],  # leaf.candidates,
            expression, attributes, evaluation, vars)

        if len(leaf_candidates) < match_count[0]:
            return

        # STRANGE: candidate in leaf_candidates causes BusError for
        # Real ^ Integer (e.g. 2.0^3) when not converted to a set!
        leaf_candidates = set(leaf_candidates)

        candidates = rest_expression[1]

        # "Artificially" only use more leaves than specified for some kind
        # of pattern.
        # TODO: This could be further optimized!
        try_flattened = (
            ('System`Flat' in attributes) and (leaf.get_head_name() in (
                    system_symbols(
                        'Pattern', 'PatternTest', 'Condition', 'Optional',
                        'Blank', 'BlankSequence', 'BlankNullSequence',
                        'Alternatives', 'OptionsPattern', 'Repeated',
                        'RepeatedNull'))))

        if try_flattened:
            set_lengths = (match_count[0], None)
        else:
            set_lengths = match_count

        # try_flattened is used later to decide whether wrapping of leaves
        # into one operand may occur.
        # This can of course also be when flat and same head.
        try_flattened = try_flattened or ((
            'System`Flat' in attributes) and leaf.get_head() == expression.head)

        less_first = len(rest_leaves) > 0

        if 'System`Orderless' in attributes:
            sets = None
            if leaf.get_head_name() == 'System`Pattern':
                varname = leaf.leaves[0].get_name()
                existing = vars.get(varname, None)
                if existing is not None:
                    head = existing.get_head()
                    if (head.get_name() == 'System`Sequence' or (
                            'System`Flat' in attributes and
                            head == expression.get_head())):
                        needed = existing.leaves
                    else:
                        needed = [existing]
                    available = candidates[:]
                    for needed_leaf in needed:
                        if (needed_leaf in available and        # nopep8
                            needed_leaf in leaf_candidates):
                            available.remove(needed_leaf)
                        else:
                            return
                    sets = [(needed, ([], available))]

            if sets is None:
                sets = subsets(candidates, included=leaf_candidates,
                               less_first=less_first, *set_lengths)
        else:
            sets = subranges(candidates, flexible_start=first and not fully,
                             included=leaf_candidates, less_first=less_first,
                             *set_lengths)

        # print "Match %s in %s" % (leaf, expression)

        if rest_leaves:
            next_leaf = rest_leaves[0]
            next_rest_leaves = rest_leaves[1:]
        next_depth = depth + 1
        next_index = leaf_index + 1

        for items, items_rest in sets:
            # try:
            # print (u"  " + u", ".join(unicode(item)
            #        for item in items)).encode('utf-8')
            # except
            # Include wrappings like Plus[a, b] only if not all items taken
            # - in that case we would match the same expression over and over.

            include_flattened = (try_flattened and
                                 0 < len(items) < len(expression.leaves))

            # Don't try flattened when the expression would remain the same!

            def leaf_yield(next_vars, next_rest):
                # if next_rest is None:
                #    next_rest = ([], [])
                # yield_func(next_vars, (rest_expression[0] + items_rest[0],
                # next_rest[1]))
                if next_rest is None:
                    yield_func(
                        next_vars,
                        (rest_expression[0] + items_rest[0], []))
                else:
                    yield_func(
                        next_vars,
                        (rest_expression[0] + items_rest[0], next_rest[1]))

            def match_yield(new_vars, _):
                if rest_leaves:
                    self.match_leaf(
                        leaf_yield, next_leaf, next_rest_leaves, items_rest,
                        new_vars, expression, attributes, evaluation,
                        fully=fully, depth=next_depth, leaf_index=next_index,
                        leaf_count=leaf_count, wrap_oneid=wrap_oneid)
                else:
                    if not fully or (not items_rest[0] and not items_rest[1]):
                        yield_func(new_vars, items_rest)

            def yield_wrapping(item):
                leaf.match(match_yield, item, vars, evaluation, fully=True,
                           head=expression.head, leaf_index=leaf_index,
                           leaf_count=leaf_count, wrap_oneid=wrap_oneid)

            self.get_wrappings(
                yield_wrapping, items, match_count[1], expression, attributes,
                include_flattened=include_flattened)
Ejemplo n.º 7
0
    def match_leaf(self,
                   yield_func,
                   leaf,
                   rest_leaves,
                   rest_expression,
                   vars,
                   expression,
                   attributes,
                   evaluation,
                   leaf_index=1,
                   leaf_count=None,
                   first=False,
                   fully=True,
                   depth=1,
                   wrap_oneid=True):

        if rest_expression is None:
            rest_expression = ([], [])

        evaluation.check_stopped()

        match_count = leaf.get_match_count(vars)
        leaf_candidates = leaf.get_match_candidates(
            rest_expression[1],  # leaf.candidates,
            expression,
            attributes,
            evaluation,
            vars)

        if len(leaf_candidates) < match_count[0]:
            return

        candidates = rest_expression[1]

        # "Artificially" only use more leaves than specified for some kind
        # of pattern.
        # TODO: This could be further optimized!
        try_flattened = (('System`Flat' in attributes)
                         and (leaf.get_head_name() in (system_symbols(
                             'Pattern', 'PatternTest', 'Condition', 'Optional',
                             'Blank', 'BlankSequence', 'BlankNullSequence',
                             'Alternatives', 'OptionsPattern', 'Repeated',
                             'RepeatedNull'))))

        if try_flattened:
            set_lengths = (match_count[0], None)
        else:
            set_lengths = match_count

        # try_flattened is used later to decide whether wrapping of leaves
        # into one operand may occur.
        # This can of course also be when flat and same head.
        try_flattened = try_flattened or (('System`Flat' in attributes) and
                                          leaf.get_head() == expression.head)

        less_first = len(rest_leaves) > 0

        if 'System`Orderless' in attributes:
            # we only want leaf_candidates to be a set if we're orderless.
            # otherwise, constructing a set() is very slow for large lists.
            # performance test case:
            # x = Range[100000]; Timing[Combinatorica`BinarySearch[x, 100]]
            leaf_candidates = set(leaf_candidates)  # for fast lookup

            sets = None
            if leaf.get_head_name() == 'System`Pattern':
                varname = leaf.leaves[0].get_name()
                existing = vars.get(varname, None)
                if existing is not None:
                    head = existing.get_head()
                    if (head.get_name() == 'System`Sequence'
                            or ('System`Flat' in attributes
                                and head == expression.get_head())):
                        needed = existing.leaves
                    else:
                        needed = [existing]
                    available = candidates[:]
                    for needed_leaf in needed:
                        if (needed_leaf in available and  # nopep8
                                needed_leaf in leaf_candidates):
                            available.remove(needed_leaf)
                        else:
                            return
                    sets = [(needed, ([], available))]

            if sets is None:
                sets = subsets(candidates,
                               included=leaf_candidates,
                               less_first=less_first,
                               *set_lengths)
        else:
            sets = subranges(candidates,
                             flexible_start=first and not fully,
                             included=leaf_candidates,
                             less_first=less_first,
                             *set_lengths)

        if rest_leaves:
            next_leaf = rest_leaves[0]
            next_rest_leaves = rest_leaves[1:]
        next_depth = depth + 1
        next_index = leaf_index + 1

        for items, items_rest in sets:
            # Include wrappings like Plus[a, b] only if not all items taken
            # - in that case we would match the same expression over and over.

            include_flattened = (try_flattened
                                 and 0 < len(items) < len(expression.leaves))

            # Don't try flattened when the expression would remain the same!

            def leaf_yield(next_vars, next_rest):
                # if next_rest is None:
                #    next_rest = ([], [])
                # yield_func(next_vars, (rest_expression[0] + items_rest[0],
                # next_rest[1]))
                if next_rest is None:
                    yield_func(next_vars,
                               (rest_expression[0] + items_rest[0], []))
                else:
                    yield_func(
                        next_vars,
                        (rest_expression[0] + items_rest[0], next_rest[1]))

            def match_yield(new_vars, _):
                if rest_leaves:
                    self.match_leaf(leaf_yield,
                                    next_leaf,
                                    next_rest_leaves,
                                    items_rest,
                                    new_vars,
                                    expression,
                                    attributes,
                                    evaluation,
                                    fully=fully,
                                    depth=next_depth,
                                    leaf_index=next_index,
                                    leaf_count=leaf_count,
                                    wrap_oneid=wrap_oneid)
                else:
                    if not fully or (not items_rest[0] and not items_rest[1]):
                        yield_func(new_vars, items_rest)

            def yield_wrapping(item):
                leaf.match(match_yield,
                           item,
                           vars,
                           evaluation,
                           fully=True,
                           head=expression.head,
                           leaf_index=leaf_index,
                           leaf_count=leaf_count,
                           wrap_oneid=wrap_oneid)

            self.get_wrappings(yield_wrapping,
                               items,
                               match_count[1],
                               expression,
                               attributes,
                               include_flattened=include_flattened)