2024-07-28 23:50:51 -06:00
|
|
|
package tss
|
|
|
|
|
|
|
|
import "io"
|
|
|
|
import "strconv"
|
|
|
|
import "git.tebibyte.media/sashakoshka/goparse"
|
|
|
|
|
|
|
|
type parser struct {
|
|
|
|
parse.Parser
|
|
|
|
sheet Sheet
|
|
|
|
lexer parse.Lexer
|
|
|
|
}
|
|
|
|
|
|
|
|
func newParser (lexer parse.Lexer) *parser {
|
|
|
|
return &parser {
|
|
|
|
sheet: Sheet {
|
|
|
|
Variables: make(map[string] ValueList),
|
|
|
|
},
|
|
|
|
Parser: parse.Parser {
|
|
|
|
Lexer: lexer,
|
|
|
|
TokenNames: tokenNames,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func Parse (lexer parse.Lexer) (Sheet, error) {
|
|
|
|
parser := newParser(lexer)
|
|
|
|
err := parser.parse()
|
|
|
|
if err == io.EOF { err = nil }
|
|
|
|
if err != nil { return Sheet { }, err }
|
|
|
|
return parser.sheet, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (this *parser) parse () error {
|
|
|
|
err := this.Next()
|
|
|
|
if err != nil { return err }
|
|
|
|
for this.Token.Kind != parse.EOF {
|
|
|
|
err = this.parseTopLevel()
|
|
|
|
if err != nil { return err }
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (this *parser) parseTopLevel () error {
|
|
|
|
err := this.ExpectDesc("variable or rule", Dollar, Ident, Star)
|
|
|
|
if err != nil { return err }
|
|
|
|
if this.EOF() { return nil }
|
|
|
|
pos := this.Pos()
|
|
|
|
|
|
|
|
switch this.Kind() {
|
|
|
|
case Dollar:
|
|
|
|
name, variable, err := this.parseVariable()
|
|
|
|
if err != nil { return err }
|
|
|
|
if _, exists := this.sheet.Variables[name]; exists {
|
|
|
|
return parse.Errorf(pos, "variable %s already declared", name)
|
|
|
|
}
|
|
|
|
this.sheet.Variables[name] = variable
|
|
|
|
|
|
|
|
case Ident, Star:
|
|
|
|
rule, err := this.parseRule()
|
|
|
|
if err != nil { return err }
|
|
|
|
this.sheet.Rules = append(this.sheet.Rules, rule)
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (this *parser) parseVariable () (string, ValueList, error) {
|
|
|
|
err := this.Expect(Dollar)
|
|
|
|
if err != nil { return "", nil, err }
|
|
|
|
err = this.ExpectNext(Ident)
|
|
|
|
if err != nil { return "", nil, err }
|
2024-07-29 13:13:02 -06:00
|
|
|
name := this.Value()
|
2024-07-28 23:50:51 -06:00
|
|
|
err = this.ExpectNext(Equals)
|
|
|
|
if err != nil { return "", nil, err }
|
|
|
|
this.Next()
|
|
|
|
values, err := this.parseValueList()
|
|
|
|
if err != nil { return "", nil, err }
|
|
|
|
err = this.Expect(Semicolon)
|
|
|
|
if err != nil { return "", nil, err }
|
|
|
|
return name, values, this.Next()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (this *parser) parseRule () (Rule, error) {
|
|
|
|
rule := Rule {
|
|
|
|
Attrs: make(map[string] []ValueList),
|
|
|
|
}
|
|
|
|
|
|
|
|
selector, err := this.parseSelector()
|
|
|
|
if err != nil { return Rule { }, err }
|
|
|
|
rule.Selector = selector
|
2024-07-29 13:13:02 -06:00
|
|
|
err = this.Expect(LBrace)
|
2024-07-28 23:50:51 -06:00
|
|
|
if err != nil { return Rule { }, err }
|
|
|
|
|
|
|
|
for {
|
2024-07-29 13:13:02 -06:00
|
|
|
this.Next()
|
|
|
|
if this.Is(RBrace) { break }
|
2024-07-28 23:50:51 -06:00
|
|
|
pos := this.Pos()
|
|
|
|
name, attr, err := this.parseAttr()
|
2024-07-29 13:13:02 -06:00
|
|
|
if err != nil { return Rule { }, err }
|
|
|
|
err = this.Expect(Semicolon)
|
|
|
|
if err != nil { return Rule { }, err }
|
|
|
|
if _, exists := rule.Attrs[name]; exists {
|
2024-07-28 23:50:51 -06:00
|
|
|
return Rule { }, parse.Errorf (
|
|
|
|
pos,
|
|
|
|
"attribute %s already declared in this rule",
|
|
|
|
name)
|
|
|
|
}
|
|
|
|
rule.Attrs[name] = attr
|
|
|
|
}
|
|
|
|
|
|
|
|
return rule, this.Next()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (this *parser) parseSelector () (Selector, error) {
|
|
|
|
selector := Selector { }
|
|
|
|
|
|
|
|
// package
|
|
|
|
err := this.ExpectDesc("selector", Ident, Star)
|
|
|
|
if err != nil { return Selector { }, err }
|
|
|
|
if this.Is(Ident) {
|
|
|
|
selector.Package = this.Value()
|
|
|
|
}
|
|
|
|
|
|
|
|
err = this.ExpectNext(Dot)
|
|
|
|
if err != nil { return Selector { }, err }
|
|
|
|
|
|
|
|
// object
|
|
|
|
err = this.ExpectNext(Ident, Star)
|
|
|
|
if err != nil { return Selector { }, err }
|
|
|
|
if this.Is(Ident) {
|
|
|
|
selector.Object = this.Value()
|
|
|
|
}
|
|
|
|
|
|
|
|
// tags
|
2024-07-29 13:13:02 -06:00
|
|
|
err = this.ExpectNext(LBracket)
|
2024-07-28 23:50:51 -06:00
|
|
|
if err == nil {
|
|
|
|
for {
|
2024-08-14 09:58:33 -06:00
|
|
|
this.Next()
|
2024-07-30 11:01:24 -06:00
|
|
|
err := this.Expect(Ident, String, RBracket)
|
2024-07-28 23:50:51 -06:00
|
|
|
if err != nil { return Selector { }, err }
|
2024-07-30 11:01:24 -06:00
|
|
|
if this.Is(RBracket) { break }
|
|
|
|
if this.Is(Comma) { this.Next() }
|
2024-07-28 23:50:51 -06:00
|
|
|
selector.Tags = append(selector.Tags, this.Value())
|
2024-07-29 13:13:02 -06:00
|
|
|
err = this.ExpectNext(Comma, RBracket)
|
2024-07-28 23:50:51 -06:00
|
|
|
if err != nil { return Selector { }, err }
|
2024-08-14 09:58:33 -06:00
|
|
|
if this.Is(RBracket) { break }
|
2024-07-28 23:50:51 -06:00
|
|
|
}
|
2024-07-30 11:01:24 -06:00
|
|
|
this.Next()
|
2024-07-28 23:50:51 -06:00
|
|
|
}
|
|
|
|
|
2024-07-29 13:13:02 -06:00
|
|
|
return selector, nil
|
2024-07-28 23:50:51 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
func (this *parser) parseAttr () (string, []ValueList, error) {
|
|
|
|
err := this.ExpectDesc("attr", Ident)
|
|
|
|
if err != nil { return "", nil, err }
|
|
|
|
name := this.Value()
|
|
|
|
|
|
|
|
err = this.ExpectNext(Colon)
|
|
|
|
if err != nil { return "", nil, err }
|
|
|
|
|
|
|
|
attr := []ValueList { }
|
|
|
|
this.Next()
|
|
|
|
for {
|
|
|
|
err := this.ExpectDesc (
|
2024-07-29 13:13:02 -06:00
|
|
|
"value, Comma, or Semicolon",
|
|
|
|
Number, Color, String, Ident, Dollar, Slash,
|
|
|
|
Comma, Semicolon)
|
2024-07-28 23:50:51 -06:00
|
|
|
if err != nil { return "", nil, err }
|
2024-07-30 11:01:24 -06:00
|
|
|
if this.Is(Semicolon) { break }
|
|
|
|
if this.Is(Comma) { this.Next() }
|
2024-07-28 23:50:51 -06:00
|
|
|
valueList, err := this.parseValueList()
|
|
|
|
if err != nil { return "", nil, err }
|
|
|
|
attr = append(attr, valueList)
|
2024-07-29 13:13:02 -06:00
|
|
|
err = this.Expect(Comma, Semicolon)
|
2024-07-28 23:50:51 -06:00
|
|
|
if err != nil { return "", nil, err }
|
|
|
|
}
|
|
|
|
|
2024-07-29 13:13:02 -06:00
|
|
|
return name, attr, nil
|
2024-07-28 23:50:51 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
func (this *parser) parseValueList () (ValueList, error) {
|
|
|
|
list := ValueList { }
|
|
|
|
for {
|
|
|
|
err := this.ExpectDesc (
|
|
|
|
"value",
|
2024-07-29 13:13:02 -06:00
|
|
|
Number, Color, String, Ident, Dollar, Slash)
|
2024-07-28 23:50:51 -06:00
|
|
|
if err != nil { break }
|
|
|
|
switch this.Kind() {
|
|
|
|
case Number:
|
|
|
|
number, err := strconv.Atoi(this.Value())
|
|
|
|
if err != nil { return nil, err }
|
|
|
|
list = append(list, ValueNumber(number))
|
|
|
|
case Color:
|
|
|
|
color, ok := parseColor([]rune(this.Value()))
|
|
|
|
if !ok {
|
|
|
|
return nil, parse.Errorf (
|
|
|
|
this.Pos(),
|
|
|
|
"malformed color literal")
|
|
|
|
}
|
|
|
|
list = append(list, ValueColor(color))
|
|
|
|
case String:
|
|
|
|
list = append(list, ValueString(this.Value()))
|
|
|
|
case Ident:
|
|
|
|
list = append(list, ValueKeyword(this.Value()))
|
|
|
|
case Dollar:
|
|
|
|
err := this.ExpectNext(Ident)
|
|
|
|
if err != nil { return nil, err }
|
|
|
|
list = append(list, ValueVariable(this.Value()))
|
2024-07-29 13:13:02 -06:00
|
|
|
case Slash:
|
|
|
|
list = append(list, ValueCut { })
|
2024-07-28 23:50:51 -06:00
|
|
|
}
|
2024-07-29 13:13:02 -06:00
|
|
|
this.Next()
|
2024-07-28 23:50:51 -06:00
|
|
|
}
|
2024-07-29 13:13:02 -06:00
|
|
|
return list, nil
|
2024-07-28 23:50:51 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
func parseColor (runes []rune) (uint32, bool) {
|
|
|
|
digits := make([]uint32, len(runes))
|
|
|
|
for index, run := range runes {
|
|
|
|
digit := hexDigit(run)
|
|
|
|
if digit < 0 { return 0, false }
|
|
|
|
digits[index] = uint32(digit)
|
|
|
|
}
|
|
|
|
switch len(runes) {
|
|
|
|
case 3:
|
|
|
|
return digits[0] << 28 | digits[0] << 24 |
|
|
|
|
digits[1] << 20 | digits[1] << 16 |
|
|
|
|
digits[2] << 12 | digits[2] << 8 | 0xFF, true
|
|
|
|
case 6:
|
|
|
|
return digits[0] << 28 | digits[1] << 24 |
|
|
|
|
digits[2] << 20 | digits[3] << 16 |
|
|
|
|
digits[4] << 12 | digits[5] << 8 | 0xFF, true
|
|
|
|
case 4:
|
|
|
|
return digits[0] << 28 | digits[0] << 24 |
|
|
|
|
digits[1] << 20 | digits[1] << 16 |
|
|
|
|
digits[2] << 12 | digits[2] << 8 |
|
|
|
|
digits[3] << 4 | digits[3] << 0, true
|
|
|
|
case 8:
|
|
|
|
return digits[0] << 28 | digits[1] << 24 |
|
|
|
|
digits[2] << 20 | digits[3] << 16 |
|
|
|
|
digits[4] << 12 | digits[5] << 8 |
|
|
|
|
digits[6] << 4 | digits[7] << 0, true
|
|
|
|
default: return 0, false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func hexDigit (digit rune) int {
|
|
|
|
switch {
|
|
|
|
case digit >= '0' && digit <= '9': return int(digit - '0')
|
|
|
|
case digit >= 'a' && digit <= 'f': return int(digit - 'a') + 10
|
|
|
|
case digit >= 'A' && digit <= 'F': return int(digit - 'A') + 10
|
|
|
|
default: return -1
|
|
|
|
}
|
|
|
|
}
|