csspool 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/CHANGELOG.txt +4 -0
 - data/Manifest.txt +32 -0
 - data/README.txt +71 -0
 - data/Rakefile +56 -0
 - data/lib/css/sac/attribute_condition.rb +78 -0
 - data/lib/css/sac/condition.rb +21 -0
 - data/lib/css/sac/document_handler.rb +67 -0
 - data/lib/css/sac/error_handler.rb +14 -0
 - data/lib/css/sac/generated_property_parser.rb +9214 -0
 - data/lib/css/sac/lexeme.rb +29 -0
 - data/lib/css/sac/lexical_unit.rb +111 -0
 - data/lib/css/sac/parse_exception.rb +6 -0
 - data/lib/css/sac/parser.rb +98 -0
 - data/lib/css/sac/property_parser.rb +47 -0
 - data/lib/css/sac/selectors.rb +87 -0
 - data/lib/css/sac/token.rb +27 -0
 - data/lib/css/sac/tokenizer.rb +186 -0
 - data/lib/css/sac.rb +13 -0
 - data/lib/parser.y +287 -0
 - data/lib/property_parser.y +2346 -0
 - data/lib/property_parser.y.erb +1321 -0
 - data/test/helper.rb +7 -0
 - data/test/test_all.rb +4 -0
 - data/test/test_lexeme.rb +39 -0
 - data/test/test_lexical_unit.rb +96 -0
 - data/test/test_parse_error.rb +199 -0
 - data/test/test_parser.rb +183 -0
 - data/test/test_property_parser.rb +593 -0
 - data/test/test_selector_as_string.rb +83 -0
 - data/test/test_selector_parser.rb +170 -0
 - data/test/test_token.rb +24 -0
 - data/test/test_tokenizer.rb +117 -0
 - metadata +88 -0
 
| 
         @@ -0,0 +1,29 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module CSS
         
     | 
| 
      
 2 
     | 
    
         
            +
              module SAC
         
     | 
| 
      
 3 
     | 
    
         
            +
                class Lexeme
         
     | 
| 
      
 4 
     | 
    
         
            +
                  attr_reader :name, :pattern
         
     | 
| 
      
 5 
     | 
    
         
            +
                  
         
     | 
| 
      
 6 
     | 
    
         
            +
                  def initialize(name, pattern=nil, &block)
         
     | 
| 
      
 7 
     | 
    
         
            +
                    raise ArgumentError, "name required" unless name
         
     | 
| 
      
 8 
     | 
    
         
            +
                    
         
     | 
| 
      
 9 
     | 
    
         
            +
                    @name = name
         
     | 
| 
      
 10 
     | 
    
         
            +
                    patterns = []
         
     | 
| 
      
 11 
     | 
    
         
            +
                    
         
     | 
| 
      
 12 
     | 
    
         
            +
                    patterns << pattern if pattern
         
     | 
| 
      
 13 
     | 
    
         
            +
                    yield(patterns) if block_given?
         
     | 
| 
      
 14 
     | 
    
         
            +
                    
         
     | 
| 
      
 15 
     | 
    
         
            +
                    if patterns.empty?
         
     | 
| 
      
 16 
     | 
    
         
            +
                      raise ArgumentError, "at least one pattern required"
         
     | 
| 
      
 17 
     | 
    
         
            +
                    end
         
     | 
| 
      
 18 
     | 
    
         
            +
                    
         
     | 
| 
      
 19 
     | 
    
         
            +
                    patterns.collect! do |pattern|
         
     | 
| 
      
 20 
     | 
    
         
            +
                      source = pattern.source
         
     | 
| 
      
 21 
     | 
    
         
            +
                      source = "\\A#{source}"
         
     | 
| 
      
 22 
     | 
    
         
            +
                      Regexp.new(source, Regexp::IGNORECASE + Regexp::MULTILINE)
         
     | 
| 
      
 23 
     | 
    
         
            +
                    end
         
     | 
| 
      
 24 
     | 
    
         
            +
             
     | 
| 
      
 25 
     | 
    
         
            +
                    @pattern = Regexp.union(*patterns)
         
     | 
| 
      
 26 
     | 
    
         
            +
                  end
         
     | 
| 
      
 27 
     | 
    
         
            +
                end
         
     | 
| 
      
 28 
     | 
    
         
            +
              end
         
     | 
| 
      
 29 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,111 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module CSS
         
     | 
| 
      
 2 
     | 
    
         
            +
              module SAC
         
     | 
| 
      
 3 
     | 
    
         
            +
                class LexicalUnit
         
     | 
| 
      
 4 
     | 
    
         
            +
                  attr_accessor :dimension_unit_text,
         
     | 
| 
      
 5 
     | 
    
         
            +
                                :lexical_unit_type,
         
     | 
| 
      
 6 
     | 
    
         
            +
                                :float_value,
         
     | 
| 
      
 7 
     | 
    
         
            +
                                :integer_value,
         
     | 
| 
      
 8 
     | 
    
         
            +
                                :string_value,
         
     | 
| 
      
 9 
     | 
    
         
            +
                                :parameters,
         
     | 
| 
      
 10 
     | 
    
         
            +
                                :function_name
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
                  alias :to_s :string_value
         
     | 
| 
      
 13 
     | 
    
         
            +
                end
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
                class Function < LexicalUnit
         
     | 
| 
      
 16 
     | 
    
         
            +
                  FUNCTIONS = {
         
     | 
| 
      
 17 
     | 
    
         
            +
                    'counter'   => :SAC_COUNTER_FUNCTION,
         
     | 
| 
      
 18 
     | 
    
         
            +
                    'counters'  => :SAC_COUNTERS_FUNCTION,
         
     | 
| 
      
 19 
     | 
    
         
            +
                    'rect'      => :SAC_RECT_FUNCTION,
         
     | 
| 
      
 20 
     | 
    
         
            +
                  }
         
     | 
| 
      
 21 
     | 
    
         
            +
                  def initialize(name, params)
         
     | 
| 
      
 22 
     | 
    
         
            +
                    self.string_value = "#{name}#{params.join(', ')})"
         
     | 
