...

Source file src/github.com/andybalholm/cascadia/parser.go

Documentation: github.com/andybalholm/cascadia

     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  }
   890  

View as plain text