func ParseAttributes(reader text.Reader) (Attributes, bool) {
	savedLine, savedPosition := reader.Position()
	reader.SkipSpaces()
	if reader.Peek() != '{' {
		reader.SetPosition(savedLine, savedPosition)
		return nil, false
	}
	reader.Advance(1)
	attrs := Attributes{}
	for {
		if reader.Peek() == '}' {
			reader.Advance(1)
			return attrs, true
		}
		attr, ok := parseAttribute(reader)
		if !ok {
			reader.SetPosition(savedLine, savedPosition)
			return nil, false
		}
		if bytes.Equal(attr.Name, attrNameClass) {
			if !attrs.findUpdate(attrNameClass, func(v interface{}) interface{} {
				ret := make([]byte, 0, len(v.([]byte))+1+len(attr.Value.([]byte)))
				ret = append(ret, v.([]byte)...)
				return append(append(ret, ' '), attr.Value.([]byte)...)
			}) {
				attrs = append(attrs, attr)
			}
		} else {
			attrs = append(attrs, attr)
		}
		reader.SkipSpaces()
		if reader.Peek() == ',' {
			reader.Advance(1)
			reader.SkipSpaces()
		}
	}
}
ParseAttributes parses attributes into a map.
ParseAttributes returns a parsed attributes and true if could parse
attributes, otherwise nil and false.
package parser
import (
	"bytes"
	"io"
	"strconv"
	"github.com/yuin/goldmark/text"
	"github.com/yuin/goldmark/util"
)
var attrNameID = []byte("id")
var attrNameClass = []byte("class")
type Attribute struct {
	Name  []byte
	Value interface{}
}
type Attributes []Attribute
func (as Attributes) Find(name []byte) (interface{}, bool) {
	for _, a := range as {
		if bytes.Equal(a.Name, name) {
			return a.Value, true
		}
	}
	return nil, false
}
func (as Attributes) findUpdate(name []byte, cb func(v interface{}) interface{}) bool {
	for i, a := range as {
		if bytes.Equal(a.Name, name) {
			as[i].Value = cb(a.Value)
			return true
		}
	}
	return false
}