remote-darkness/internal/css/parser.go

128 lines
4.1 KiB
Go
Raw Normal View History

2018-06-06 18:34:27 +00:00
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
}