def read_at_rule(self, at_keyword_token, tokens): """Read an at-rule from a token stream. :param at_keyword_token: The ATKEYWORD token that starts this at-rule You may have read it already to distinguish the rule from a ruleset. :param tokens: An iterator of subsequent tokens. Will be consumed just enough for one at-rule. :return: An unparsed :class:`AtRule`. :raises: :class:`~.parsing.ParseError` if the head is invalid for the core grammar. The body is **not** validated. See :class:`AtRule`. """ # CSS syntax is case-insensitive at_keyword = at_keyword_token.value.lower() head = [] # For the ParseError in case `tokens` is empty: token = at_keyword_token for token in tokens: if token.type in '{;': break # Ignore white space just after the at-keyword. else: head.append(token) # On unexpected end of stylesheet, pretend that a ';' was there head = strip_whitespace(head) for head_token in head: validate_any(head_token, 'at-rule head') body = token.content if token.type == '{' else None return AtRule(at_keyword, head, body, at_keyword_token.line, at_keyword_token.column)
def parse_ruleset(self, first_token, tokens): """Parse a ruleset: a selector followed by declaration block. :param first_token: The first token of the ruleset (probably of the selector). You may have read it already to distinguish the rule from an at-rule. :param tokens: an iterator of subsequent tokens. Will be consumed just enough for one ruleset. :return: a tuple of a :class:`RuleSet` and an error list. The errors are recovered :class:`~.parsing.ParseError` in declarations. (Parsing continues from the next declaration on such errors.) :raises: :class:`~.parsing.ParseError` if the selector is invalid for the core grammar. Note a that a selector can be valid for the core grammar but not for CSS 2.1 or another level. """ selector = [] for token in chain([first_token], tokens): if token.type == '{': # Parse/validate once we’ve read the whole rule selector = strip_whitespace(selector) if not selector: raise ParseError(first_token, 'empty selector') for selector_token in selector: validate_any(selector_token, 'selector') declarations, errors = self.parse_declaration_list( token.content) ruleset = RuleSet(selector, declarations, first_token.line, first_token.column) return ruleset, errors else: selector.append(token) raise ParseError(token, 'no declaration block found for ruleset')