Class: CSS::Selectors::Parser
- Inherits:
-
Object
- Object
- CSS::Selectors::Parser
- 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-*`, including `An+B of S`), attribute selectors with case- insensitive `i` / `s` flags, the `&` nesting selector, and namespace prefixes (`*|name`, `|name`; a declared prefix is rejected — there is no `@namespace` support).
Out of scope: the column combinator ‘||`, and forgiving vs strict selector list distinctions.
Constant Summary collapse
- SELECTOR_LIST_PSEUDOS =
%w[is where not matches].freeze
- ANB_PSEUDOS =
%w[nth-child nth-last-child nth-of-type nth-last-of-type].freeze
- KNOWN_PSEUDO_ELEMENTS =
Per the Selectors grammar (and every browser’s querySelector), a known pseudo-element is a valid selector that simply matches no element, while an unknown one (‘::example`) is a syntax error. Vendor-prefixed `::-webkit-…` are accepted leniently since they match nothing.
%w[ before after first-line first-letter marker placeholder selection backdrop file-selector-button target-text grammar-error spelling-error highlight cue cue-region part slotted details-content view-transition view-transition-group view-transition-image-pair view-transition-old view-transition-new ].to_set.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
Class Method Summary collapse
-
.parse_nesting_selector_list(input) ⇒ Object
Like ‘parse_selector_list`, but each complex selector may begin with a combinator (`>` / `+` / `~`), synthesising a leading `&`.
- .parse_selector(input) ⇒ Object
- .parse_selector_list(input) ⇒ Object
Instance Method Summary collapse
-
#initialize(tokens) ⇒ Parser
constructor
A new instance of Parser.
- #parse_complex_selector ⇒ Object
-
#parse_nesting_complex_selector ⇒ Object
A nested-rule complex selector may start with a combinator, which implies a leading ‘&` (`> .c` → `& > .c`), preserving the `compounds == combinators + 1` invariant.
- #parse_nesting_selector_list ⇒ Object
- #parse_nesting_selector_list_complete ⇒ Object
- #parse_selector_complete ⇒ Object
-
#parse_selector_list ⇒ Object
A comma-separated list of complex selectors, terminated by EOF or ‘)` (for use inside functional pseudos like `:is(…)`).
- #parse_selector_list_complete ⇒ Object
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.
98 99 100 |
# File 'lib/css/selectors/parser.rb', line 98 def initialize(tokens) init_cursor(tokens) end |
Class Method Details
.parse_nesting_selector_list(input) ⇒ Object
Like ‘parse_selector_list`, but each complex selector may begin with a combinator (`>` / `+` / `~`), synthesising a leading `&`. Used for CSS Nesting nested rules, whose preludes follow the `<relative-selector-list>` grammar.
54 55 56 |
# File 'lib/css/selectors/parser.rb', line 54 def parse_nesting_selector_list(input) new(tokens_from(input)).parse_nesting_selector_list_complete end |
.parse_selector(input) ⇒ Object
46 47 48 |
# File 'lib/css/selectors/parser.rb', line 46 def parse_selector(input) new(tokens_from(input)).parse_selector_complete end |
.parse_selector_list(input) ⇒ Object
42 43 44 |
# File 'lib/css/selectors/parser.rb', line 42 def parse_selector_list(input) new(tokens_from(input)).parse_selector_list_complete end |
Instance Method Details
#parse_complex_selector ⇒ Object
145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 |
# File 'lib/css/selectors/parser.rb', line 145 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_nesting_complex_selector ⇒ Object
A nested-rule complex selector may start with a combinator, which implies a leading ‘&` (`> .c` → `& > .c`), preserving the `compounds == combinators + 1` invariant.
194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 |
# File 'lib/css/selectors/parser.rb', line 194 def parse_nesting_complex_selector skip_whitespace t = peek if t.type == :delim && (combo = combinator_for_delim(t.value)) consume skip_whitespace rest = parse_complex_selector return ComplexSelector.new( compounds: [CompoundSelector.new(components: [NestingSelector.new]), *rest.compounds], combinators: [combo, *rest.combinators] ) end parse_complex_selector end |
#parse_nesting_selector_list ⇒ Object
172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 |
# File 'lib/css/selectors/parser.rb', line 172 def parse_nesting_selector_list skip_whitespace parse_error!('empty selector list') if list_terminator?(peek) selectors = [parse_nesting_complex_selector] loop do skip_whitespace break unless peek.type == :comma consume skip_whitespace selectors << parse_nesting_complex_selector end SelectorList.new(selectors:) end |
#parse_nesting_selector_list_complete ⇒ Object
162 163 164 165 166 167 168 169 170 |
# File 'lib/css/selectors/parser.rb', line 162 def parse_nesting_selector_list_complete list = parse_nesting_selector_list skip_whitespace parse_error!("trailing tokens after selector list: #{peek.type}") unless peek.type == :eof list end |
#parse_selector_complete ⇒ Object
112 113 114 115 116 117 118 119 120 121 122 |
# File 'lib/css/selectors/parser.rb', line 112 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_list ⇒ Object
A comma-separated list of complex selectors, terminated by EOF or ‘)` (for use inside functional pseudos like `:is(…)`).
126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 |
# File 'lib/css/selectors/parser.rb', line 126 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_complete ⇒ Object
102 103 104 105 106 107 108 109 110 |
# File 'lib/css/selectors/parser.rb', line 102 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 |