<commit_before>from tinycss2 import (
    parse_stylesheet, parse_declaration_list, parse_one_component_value,
    ast, nth, color3
)
from bericht.node.style import Style

__all__ = ('CSS',)


class Combinator:

    DESCENDANT = 1          # E F    an F element descendant of an E element
    CHILD = 2               # E > F  an F element child of an E element
    NEXT_SIBLING = 3        # E + F  an F element immediately preceded by an E element
    SUBSEQUENT_SIBLING = 4  # E ~ F  an F element preceded by an E element

    __slots__ = ('type', 'simple_selectors')

    def __init__(self):
        self.type = None
        self.simple_selectors = []

    @staticmethod
    def select_tag(tag):
        return lambda node: tag == node.tag

    @staticmethod
    def select_id(node_id):
        return lambda node: node_id == node.id

    @staticmethod
    def select_class(name):
        return lambda node: name in node.classes

    @staticmethod
    def select_position(a_b):
        a, b = a_b

        def position_test(node):
            if a == 0:  # nth-child(0n+b)
                return node.position == b
            else:  # nth-child(an+b)
                return node.position >= b and (node.position-b) % a == 0

        return position_test

    @staticmethod
    def select_last_child(last_child):
        return lambda node: last_child == node.last_child

    def add(self, selector, value, negate=False):
        matcher = selector(value)
        if negate:
            self.simple_selectors.append(lambda node: not matcher(node))
        else:
            self.simple_selectors.append(matcher)

    def matches(self, node):
        for simple_selector in self.simple_selectors:
            if not simple_selector(node):
                return False
        return True


class Selector:

    __slots__ = ('combinators',)

    def __init__(self, combinators):
        self.combinators = combinators

    def matches(self, node):
        for combinator in reversed(self.combinators):
            if not combinator.matches(node):
                return False
            node = node.parent
        return True


class Declarations:

    def __init__(self, content):
        self.content = content
        self.attrs = {}
        for declaration in parse_declaration_list(content, skip_comments=True, skip_whitespace=True):
            if isinstance(declaration, ast.AtRule):
                if declaration.at_keyword == 'bottom-right':
                    at_dec = Declarations(declaration.content)
                    self.attrs['page_bottom_right_content'] = at_dec.attrs['content']
                else:
                    raise NotImplementedError
            else:
                value_component = parse_one_component_value(declaration.value, skip_comments=True)
                if 'color' in declaration.name:
                    self.attrs[declaration.name.replace('-', '_')] = color3.parse_color(value_component)
                elif 'content' == declaration.name:
                    self.attrs[declaration.name.replace('-', '_')] = parse_content_value(declaration.value)
                else:<commit_msg>+ dimensions support<commit_after>from tinycss2 import (
    parse_stylesheet, parse_declaration_list, parse_one_component_value,
    ast, nth, color3
)
from bericht.node.style import Style

__all__ = ('CSS',)

inch = 72.0
cm = inch / 2.54
mm = cm * 0.1

units = {
    'px': 1,
    'pt': 1,
    'mm': mm,
    'cm': cm,
    'inch': inch
}


def convert(unit, value):
    if unit in units:
        return value * units[unit]
    raise NotImplementedError(
        'Dimension unit "{}" is not implemented yet.'.format(value)
    )


class Combinator:

    DESCENDANT = 1          # E F    an F element descendant of an E element
    CHILD = 2               # E > F  an F element child of an E element
    NEXT_SIBLING = 3        # E + F  an F element immediately preceded by an E element
    SUBSEQUENT_SIBLING = 4  # E ~ F  an F element preceded by an E element

    __slots__ = ('type', 'simple_selectors')

    def __init__(self):
        self.type = None
        self.simple_selectors = []

    @staticmethod
    def select_tag(tag):
        return lambda node: tag == node.tag

    @staticmethod
    def select_id(node_id):
        return lambda node: node_id == node.id

    @staticmethod
    def select_class(name):
        return lambda node: name in node.classes

    @staticmethod
    def select_position(a_b):
        a, b = a_b

        def position_test(node):
            if a == 0:  # nth-child(0n+b)
                return node.position == b
            else:  # nth-child(an+b)
                return node.position >= b and (node.position-b) % a == 0

        return position_test

    @staticmethod
    def select_last_child(last_child):
        return lambda node: last_child == node.last_child

    def add(self, selector, value, negate=False):
        matcher = selector(value)
        if negate:
            self.simple_selectors.append(lambda node: not matcher(node))
        else:
            self.simple_selectors.append(matcher)

    def matches(self, node):
        for simple_selector in self.simple_selectors:
            if not simple_selector(node):
                return False
        return True


class Selector:

    __slots__ = ('combinators',)

    def __init__(self, combinators):
        self.combinators = combinators

    def matches(self, node):
        for combinator in reversed(self.combinators):
            if not combinator.matches(node):
                return False
            node = node.parent
        return True


class Declarations:

    def __init__(self, content):
        self.content = content
        self.attrs = {}
        for declaration in parse_declaration_list(content, skip_comments=True, skip_whitespace=True):
            if isinstance(declaration, ast.AtRule):
                if declaration.at_keyword == 'bottom-right':
                    at_dec = Declarations(declaration.content)
                    self.attrs['page_bottom_right_content'] = at_dec.attrs['content']
                else:
                    raise NotImplementedError
            else:
                value_component = parse_one_component_value(declaration.value, skip_comments=True)
                if 'color' in declaration.name:
                    self.attrs[declaration.name.replace('-', '_')] = color3.parse_color(value_component)
                elif 'content' == declaration.name:
                    self.attrs[declaration.name.replace('-', '_')] = parse_content_value(declaration.value)
                elif isinstance(value_component, ast.DimensionToken):
                    self.attrs[declaration.name.replace('-', '_')] = convert(
                        value_component.lower_unit, value_component.value
                    )
                else:
{'fraction_non_alphanumeric': 0.08995173321632295, 'fraction_numerical': 0.004387889425186486, 'mean_word_length': 2.088527551942186, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 7, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1312724', 'n_tokens_mistral': 2014, 'n_tokens_neox': 1908, 'n_words': 505}