all repos

rss-tools @ a5ac527

get rss feed from sources that(i need and) dont provide one

rss-tools/vendor/github.com/andybalholm/cascadia/parser.go (view raw)

Oleksandr Smirnov Oleksandr Smirnov
olexsmir@gmail.com
we're vendoring now, 7 days ago
1
// Package cascadia is an implementation of CSS selectors.
2
package cascadia
3
4
import (
5
	"errors"
6
	"fmt"
7
	"regexp"
8
	"strconv"
9
	"strings"
10
)
11
12
// a parser for CSS selectors
13
type parser struct {
14
	s string // the source text
15
	i int    // the current position
16
17
	// if `false`, parsing a pseudo-element
18
	// returns an error.
19
	acceptPseudoElements bool
20
}
21
22
// parseEscape parses a backslash escape.
23
func (p *parser) parseEscape() (result string, err error) {
24
	if len(p.s) < p.i+2 || p.s[p.i] != '\\' {
25
		return "", errors.New("invalid escape sequence")
26
	}
27
28
	start := p.i + 1
29
	c := p.s[start]
30
	switch {
31
	case c == '\r' || c == '\n' || c == '\f':
32
		return "", errors.New("escaped line ending outside string")
33
	case hexDigit(c):
34
		// unicode escape (hex)
35
		var i int
36
		for i = start; i < start+6 && i < len(p.s) && hexDigit(p.s[i]); i++ {
37
			// empty
38
		}
39
		v, _ := strconv.ParseUint(p.s[start:i], 16, 64)
40
		if len(p.s) > i {
41
			switch p.s[i] {
42
			case '\r':
43
				i++
44
				if len(p.s) > i && p.s[i] == '\n' {
45
					i++
46
				}
47
			case ' ', '\t', '\n', '\f':
48
				i++
49
			}
50
		}
51
		p.i = i
52
		return string(rune(v)), nil
53
	}
54
55
	// Return the literal character after the backslash.
56
	result = p.s[start : start+1]
57
	p.i += 2
58
	return result, nil
59
}
60
61
// toLowerASCII returns s with all ASCII capital letters lowercased.
62
func toLowerASCII(s string) string {
63
	var b []byte
64
	for i := 0; i < len(s); i++ {
65
		if c := s[i]; 'A' <= c && c <= 'Z' {
66
			if b == nil {
67
				b = make([]byte, len(s))
68
				copy(b, s)
69
			}
70
			b[i] = s[i] + ('a' - 'A')
71
		}
72
	}
73
74
	if b == nil {
75
		return s
76
	}
77
78
	return string(b)
79
}
80
81
func hexDigit(c byte) bool {
82
	return '0' <= c && c <= '9' || 'a' <= c && c <= 'f' || 'A' <= c && c <= 'F'
83
}
84
85
// nameStart returns whether c can be the first character of an identifier
86
// (not counting an initial hyphen, or an escape sequence).
87
func nameStart(c byte) bool {
88
	return 'a' <= c && c <= 'z' || 'A' <= c && c <= 'Z' || c == '_' || c > 127
89
}
90
91
// nameChar returns whether c can be a character within an identifier
92
// (not counting an escape sequence).
93
func nameChar(c byte) bool {
94
	return 'a' <= c && c <= 'z' || 'A' <= c && c <= 'Z' || c == '_' || c > 127 ||
95
		c == '-' || '0' <= c && c <= '9'
96
}
97
98
// parseIdentifier parses an identifier.
99
func (p *parser) parseIdentifier() (result string, err error) {
100
	const prefix = '-'
101
	var numPrefix int
102
103
	for len(p.s) > p.i && p.s[p.i] == prefix {
104
		p.i++
105
		numPrefix++
106
	}
107
108
	if len(p.s) <= p.i {
109
		return "", errors.New("expected identifier, found EOF instead")
110
	}
111
112
	if c := p.s[p.i]; !(nameStart(c) || c == '\\') {
113
		return "", fmt.Errorf("expected identifier, found %c instead", c)
114
	}
115
116
	result, err = p.parseName()
117
	if numPrefix > 0 && err == nil {
118
		result = strings.Repeat(string(prefix), numPrefix) + result
119
	}
120
	return
121
}
122
123
// parseName parses a name (which is like an identifier, but doesn't have
124
// extra restrictions on the first character).
125
func (p *parser) parseName() (result string, err error) {
126
	i := p.i
127
loop:
128
	for i < len(p.s) {
129
		c := p.s[i]
130
		switch {
131
		case nameChar(c):
132
			start := i
133
			for i < len(p.s) && nameChar(p.s[i]) {
134
				i++
135
			}
136
			result += p.s[start:i]
137
		case c == '\\':
138
			p.i = i
139
			val, err := p.parseEscape()
140
			if err != nil {
141
				return "", err
142
			}
143
			i = p.i
144
			result += val
145
		default:
146
			break loop
147
		}
148
	}
149
150
	if result == "" {
151
		return "", errors.New("expected name, found EOF instead")
152
	}
153
154
	p.i = i
155
	return result, nil
156
}
157
158
// parseString parses a single- or double-quoted string.
159
func (p *parser) parseString() (result string, err error) {
160
	i := p.i
161
	if len(p.s) < i+2 {
162
		return "", errors.New("expected string, found EOF instead")
163
	}
164
165
	quote := p.s[i]
166
	i++
167
168
loop:
169
	for i < len(p.s) {
170
		switch p.s[i] {
171
		case '\\':
172
			if len(p.s) > i+1 {
173
				switch c := p.s[i+1]; c {
174
				case '\r':
175
					if len(p.s) > i+2 && p.s[i+2] == '\n' {
176
						i += 3
177
						continue loop
178
					}
179
					fallthrough
180
				case '\n', '\f':
181
					i += 2
182
					continue loop
183
				}
184
			}
185
			p.i = i
186
			val, err := p.parseEscape()
187
			if err != nil {
188
				return "", err
189
			}
190
			i = p.i
191
			result += val
192
		case quote:
193
			break loop
194
		case '\r', '\n', '\f':
195
			return "", errors.New("unexpected end of line in string")
196
		default:
197
			start := i
198
			for i < len(p.s) {
199
				if c := p.s[i]; c == quote || c == '\\' || c == '\r' || c == '\n' || c == '\f' {
200
					break
201
				}
202
				i++
203
			}
204
			result += p.s[start:i]
205
		}
206
	}
207
208
	if i >= len(p.s) {
209
		return "", errors.New("EOF in string")
210
	}
211
212
	// Consume the final quote.
213
	i++
214
215
	p.i = i
216
	return result, nil
217
}
218
219
// parseRegex parses a regular expression; the end is defined by encountering an
220
// unmatched closing ')' or ']' which is not consumed
221
func (p *parser) parseRegex() (rx *regexp.Regexp, err error) {
222
	i := p.i
223
	if len(p.s) < i+2 {
224
		return nil, errors.New("expected regular expression, found EOF instead")
225
	}
226
227
	// number of open parens or brackets;
228
	// when it becomes negative, finished parsing regex
229
	open := 0
230
231
loop:
232
	for i < len(p.s) {
233
		switch p.s[i] {
234
		case '(', '[':
235
			open++
236
		case ')', ']':
237
			open--
238
			if open < 0 {
239
				break loop
240
			}
241
		}
242
		i++
243
	}
244
245
	if i >= len(p.s) {
246
		return nil, errors.New("EOF in regular expression")
247
	}
248
	rx, err = regexp.Compile(p.s[p.i:i])
249
	p.i = i
250
	return rx, err
251
}
252
253
// skipWhitespace consumes whitespace characters and comments.
254
// It returns true if there was actually anything to skip.
255
func (p *parser) skipWhitespace() bool {
256
	i := p.i
257
	for i < len(p.s) {
258
		switch p.s[i] {
259
		case ' ', '\t', '\r', '\n', '\f':
260
			i++
261
			continue
262
		case '/':
263
			if strings.HasPrefix(p.s[i:], "/*") {
264
				end := strings.Index(p.s[i+len("/*"):], "*/")
265
				if end != -1 {
266
					i += end + len("/**/")
267
					continue
268
				}
269
			}
270
		}
271
		break
272
	}
273
274
	if i > p.i {
275
		p.i = i
276
		return true
277
	}
278
279
	return false
280
}
281
282
// consumeParenthesis consumes an opening parenthesis and any following
283
// whitespace. It returns true if there was actually a parenthesis to skip.
284
func (p *parser) consumeParenthesis() bool {
285
	if p.i < len(p.s) && p.s[p.i] == '(' {
286
		p.i++
287
		p.skipWhitespace()
288
		return true
289
	}
290
	return false
291
}
292
293
// consumeClosingParenthesis consumes a closing parenthesis and any preceding
294
// whitespace. It returns true if there was actually a parenthesis to skip.
295
func (p *parser) consumeClosingParenthesis() bool {
296
	i := p.i
297
	p.skipWhitespace()
298
	if p.i < len(p.s) && p.s[p.i] == ')' {
299
		p.i++
300
		return true
301
	}
302
	p.i = i
303
	return false
304
}
305
306
// parseTypeSelector parses a type selector (one that matches by tag name).
307
func (p *parser) parseTypeSelector() (result tagSelector, err error) {
308
	tag, err := p.parseIdentifier()
309
	if err != nil {
310
		return
311
	}
312
	return tagSelector{tag: toLowerASCII(tag)}, nil
313
}
314
315
// parseIDSelector parses a selector that matches by id attribute.
316
func (p *parser) parseIDSelector() (idSelector, error) {
317
	if p.i >= len(p.s) {
318
		return idSelector{}, fmt.Errorf("expected id selector (#id), found EOF instead")
319
	}
320
	if p.s[p.i] != '#' {
321
		return idSelector{}, fmt.Errorf("expected id selector (#id), found '%c' instead", p.s[p.i])
322
	}
323
324
	p.i++
325
	id, err := p.parseName()
326
	if err != nil {
327
		return idSelector{}, err
328
	}
329
330
	return idSelector{id: id}, nil
331
}
332
333
// parseClassSelector parses a selector that matches by class attribute.
334
func (p *parser) parseClassSelector() (classSelector, error) {
335
	if p.i >= len(p.s) {
336
		return classSelector{}, fmt.Errorf("expected class selector (.class), found EOF instead")
337
	}
338
	if p.s[p.i] != '.' {
339
		return classSelector{}, fmt.Errorf("expected class selector (.class), found '%c' instead", p.s[p.i])
340
	}
341
342
	p.i++
343
	class, err := p.parseIdentifier()
344
	if err != nil {
345
		return classSelector{}, err
346
	}
347
348
	return classSelector{class: class}, nil
349
}
350
351
// parseAttributeSelector parses a selector that matches by attribute value.
352
func (p *parser) parseAttributeSelector() (attrSelector, error) {
353
	if p.i >= len(p.s) {
354
		return attrSelector{}, fmt.Errorf("expected attribute selector ([attribute]), found EOF instead")
355
	}
356
	if p.s[p.i] != '[' {
357
		return attrSelector{}, fmt.Errorf("expected attribute selector ([attribute]), found '%c' instead", p.s[p.i])
358
	}
359
360
	p.i++
361
	p.skipWhitespace()
362
	key, err := p.parseIdentifier()
363
	if err != nil {
364
		return attrSelector{}, err
365
	}
366
	key = toLowerASCII(key)
367
368
	p.skipWhitespace()
369
	if p.i >= len(p.s) {
370
		return attrSelector{}, errors.New("unexpected EOF in attribute selector")
371
	}
372
373
	if p.s[p.i] == ']' {
374
		p.i++
375
		return attrSelector{key: key, operation: ""}, nil
376
	}
377
378
	if p.i+2 >= len(p.s) {
379
		return attrSelector{}, errors.New("unexpected EOF in attribute selector")
380
	}
381
382
	op := p.s[p.i : p.i+2]
383
	if op[0] == '=' {
384
		op = "="
385
	} else if op[1] != '=' {
386
		return attrSelector{}, fmt.Errorf(`expected equality operator, found "%s" instead`, op)
387
	}
388
	p.i += len(op)
389
390
	p.skipWhitespace()
391
	if p.i >= len(p.s) {
392
		return attrSelector{}, errors.New("unexpected EOF in attribute selector")
393
	}
394
	var val string
395
	var rx *regexp.Regexp
396
	if op == "#=" {
397
		rx, err = p.parseRegex()
398
	} else {
399
		switch p.s[p.i] {
400
		case '\'', '"':
401
			val, err = p.parseString()
402
		default:
403
			val, err = p.parseIdentifier()
404
		}
405
	}
406
	if err != nil {
407
		return attrSelector{}, err
408
	}
409
410
	p.skipWhitespace()
411
	if p.i >= len(p.s) {
412
		return attrSelector{}, errors.New("unexpected EOF in attribute selector")
413
	}
414
415
	// check if the attribute contains an ignore case flag
416
	ignoreCase := false
417
	if p.s[p.i] == 'i' || p.s[p.i] == 'I' {
418
		ignoreCase = true
419
		p.i++
420
	}
421
422
	p.skipWhitespace()
423
	if p.i >= len(p.s) {
424
		return attrSelector{}, errors.New("unexpected EOF in attribute selector")
425
	}
426
427
	if p.s[p.i] != ']' {
428
		return attrSelector{}, fmt.Errorf("expected ']', found '%c' instead", p.s[p.i])
429
	}
430
	p.i++
431
432
	switch op {
433
	case "=", "!=", "~=", "|=", "^=", "$=", "*=", "#=":
434
		return attrSelector{key: key, val: val, operation: op, regexp: rx, insensitive: ignoreCase}, nil
435
	default:
436
		return attrSelector{}, fmt.Errorf("attribute operator %q is not supported", op)
437
	}
438
}
439
440
var (
441
	errExpectedParenthesis        = errors.New("expected '(' but didn't find it")
442
	errExpectedClosingParenthesis = errors.New("expected ')' but didn't find it")
443
	errUnmatchedParenthesis       = errors.New("unmatched '('")
444
)
445
446
// parsePseudoclassSelector parses a pseudoclass selector like :not(p) or a pseudo-element
447
// For backwards compatibility, both ':' and '::' prefix are allowed for pseudo-elements.
448
// https://drafts.csswg.org/selectors-3/#pseudo-elements
449
// Returning a nil `Sel` (and a nil `error`) means we found a pseudo-element.
450
func (p *parser) parsePseudoclassSelector() (out Sel, pseudoElement string, err error) {
451
	if p.i >= len(p.s) {
452
		return nil, "", fmt.Errorf("expected pseudoclass selector (:pseudoclass), found EOF instead")
453
	}
454
	if p.s[p.i] != ':' {
455
		return nil, "", fmt.Errorf("expected attribute selector (:pseudoclass), found '%c' instead", p.s[p.i])
456
	}
457
458
	p.i++
459
	var mustBePseudoElement bool
460
	if p.i >= len(p.s) {
461
		return nil, "", fmt.Errorf("got empty pseudoclass (or pseudoelement)")
462
	}
463
	if p.s[p.i] == ':' { // we found a pseudo-element
464
		mustBePseudoElement = true
465
		p.i++
466
	}
467
468
	name, err := p.parseIdentifier()
469
	if err != nil {
470
		return
471
	}
472
	name = toLowerASCII(name)
473
	if mustBePseudoElement && (name != "after" && name != "backdrop" && name != "before" &&
474
		name != "cue" && name != "first-letter" && name != "first-line" && name != "grammar-error" &&
475
		name != "marker" && name != "placeholder" && name != "selection" && name != "spelling-error") {
476
		return out, "", fmt.Errorf("unknown pseudoelement :%s", name)
477
	}
478
479
	switch name {
480
	case "not", "has", "haschild":
481
		if !p.consumeParenthesis() {
482
			return out, "", errExpectedParenthesis
483
		}
484
		sel, parseErr := p.parseSelectorGroup()
485
		if parseErr != nil {
486
			return out, "", parseErr
487
		}
488
		if !p.consumeClosingParenthesis() {
489
			return out, "", errExpectedClosingParenthesis
490
		}
491
492
		out = relativePseudoClassSelector{name: name, match: sel}
493
494
	case "contains", "containsown":
495
		if !p.consumeParenthesis() {
496
			return out, "", errExpectedParenthesis
497
		}
498
		if p.i == len(p.s) {
499
			return out, "", errUnmatchedParenthesis
500
		}
501
		var val string
502
		switch p.s[p.i] {
503
		case '\'', '"':
504
			val, err = p.parseString()
505
		default:
506
			val, err = p.parseIdentifier()
507
		}
508
		if err != nil {
509
			return out, "", err
510
		}
511
		val = strings.ToLower(val)
512
		p.skipWhitespace()
513
		if p.i >= len(p.s) {
514
			return out, "", errors.New("unexpected EOF in pseudo selector")
515
		}
516
		if !p.consumeClosingParenthesis() {
517
			return out, "", errExpectedClosingParenthesis
518
		}
519
520
		out = containsPseudoClassSelector{own: name == "containsown", value: val}
521
522
	case "matches", "matchesown":
523
		if !p.consumeParenthesis() {
524
			return out, "", errExpectedParenthesis
525
		}
526
		rx, err := p.parseRegex()
527
		if err != nil {
528
			return out, "", err
529
		}
530
		if p.i >= len(p.s) {
531
			return out, "", errors.New("unexpected EOF in pseudo selector")
532
		}
533
		if !p.consumeClosingParenthesis() {
534
			return out, "", errExpectedClosingParenthesis
535
		}
536
537
		out = regexpPseudoClassSelector{own: name == "matchesown", regexp: rx}
538
539
	case "nth-child", "nth-last-child", "nth-of-type", "nth-last-of-type":
540
		if !p.consumeParenthesis() {
541
			return out, "", errExpectedParenthesis
542
		}
543
		a, b, err := p.parseNth()
544
		if err != nil {
545
			return out, "", err
546
		}
547
		if !p.consumeClosingParenthesis() {
548
			return out, "", errExpectedClosingParenthesis
549
		}
550
		last := name == "nth-last-child" || name == "nth-last-of-type"
551
		ofType := name == "nth-of-type" || name == "nth-last-of-type"
552
		out = nthPseudoClassSelector{a: a, b: b, last: last, ofType: ofType}
553
554
	case "first-child":
555
		out = nthPseudoClassSelector{a: 0, b: 1, ofType: false, last: false}
556
	case "last-child":
557
		out = nthPseudoClassSelector{a: 0, b: 1, ofType: false, last: true}
558
	case "first-of-type":
559
		out = nthPseudoClassSelector{a: 0, b: 1, ofType: true, last: false}
560
	case "last-of-type":
561
		out = nthPseudoClassSelector{a: 0, b: 1, ofType: true, last: true}
562
	case "only-child":
563
		out = onlyChildPseudoClassSelector{ofType: false}
564
	case "only-of-type":
565
		out = onlyChildPseudoClassSelector{ofType: true}
566
	case "input":
567
		out = inputPseudoClassSelector{}
568
	case "empty":
569
		out = emptyElementPseudoClassSelector{}
570
	case "root":
571
		out = rootPseudoClassSelector{}
572
	case "link":
573
		out = linkPseudoClassSelector{}
574
	case "lang":
575
		if !p.consumeParenthesis() {
576
			return out, "", errExpectedParenthesis
577
		}
578
		if p.i == len(p.s) {
579
			return out, "", errUnmatchedParenthesis
580
		}
581
		val, err := p.parseIdentifier()
582
		if err != nil {
583
			return out, "", err
584
		}
585
		val = strings.ToLower(val)
586
		p.skipWhitespace()
587
		if p.i >= len(p.s) {
588
			return out, "", errors.New("unexpected EOF in pseudo selector")
589
		}
590
		if !p.consumeClosingParenthesis() {
591
			return out, "", errExpectedClosingParenthesis
592
		}
593
		out = langPseudoClassSelector{lang: val}
594
	case "enabled":
595
		out = enabledPseudoClassSelector{}
596
	case "disabled":
597
		out = disabledPseudoClassSelector{}
598
	case "checked":
599
		out = checkedPseudoClassSelector{}
600
	case "visited", "hover", "active", "focus", "target":
601
		// Not applicable in a static context: never match.
602
		out = neverMatchSelector{value: ":" + name}
603
	case "after", "backdrop", "before", "cue", "first-letter", "first-line", "grammar-error", "marker", "placeholder", "selection", "spelling-error":
604
		return nil, name, nil
605
	default:
606
		return out, "", fmt.Errorf("unknown pseudoclass or pseudoelement :%s", name)
607
	}
608
	return
609
}
610
611
// parseInteger parses a  decimal integer.
612
func (p *parser) parseInteger() (int, error) {
613
	i := p.i
614
	start := i
615
	for i < len(p.s) && '0' <= p.s[i] && p.s[i] <= '9' {
616
		i++
617
	}
618
	if i == start {
619
		return 0, errors.New("expected integer, but didn't find it")
620
	}
621
	p.i = i
622
623
	val, err := strconv.Atoi(p.s[start:i])
624
	if err != nil {
625
		return 0, err
626
	}
627
628
	return val, nil
629
}
630
631
// parseNth parses the argument for :nth-child (normally of the form an+b).
632
func (p *parser) parseNth() (a, b int, err error) {
633
	// initial state
634
	if p.i >= len(p.s) {
635
		goto eof
636
	}
637
	switch p.s[p.i] {
638
	case '-':
639
		p.i++
640
		goto negativeA
641
	case '+':
642
		p.i++
643
		goto positiveA
644
	case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
645
		goto positiveA
646
	case 'n', 'N':
647
		a = 1
648
		p.i++
649
		goto readN
650
	case 'o', 'O', 'e', 'E':
651
		id, nameErr := p.parseName()
652
		if nameErr != nil {
653
			return 0, 0, nameErr
654
		}
655
		id = toLowerASCII(id)
656
		if id == "odd" {
657
			return 2, 1, nil
658
		}
659
		if id == "even" {
660
			return 2, 0, nil
661
		}
662
		return 0, 0, fmt.Errorf("expected 'odd' or 'even', but found '%s' instead", id)
663
	default:
664
		goto invalid
665
	}
666
667
positiveA:
668
	if p.i >= len(p.s) {
669
		goto eof
670
	}
671
	switch p.s[p.i] {
672
	case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
673
		a, err = p.parseInteger()
674
		if err != nil {
675
			return 0, 0, err
676
		}
677
		goto readA
678
	case 'n', 'N':
679
		a = 1
680
		p.i++
681
		goto readN
682
	default:
683
		goto invalid
684
	}
685
686
negativeA:
687
	if p.i >= len(p.s) {
688
		goto eof
689
	}
690
	switch p.s[p.i] {
691
	case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
692
		a, err = p.parseInteger()
693
		if err != nil {
694
			return 0, 0, err
695
		}
696
		a = -a
697
		goto readA
698
	case 'n', 'N':
699
		a = -1
700
		p.i++
701
		goto readN
702
	default:
703
		goto invalid
704
	}
705
706
readA:
707
	if p.i >= len(p.s) {
708
		goto eof
709
	}
710
	switch p.s[p.i] {
711
	case 'n', 'N':
712
		p.i++
713
		goto readN
714
	default:
715
		// The number we read as a is actually b.
716
		return 0, a, nil
717
	}
718
719
readN:
720
	p.skipWhitespace()
721
	if p.i >= len(p.s) {
722
		goto eof
723
	}
724
	switch p.s[p.i] {
725
	case '+':
726
		p.i++
727
		p.skipWhitespace()
728
		b, err = p.parseInteger()
729
		if err != nil {
730
			return 0, 0, err
731
		}
732
		return a, b, nil
733
	case '-':
734
		p.i++
735
		p.skipWhitespace()
736
		b, err = p.parseInteger()
737
		if err != nil {
738
			return 0, 0, err
739
		}
740
		return a, -b, nil
741
	default:
742
		return a, 0, nil
743
	}
744
745
eof:
746
	return 0, 0, errors.New("unexpected EOF while attempting to parse expression of form an+b")
747
748
invalid:
749
	return 0, 0, errors.New("unexpected character while attempting to parse expression of form an+b")
750
}
751
752
// parseSimpleSelectorSequence parses a selector sequence that applies to
753
// a single element.
754
func (p *parser) parseSimpleSelectorSequence() (Sel, error) {
755
	var selectors []Sel
756
757
	if p.i >= len(p.s) {
758
		return nil, errors.New("expected selector, found EOF instead")
759
	}
760
761
	switch p.s[p.i] {
762
	case '*':
763
		// It's the universal selector. Just skip over it, since it doesn't affect the meaning.
764
		p.i++
765
		if p.i+2 < len(p.s) && p.s[p.i:p.i+2] == "|*" { // other version of universal selector
766
			p.i += 2
767
		}
768
	case '#', '.', '[', ':':
769
		// There's no type selector. Wait to process the other till the main loop.
770
	default:
771
		r, err := p.parseTypeSelector()
772
		if err != nil {
773
			return nil, err
774
		}
775
		selectors = append(selectors, r)
776
	}
777
778
	var pseudoElement string
779
loop:
780
	for p.i < len(p.s) {
781
		var (
782
			ns               Sel
783
			newPseudoElement string
784
			err              error
785
		)
786
		switch p.s[p.i] {
787
		case '#':
788
			ns, err = p.parseIDSelector()
789
		case '.':
790
			ns, err = p.parseClassSelector()
791
		case '[':
792
			ns, err = p.parseAttributeSelector()
793
		case ':':
794
			ns, newPseudoElement, err = p.parsePseudoclassSelector()
795
		default:
796
			break loop
797
		}
798
		if err != nil {
799
			return nil, err
800
		}
801
		// From https://drafts.csswg.org/selectors-3/#pseudo-elements :
802
		// "Only one pseudo-element may appear per selector, and if present
803
		// it must appear after the sequence of simple selectors that
804
		// represents the subjects of the selector.""
805
		if ns == nil { // we found a pseudo-element
806
			if pseudoElement != "" {
807
				return nil, fmt.Errorf("only one pseudo-element is accepted per selector, got %s and %s", pseudoElement, newPseudoElement)
808
			}
809
			if !p.acceptPseudoElements {
810
				return nil, fmt.Errorf("pseudo-element %s found, but pseudo-elements support is disabled", newPseudoElement)
811
			}
812
			pseudoElement = newPseudoElement
813
		} else {
814
			if pseudoElement != "" {
815
				return nil, fmt.Errorf("pseudo-element %s must be at the end of selector", pseudoElement)
816
			}
817
			selectors = append(selectors, ns)
818
		}
819
820
	}
821
	if len(selectors) == 1 && pseudoElement == "" { // no need wrap the selectors in compoundSelector
822
		return selectors[0], nil
823
	}
824
	return compoundSelector{selectors: selectors, pseudoElement: pseudoElement}, nil
825
}
826
827
// parseSelector parses a selector that may include combinators.
828
func (p *parser) parseSelector() (Sel, error) {
829
	p.skipWhitespace()
830
	result, err := p.parseSimpleSelectorSequence()
831
	if err != nil {
832
		return nil, err
833
	}
834
835
	for {
836
		var (
837
			combinator byte
838
			c          Sel
839
		)
840
		if p.skipWhitespace() {
841
			combinator = ' '
842
		}
843
		if p.i >= len(p.s) {
844
			return result, nil
845
		}
846
847
		switch p.s[p.i] {
848
		case '+', '>', '~':
849
			combinator = p.s[p.i]
850
			p.i++
851
			p.skipWhitespace()
852
		case ',', ')':
853
			// These characters can't begin a selector, but they can legally occur after one.
854
			return result, nil
855
		}
856
857
		if combinator == 0 {
858
			return result, nil
859
		}
860
861
		c, err = p.parseSimpleSelectorSequence()
862
		if err != nil {
863
			return nil, err
864
		}
865
		result = combinedSelector{first: result, combinator: combinator, second: c}
866
	}
867
}
868
869
// parseSelectorGroup parses a group of selectors, separated by commas.
870
func (p *parser) parseSelectorGroup() (SelectorGroup, error) {
871
	current, err := p.parseSelector()
872
	if err != nil {
873
		return nil, err
874
	}
875
	result := SelectorGroup{current}
876
877
	for p.i < len(p.s) {
878
		if p.s[p.i] != ',' {
879
			break
880
		}
881
		p.i++
882
		c, err := p.parseSelector()
883
		if err != nil {
884
			return nil, err
885
		}
886
		result = append(result, c)
887
	}
888
	return result, nil
889
}