128 lines
4.1 KiB
Go
128 lines
4.1 KiB
Go
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
|
|
}
|