parse.py 2.4 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677
  1. def split_selectors(raw_selector):
  2. """
  3. Split a selector with commas and arbitrary whitespaces into a list of
  4. selector swith single-space whitespaces.
  5. """
  6. return [' '.join(s.split()) for s in raw_selector.split(',')]
  7. def parse_groups(css):
  8. """
  9. Parse CSS code one character at a time. This is more efficient than
  10. simply splitting on brackets, especially for large style sheets. All
  11. comments are ignored (both inline and multiline).
  12. """
  13. stack = char = ''
  14. prev_char = None
  15. lineno = 1
  16. properties = []
  17. current_group = root_group = []
  18. groups = [(None, root_group)]
  19. selectors = None
  20. comment = False
  21. try:
  22. for c in css:
  23. char = c
  24. if comment:
  25. # Comment end?
  26. if c == '/' and prev_char == '*':
  27. comment = False
  28. elif c == '{':
  29. # Block start
  30. if selectors is not None:
  31. # Block is nested, save group selector
  32. current_group = []
  33. groups.append((selectors, current_group))
  34. selectors = split_selectors(stack)
  35. #print stack.strip(), '->', selectors
  36. stack = ''
  37. assert len(selectors)
  38. elif c == '}':
  39. if selectors is None:
  40. # Closing group
  41. current_group = root_group
  42. else:
  43. # Closing block
  44. current_group.append((selectors, properties))
  45. selectors = None
  46. properties = []
  47. elif c == ';':
  48. # Property definition
  49. assert selectors is not None
  50. if stack.strip():
  51. parts = stack.split(':', 1)
  52. assert len(parts) == 2
  53. name, value = map(str.strip, parts)
  54. assert '\n' not in name
  55. properties.append((name, value))
  56. stack = ''
  57. elif c == '*' and prev_char == '/':
  58. # Comment start
  59. comment = True
  60. stack = stack[:-1]
  61. else:
  62. if c == '\n':
  63. lineno += 1
  64. stack += c
  65. prev_char = c
  66. except AssertionError:
  67. raise Exception('unexpected \'%c\' on line %d' % (char, lineno))
  68. return groups