Source File
parser.go
Belonging Package
github.com/andybalholm/cascadia
package cascadia
import (
)
var int
func ( *parser) () ( string, error) {
:= false
if len(.s) > .i && .s[.i] == '-' {
= true
.i++
}
if len(.s) <= .i {
return "", errors.New("expected identifier, found EOF instead")
}
if := .s[.i]; !(nameStart() || == '\\') {
return "", fmt.Errorf("expected identifier, found %c instead", )
}
, = .parseName()
if && == nil {
= "-" +
}
return
}
func ( *parser) () ( string, error) {
:= .i
:
for < len(.s) {
:= .s[]
switch {
case nameChar():
:=
for < len(.s) && nameChar(.s[]) {
++
}
+= .s[:]
case == '\\':
.i =
, := .parseEscape()
if != nil {
return "",
}
= .i
+=
default:
break
}
}
if == "" {
return "", errors.New("expected name, found EOF instead")
}
.i =
return , nil
}
func ( *parser) () ( string, error) {
:= .i
if len(.s) < +2 {
return "", errors.New("expected string, found EOF instead")
}
:= .s[]
++
:
for < len(.s) {
switch .s[] {
case '\\':
if len(.s) > +1 {
switch := .s[+1]; {
case '\r':
if len(.s) > +2 && .s[+2] == '\n' {
+= 3
continue
}
fallthrough
case '\n', '\f':
+= 2
continue
}
}
.i =
, := .parseEscape()
if != nil {
return "",
}
= .i
+=
case :
break
case '\r', '\n', '\f':
return "", errors.New("unexpected end of line in string")
default:
:=
for < len(.s) {
if := .s[]; == || == '\\' || == '\r' || == '\n' || == '\f' {
break
}
++
}
+= .s[:]
}
}
if >= len(.s) {
return "", errors.New("EOF in string")
}
func ( *parser) () ( tagSelector, error) {
, := .parseIdentifier()
if != nil {
return
}
return tagSelector{tag: toLowerASCII()}, nil
}
func ( *parser) () (idSelector, error) {
if .i >= len(.s) {
return idSelector{}, fmt.Errorf("expected id selector (#id), found EOF instead")
}
if .s[.i] != '#' {
return idSelector{}, fmt.Errorf("expected id selector (#id), found '%c' instead", .s[.i])
}
.i++
, := .parseName()
if != nil {
return idSelector{},
}
return idSelector{id: }, nil
}
func ( *parser) () (classSelector, error) {
if .i >= len(.s) {
return classSelector{}, fmt.Errorf("expected class selector (.class), found EOF instead")
}
if .s[.i] != '.' {
return classSelector{}, fmt.Errorf("expected class selector (.class), found '%c' instead", .s[.i])
}
.i++
, := .parseIdentifier()
if != nil {
return classSelector{},
}
return classSelector{class: }, nil
}
func ( *parser) () (attrSelector, error) {
if .i >= len(.s) {
return attrSelector{}, fmt.Errorf("expected attribute selector ([attribute]), found EOF instead")
}
if .s[.i] != '[' {
return attrSelector{}, fmt.Errorf("expected attribute selector ([attribute]), found '%c' instead", .s[.i])
}
.i++
.skipWhitespace()
, := .parseIdentifier()
if != nil {
return attrSelector{},
}
= toLowerASCII()
.skipWhitespace()
if .i >= len(.s) {
return attrSelector{}, errors.New("unexpected EOF in attribute selector")
}
if .s[.i] == ']' {
.i++
return attrSelector{key: , operation: ""}, nil
}
if .i+2 >= len(.s) {
return attrSelector{}, errors.New("unexpected EOF in attribute selector")
}
:= .s[.i : .i+2]
if [0] == '=' {
= "="
} else if [1] != '=' {
return attrSelector{}, fmt.Errorf(`expected equality operator, found "%s" instead`, )
}
.i += len()
.skipWhitespace()
if .i >= len(.s) {
return attrSelector{}, errors.New("unexpected EOF in attribute selector")
}
var string
var *regexp.Regexp
if == "#=" {
, = .parseRegex()
} else {
switch .s[.i] {
case '\'', '"':
, = .parseString()
default:
, = .parseIdentifier()
}
}
if != nil {
return attrSelector{},
}
.skipWhitespace()
if .i >= len(.s) {
return attrSelector{}, errors.New("unexpected EOF in attribute selector")
}
if .s[.i] != ']' {
return attrSelector{}, fmt.Errorf("expected ']', found '%c' instead", .s[.i])
}
.i++
switch {
case "=", "!=", "~=", "|=", "^=", "$=", "*=", "#=":
return attrSelector{key: , val: , operation: , regexp: }, nil
default:
return attrSelector{}, fmt.Errorf("attribute operator %q is not supported", )
}
}
var errExpectedParenthesis = errors.New("expected '(' but didn't find it")
var errExpectedClosingParenthesis = errors.New("expected ')' but didn't find it")
var errUnmatchedParenthesis = errors.New("unmatched '('")
func ( *parser) () ( Sel, error) {
if .i >= len(.s) {
return nil, fmt.Errorf("expected pseudoclass selector (:pseudoclass), found EOF instead")
}
if .s[.i] != ':' {
return nil, fmt.Errorf("expected attribute selector (:pseudoclass), found '%c' instead", .s[.i])
}
.i++
if .s[.i] == ':' { // we found a pseudo-element
.i++
}
, := .parseIdentifier()
if != nil {
return
}
= toLowerASCII()
switch {
case "not", "has", "haschild":
if !.consumeParenthesis() {
return , errExpectedParenthesis
}
, := .parseSelectorGroup()
if != nil {
return ,
}
if !.consumeClosingParenthesis() {
return , errExpectedClosingParenthesis
}
= relativePseudoClassSelector{name: , match: }
case "contains", "containsown":
if !.consumeParenthesis() {
return , errExpectedParenthesis
}
if .i == len(.s) {
return , errUnmatchedParenthesis
}
var string
switch .s[.i] {
case '\'', '"':
, = .parseString()
default:
, = .parseIdentifier()
}
if != nil {
return ,
}
= strings.ToLower()
.skipWhitespace()
if .i >= len(.s) {
return , errors.New("unexpected EOF in pseudo selector")
}
if !.consumeClosingParenthesis() {
return , errExpectedClosingParenthesis
}
= containsPseudoClassSelector{own: == "containsown", value: }
case "matches", "matchesown":
if !.consumeParenthesis() {
return , errExpectedParenthesis
}
, := .parseRegex()
if != nil {
return ,
}
if .i >= len(.s) {
return , errors.New("unexpected EOF in pseudo selector")
}
if !.consumeClosingParenthesis() {
return , errExpectedClosingParenthesis
}
= regexpPseudoClassSelector{own: == "matchesown", regexp: }
case "nth-child", "nth-last-child", "nth-of-type", "nth-last-of-type":
if !.consumeParenthesis() {
return , errExpectedParenthesis
}
, , := .parseNth()
if != nil {
return ,
}
if !.consumeClosingParenthesis() {
return , errExpectedClosingParenthesis
}
:= == "nth-last-child" || == "nth-last-of-type"
:= == "nth-of-type" || == "nth-last-of-type"
= nthPseudoClassSelector{a: , b: , last: , ofType: }
case "first-child":
= nthPseudoClassSelector{a: 0, b: 1, ofType: false, last: false}
case "last-child":
= nthPseudoClassSelector{a: 0, b: 1, ofType: false, last: true}
case "first-of-type":
= nthPseudoClassSelector{a: 0, b: 1, ofType: true, last: false}
case "last-of-type":
= nthPseudoClassSelector{a: 0, b: 1, ofType: true, last: true}
case "only-child":
= onlyChildPseudoClassSelector{ofType: false}
case "only-of-type":
= onlyChildPseudoClassSelector{ofType: true}
case "input":
= inputPseudoClassSelector{}
case "empty":
= emptyElementPseudoClassSelector{}
case "root":
= rootPseudoClassSelector{}
case "after", "backdrop", "before", "cue", "first-letter", "first-line", "grammar-error", "marker", "placeholder", "selection", "spelling-error":
return , errors.New("pseudo-elements are not yet supported")
default:
return , fmt.Errorf("unknown pseudoclass or pseudoelement :%s", )
}
return
}
if .i >= len(.s) {
goto
}
switch .s[.i] {
case '-':
.i++
goto
case '+':
.i++
goto
case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
goto
case 'n', 'N':
= 1
.i++
goto
case 'o', 'O', 'e', 'E':
, := .parseName()
if != nil {
return 0, 0,
}
= toLowerASCII()
if == "odd" {
return 2, 1, nil
}
if == "even" {
return 2, 0, nil
}
return 0, 0, fmt.Errorf("expected 'odd' or 'even', but found '%s' instead", )
default:
goto
}
:
if .i >= len(.s) {
goto
}
switch .s[.i] {
case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
, = .parseInteger()
if != nil {
return 0, 0,
}
goto
case 'n', 'N':
= 1
.i++
goto
default:
goto
}
:
if .i >= len(.s) {
goto
}
switch .s[.i] {
case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
, = .parseInteger()
if != nil {
return 0, 0,
}
= -
goto
case 'n', 'N':
= -1
.i++
goto
default:
goto
}
:
if .i >= len(.s) {
goto
}
switch .s[.i] {
case 'n', 'N':
.i++
goto
return 0, , nil
}
:
.skipWhitespace()
if .i >= len(.s) {
goto
}
switch .s[.i] {
case '+':
.i++
.skipWhitespace()
, = .parseInteger()
if != nil {
return 0, 0,
}
return , , nil
case '-':
.i++
.skipWhitespace()
, = .parseInteger()
if != nil {
return 0, 0,
}
return , -, nil
default:
return , 0, nil
}
:
return 0, 0, errors.New("unexpected EOF while attempting to parse expression of form an+b")
:
return 0, 0, errors.New("unexpected character while attempting to parse expression of form an+b")
}
.i++
default:
, := .parseTypeSelector()
if != nil {
return nil,
}
= append(, )
}
:
for .i < len(.s) {
var (
Sel
error
)
switch .s[.i] {
case '#':
, = .parseIDSelector()
case '.':
, = .parseClassSelector()
case '[':
, = .parseAttributeSelector()
case ':':
, = .parsePseudoclassSelector()
default:
break
}
if != nil {
return nil,
}
= append(, )
}
if len() == 1 { // no need wrap the selectors in compoundSelector
return [0], nil
}
return compoundSelector{selectors: }, nil
}
func ( *parser) () (Sel, error) {
.skipWhitespace()
, := .parseSimpleSelectorSequence()
if != nil {
return nil,
}
for {
var (
byte
Sel
)
if .skipWhitespace() {
= ' '
}
if .i >= len(.s) {
return , nil
}
switch .s[.i] {
case '+', '>', '~':
= .s[.i]
.i++
.skipWhitespace()
return , nil
}
if == 0 {
return , nil
}
, = .parseSimpleSelectorSequence()
if != nil {
return nil,
}
= combinedSelector{first: , combinator: , second: }
}
}
func ( *parser) () (SelectorGroup, error) {
, := .parseSelector()
if != nil {
return nil,
}
:= SelectorGroup{}
for .i < len(.s) {
if .s[.i] != ',' {
break
}
.i++
, := .parseSelector()
if != nil {
return nil,
}
= append(, )
}
return , nil
![]() |
The pages are generated with Golds v0.3.2-preview. (GOOS=darwin GOARCH=amd64) Golds is a Go 101 project developed by Tapir Liu. PR and bug reports are welcome and can be submitted to the issue list. Please follow @Go100and1 (reachable from the left QR code) to get the latest news of Golds. |