Class: CSS::Selectors::Parser

Inherits:
Object
  • Object
show all
Includes:
TokenCursor
Defined in:
lib/css/selectors/parser.rb

Overview

Parser for CSS Selectors Level 4. Covers compound and complex selectors, the four standard combinators (descendant, child, next- sibling, subsequent-sibling), pseudo-classes / pseudo-elements (with recursive parsing of ‘:not/:is/:where/:has` and AnB parsing of `:nth-*`), attribute selectors with case-insensitive `i` / `s` flags, and the `&` nesting selector.

Out of scope (intermediate plan): namespace prefixes, the column combinator ‘||`, and forgiving vs strict selector list distinctions.

Constant Summary collapse

SELECTOR_LIST_PSEUDOS =

‘:has()` is intentionally excluded — it takes a relative selector list (each item may start with a combinator) which would require extending the ComplexSelector AST. Falls back to opaque component values for now.

%w[is where not matches].freeze
ANB_PSEUDOS =
%w[nth-child nth-last-child nth-of-type nth-last-of-type].freeze
ATTR_MATCHERS =
{
  '~' => :includes,
  '|' => :dash,
  '^' => :prefix,
  '$' => :suffix,
  '*' => :substring
}.freeze
BRACKET_TYPE_FOR_OPEN =
BRACKET_OPEN_CHAR.invert.freeze
BRACKET_CLOSE_TYPE_FOR_OPEN =
BRACKET_OPEN_CHAR.to_h {|type, ch| [ch, BRACKET_CLOSE_TYPE.fetch(type)] }.freeze

Constants included from TokenCursor

TokenCursor::EOF_TOKEN

Class Method Summary collapse

Instance Method Summary collapse

Methods included from TokenCursor

#consume, #eof?, #init_cursor, #parse_error!, #peek, #peek_token, #skip_whitespace

Constructor Details

#initialize(tokens) ⇒ Parser

Returns a new instance of Parser.



79
80
81
# File 'lib/css/selectors/parser.rb', line 79

def initialize(tokens)
  init_cursor(tokens)
end

Class Method Details

.parse_selector(input) ⇒ Object



35
36
37
# File 'lib/css/selectors/parser.rb', line 35

def parse_selector(input)
  new(tokens_from(input)).parse_selector_complete
end

.parse_selector_list(input) ⇒ Object



31
32
33
# File 'lib/css/selectors/parser.rb', line 31

def parse_selector_list(input)
  new(tokens_from(input)).parse_selector_list_complete
end

Instance Method Details

#parse_complex_selectorObject



126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
# File 'lib/css/selectors/parser.rb', line 126

def parse_complex_selector
  skip_whitespace

  compounds   = [parse_compound_selector]
  combinators = []

  loop do
    combo = try_consume_combinator
    break if combo.nil?

    compounds   << parse_compound_selector
    combinators << combo
  end

  ComplexSelector.new(compounds:, combinators:)
end

#parse_selector_completeObject



93
94
95
96
97
98
99
100
101
102
103
# File 'lib/css/selectors/parser.rb', line 93

def parse_selector_complete
  skip_whitespace

  cs = parse_complex_selector

  skip_whitespace

  parse_error!("trailing tokens after selector: #{peek.type}") unless peek.type == :eof

  cs
end

#parse_selector_listObject

A comma-separated list of complex selectors, terminated by EOF or ‘)` (for use inside functional pseudos like `:is(…)`).



107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
# File 'lib/css/selectors/parser.rb', line 107

def parse_selector_list
  skip_whitespace

  parse_error!('empty selector list') if list_terminator?(peek)

  selectors = [parse_complex_selector]

  loop do
    skip_whitespace
    break unless peek.type == :comma

    consume
    skip_whitespace
    selectors << parse_complex_selector
  end

  SelectorList.new(selectors:)
end

#parse_selector_list_completeObject



83
84
85
86
87
88
89
90
91
# File 'lib/css/selectors/parser.rb', line 83

def parse_selector_list_complete
  list = parse_selector_list

  skip_whitespace

  parse_error!("trailing tokens after selector list: #{peek.type}") unless peek.type == :eof

  list
end