package css import ( "fmt" "github.com/gorilla/css/scanner" ) type CSSParser struct { *scanner.Scanner } func (parser *CSSParser) tolerantRead(extraTokens []*CSSToken) (token *scanner.Token) { tokenLoop: for { token = parser.Scanner.Next() switch { case isNativeNonMachineContent(token): if extraTokens != nil { extraTokens = append(extraTokens, &CSSToken{Type: TokenType_Native, NativeToken: token}) } default: break tokenLoop } } return } func (parser *CSSParser) readPropertyValue() (result *CSSToken, token *scanner.Token, err error) { result = &CSSToken{Type: TokenType_PropertyValue, SubTokens: []*CSSToken{}} propValueLoop: for { token = parser.tolerantRead(result.SubTokens) switch { case token.Type == scanner.TokenChar && token.Value == "}" || token.Value == ";": // closure or prop end break propValueLoop case token.Type == scanner.TokenEOF || token.Type == scanner.TokenError: err = fmt.Errorf("expected continuation of property value but got %s", token.String()) default: result.SubTokens = append(result.SubTokens, &CSSToken{Type: TokenType_Native, NativeToken: token}) } } return } // ",selector,selector,...selector{" func (parser *CSSParser) readSelectors(currentToken *scanner.Token) (result []*CSSToken, token *scanner.Token, err error) { result = []*CSSToken{} currentSelector := &CSSToken{Type: TokenType_Selector, SubTokens: []*CSSToken{}} token = currentToken selectorsLoop: for { switch { case token.Type == scanner.TokenChar && token.Value == "{": // closure begin result = append(result, currentSelector) break selectorsLoop case token.Type == scanner.TokenChar && token.Value == ",": // new selector upcoming result = append(result, currentSelector, &CSSToken{Type: TokenType_Native, NativeToken: token}) currentSelector = &CSSToken{Type: TokenType_Selector, SubTokens: []*CSSToken{}} case token.Type == scanner.TokenS && len(currentSelector.SubTokens) <= 0: // space, current selector still empty result = append(result, &CSSToken{Type: TokenType_Native, NativeToken: token}) case token.Type == scanner.TokenEOF || token.Type == scanner.TokenError: err = fmt.Errorf("expected selector but got %s", token.String()) return default: currentSelector.SubTokens = append(currentSelector.SubTokens, &CSSToken{Type: TokenType_Native, NativeToken: token}) } token = parser.tolerantRead(result) } return } // "prop;prop;...prop}" func (parser *CSSParser) readRuleClosure() (result []*CSSToken, token *scanner.Token, err error) { result = []*CSSToken{} closureLoop: for { token = parser.tolerantRead(result) switch { case token.Type == scanner.TokenChar && token.Value == "}": // closure end break closureLoop case token.Type == scanner.TokenEOF || token.Type == scanner.TokenError: err = fmt.Errorf("expected continuation of rule but got %s", token.String()) return case token.Type == scanner.TokenChar, token.Type == scanner.TokenIdent: // property name property := &CSSToken{Type: TokenType_Property, SubTokens: []*CSSToken{}} propName := &CSSToken{Type: TokenType_PropertyName, SubTokens: []*CSSToken{}} propNameLoop: for { switch { case token.Type == scanner.TokenChar && token.Value == ":": // end of prop name property.SubTokens = append(property.SubTokens, propName, &CSSToken{Type: TokenType_Native, NativeToken: token}) break propNameLoop default: propName.SubTokens = append(propName.SubTokens, &CSSToken{Type: TokenType_Native, NativeToken: token}) } token = parser.tolerantRead(result) } // prop value var intermediate *CSSToken intermediate, token, err = parser.readPropertyValue() if err != nil { break closureLoop } property.SubTokens = append(property.SubTokens, intermediate) result = append(result, property) if token.Value == ";" { result = append(result, &CSSToken{Type: TokenType_Native, NativeToken: token}) } if token.Value == "}" { break closureLoop } default: result = append(result, &CSSToken{Type: TokenType_Native, NativeToken: token}) } } return }