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 } name := this.Value() 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 err = this.Expect(LBrace) if err != nil { return Rule { }, err } for { this.Next() if this.Is(RBrace) { break } pos := this.Pos() name, attr, err := this.parseAttr() if err != nil { return Rule { }, err } err = this.Expect(Semicolon) if err != nil { return Rule { }, err } if _, exists := rule.Attrs[name]; exists { 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 err = this.ExpectNext(LBracket) if err == nil { this.Next() for { err := this.Expect(Ident, String, RBracket) if err != nil { return Selector { }, err } if this.Is(RBracket) { break } if this.Is(Comma) { this.Next() } selector.Tags = append(selector.Tags, this.Value()) err = this.ExpectNext(Comma, RBracket) if err != nil { return Selector { }, err } } this.Next() } return selector, nil } 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 ( "value, Comma, or Semicolon", Number, Color, String, Ident, Dollar, Slash, Comma, Semicolon) if err != nil { return "", nil, err } if this.Is(Semicolon) { break } if this.Is(Comma) { this.Next() } valueList, err := this.parseValueList() if err != nil { return "", nil, err } attr = append(attr, valueList) err = this.Expect(Comma, Semicolon) if err != nil { return "", nil, err } } return name, attr, nil } func (this *parser) parseValueList () (ValueList, error) { list := ValueList { } for { err := this.ExpectDesc ( "value", Number, Color, String, Ident, Dollar, Slash) 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())) case Slash: list = append(list, ValueCut { }) } this.Next() } return list, nil } 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 } }