home *** CD-ROM | disk | FTP | other *** search
/ Chip 2011 November / CHIP_2011_11.iso / Programy / Narzedzia / Inkscape / Inkscape-0.48.2-1-win32.exe / python / Lib / site-packages / lxml / cssselect.py < prev    next >
Encoding:
Python Source  |  2010-05-29  |  29.6 KB  |  990 lines

  1. """CSS Selectors based on XPath.
  2.  
  3. This module supports selecting XML/HTML tags based on CSS selectors.
  4. See the `CSSSelector` class for details.
  5. """
  6.  
  7. import re
  8. from lxml import etree
  9.  
  10. __all__ = ['SelectorSyntaxError', 'ExpressionError',
  11.            'CSSSelector']
  12.  
  13. try:
  14.     _basestring = basestring
  15. except NameError:
  16.     _basestring = str
  17.  
  18. class SelectorSyntaxError(SyntaxError):
  19.     pass
  20.  
  21. class ExpressionError(RuntimeError):
  22.     pass
  23.  
  24. class CSSSelector(etree.XPath):
  25.     """A CSS selector.
  26.  
  27.     Usage::
  28.  
  29.         >>> from lxml import etree, cssselect
  30.         >>> select = cssselect.CSSSelector("a tag > child")
  31.  
  32.         >>> root = etree.XML("<a><b><c/><tag><child>TEXT</child></tag></b></a>")
  33.         >>> [ el.tag for el in select(root) ]
  34.         ['child']
  35.     """
  36.     def __init__(self, css):
  37.         path = css_to_xpath(css)
  38.         etree.XPath.__init__(self, path)
  39.         self.css = css
  40.  
  41.     def __repr__(self):
  42.         return '<%s %s for %r>' % (
  43.             self.__class__.__name__,
  44.             hex(abs(id(self)))[2:],
  45.             self.css)
  46.  
  47. ##############################
  48. ## Token objects:
  49.  
  50. try:
  51.     _unicode = unicode
  52.     _unichr = unichr
  53. except NameError:
  54.     # Python 3
  55.     _unicode = str
  56.     _unichr = chr
  57.  
  58. class _UniToken(_unicode):
  59.     def __new__(cls, contents, pos):
  60.         obj = _unicode.__new__(cls, contents)
  61.         obj.pos = pos
  62.         return obj
  63.         
  64.     def __repr__(self):
  65.         return '%s(%s, %r)' % (
  66.             self.__class__.__name__,
  67.             _unicode.__repr__(self),
  68.             self.pos)
  69.  
  70. class Symbol(_UniToken):
  71.     pass
  72.  
  73. class String(_UniToken):
  74.     pass
  75.  
  76. class Token(_UniToken):
  77.     pass
  78.  
  79. ############################################################
  80. ## Parsing
  81. ############################################################
  82.  
  83. ##############################
  84. ## Syntax objects:
  85.  
  86. class Class(object):
  87.     """
  88.     Represents selector.class_name
  89.     """
  90.  
  91.     def __init__(self, selector, class_name):
  92.         self.selector = selector
  93.         self.class_name = class_name
  94.  
  95.     def __repr__(self):
  96.         return '%s[%r.%s]' % (
  97.             self.__class__.__name__,
  98.             self.selector,
  99.             self.class_name)
  100.  
  101.     def xpath(self):
  102.         sel_xpath = self.selector.xpath()
  103.         sel_xpath.add_condition(
  104.             "contains(concat(' ', normalize-space(@class), ' '), %s)" % xpath_literal(' '+self.class_name+' '))
  105.         return sel_xpath
  106.  
  107. class Function(object):
  108.     """
  109.     Represents selector:name(expr)
  110.     """
  111.  
  112.     unsupported = [
  113.         'target', 'lang', 'enabled', 'disabled',]
  114.  
  115.     def __init__(self, selector, type, name, expr):
  116.         self.selector = selector
  117.         self.type = type
  118.         self.name = name
  119.         self.expr = expr
  120.  
  121.     def __repr__(self):
  122.         return '%s[%r%s%s(%r)]' % (
  123.             self.__class__.__name__,
  124.             self.selector,
  125.             self.type, self.name, self.expr)
  126.  
  127.     def xpath(self):
  128.         sel_path = self.selector.xpath()
  129.         if self.name in self.unsupported:
  130.             raise ExpressionError(
  131.                 "The psuedo-class %r is not supported" % self.name)
  132.         method = '_xpath_' + self.name.replace('-', '_')
  133.         if not hasattr(self, method):
  134.             raise ExpressionError(
  135.                 "The psuedo-class %r is unknown" % self.name)
  136.         method = getattr(self, method)
  137.         return method(sel_path, self.expr)
  138.  
  139.     def _xpath_nth_child(self, xpath, expr, last=False,
  140.                          add_name_test=True):
  141.         a, b = parse_series(expr)
  142.         if not a and not b and not last:
  143.             # a=0 means nothing is returned...
  144.             xpath.add_condition('false() and position() = 0')
  145.             return xpath
  146.         if add_name_test:
  147.             xpath.add_name_test()
  148.         xpath.add_star_prefix()
  149.         if a == 0:
  150.             if last:
  151.                 b = 'last() - %s' % b
  152.             xpath.add_condition('position() = %s' % b)
  153.             return xpath
  154.         if last:
  155.             # FIXME: I'm not sure if this is right
  156.             a = -a
  157.             b = -b
  158.         if b > 0:
  159.             b_neg = str(-b)
  160.         else:
  161.             b_neg = '+%s' % (-b)
  162.         if a != 1:
  163.             expr = ['(position() %s) mod %s = 0' % (b_neg, a)]
  164.         else:
  165.             expr = []
  166.         if b >= 0:
  167.             expr.append('position() >= %s' % b)
  168.         elif b < 0 and last:
  169.             expr.append('position() < (last() %s)' % b)
  170.         expr = ' and '.join(expr)
  171.         if expr:
  172.             xpath.add_condition(expr)
  173.         return xpath
  174.         # FIXME: handle an+b, odd, even
  175.         # an+b means every-a, plus b, e.g., 2n+1 means odd
  176.         # 0n+b means b
  177.         # n+0 means a=1, i.e., all elements
  178.         # an means every a elements, i.e., 2n means even
  179.         # -n means -1n
  180.         # -1n+6 means elements 6 and previous
  181.  
  182.     def _xpath_nth_last_child(self, xpath, expr):
  183.         return self._xpath_nth_child(xpath, expr, last=True)
  184.  
  185.     def _xpath_nth_of_type(self, xpath, expr):
  186.         if xpath.element == '*':
  187.             raise NotImplementedError(
  188.                 "*:nth-of-type() is not implemented")
  189.         return self._xpath_nth_child(xpath, expr, add_name_test=False)
  190.  
  191.     def _xpath_nth_last_of_type(self, xpath, expr):
  192.         return self._xpath_nth_child(xpath, expr, last=True, add_name_test=False)
  193.  
  194.     def _xpath_contains(self, xpath, expr):
  195.         # text content, minus tags, must contain expr
  196.         if isinstance(expr, Element):
  197.             expr = expr._format_element()
  198.         xpath.add_condition('contains(css:lower-case(string(.)), %s)'
  199.                             % xpath_literal(expr.lower()))
  200.         # FIXME: Currently case insensitive matching doesn't seem to be happening
  201.         return xpath
  202.  
  203.     def _xpath_not(self, xpath, expr):
  204.         # everything for which not expr applies
  205.         expr = expr.xpath()
  206.         cond = expr.condition
  207.         # FIXME: should I do something about element_path?
  208.         xpath.add_condition('not(%s)' % cond)
  209.         return xpath
  210.  
  211. def _make_lower_case(context, s):
  212.     return s.lower()
  213.  
  214. ns = etree.FunctionNamespace('http://codespeak.net/lxml/css/')
  215. ns.prefix = 'css'
  216. ns['lower-case'] = _make_lower_case
  217.  
  218. class Pseudo(object):
  219.     """
  220.     Represents selector:ident
  221.     """
  222.  
  223.     unsupported = ['indeterminate', 'first-line', 'first-letter',
  224.                    'selection', 'before', 'after', 'link', 'visited',
  225.                    'active', 'focus', 'hover']
  226.  
  227.     def __init__(self, element, type, ident):
  228.         self.element = element
  229.         assert type in (':', '::')
  230.         self.type = type
  231.         self.ident = ident
  232.  
  233.     def __repr__(self):
  234.         return '%s[%r%s%s]' % (
  235.             self.__class__.__name__,
  236.             self.element,
  237.             self.type, self.ident)
  238.  
  239.     def xpath(self):
  240.         el_xpath = self.element.xpath()
  241.         if self.ident in self.unsupported:
  242.             raise ExpressionError(
  243.                 "The psuedo-class %r is unsupported" % self.ident)
  244.         method = '_xpath_' + self.ident.replace('-', '_')
  245.         if not hasattr(self, method):
  246.             raise ExpressionError(
  247.                 "The psuedo-class %r is unknown" % self.ident)
  248.         method = getattr(self, method)
  249.         el_xpath = method(el_xpath)
  250.         return el_xpath
  251.  
  252.     def _xpath_checked(self, xpath):
  253.         # FIXME: is this really all the elements?
  254.         xpath.add_condition("(@selected or @checked) and (name(.) = 'input' or name(.) = 'option')")
  255.         return xpath
  256.  
  257.     def _xpath_root(self, xpath):
  258.         # if this element is the root element
  259.         raise NotImplementedError
  260.  
  261.     def _xpath_first_child(self, xpath):
  262.         xpath.add_star_prefix()
  263.         xpath.add_name_test()
  264.         xpath.add_condition('position() = 1')
  265.         return xpath
  266.  
  267.     def _xpath_last_child(self, xpath):
  268.         xpath.add_star_prefix()
  269.         xpath.add_name_test()
  270.         xpath.add_condition('position() = last()')
  271.         return xpath
  272.  
  273.     def _xpath_first_of_type(self, xpath):
  274.         if xpath.element == '*':
  275.             raise NotImplementedError(
  276.                 "*:first-of-type is not implemented")
  277.         xpath.add_star_prefix()
  278.         xpath.add_condition('position() = 1')
  279.         return xpath
  280.  
  281.     def _xpath_last_of_type(self, xpath):
  282.         if xpath.element == '*':
  283.             raise NotImplementedError(
  284.                 "*:last-of-type is not implemented")
  285.         xpath.add_star_prefix()
  286.         xpath.add_condition('position() = last()')
  287.         return xpath
  288.  
  289.     def _xpath_only_child(self, xpath):
  290.         xpath.add_name_test()
  291.         xpath.add_star_prefix()
  292.         xpath.add_condition('last() = 1')
  293.         return xpath
  294.  
  295.     def _xpath_only_of_type(self, xpath):
  296.         if xpath.element == '*':
  297.             raise NotImplementedError(
  298.                 "*:only-of-type is not implemented")
  299.         xpath.add_condition('last() = 1')
  300.         return xpath
  301.  
  302.     def _xpath_empty(self, xpath):
  303.         xpath.add_condition("not(*) and not(normalize-space())")
  304.         return xpath
  305.  
  306. class Attrib(object):
  307.     """
  308.     Represents selector[namespace|attrib operator value]
  309.     """
  310.  
  311.     def __init__(self, selector, namespace, attrib, operator, value):
  312.         self.selector = selector
  313.         self.namespace = namespace
  314.         self.attrib = attrib
  315.         self.operator = operator
  316.         self.value = value
  317.  
  318.     def __repr__(self):
  319.         if self.operator == 'exists':
  320.             return '%s[%r[%s]]' % (
  321.                 self.__class__.__name__,
  322.                 self.selector,
  323.                 self._format_attrib())
  324.         else:
  325.             return '%s[%r[%s %s %r]]' % (
  326.                 self.__class__.__name__,
  327.                 self.selector,
  328.                 self._format_attrib(),
  329.                 self.operator,
  330.                 self.value)
  331.  
  332.     def _format_attrib(self):
  333.         if self.namespace == '*':
  334.             return self.attrib
  335.         else:
  336.             return '%s|%s' % (self.namespace, self.attrib)
  337.  
  338.     def _xpath_attrib(self):
  339.         # FIXME: if attrib is *?
  340.         if self.namespace == '*':
  341.             return '@' + self.attrib
  342.         else:
  343.             return '@%s:%s' % (self.namespace, self.attrib)
  344.  
  345.     def xpath(self):
  346.         path = self.selector.xpath()
  347.         attrib = self._xpath_attrib()
  348.         value = self.value
  349.         if self.operator == 'exists':
  350.             assert not value
  351.             path.add_condition(attrib)
  352.         elif self.operator == '=':
  353.             path.add_condition('%s = %s' % (attrib,
  354.                                             xpath_literal(value)))
  355.         elif self.operator == '!=':
  356.             # FIXME: this seems like a weird hack...
  357.             if value:
  358.                 path.add_condition('not(%s) or %s != %s'
  359.                                    % (attrib, attrib, xpath_literal(value)))
  360.             else:
  361.                 path.add_condition('%s != %s'
  362.                                    % (attrib, xpath_literal(value)))
  363.             #path.add_condition('%s != %s' % (attrib, xpath_literal(value)))
  364.         elif self.operator == '~=':
  365.             path.add_condition("contains(concat(' ', normalize-space(%s), ' '), %s)" % (attrib, xpath_literal(' '+value+' ')))
  366.         elif self.operator == '|=':
  367.             # Weird, but true...
  368.             path.add_condition('%s = %s or starts-with(%s, %s)' % (
  369.                 attrib, xpath_literal(value),
  370.                 attrib, xpath_literal(value + '-')))
  371.         elif self.operator == '^=':
  372.             path.add_condition('starts-with(%s, %s)' % (
  373.                 attrib, xpath_literal(value)))
  374.         elif self.operator == '$=':
  375.             # Oddly there is a starts-with in XPath 1.0, but not ends-with
  376.             path.add_condition('substring(%s, string-length(%s)-%s) = %s'
  377.                                % (attrib, attrib, len(value)-1, xpath_literal(value)))
  378.         elif self.operator == '*=':
  379.             # FIXME: case sensitive?
  380.             path.add_condition('contains(%s, %s)' % (
  381.                 attrib, xpath_literal(value)))
  382.         else:
  383.             assert 0, ("Unknown operator: %r" % self.operator)
  384.         return path
  385.  
  386. class Element(object):
  387.     """
  388.     Represents namespace|element
  389.     """
  390.  
  391.     def __init__(self, namespace, element):
  392.         self.namespace = namespace
  393.         self.element = element
  394.  
  395.     def __repr__(self):
  396.         return '%s[%s]' % (
  397.             self.__class__.__name__,
  398.             self._format_element())
  399.  
  400.     def _format_element(self):
  401.         if self.namespace == '*':
  402.             return self.element
  403.         else:
  404.             return '%s|%s' % (self.namespace, self.element)
  405.  
  406.     def xpath(self):
  407.         if self.namespace == '*':
  408.             el = self.element.lower()
  409.         else:
  410.             # FIXME: Should we lowercase here?
  411.             el = '%s:%s' % (self.namespace, self.element)
  412.         return XPathExpr(element=el)
  413.  
  414. class Hash(object):
  415.     """
  416.     Represents selector#id
  417.     """
  418.  
  419.     def __init__(self, selector, id):
  420.         self.selector = selector
  421.         self.id = id
  422.  
  423.     def __repr__(self):
  424.         return '%s[%r#%s]' % (
  425.             self.__class__.__name__,
  426.             self.selector, self.id)
  427.  
  428.     def xpath(self):
  429.         path = self.selector.xpath()
  430.         path.add_condition('@id = %s' % xpath_literal(self.id))
  431.         return path
  432.  
  433. class Or(object):
  434.  
  435.     def __init__(self, items):
  436.         self.items = items
  437.     def __repr__(self):
  438.         return '%s(%r)' % (
  439.             self.__class__.__name__,
  440.             self.items)    
  441.  
  442.     def xpath(self):
  443.         paths = [item.xpath() for item in self.items]
  444.         return XPathExprOr(paths)
  445.  
  446. class CombinedSelector(object):
  447.  
  448.     _method_mapping = {
  449.         ' ': 'descendant',
  450.         '>': 'child',
  451.         '+': 'direct_adjacent',
  452.         '~': 'indirect_adjacent',
  453.         }
  454.  
  455.     def __init__(self, selector, combinator, subselector):
  456.         assert selector is not None
  457.         self.selector = selector
  458.         self.combinator = combinator
  459.         self.subselector = subselector
  460.  
  461.     def __repr__(self):
  462.         if self.combinator == ' ':
  463.             comb = '<followed>'
  464.         else:
  465.             comb = self.combinator
  466.         return '%s[%r %s %r]' % (
  467.             self.__class__.__name__,
  468.             self.selector,
  469.             comb,
  470.             self.subselector)
  471.  
  472.     def xpath(self):
  473.         if self.combinator not in self._method_mapping:
  474.             raise ExpressionError(
  475.                 "Unknown combinator: %r" % self.combinator)
  476.         method = '_xpath_' + self._method_mapping[self.combinator]
  477.         method = getattr(self, method)
  478.         path = self.selector.xpath()
  479.         return method(path, self.subselector)
  480.  
  481.     def _xpath_descendant(self, xpath, sub):
  482.         # when sub is a descendant in any way of xpath
  483.         xpath.join('/descendant::', sub.xpath())
  484.         return xpath
  485.     
  486.     def _xpath_child(self, xpath, sub):
  487.         # when sub is an immediate child of xpath
  488.         xpath.join('/', sub.xpath())
  489.         return xpath
  490.  
  491.     def _xpath_direct_adjacent(self, xpath, sub):
  492.         # when sub immediately follows xpath
  493.         xpath.join('/following-sibling::', sub.xpath())
  494.         xpath.add_name_test()
  495.         xpath.add_condition('position() = 1')
  496.         return xpath
  497.  
  498.     def _xpath_indirect_adjacent(self, xpath, sub):
  499.         # when sub comes somewhere after xpath as a sibling
  500.         xpath.join('/following-sibling::', sub.xpath())
  501.         return xpath
  502.  
  503. ##############################
  504. ## XPathExpr objects:
  505.  
  506. _el_re = re.compile(r'^\w+\s*$', re.UNICODE)
  507. _id_re = re.compile(r'^(\w*)#(\w+)\s*$', re.UNICODE)
  508. _class_re = re.compile(r'^(\w*)\.(\w+)\s*$', re.UNICODE)
  509.  
  510. def css_to_xpath(css_expr, prefix='descendant-or-self::'):
  511.     if isinstance(css_expr, _basestring):
  512.         match = _el_re.search(css_expr)
  513.         if match is not None:
  514.             return '%s%s' % (prefix, match.group(0).strip())
  515.         match = _id_re.search(css_expr)
  516.         if match is not None:
  517.             return "%s%s[@id = '%s']" % (
  518.                 prefix, match.group(1) or '*', match.group(2))
  519.         match = _class_re.search(css_expr)
  520.         if match is not None:
  521.             return "%s%s[contains(concat(' ', normalize-space(@class), ' '), ' %s ')]" % (
  522.                 prefix, match.group(1) or '*', match.group(2))
  523.         css_expr = parse(css_expr)
  524.     expr = css_expr.xpath()
  525.     assert expr is not None, (
  526.         "Got None for xpath expression from %s" % repr(css_expr))
  527.     if prefix:
  528.         expr.add_prefix(prefix)
  529.     return _unicode(expr)
  530.  
  531. class XPathExpr(object):
  532.  
  533.     def __init__(self, prefix=None, path=None, element='*', condition=None,
  534.                  star_prefix=False):
  535.         self.prefix = prefix
  536.         self.path = path
  537.         self.element = element
  538.         self.condition = condition
  539.         self.star_prefix = star_prefix
  540.  
  541.     def __str__(self):
  542.         path = ''
  543.         if self.prefix is not None:
  544.             path += _unicode(self.prefix)
  545.         if self.path is not None:
  546.             path += _unicode(self.path)
  547.         path += _unicode(self.element)
  548.         if self.condition:
  549.             path += '[%s]' % self.condition
  550.         return path
  551.  
  552.     def __repr__(self):
  553.         return '%s[%s]' % (
  554.             self.__class__.__name__, self)
  555.  
  556.     def add_condition(self, condition):
  557.         if self.condition:
  558.             self.condition = '%s and (%s)' % (self.condition, condition)
  559.         else:
  560.             self.condition = condition
  561.  
  562.     def add_path(self, part):
  563.         if self.path is None:
  564.             self.path = self.element
  565.         else:
  566.             self.path += self.element
  567.         self.element = part
  568.  
  569.     def add_prefix(self, prefix):
  570.         if self.prefix:
  571.             self.prefix = prefix + self.prefix
  572.         else:
  573.             self.prefix = prefix
  574.  
  575.     def add_name_test(self):
  576.         if self.element == '*':
  577.             # We weren't doing a test anyway
  578.             return
  579.         self.add_condition("name() = %s" % xpath_literal(self.element))
  580.         self.element = '*'
  581.  
  582.     def add_star_prefix(self):
  583.         """
  584.         Adds a /* prefix if there is no prefix.  This is when you need
  585.         to keep context's constrained to a single parent.
  586.         """
  587.         if self.path:
  588.             self.path += '*/'
  589.         else:
  590.             self.path = '*/'
  591.         self.star_prefix = True
  592.  
  593.     def join(self, combiner, other):
  594.         prefix = _unicode(self)
  595.         prefix += combiner
  596.         path = (other.prefix or '') + (other.path or '')
  597.         # We don't need a star prefix if we are joining to this other
  598.         # prefix; so we'll get rid of it
  599.         if other.star_prefix and path == '*/':
  600.             path = ''
  601.         self.prefix = prefix
  602.         self.path = path
  603.         self.element = other.element
  604.         self.condition = other.condition
  605.  
  606. class XPathExprOr(XPathExpr):
  607.     """
  608.     Represents |'d expressions.  Note that unfortunately it isn't
  609.     the union, it's the sum, so duplicate elements will appear.
  610.     """
  611.  
  612.     def __init__(self, items, prefix=None):
  613.         for item in items:
  614.             assert item is not None
  615.         self.items = items
  616.         self.prefix = prefix
  617.  
  618.     def __str__(self):
  619.         prefix = self.prefix or ''
  620.         return ' | '.join(["%s%s" % (prefix,i) for i in self.items])
  621.  
  622. split_at_single_quotes = re.compile("('+)").split
  623.  
  624. def xpath_literal(s):
  625.     if isinstance(s, Element):
  626.         # This is probably a symbol that looks like an expression...
  627.         s = s._format_element()
  628.     else:
  629.         s = _unicode(s)
  630.     if "'" not in s:
  631.         s = "'%s'" % s
  632.     elif '"' not in s:
  633.         s = '"%s"' % s
  634.     else:
  635.         s = "concat(%s)" % ','.join([
  636.             (("'" in part) and '"%s"' or "'%s'") % part
  637.             for part in split_at_single_quotes(s) if part
  638.             ])
  639.     return s
  640.  
  641. ##############################
  642. ## Parsing functions
  643.  
  644. def parse(string):
  645.     stream = TokenStream(tokenize(string))
  646.     stream.source = string
  647.     try:
  648.         return parse_selector_group(stream)
  649.     except SelectorSyntaxError:
  650.         import sys
  651.         e = sys.exc_info()[1]
  652.         message = "%s at %s -> %r" % (
  653.             e, stream.used, stream.peek())
  654.         e.msg = message
  655.         if sys.version_info < (2,6):
  656.             e.message = message
  657.         e.args = tuple([message])
  658.         raise
  659.  
  660. def parse_selector_group(stream):
  661.     result = []
  662.     while 1:
  663.         result.append(parse_selector(stream))
  664.         if stream.peek() == ',':
  665.             stream.next()
  666.         else:
  667.             break
  668.     if len(result) == 1:
  669.         return result[0]
  670.     else:
  671.         return Or(result)
  672.  
  673. def parse_selector(stream):
  674.     result = parse_simple_selector(stream)
  675.     while 1:
  676.         peek = stream.peek()
  677.         if peek == ',' or peek is None:
  678.             return result
  679.         elif peek in ('+', '>', '~'):
  680.             # A combinator
  681.             combinator = stream.next()
  682.         else:
  683.             combinator = ' '
  684.         consumed = len(stream.used)
  685.         next_selector = parse_simple_selector(stream)
  686.         if consumed == len(stream.used):
  687.             raise SelectorSyntaxError(
  688.                 "Expected selector, got '%s'" % stream.peek())
  689.         result = CombinedSelector(result, combinator, next_selector)
  690.     return result
  691.  
  692. def parse_simple_selector(stream):
  693.     peek = stream.peek()
  694.     if peek != '*' and not isinstance(peek, Symbol):
  695.         element = namespace = '*'
  696.     else:
  697.         next = stream.next()
  698.         if next != '*' and not isinstance(next, Symbol):
  699.             raise SelectorSyntaxError(
  700.                 "Expected symbol, got '%s'" % next)
  701.         if stream.peek() == '|':
  702.             namespace = next
  703.             stream.next()
  704.             element = stream.next()
  705.             if element != '*' and not isinstance(next, Symbol):
  706.                 raise SelectorSyntaxError(
  707.                     "Expected symbol, got '%s'" % next)
  708.         else:
  709.             namespace = '*'
  710.             element = next
  711.     result = Element(namespace, element)
  712.     has_hash = False
  713.     while 1:
  714.         peek = stream.peek()
  715.         if peek == '#':
  716.             if has_hash:
  717.                 # You can't have two hashes
  718.                 # (FIXME: is there some more general rule I'm missing?)
  719.                 break
  720.             stream.next()
  721.             result = Hash(result, stream.next())
  722.             has_hash = True
  723.             continue
  724.         elif peek == '.':
  725.             stream.next()
  726.             result = Class(result, stream.next())
  727.             continue
  728.         elif peek == '[':
  729.             stream.next()
  730.             result = parse_attrib(result, stream)
  731.             next = stream.next()
  732.             if not next == ']':
  733.                 raise SelectorSyntaxError(
  734.                     "] expected, got '%s'" % next)
  735.             continue
  736.         elif peek == ':' or peek == '::':
  737.             type = stream.next()
  738.             ident = stream.next()
  739.             if not isinstance(ident, Symbol):
  740.                 raise SelectorSyntaxError(
  741.                     "Expected symbol, got '%s'" % ident)
  742.             if stream.peek() == '(':
  743.                 stream.next()
  744.                 peek = stream.peek()
  745.                 if isinstance(peek, String):
  746.                     selector = stream.next()
  747.                 elif isinstance(peek, Symbol) and is_int(peek):
  748.                     selector = int(stream.next())
  749.                 else:
  750.                     # FIXME: parse_simple_selector, or selector, or...?
  751.                     selector = parse_simple_selector(stream)
  752.                 next = stream.next()
  753.                 if not next == ')':
  754.                     raise SelectorSyntaxError(
  755.                         "Expected ')', got '%s' and '%s'"
  756.                         % (next, selector))
  757.                 result = Function(result, type, ident, selector)
  758.             else:
  759.                 result = Pseudo(result, type, ident)
  760.             continue
  761.         else:
  762.             if peek == ' ':
  763.                 stream.next()
  764.             break
  765.         # FIXME: not sure what "negation" is
  766.     return result
  767.  
  768. def is_int(v):
  769.     try:
  770.         int(v)
  771.     except ValueError:
  772.         return False
  773.     else:
  774.         return True
  775.  
  776. def parse_attrib(selector, stream):
  777.     attrib = stream.next()
  778.     if stream.peek() == '|':
  779.         namespace = attrib
  780.         stream.next()
  781.         attrib = stream.next()
  782.     else:
  783.         namespace = '*'
  784.     if stream.peek() == ']':
  785.         return Attrib(selector, namespace, attrib, 'exists', None)
  786.     op = stream.next()
  787.     if not op in ('^=', '$=', '*=', '=', '~=', '|=', '!='):
  788.         raise SelectorSyntaxError(
  789.             "Operator expected, got '%s'" % op)
  790.     value = stream.next()
  791.     if not isinstance(value, (Symbol, String)):
  792.         raise SelectorSyntaxError(
  793.             "Expected string or symbol, got '%s'" % value)
  794.     return Attrib(selector, namespace, attrib, op, value)
  795.  
  796. def parse_series(s):
  797.     """
  798.     Parses things like '1n+2', or 'an+b' generally, returning (a, b)
  799.     """
  800.     if isinstance(s, Element):
  801.         s = s._format_element()
  802.     if not s or s == '*':
  803.         # Happens when there's nothing, which the CSS parser thinks of as *
  804.         return (0, 0)
  805.     if isinstance(s, int):
  806.         # Happens when you just get a number
  807.         return (0, s)
  808.     if s == 'odd':
  809.         return (2, 1)
  810.     elif s == 'even':
  811.         return (2, 0)
  812.     elif s == 'n':
  813.         return (1, 0)
  814.     if 'n' not in s:
  815.         # Just a b
  816.         return (0, int(s))
  817.     a, b = s.split('n', 1)
  818.     if not a:
  819.         a = 1
  820.     elif a == '-' or a == '+':
  821.         a = int(a+'1')
  822.     else:
  823.         a = int(a)
  824.     if not b:
  825.         b = 0
  826.     elif b == '-' or b == '+':
  827.         b = int(b+'1')
  828.     else:
  829.         b = int(b)
  830.     return (a, b)
  831.     
  832.  
  833. ############################################################
  834. ## Tokenizing
  835. ############################################################
  836.  
  837. _whitespace_re = re.compile(r'\s+', re.UNICODE)
  838.  
  839. _comment_re = re.compile(r'/\*.*?\*/', re.DOTALL)
  840.  
  841. _count_re = re.compile(r'[+-]?\d*n(?:[+-]\d+)?')
  842.  
  843. def tokenize(s):
  844.     pos = 0
  845.     s = _comment_re.sub('', s)
  846.     while 1:
  847.         match = _whitespace_re.match(s, pos=pos)
  848.         if match:
  849.             preceding_whitespace_pos = pos
  850.             pos = match.end()
  851.         else:
  852.             preceding_whitespace_pos = 0
  853.         if pos >= len(s):
  854.             return
  855.         match = _count_re.match(s, pos=pos)
  856.         if match and match.group() != 'n':
  857.             sym = s[pos:match.end()]
  858.             yield Symbol(sym, pos)
  859.             pos = match.end()
  860.             continue
  861.         c = s[pos]
  862.         c2 = s[pos:pos+2]
  863.         if c2 in ('~=', '|=', '^=', '$=', '*=', '::', '!='):
  864.             yield Token(c2, pos)
  865.             pos += 2
  866.             continue
  867.         if c in '>+~,.*=[]()|:#':
  868.             if c in '.#' and preceding_whitespace_pos > 0:
  869.                 yield Token(' ', preceding_whitespace_pos)
  870.             yield Token(c, pos)
  871.             pos += 1
  872.             continue
  873.         if c == '"' or c == "'":
  874.             # Quoted string
  875.             old_pos = pos
  876.             sym, pos = tokenize_escaped_string(s, pos)
  877.             yield String(sym, old_pos)
  878.             continue
  879.         old_pos = pos
  880.         sym, pos = tokenize_symbol(s, pos)
  881.         yield Symbol(sym, old_pos)
  882.         continue
  883.  
  884. split_at_string_escapes = re.compile(r'(\\(?:%s))'
  885.                                      % '|'.join(['[A-Fa-f0-9]{1,6}(?:\r\n|\s)?',
  886.                                                  '[^A-Fa-f0-9]'])).split
  887.  
  888. def unescape_string_literal(literal):
  889.     substrings = []
  890.     for substring in split_at_string_escapes(literal):
  891.         if not substring:
  892.             continue
  893.         elif '\\' in substring:
  894.             if substring[0] == '\\' and len(substring) > 1:
  895.                 substring = substring[1:]
  896.                 if substring[0] in '0123456789ABCDEFabcdef':
  897.                     # int() correctly ignores the potentially trailing whitespace
  898.                     substring = _unichr(int(substring, 16))
  899.             else:
  900.                 raise SelectorSyntaxError(
  901.                     "Invalid escape sequence %r in string %r"
  902.                     % (substring.split('\\')[1], literal))
  903.         substrings.append(substring)
  904.     return ''.join(substrings)
  905.  
  906. def tokenize_escaped_string(s, pos):
  907.     quote = s[pos]
  908.     assert quote in ('"', "'")
  909.     pos = pos+1
  910.     start = pos
  911.     while 1:
  912.         next = s.find(quote, pos)
  913.         if next == -1:
  914.             raise SelectorSyntaxError(
  915.                 "Expected closing %s for string in: %r"
  916.                 % (quote, s[start:]))
  917.         result = s[start:next]
  918.         if result.endswith('\\'):
  919.             # next quote character is escaped
  920.             pos = next+1
  921.             continue
  922.         if '\\' in result:
  923.             result = unescape_string_literal(result)
  924.         return result, next+1
  925.     
  926. _illegal_symbol = re.compile(r'[^\w\\-]', re.UNICODE)
  927.  
  928. def tokenize_symbol(s, pos):
  929.     start = pos
  930.     match = _illegal_symbol.search(s, pos=pos)
  931.     if not match:
  932.         # Goes to end of s
  933.         return s[start:], len(s)
  934.     if match.start() == pos:
  935.         assert 0, (
  936.             "Unexpected symbol: %r at %s" % (s[pos], pos))
  937.     if not match:
  938.         result = s[start:]
  939.         pos = len(s)
  940.     else:
  941.         result = s[start:match.start()]
  942.         pos = match.start()
  943.     try:
  944.         result = result.encode('ASCII', 'backslashreplace').decode('unicode_escape')
  945.     except UnicodeDecodeError:
  946.         import sys
  947.         e = sys.exc_info()[1]
  948.         raise SelectorSyntaxError(
  949.             "Bad symbol %r: %s" % (result, e))
  950.     return result, pos
  951.  
  952. class TokenStream(object):
  953.  
  954.     def __init__(self, tokens, source=None):
  955.         self.used = []
  956.         self.tokens = iter(tokens)
  957.         self.source = source
  958.         self.peeked = None
  959.         self._peeking = False
  960.         try:
  961.             self.next_token = self.tokens.next
  962.         except AttributeError:
  963.             # Python 3
  964.             self.next_token = self.tokens.__next__
  965.  
  966.     def next(self):
  967.         if self._peeking:
  968.             self._peeking = False
  969.             self.used.append(self.peeked)
  970.             return self.peeked
  971.         else:
  972.             try:
  973.                 next = self.next_token()
  974.                 self.used.append(next)
  975.                 return next
  976.             except StopIteration:
  977.                 return None
  978.  
  979.     def __iter__(self):
  980.         return iter(self.next, None)
  981.  
  982.     def peek(self):
  983.         if not self._peeking:
  984.             try:
  985.                 self.peeked = self.next_token()
  986.             except StopIteration:
  987.                 return None
  988.             self._peeking = True
  989.         return self.peeked
  990.