| 
      
 23 
     | 
    
         
            +
                    name =~ /^(.*)\(/
         
     | 
| 
      
 24 
     | 
    
         
            +
                    self.function_name = $1
         
     | 
| 
      
 25 
     | 
    
         
            +
                    self.parameters = params
         
     | 
| 
      
 26 
     | 
    
         
            +
                    self.lexical_unit_type = FUNCTIONS[self.function_name] || :SAC_FUNCTION
         
     | 
| 
      
 27 
     | 
    
         
            +
                  end
         
     | 
| 
      
 28 
     | 
    
         
            +
                end
         
     | 
| 
      
 29 
     | 
    
         
            +
             
     | 
| 
      
 30 
     | 
    
         
            +
                class Color < LexicalUnit
         
     | 
| 
      
 31 
     | 
    
         
            +
                  def initialize(value)
         
     | 
| 
      
 32 
     | 
    
         
            +
                    self.string_value = value
         
     | 
| 
      
 33 
     | 
    
         
            +
                    self.lexical_unit_type = :SAC_RGBCOLOR
         
     | 
| 
      
 34 
     | 
    
         
            +
                    if value =~ /^#(\d{1,2})(\d{1,2})(\d{1,2})$/
         
     | 
| 
      
 35 
     | 
    
         
            +
                      self.parameters = [
         
     | 
| 
      
 36 
     | 
    
         
            +
                        Number.new($1.hex, '', :SAC_INTEGER),
         
     | 
| 
      
 37 
     | 
    
         
            +
                        Number.new($2.hex, '', :SAC_INTEGER),
         
     | 
| 
      
 38 
     | 
    
         
            +
                        Number.new($3.hex, '', :SAC_INTEGER)
         
     | 
| 
      
 39 
     | 
    
         
            +
                      ]
         
     | 
| 
      
 40 
     | 
    
         
            +
                    else
         
     | 
| 
      
 41 
     | 
    
         
            +
                      self.parameters = [LexicalIdent.new(value)]
         
     | 
| 
      
 42 
     | 
    
         
            +
                    end
         
     | 
| 
      
 43 
     | 
    
         
            +
                  end
         
     | 
| 
      
 44 
     | 
    
         
            +
                end
         
     | 
| 
      
 45 
     | 
    
         
            +
             
     | 
| 
      
 46 
     | 
    
         
            +
                class LexicalString < LexicalUnit
         
     | 
| 
      
 47 
     | 
    
         
            +
                  def initialize(value)
         
     | 
| 
      
 48 
     | 
    
         
            +
                    self.string_value = value
         
     | 
| 
      
 49 
     | 
    
         
            +
                    self.lexical_unit_type = :SAC_STRING_VALUE
         
     | 
| 
      
 50 
     | 
    
         
            +
                  end
         
     | 
| 
      
 51 
     | 
    
         
            +
                end
         
     | 
| 
      
 52 
     | 
    
         
            +
             
     | 
| 
      
 53 
     | 
    
         
            +
                class LexicalIdent < LexicalUnit
         
     | 
| 
      
 54 
     | 
    
         
            +
                  def initialize(value)
         
     | 
| 
      
 55 
     | 
    
         
            +
                    self.string_value = value
         
     | 
| 
      
 56 
     | 
    
         
            +
                    self.lexical_unit_type = :SAC_IDENT
         
     | 
| 
      
 57 
     | 
    
         
            +
                  end
         
     | 
| 
      
 58 
     | 
    
         
            +
                end
         
     | 
| 
      
 59 
     | 
    
         
            +
             
     | 
| 
      
 60 
     | 
    
         
            +
                class LexicalURI < LexicalUnit
         
     | 
| 
      
 61 
     | 
    
         
            +
                  def initialize(value)
         
     | 
| 
      
 62 
     | 
    
         
            +
                    self.string_value = value.gsub(/^url\(/, '').gsub(/\)$/, '')
         
     | 
| 
      
 63 
     | 
    
         
            +
                    self.lexical_unit_type = :SAC_URI
         
     | 
| 
      
 64 
     | 
    
         
            +
                  end
         
     | 
| 
      
 65 
     | 
    
         
            +
                end
         
     | 
| 
      
 66 
     | 
    
         
            +
             
     | 
| 
      
 67 
     | 
    
         
            +
                class Number < LexicalUnit
         
     | 
| 
      
 68 
     | 
    
         
            +
                  NON_NEGATIVE_UNITS = [
         
     | 
| 
      
 69 
     | 
    
         
            +
                    :SAC_DEGREE,
         
     | 
| 
      
 70 
     | 
    
         
            +
                    :SAC_GRADIAN,
         
     | 
| 
      
 71 
     | 
    
         
            +
                    :SAC_RADIAN,
         
     | 
| 
      
 72 
     | 
    
         
            +
                    :SAC_MILLISECOND,
         
     | 
| 
      
 73 
     | 
    
         
            +
                    :SAC_SECOND,
         
     | 
| 
      
 74 
     | 
    
         
            +
                    :SAC_HERTZ,
         
     | 
| 
      
 75 
     | 
    
         
            +
                    :SAC_KILOHERTZ,
         
     | 
| 
      
 76 
     | 
    
         
            +
                  ]
         
     | 
| 
      
 77 
     | 
    
         
            +
                  UNITS = {
         
     | 
| 
      
 78 
     | 
    
         
            +
                    'deg'   => :SAC_DEGREE,
         
     | 
| 
      
 79 
     | 
    
         
            +
                    'rad'   => :SAC_RADIAN,
         
     | 
| 
      
 80 
     | 
    
         
            +
                    'grad'  => :SAC_GRADIAN,
         
     | 
| 
      
 81 
     | 
    
         
            +
                    'ms'    => :SAC_MILLISECOND,
         
     | 
| 
      
 82 
     | 
    
         
            +
                    's'     => :SAC_SECOND,
         
     | 
| 
      
 83 
     | 
    
         
            +
                    'hz'    => :SAC_HERTZ,
         
     | 
| 
      
 84 
     | 
    
         
            +
                    'khz'   => :SAC_KILOHERTZ,
         
     | 
| 
      
 85 
     | 
    
         
            +
                    'px'    => :SAC_PIXEL,
         
     | 
| 
      
 86 
     | 
    
         
            +
                    'cm'    => :SAC_CENTIMETER,
         
     | 
| 
      
 87 
     | 
    
         
            +
                    'mm'    => :SAC_MILLIMETER,
         
     | 
| 
      
 88 
     | 
    
         
            +
                    'in'    => :SAC_INCH,
         
     | 
| 
      
 89 
     | 
    
         
            +
                    'pt'    => :SAC_POINT,
         
     | 
| 
      
 90 
     | 
    
         
            +
                    'pc'    => :SAC_PICA,
         
     | 
| 
      
 91 
     | 
    
         
            +
                    '%'     => :SAC_PERCENTAGE,
         
     | 
| 
      
 92 
     | 
    
         
            +
                    'em'    => :SAC_EM,
         
     | 
| 
      
 93 
     | 
    
         
            +
                    'ex'    => :SAC_EX,
         
     | 
| 
      
 94 
     | 
    
         
            +
                  }
         
     | 
| 
      
 95 
     | 
    
         
            +
                  def initialize(value, unit = nil, type = nil)
         
     | 
| 
      
 96 
     | 
    
         
            +
                    if value.is_a?(String)
         
     | 
| 
      
 97 
     | 
    
         
            +
                      value =~ /^(-?[0-9.]*)(.*)$/
         
     | 
| 
      
 98 
     | 
    
         
            +
                      value = $1
         
     | 
| 
      
 99 
     | 
    
         
            +
                      unit  ||= $2
         
     | 
| 
      
 100 
     | 
    
         
            +
                    end
         
     | 
| 
      
 101 
     | 
    
         
            +
                    type  ||= UNITS[self.dimension_unit_text]
         
     | 
| 
      
 102 
     | 
    
         
            +
                    self.string_value = "#{value}#{unit}"
         
     | 
| 
      
 103 
     | 
    
         
            +
                    self.float_value = value.to_f
         
     | 
| 
      
 104 
     | 
    
         
            +
                    self.integer_value = value.to_i
         
     | 
| 
      
 105 
     | 
    
         
            +
                    self.dimension_unit_text = unit.downcase
         
     | 
| 
      
 106 
     | 
    
         
            +
                    self.lexical_unit_type = UNITS[self.dimension_unit_text] ||
         
     | 
| 
      
 107 
     | 
    
         
            +
                      (value =~ /\./ ? :SAC_NUMBER : :SAC_INTEGER)
         
     | 
| 
      
 108 
     | 
    
         
            +
                  end
         
     | 
| 
      
 109 
     | 
    
         
            +
                end
         
     | 
| 
      
 110 
     | 
    
         
            +
              end
         
     | 
| 
      
 111 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,98 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require "css/sac/document_handler"
         
     | 
| 
      
 2 
     | 
    
         
            +
            require "css/sac/error_handler"
         
     | 
| 
      
 3 
     | 
    
         
            +
            require "css/sac/generated_parser"
         
     | 
| 
      
 4 
     | 
    
         
            +
            require "css/sac/lexical_unit"
         
     | 
| 
      
 5 
     | 
    
         
            +
            require "css/sac/parse_exception"
         
     | 
| 
      
 6 
     | 
    
         
            +
            require "css/sac/tokenizer"
         
     | 
| 
      
 7 
     | 
    
         
            +
            require "css/sac/property_parser"
         
     | 
| 
      
 8 
     | 
    
         
            +
            require "css/sac/condition"
         
     | 
| 
      
 9 
     | 
    
         
            +
            require "css/sac/attribute_condition"
         
     | 
| 
      
 10 
     | 
    
         
            +
            require "css/sac/selectors"
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
            module CSS
         
     | 
| 
      
 13 
     | 
    
         
            +
              module SAC
         
     | 
| 
      
 14 
     | 
    
         
            +
                class Parser < CSS::SAC::GeneratedParser
         
     | 
| 
      
 15 
     | 
    
         
            +
                  # The version of CSSPool you're using
         
     | 
| 
      
 16 
     | 
    
         
            +
                  VERSION = '0.1.0'
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
      
 18 
     | 
    
         
            +
                  TOKENIZER = Tokenizer.new
         
     | 
| 
      
 19 
     | 
    
         
            +
                  
         
     | 
| 
      
 20 
     | 
    
         
            +
                  attr_accessor :document_handler, :error_handler, :logger
         
     | 
| 
      
 21 
     | 
    
         
            +
             
     | 
| 
      
 22 
     | 
    
         
            +
                  def initialize(document_handler = nil)
         
     | 
| 
      
 23 
     | 
    
         
            +
                    @error_handler = ErrorHandler.new
         
     | 
| 
      
 24 
     | 
    
         
            +
                    @document_handler = document_handler || DocumentHandler.new()
         
     | 
| 
      
 25 
     | 
    
         
            +
                    @property_parser = PropertyParser.new()
         
     | 
| 
      
 26 
     | 
    
         
            +
                    @tokenizer = TOKENIZER
         
     | 
| 
      
 27 
     | 
    
         
            +
                    @logger = nil
         
     | 
| 
      
 28 
     | 
    
         
            +
                  end
         
     | 
| 
      
 29 
     | 
    
         
            +
             
     | 
| 
      
 30 
     | 
    
         
            +
                  def parse_style_sheet(string)
         
     | 
| 
      
 31 
     | 
    
         
            +
                    @yydebug = true
         
     | 
| 
      
 32 
     | 
    
         
            +
                    @tokens = TOKENIZER.tokenize(string)
         
     | 
| 
      
 33 
     | 
    
         
            +
                    @position = 0
         
     | 
| 
      
 34 
     | 
    
         
            +
             
     | 
| 
      
 35 
     | 
    
         
            +
                    self.document_handler.start_document(string)
         
     | 
| 
      
 36 
     | 
    
         
            +
                    do_parse
         
     | 
| 
      
 37 
     | 
    
         
            +
                    self.document_handler.end_document(string)
         
     | 
| 
      
 38 
     | 
    
         
            +
                  end
         
     | 
| 
      
 39 
     | 
    
         
            +
             
     | 
| 
      
 40 
     | 
    
         
            +
                  alias :parse :parse_style_sheet
         
     | 
| 
      
 41 
     | 
    
         
            +
                  alias :parse_rule :parse_style_sheet
         
     | 
| 
      
 42 
     | 
    
         
            +
             
     | 
| 
      
 43 
     | 
    
         
            +
                  # Returns the parser version.  We return CSS2, but its actually
         
     | 
| 
      
 44 
     | 
    
         
            +
                  # CSS2.1.  No font-face tags.  Sorry.
         
     | 
| 
      
 45 
     | 
    
         
            +
                  def parser_version
         
     | 
| 
      
 46 
     | 
    
         
            +
                    "http://www.w3.org/TR/REC-CSS2"
         
     | 
| 
      
 47 
     | 
    
         
            +
                  end
         
     | 
| 
      
 48 
     | 
    
         
            +
             
     | 
| 
      
 49 
     | 
    
         
            +
                  attr_reader :property_parser
         
     | 
| 
      
 50 
     | 
    
         
            +
                  attr_reader :tokenizer
         
     | 
| 
      
 51 
     | 
    
         
            +
             
     | 
| 
      
 52 
     | 
    
         
            +
                  private # Bro.
         
     | 
| 
      
 53 
     | 
    
         
            +
             
     | 
| 
      
 54 
     | 
    
         
            +
                  # We have to eliminate matching pairs.
         
     | 
| 
      
 55 
     | 
    
         
            +
                  # http://www.w3.org/TR/CSS21/syndata.html#parsing-errors
         
     | 
| 
      
 56 
     | 
    
         
            +
                  # See the malformed declarations section
         
     | 
| 
      
 57 
     | 
    
         
            +
                  def eliminate_pair_matches(error_value)
         
     | 
| 
      
 58 
     | 
    
         
            +
                    pairs = {}
         
     | 
| 
      
 59 
     | 
    
         
            +
                    pairs['"'] = '"'
         
     | 
| 
      
 60 
     | 
    
         
            +
                    pairs["'"] = "'"
         
     | 
| 
      
 61 
     | 
    
         
            +
                    pairs['{'] = '}'
         
     | 
| 
      
 62 
     | 
    
         
            +
                    pairs['['] = ']'
         
     | 
| 
      
 63 
     | 
    
         
            +
                    pairs['('] = ')'
         
     | 
| 
      
 64 
     | 
    
         
            +
             
     | 
| 
      
 65 
     | 
    
         
            +
                    error_value.strip!
         
     | 
| 
      
 66 
     | 
    
         
            +
                    if pairs[error_value]
         
     | 
| 
      
 67 
     | 
    
         
            +
                      logger.warn("Eliminating pair for: #{error_value}") if logger
         
     | 
| 
      
 68 
     | 
    
         
            +
                      loop {
         
     | 
| 
      
 69 
     | 
    
         
            +
                        token = next_token
         
     | 
| 
      
 70 
     | 
    
         
            +
                        eliminate_pair_matches(token[1])
         
     | 
| 
      
 71 
     | 
    
         
            +
                        logger.warn("Eliminated token: #{token.join(' ')}") if logger
         
     | 
| 
      
 72 
     | 
    
         
            +
                        break if token[1] == pairs[error_value]
         
     | 
| 
      
 73 
     | 
    
         
            +
                      }
         
     | 
| 
      
 74 
     | 
    
         
            +
                    end
         
     | 
| 
      
 75 
     | 
    
         
            +
                  end
         
     | 
| 
      
 76 
     | 
    
         
            +
             
     | 
| 
      
 77 
     | 
    
         
            +
                  def on_error(error_token_id, error_value, value_stack)
         
     | 
| 
      
 78 
     | 
    
         
            +
                    if logger
         
     | 
| 
      
 79 
     | 
    
         
            +
                      logger.error(token_to_str(error_token_id))
         
     | 
| 
      
 80 
     | 
    
         
            +
                      logger.error("error value: #{error_value}")
         
     | 
| 
      
 81 
     | 
    
         
            +
                    end
         
     | 
| 
      
 82 
     | 
    
         
            +
                    eliminate_pair_matches(error_value)
         
     | 
| 
      
 83 
     | 
    
         
            +
                  end
         
     | 
| 
      
 84 
     | 
    
         
            +
             
     | 
| 
      
 85 
     | 
    
         
            +
                  def next_token
         
     | 
| 
      
 86 
     | 
    
         
            +
                    return [false, false] if @position >= @tokens.length 
         
     | 
| 
      
 87 
     | 
    
         
            +
             
     | 
| 
      
 88 
     | 
    
         
            +
                    n_token = @tokens[@position]
         
     | 
| 
      
 89 
     | 
    
         
            +
                    @position += 1
         
     | 
| 
      
 90 
     | 
    
         
            +
                    if n_token.name == :COMMENT
         
     | 
| 
      
 91 
     | 
    
         
            +
                      self.document_handler.comment(n_token.value)
         
     | 
| 
      
 92 
     | 
    
         
            +
                      return next_token
         
     | 
| 
      
 93 
     | 
    
         
            +
                    end
         
     | 
| 
      
 94 
     | 
    
         
            +
                    n_token.to_racc_token
         
     | 
| 
      
 95 
     | 
    
         
            +
                  end
         
     | 
| 
      
 96 
     | 
    
         
            +
                end
         
     | 
| 
      
 97 
     | 
    
         
            +
              end
         
     | 
| 
      
 98 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,47 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require "css/sac/generated_property_parser"
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module CSS
         
     | 
| 
      
 4 
     | 
    
         
            +
              module SAC
         
     | 
| 
      
 5 
     | 
    
         
            +
                class PropertyParser < CSS::SAC::GeneratedPropertyParser
         
     | 
| 
      
 6 
     | 
    
         
            +
                  def initialize
         
     | 
| 
      
 7 
     | 
    
         
            +
                    @tokens = []
         
     | 
| 
      
 8 
     | 
    
         
            +
                    @token_table = Racc_arg[10]
         
     | 
| 
      
 9 
     | 
    
         
            +
                  end
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
                  def parse_tokens(tokens)
         
     | 
| 
      
 12 
     | 
    
         
            +
                    negate = false # Nasty hack for unary minus
         
     | 
| 
      
 13 
     | 
    
         
            +
                    @tokens = tokens.find_all { |x| x.name != :S }.map { |token|
         
     | 
| 
      
 14 
     | 
    
         
            +
                      tok = if @token_table.has_key?(token.value)
         
     | 
| 
      
 15 
     | 
    
         
            +
                              [token.value, token.value]
         
     | 
| 
      
 16 
     | 
    
         
            +
                            else
         
     | 
| 
      
 17 
     | 
    
         
            +
                              if token.name == :delim && !@token_table.has_key?(token.value)
         
     | 
| 
      
 18 
     | 
    
         
            +
                                negate = true if token.value == '-'
         
     | 
| 
      
 19 
     | 
    
         
            +
                                nil
         
     | 
| 
      
 20 
     | 
    
         
            +
                              else
         
     | 
| 
      
 21 
     | 
    
         
            +
                                token.to_racc_token
         
     | 
| 
      
 22 
     | 
    
         
            +
                              end
         
     | 
| 
      
 23 
     | 
    
         
            +
                            end
         
     | 
| 
      
 24 
     | 
    
         
            +
             
     | 
| 
      
 25 
     | 
    
         
            +
                      if negate && tok
         
     | 
| 
      
 26 
     | 
    
         
            +
                        tok[1] = "-#{tok[1]}"
         
     | 
| 
      
 27 
     | 
    
         
            +
                        negate = false
         
     | 
| 
      
 28 
     | 
    
         
            +
                      end
         
     | 
| 
      
 29 
     | 
    
         
            +
             
     | 
| 
      
 30 
     | 
    
         
            +
                      tok
         
     | 
| 
      
 31 
     | 
    
         
            +
                    }.compact
         
     | 
| 
      
 32 
     | 
    
         
            +
             
     | 
| 
      
 33 
     | 
    
         
            +
                    begin
         
     | 
| 
      
 34 
     | 
    
         
            +
                      return do_parse
         
     | 
| 
      
 35 
     | 
    
         
            +
                    rescue ParseError => e
         
     | 
| 
      
 36 
     | 
    
         
            +
                      return nil
         
     | 
| 
      
 37 
     | 
    
         
            +
                    end
         
     | 
| 
      
 38 
     | 
    
         
            +
                  end
         
     | 
| 
      
 39 
     | 
    
         
            +
             
     | 
| 
      
 40 
     | 
    
         
            +
                  private
         
     | 
| 
      
 41 
     | 
    
         
            +
                  def next_token
         
     | 
| 
      
 42 
     | 
    
         
            +
                    return [false, false] if @tokens.empty?
         
     | 
| 
      
 43 
     | 
    
         
            +
                    @tokens.shift
         
     | 
| 
      
 44 
     | 
    
         
            +
                  end
         
     | 
| 
      
 45 
     | 
    
         
            +
                end
         
     | 
| 
      
 46 
     | 
    
         
            +
              end
         
     | 
| 
      
 47 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,87 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module CSS
         
     | 
| 
      
 2 
     | 
    
         
            +
              module SAC
         
     | 
| 
      
 3 
     | 
    
         
            +
                class Selector
         
     | 
| 
      
 4 
     | 
    
         
            +
                  attr_reader :selector_type
         
     | 
| 
      
 5 
     | 
    
         
            +
                end
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
                class SimpleSelector < Selector
         
     | 
| 
      
 8 
     | 
    
         
            +
                  def initialize
         
     | 
| 
      
 9 
     | 
    
         
            +
                    @selector_type = :SAC_ANY_NODE_SELECTOR
         
     | 
| 
      
 10 
     | 
    
         
            +
                  end
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
                  def to_css
         
     | 
| 
      
 13 
     | 
    
         
            +
                    case @selector_type
         
     | 
| 
      
 14 
     | 
    
         
            +
                    when :SAC_ANY_NODE_SELECTOR
         
     | 
| 
      
 15 
     | 
    
         
            +
                      '*'
         
     | 
| 
      
 16 
     | 
    
         
            +
                    end
         
     | 
| 
      
 17 
     | 
    
         
            +
                  end
         
     | 
| 
      
 18 
     | 
    
         
            +
                end
         
     | 
| 
      
 19 
     | 
    
         
            +
             
     | 
| 
      
 20 
     | 
    
         
            +
                class ElementSelector < SimpleSelector
         
     | 
| 
      
 21 
     | 
    
         
            +
                  attr_reader :local_name
         
     | 
| 
      
 22 
     | 
    
         
            +
                  alias :name :local_name
         
     | 
| 
      
 23 
     | 
    
         
            +
             
     | 
| 
      
 24 
     | 
    
         
            +
                  def initialize(name)
         
     | 
| 
      
 25 
     | 
    
         
            +
                    super()
         
     | 
| 
      
 26 
     | 
    
         
            +
                    @selector_type = :SAC_ELEMENT_NODE_SELECTOR
         
     | 
| 
      
 27 
     | 
    
         
            +
                    @local_name = name
         
     | 
| 
      
 28 
     | 
    
         
            +
                  end
         
     | 
| 
      
 29 
     | 
    
         
            +
             
     | 
| 
      
 30 
     | 
    
         
            +
                  def to_css
         
     | 
| 
      
 31 
     | 
    
         
            +
                    local_name
         
     | 
| 
      
 32 
     | 
    
         
            +
                  end
         
     | 
| 
      
 33 
     | 
    
         
            +
                end
         
     | 
| 
      
 34 
     | 
    
         
            +
             
     | 
| 
      
 35 
     | 
    
         
            +
                class ConditionalSelector < SimpleSelector
         
     | 
| 
      
 36 
     | 
    
         
            +
                  attr_accessor :condition, :simple_selector
         
     | 
| 
      
 37 
     | 
    
         
            +
                  alias :selector :simple_selector
         
     | 
| 
      
 38 
     | 
    
         
            +
             
     | 
| 
      
 39 
     | 
    
         
            +
                  def initialize(selector, condition)
         
     | 
| 
      
 40 
     | 
    
         
            +
                    @condition  = condition
         
     | 
| 
      
 41 
     | 
    
         
            +
                    @simple_selector   = selector
         
     | 
| 
      
 42 
     | 
    
         
            +
                    @selector_type = :SAC_CONDITIONAL_SELECTOR
         
     | 
| 
      
 43 
     | 
    
         
            +
                  end
         
     | 
| 
      
 44 
     | 
    
         
            +
             
     | 
| 
      
 45 
     | 
    
         
            +
                  def to_css
         
     | 
| 
      
 46 
     | 
    
         
            +
                    [selector, condition].map { |x|
         
     | 
| 
      
 47 
     | 
    
         
            +
                      x ? x.to_css : ''
         
     | 
| 
      
 48 
     | 
    
         
            +
                    }.join('')
         
     | 
| 
      
 49 
     | 
    
         
            +
                  end
         
     | 
| 
      
 50 
     | 
    
         
            +
                end
         
     | 
| 
      
 51 
     | 
    
         
            +
             
     | 
| 
      
 52 
     | 
    
         
            +
                class DescendantSelector < SimpleSelector
         
     | 
| 
      
 53 
     | 
    
         
            +
                  attr_accessor :ancestor_selector, :simple_selector
         
     | 
| 
      
 54 
     | 
    
         
            +
                  alias :selector :simple_selector
         
     | 
| 
      
 55 
     | 
    
         
            +
             
     | 
| 
      
 56 
     | 
    
         
            +
                  def initialize(ancestor, selector, type)
         
     | 
| 
      
 57 
     | 
    
         
            +
                    @ancestor_selector = ancestor
         
     | 
| 
      
 58 
     | 
    
         
            +
                    @simple_selector = selector
         
     | 
| 
      
 59 
     | 
    
         
            +
                    @selector_type = type
         
     | 
| 
      
 60 
     | 
    
         
            +
                  end
         
     | 
| 
      
 61 
     | 
    
         
            +
             
     | 
| 
      
 62 
     | 
    
         
            +
                  def to_css
         
     | 
| 
      
 63 
     | 
    
         
            +
                    descent_token =
         
     | 
| 
      
 64 
     | 
    
         
            +
                      case @selector_type
         
     | 
| 
      
 65 
     | 
    
         
            +
                      when :SAC_CHILD_SELECTOR
         
     | 
| 
      
 66 
     | 
    
         
            +
                        ' > '
         
     | 
| 
      
 67 
     | 
    
         
            +
                      when :SAC_DESCENDANT_SELECTOR
         
     | 
| 
      
 68 
     | 
    
         
            +
                        ' '
         
     | 
| 
      
 69 
     | 
    
         
            +
                      end
         
     | 
| 
      
 70 
     | 
    
         
            +
                    ancestor_selector.to_css + descent_token + selector.to_css
         
     | 
| 
      
 71 
     | 
    
         
            +
                  end
         
     | 
| 
      
 72 
     | 
    
         
            +
                end
         
     | 
| 
      
 73 
     | 
    
         
            +
             
     | 
| 
      
 74 
     | 
    
         
            +
                class SiblingSelector < SimpleSelector
         
     | 
| 
      
 75 
     | 
    
         
            +
                  attr_accessor :selector, :sibling_selector
         
     | 
| 
      
 76 
     | 
    
         
            +
                  def initialize(selector, sibling)
         
     | 
| 
      
 77 
     | 
    
         
            +
                    @selector = selector
         
     | 
| 
      
 78 
     | 
    
         
            +
                    @sibling_selector = sibling
         
     | 
| 
      
 79 
     | 
    
         
            +
                    @selector_type = :SAC_DIRECT_ADJACENT_SELECTOR
         
     | 
| 
      
 80 
     | 
    
         
            +
                  end
         
     | 
| 
      
 81 
     | 
    
         
            +
             
     | 
| 
      
 82 
     | 
    
         
            +
                  def to_css
         
     | 
| 
      
 83 
     | 
    
         
            +
                    selector.to_css + ' + ' + sibling_selector.to_css
         
     | 
| 
      
 84 
     | 
    
         
            +
                  end
         
     | 
| 
      
 85 
     | 
    
         
            +
                end
         
     | 
| 
      
 86 
     | 
    
         
            +
              end
         
     | 
| 
      
 87 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,27 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module CSS
         
     | 
| 
      
 2 
     | 
    
         
            +
              module SAC
         
     | 
| 
      
 3 
     | 
    
         
            +
                class Token
         
     | 
| 
      
 4 
     | 
    
         
            +
                  attr_reader :name, :value, :position
         
     | 
| 
      
 5 
     | 
    
         
            +
                  
         
     | 
| 
      
 6 
     | 
    
         
            +
                  def initialize(name, value, position)
         
     | 
| 
      
 7 
     | 
    
         
            +
                    @name = name
         
     | 
| 
      
 8 
     | 
    
         
            +
                    @value = value
         
     | 
| 
      
 9 
     | 
    
         
            +
                    @position = position
         
     | 
| 
      
 10 
     | 
    
         
            +
                  end
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
                  def to_racc_token
         
     | 
| 
      
 13 
     | 
    
         
            +
                    [name, value]
         
     | 
| 
      
 14 
     | 
    
         
            +
                  end
         
     | 
| 
      
 15 
     | 
    
         
            +
                end
         
     | 
| 
      
 16 
     | 
    
         
            +
                
         
     | 
| 
      
 17 
     | 
    
         
            +
                class DelimiterToken < Token
         
     | 
| 
      
 18 
     | 
    
         
            +
                  def initialize(value, position)
         
     | 
| 
      
 19 
     | 
    
         
            +
                    super(:delim, value, position)
         
     | 
| 
      
 20 
     | 
    
         
            +
                  end
         
     | 
| 
      
 21 
     | 
    
         
            +
                  
         
     | 
| 
      
 22 
     | 
    
         
            +
                  def to_racc_token
         
     | 
| 
      
 23 
     | 
    
         
            +
                    [value, value]
         
     | 
| 
      
 24 
     | 
    
         
            +
                  end
         
     | 
| 
      
 25 
     | 
    
         
            +
                end  
         
     | 
| 
      
 26 
     | 
    
         
            +
              end
         
     | 
| 
      
 27 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,186 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require "css/sac/lexeme"
         
     | 
| 
      
 2 
     | 
    
         
            +
            require "css/sac/token"
         
     | 
| 
      
 3 
     | 
    
         
            +
             
     | 
| 
      
 4 
     | 
    
         
            +
            module CSS
         
     | 
| 
      
 5 
     | 
    
         
            +
              module SAC
         
     | 
| 
      
 6 
     | 
    
         
            +
                class Tokenizer
         
     | 
| 
      
 7 
     | 
    
         
            +
                  def initialize(&block)
         
     | 
| 
      
 8 
     | 
    
         
            +
                    @lexemes = []
         
     | 
| 
      
 9 
     | 
    
         
            +
                    @macros = {}
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
                    # http://www.w3.org/TR/CSS21/syndata.html
         
     | 
| 
      
 12 
     | 
    
         
            +
                    macro(:h, /([0-9a-f])/ )
         
     | 
| 
      
 13 
     | 
    
         
            +
                    macro(:nonascii, /([\200-\377])/ )
         
     | 
| 
      
 14 
     | 
    
         
            +
                    macro(:nl, /(\n|\r\n|\r|\f)/ )
         
     | 
| 
      
 15 
     | 
    
         
            +
                    macro(:unicode, /(\\#{m(:h)}{1,6}(\r\n|[ \t\r\n\f])?)/ )
         
     | 
| 
      
 16 
     | 
    
         
            +
                    macro(:escape, /(#{m(:unicode)}|\\[^\r\n\f0-9a-f])/ )
         
     | 
| 
      
 17 
     | 
    
         
            +
                    macro(:nmstart, /([_a-z]|#{m(:nonascii)}|#{m(:escape)})/ )
         
     | 
| 
      
 18 
     | 
    
         
            +
                    macro(:nmchar, /([_a-z0-9-]|#{m(:nonascii)}|#{m(:escape)})/ )
         
     | 
| 
      
 19 
     | 
    
         
            +
                    macro(:string1, /(\"([^\n\r\f\\\"]|\\#{m(:nl)}|#{m(:escape)})*\")/ )
         
     | 
| 
      
 20 
     | 
    
         
            +
                    macro(:string2, /(\'([^\n\r\f\\']|\\#{m(:nl)}|#{m(:escape)})*\')/ )
         
     | 
| 
      
 21 
     | 
    
         
            +
                    macro(:invalid1, /(\"([^\n\r\f\\\"]|\\#{m(:nl)}|#{m(:escape)})*)/ )
         
     | 
| 
      
 22 
     | 
    
         
            +
                    macro(:invalid2, /(\'([^\n\r\f\\']|\\#{m(:nl)}|#{m(:escape)})*)/ )
         
     | 
| 
      
 23 
     | 
    
         
            +
                    macro(:comment, /(\/\*[^*]*\*+([^\/*][^*]*\*+)*\/)/ )
         
     | 
| 
      
 24 
     | 
    
         
            +
                    macro(:ident, /(-?#{m(:nmstart)}#{m(:nmchar)}*)/ )
         
     | 
| 
      
 25 
     | 
    
         
            +
                    macro(:name, /(#{m(:nmchar)}+)/ )
         
     | 
| 
      
 26 
     | 
    
         
            +
                    macro(:num, /([0-9]+|[0-9]*\.[0-9]+)/ )
         
     | 
| 
      
 27 
     | 
    
         
            +
                    macro(:string, /(#{m(:string1)}|#{m(:string2)})/ )
         
     | 
| 
      
 28 
     | 
    
         
            +
                    macro(:invalid, /(#{m(:invalid1)}|#{m(:invalid2)})/ )
         
     | 
| 
      
 29 
     | 
    
         
            +
                    macro(:url, /(([!#\$%&*-~]|#{m(:nonascii)}|#{m(:escape)})*)/ )
         
     | 
| 
      
 30 
     | 
    
         
            +
                    macro(:s, /([ \t\r\n\f]+)/ )
         
     | 
| 
      
 31 
     | 
    
         
            +
                    macro(:w, /(#{m(:s)}?)/ )
         
     | 
| 
      
 32 
     | 
    
         
            +
                    macro(:A, /(a|\\0{0,4}(41|61)(\r\n|[ \t\r\n\f])?)/ )
         
     | 
| 
      
 33 
     | 
    
         
            +
                    macro(:C, /(c|\\0{0,4}(43|63)(\r\n|[ \t\r\n\f])?)/ )
         
     | 
| 
      
 34 
     | 
    
         
            +
                    macro(:D, /(d|\\0{0,4}(44|64)(\r\n|[ \t\r\n\f])?)/ )
         
     | 
| 
      
 35 
     | 
    
         
            +
                    macro(:E, /(e|\\0{0,4}(45|65)(\r\n|[ \t\r\n\f])?)/ )
         
     | 
| 
      
 36 
     | 
    
         
            +
                    macro(:G, /(g|\\0{0,4}(47|67)(\r\n|[ \t\r\n\f])?|\\g)/ )
         
     | 
| 
      
 37 
     | 
    
         
            +
                    macro(:H, /(h|\\0{0,4}(48|68)(\r\n|[ \t\r\n\f])?|\\h)/ )
         
     | 
| 
      
 38 
     | 
    
         
            +
                    macro(:I, /(i|\\0{0,4}(49|69)(\r\n|[ \t\r\n\f])?|\\i)/ )
         
     | 
| 
      
 39 
     | 
    
         
            +
                    macro(:K, /(k|\\0{0,4}(4b|6b)(\r\n|[ \t\r\n\f])?|\\k)/ )
         
     | 
| 
      
 40 
     | 
    
         
            +
                    macro(:M, /(m|\\0{0,4}(4d|6d)(\r\n|[ \t\r\n\f])?|\\m)/ )
         
     | 
| 
      
 41 
     | 
    
         
            +
                    macro(:N, /(n|\\0{0,4}(4e|6e)(\r\n|[ \t\r\n\f])?|\\n)/ )
         
     | 
| 
      
 42 
     | 
    
         
            +
                    macro(:O, /(o|\\0{0,4}(51|71)(\r\n|[ \t\r\n\f])?|\\o)/ )
         
     | 
| 
      
 43 
     | 
    
         
            +
                    macro(:P, /(p|\\0{0,4}(50|70)(\r\n|[ \t\r\n\f])?|\\p)/ )
         
     | 
| 
      
 44 
     | 
    
         
            +
                    macro(:R, /(r|\\0{0,4}(52|72)(\r\n|[ \t\r\n\f])?|\\r)/ )
         
     | 
| 
      
 45 
     | 
    
         
            +
                    macro(:S, /(s|\\0{0,4}(53|73)(\r\n|[ \t\r\n\f])?|\\s)/ )
         
     | 
| 
      
 46 
     | 
    
         
            +
                    macro(:T, /(t|\\0{0,4}(54|74)(\r\n|[ \t\r\n\f])?|\\t)/ )
         
     | 
| 
      
 47 
     | 
    
         
            +
                    macro(:X, /(x|\\0{0,4}(58|78)(\r\n|[ \t\r\n\f])?|\\x)/ )
         
     | 
| 
      
 48 
     | 
    
         
            +
                    macro(:Z, /(z|\\0{0,4}(5a|7a)(\r\n|[ \t\r\n\f])?|\\z)/ )
         
     | 
| 
      
 49 
     | 
    
         
            +
             
     | 
| 
      
 50 
     | 
    
         
            +
                    #token :COMMENT do |patterns|
         
     | 
| 
      
 51 
     | 
    
         
            +
                    #  patterns << /\/\*[^*]*\*+([^\/*][^*]*\*+)*\//
         
     | 
| 
      
 52 
     | 
    
         
            +
                    #  patterns << /#{m(:s)}+\/\*[^*]*\*+([^\/*][^*]*\*+)*\//
         
     | 
| 
      
 53 
     | 
    
         
            +
                    #end
         
     | 
| 
      
 54 
     | 
    
         
            +
             
     | 
| 
      
 55 
     | 
    
         
            +
                    token(:LBRACE, /#{m(:w)}\{/)
         
     | 
| 
      
 56 
     | 
    
         
            +
                    token(:PLUS, /#{m(:w)}\+/)
         
     | 
| 
      
 57 
     | 
    
         
            +
                    token(:GREATER, /#{m(:w)}>/)
         
     | 
| 
      
 58 
     | 
    
         
            +
                    token(:COMMA, /#{m(:w)},/)
         
     | 
| 
      
 59 
     | 
    
         
            +
             
     | 
| 
      
 60 
     | 
    
         
            +
                    token(:S, /#{m(:s)}/)
         
     | 
| 
      
 61 
     | 
    
         
            +
             
     | 
| 
      
 62 
     | 
    
         
            +
                    #token :URI do |patterns|
         
     | 
| 
      
 63 
     | 
    
         
            +
                    #  patterns << /url\(#{m(:w)}#{m(:string)}#{m(:w)}\)/
         
     | 
| 
      
 64 
     | 
    
         
            +
                    #  patterns << /url\(#{m(:w)}#{m(:url)}#{m(:w)}\)/
         
     | 
| 
      
 65 
     | 
    
         
            +
                    #end
         
     | 
| 
      
 66 
     | 
    
         
            +
             
     | 
| 
      
 67 
     | 
    
         
            +
                    token(:FUNCTION, /#{m(:ident)}\(/)
         
     | 
| 
      
 68 
     | 
    
         
            +
                    token(:IDENT, /#{m(:ident)}/)
         
     | 
| 
      
 69 
     | 
    
         
            +
             
     | 
| 
      
 70 
     | 
    
         
            +
                    token(:CDO, /<!--/)
         
     | 
| 
      
 71 
     | 
    
         
            +
                    token(:CDC, /-->/)
         
     | 
| 
      
 72 
     | 
    
         
            +
                    token(:INCLUDES, /~=/)
         
     | 
| 
      
 73 
     | 
    
         
            +
                    token(:DASHMATCH, /\|=/)
         
     | 
| 
      
 74 
     | 
    
         
            +
                    #token(:STRING, /#{m(:string)}/)
         
     | 
| 
      
 75 
     | 
    
         
            +
                    token(:INVALID, /#{m(:invalid)}/)
         
     | 
| 
      
 76 
     | 
    
         
            +
                    token(:HASH, /##{m(:name)}/)
         
     | 
| 
      
 77 
     | 
    
         
            +
                    token(:IMPORT_SYM, /@#{m(:I)}#{m(:M)}#{m(:P)}#{m(:O)}#{m(:R)}#{m(:T)}/)
         
     | 
| 
      
 78 
     | 
    
         
            +
                    token(:PAGE_SYM, /@#{m(:P)}#{m(:A)}#{m(:G)}#{m(:E)}/)
         
     | 
| 
      
 79 
     | 
    
         
            +
                    token(:MEDIA_SYM, /@#{m(:M)}#{m(:E)}#{m(:D)}#{m(:I)}#{m(:A)}/)
         
     | 
| 
      
 80 
     | 
    
         
            +
                    token(:CHARSET_SYM, /@#{m(:C)}#{m(:H)}#{m(:A)}#{m(:R)}#{m(:S)}#{m(:E)}#{m(:T)}/)
         
     | 
| 
      
 81 
     | 
    
         
            +
                    token(:IMPORTANT_SYM, /!(#{m(:w)}|#{m(:comment)})*#{m(:I)}#{m(:M)}#{m(:P)}#{m(:O)}#{m(:R)}#{m(:T)}#{m(:A)}#{m(:N)}#{m(:T)}/)
         
     | 
| 
      
 82 
     | 
    
         
            +
                    token(:EMS, /#{m(:num)}#{m(:E)}#{m(:M)}/)
         
     | 
| 
      
 83 
     | 
    
         
            +
                    token(:EXS, /#{m(:num)}#{m(:E)}#{m(:X)}/)
         
     | 
| 
      
 84 
     | 
    
         
            +
             
     | 
| 
      
 85 
     | 
    
         
            +
                    token :LENGTH do |patterns|
         
     | 
| 
      
 86 
     | 
    
         
            +
                      patterns << /#{m(:num)}#{m(:P)}#{m(:X)}/
         
     | 
| 
      
 87 
     | 
    
         
            +
                      patterns << /#{m(:num)}#{m(:C)}#{m(:M)}/
         
     | 
| 
      
 88 
     | 
    
         
            +
                      patterns << /#{m(:num)}#{m(:M)}#{m(:M)}/
         
     | 
| 
      
 89 
     | 
    
         
            +
                      patterns << /#{m(:num)}#{m(:I)}#{m(:N)}/
         
     | 
| 
      
 90 
     | 
    
         
            +
                      patterns << /#{m(:num)}#{m(:P)}#{m(:T)}/
         
     | 
| 
      
 91 
     | 
    
         
            +
                      patterns << /#{m(:num)}#{m(:P)}#{m(:C)}/
         
     | 
| 
      
 92 
     | 
    
         
            +
                    end
         
     | 
| 
      
 93 
     | 
    
         
            +
             
     | 
| 
      
 94 
     | 
    
         
            +
                    token :ANGLE do |patterns|
         
     | 
| 
      
 95 
     | 
    
         
            +
                      patterns << /#{m(:num)}#{m(:D)}#{m(:E)}#{m(:G)}/
         
     | 
| 
      
 96 
     | 
    
         
            +
                      patterns << /#{m(:num)}#{m(:R)}#{m(:A)}#{m(:D)}/
         
     | 
| 
      
 97 
     | 
    
         
            +
                      patterns << /#{m(:num)}#{m(:G)}#{m(:R)}#{m(:A)}#{m(:D)}/
         
     | 
| 
      
 98 
     | 
    
         
            +
                    end
         
     | 
| 
      
 99 
     | 
    
         
            +
             
     | 
| 
      
 100 
     | 
    
         
            +
                    token :TIME do |patterns|
         
     | 
| 
      
 101 
     | 
    
         
            +
                      patterns << /#{m(:num)}#{m(:M)}#{m(:S)}/
         
     | 
| 
      
 102 
     | 
    
         
            +
                      patterns << /#{m(:num)}#{m(:S)}/
         
     | 
| 
      
 103 
     | 
    
         
            +
                    end
         
     | 
| 
      
 104 
     | 
    
         
            +
             
     | 
| 
      
 105 
     | 
    
         
            +
                    token :FREQ do |patterns|
         
     | 
| 
      
 106 
     | 
    
         
            +
                      patterns << /#{m(:num)}#{m(:H)}#{m(:Z)}/
         
     | 
| 
      
 107 
     | 
    
         
            +
                      patterns << /#{m(:num)}#{m(:K)}#{m(:H)}#{m(:Z)}/
         
     | 
| 
      
 108 
     | 
    
         
            +
                    end
         
     | 
| 
      
 109 
     | 
    
         
            +
             
     | 
| 
      
 110 
     | 
    
         
            +
                    token(:DIMENSION, /#{m(:num)}#{m(:ident)}/)
         
     | 
| 
      
 111 
     | 
    
         
            +
                    token(:PERCENTAGE, /#{m(:num)}%/)
         
     | 
| 
      
 112 
     | 
    
         
            +
                    token(:NUMBER, /#{m(:num)}/)
         
     | 
| 
      
 113 
     | 
    
         
            +
             
     | 
| 
      
 114 
     | 
    
         
            +
             
     | 
| 
      
 115 
     | 
    
         
            +
                    yield self if block_given?
         
     | 
| 
      
 116 
     | 
    
         
            +
                  end
         
     | 
| 
      
 117 
     | 
    
         
            +
                  
         
     | 
| 
      
 118 
     | 
    
         
            +
                  def tokenize(input_data)
         
     | 
| 
      
 119 
     | 
    
         
            +
                    tokens = []
         
     | 
| 
      
 120 
     | 
    
         
            +
                    pos = 0
         
     | 
| 
      
 121 
     | 
    
         
            +
             
     | 
| 
      
 122 
     | 
    
         
            +
                    comments = input_data.scan(/\/\*[^*]*\*+\//m)
         
     | 
| 
      
 123 
     | 
    
         
            +
                    non_comments = input_data.split(/\/\*[^*]*\*+\//m)
         
     | 
| 
      
 124 
     | 
    
         
            +
             
     | 
| 
      
 125 
     | 
    
         
            +
                    # Handle a small edge case, if our CSS is *only* comments,
         
     | 
| 
      
 126 
     | 
    
         
            +
                    # the split, zip, scan trick won't work
         
     | 
| 
      
 127 
     | 
    
         
            +
                    if non_comments.length == 0
         
     | 
| 
      
 128 
     | 
    
         
            +
                      tokens = comments.map { |x| Token.new(:COMMENT, x, nil) }
         
     | 
| 
      
 129 
     | 
    
         
            +
                    else
         
     | 
| 
      
 130 
     | 
    
         
            +
                      non_comments.zip(comments).each do |non_comment, comment|
         
     | 
| 
      
 131 
     | 
    
         
            +
                        non_comment.split(/url\([^\)]*\)/m).zip(
         
     | 
| 
      
 132 
     | 
    
         
            +
                          non_comment.scan(/url\([^\)]*\)/m)
         
     | 
| 
      
 133 
     | 
    
         
            +
                        ).each do |non_url, url|
         
     | 
| 
      
 134 
     | 
    
         
            +
                          non_url.split(/"[^"]*"|'[^']*'/m).zip(
         
     | 
| 
      
 135 
     | 
    
         
            +
                            non_url.scan(/"[^"]*"|'[^']*'/m)
         
     | 
| 
      
 136 
     | 
    
         
            +
                          ).each do |non_string, quoted_string|
         
     | 
| 
      
 137 
     | 
    
         
            +
                            if non_string.length > 0 && non_string =~ /\A\s*\Z/m
         
     | 
| 
      
 138 
     | 
    
         
            +
                              tokens << Token.new(:S, non_string, nil)
         
     | 
| 
      
 139 
     | 
    
         
            +
                            else
         
     | 
| 
      
 140 
     | 
    
         
            +
                              non_string.split(/[ \t\r\n\f]*(?![{}+>]*)/m).zip(
         
     | 
| 
      
 141 
     | 
    
         
            +
                                non_string.scan(/[ \t\r\n\f]*(?![{}+>]*)/m)
         
     | 
| 
      
 142 
     | 
    
         
            +
                              ).each do |string, whitespace|
         
     | 
| 
      
 143 
     | 
    
         
            +
                                until string.empty?
         
     | 
| 
      
 144 
     | 
    
         
            +
                                  token = nil
         
     | 
| 
      
 145 
     | 
    
         
            +
                                  @lexemes.each do |lexeme|
         
     | 
| 
      
 146 
     | 
    
         
            +
                                    match = lexeme.pattern.match(string)
         
     | 
| 
      
 147 
     | 
    
         
            +
                                    if match
         
     | 
| 
      
 148 
     | 
    
         
            +
                                      token = Token.new(lexeme.name, match.to_s, pos)
         
     | 
| 
      
 149 
     | 
    
         
            +
                                      break
         
     | 
| 
      
 150 
     | 
    
         
            +
                                    end
         
     | 
| 
      
 151 
     | 
    
         
            +
                                  end
         
     | 
| 
      
 152 
     | 
    
         
            +
             
     | 
| 
      
 153 
     | 
    
         
            +
                                  token ||= DelimiterToken.new(/^./.match(string).to_s, pos)
         
     | 
| 
      
 154 
     | 
    
         
            +
             
     | 
| 
      
 155 
     | 
    
         
            +
                                  tokens << token
         
     | 
| 
      
 156 
     | 
    
         
            +
                                  string = string.slice(Range.new(token.value.length, -1))
         
     | 
| 
      
 157 
     | 
    
         
            +
                                  pos += token.value.length
         
     | 
| 
      
 158 
     | 
    
         
            +
                                end
         
     | 
| 
      
 159 
     | 
    
         
            +
                                tokens << Token.new(:S, whitespace, nil) if whitespace
         
     | 
| 
      
 160 
     | 
    
         
            +
                              end
         
     | 
| 
      
 161 
     | 
    
         
            +
                            end
         
     | 
| 
      
 162 
     | 
    
         
            +
                            tokens << Token.new(:STRING, quoted_string, nil) if quoted_string
         
     | 
| 
      
 163 
     | 
    
         
            +
                          end
         
     | 
| 
      
 164 
     | 
    
         
            +
                          tokens << Token.new(:URI, url, nil) if url
         
     | 
| 
      
 165 
     | 
    
         
            +
                        end
         
     | 
| 
      
 166 
     | 
    
         
            +
                        tokens << Token.new(:COMMENT, comment, nil) if comment
         
     | 
| 
      
 167 
     | 
    
         
            +
                      end
         
     | 
| 
      
 168 
     | 
    
         
            +
                    end
         
     | 
| 
      
 169 
     | 
    
         
            +
                    
         
     | 
| 
      
 170 
     | 
    
         
            +
                    tokens
         
     | 
| 
      
 171 
     | 
    
         
            +
                  end
         
     | 
| 
      
 172 
     | 
    
         
            +
                  
         
     | 
| 
      
 173 
     | 
    
         
            +
                  private
         
     | 
| 
      
 174 
     | 
    
         
            +
                  
         
     | 
| 
      
 175 
     | 
    
         
            +
                  def token(name, pattern=nil, &block)
         
     | 
| 
      
 176 
     | 
    
         
            +
                    @lexemes << Lexeme.new(name, pattern, &block)
         
     | 
| 
      
 177 
     | 
    
         
            +
                  end
         
     | 
| 
      
 178 
     | 
    
         
            +
             
     | 
| 
      
 179 
     | 
    
         
            +
                  def macro(name, regex=nil)
         
     | 
| 
      
 180 
     | 
    
         
            +
                    regex ? @macros[name] = regex : @macros[name].source
         
     | 
| 
      
 181 
     | 
    
         
            +
                  end
         
     | 
| 
      
 182 
     | 
    
         
            +
             
     | 
| 
      
 183 
     | 
    
         
            +
                  alias :m :macro
         
     | 
| 
      
 184 
     | 
    
         
            +
                end
         
     | 
| 
      
 185 
     | 
    
         
            +
              end
         
     | 
| 
      
 186 
     | 
    
         
            +
            end
         
     |