1 package parser
2
3 import (
4 "bytes"
5 "io"
6 "strconv"
7
8 "github.com/yuin/goldmark/text"
9 "github.com/yuin/goldmark/util"
10 )
11
12 var attrNameID = []byte("id")
13 var attrNameClass = []byte("class")
14
15
16 type Attribute struct {
17 Name []byte
18 Value interface{}
19 }
20
21
22 type Attributes []Attribute
23
24
25 func (as Attributes) Find(name []byte) (interface{}, bool) {
26 for _, a := range as {
27 if bytes.Equal(a.Name, name) {
28 return a.Value, true
29 }
30 }
31 return nil, false
32 }
33
34 func (as Attributes) findUpdate(name []byte, cb func(v interface{}) interface{}) bool {
35 for i, a := range as {
36 if bytes.Equal(a.Name, name) {
37 as[i].Value = cb(a.Value)
38 return true
39 }
40 }
41 return false
42 }
43
44
45
46
47 func ParseAttributes(reader text.Reader) (Attributes, bool) {
48 savedLine, savedPosition := reader.Position()
49 reader.SkipSpaces()
50 if reader.Peek() != '{' {
51 reader.SetPosition(savedLine, savedPosition)
52 return nil, false
53 }
54 reader.Advance(1)
55 attrs := Attributes{}
56 for {
57 if reader.Peek() == '}' {
58 reader.Advance(1)
59 return attrs, true
60 }
61 attr, ok := parseAttribute(reader)
62 if !ok {
63 reader.SetPosition(savedLine, savedPosition)
64 return nil, false
65 }
66 if bytes.Equal(attr.Name, attrNameClass) {
67 if !attrs.findUpdate(attrNameClass, func(v interface{}) interface{} {
68 ret := make([]byte, 0, len(v.([]byte))+1+len(attr.Value.([]byte)))
69 ret = append(ret, v.([]byte)...)
70 return append(append(ret, ' '), attr.Value.([]byte)...)
71 }) {
72 attrs = append(attrs, attr)
73 }
74 } else {
75 attrs = append(attrs, attr)
76 }
77 reader.SkipSpaces()
78 if reader.Peek() == ',' {
79 reader.Advance(1)
80 reader.SkipSpaces()
81 }
82 }
83 }
84
85 func parseAttribute(reader text.Reader) (Attribute, bool) {
86 reader.SkipSpaces()
87 c := reader.Peek()
88 if c == '#' || c == '.' {
89 reader.Advance(1)
90 line, _ := reader.PeekLine()
91 i := 0
92
93
94
95 for ; i < len(line) && !util.IsSpace(line[i]) &&
96 (!util.IsPunct(line[i]) || line[i] == '_' ||
97 line[i] == '-' || line[i] == ':' || line[i] == '.'); i++ {
98 }
99 name := attrNameClass
100 if c == '#' {
101 name = attrNameID
102 }
103 reader.Advance(i)
104 return Attribute{Name: name, Value: line[0:i]}, true
105 }
106 line, _ := reader.PeekLine()
107 if len(line) == 0 {
108 return Attribute{}, false
109 }
110 c = line[0]
111 if !((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') ||
112 c == '_' || c == ':') {
113 return Attribute{}, false
114 }
115 i := 0
116 for ; i < len(line); i++ {
117 c = line[i]
118 if !((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') ||
119 (c >= '0' && c <= '9') ||
120 c == '_' || c == ':' || c == '.' || c == '-') {
121 break
122 }
123 }
124 name := line[:i]
125 reader.Advance(i)
126 reader.SkipSpaces()
127 c = reader.Peek()
128 if c != '=' {
129 return Attribute{}, false
130 }
131 reader.Advance(1)
132 reader.SkipSpaces()
133 value, ok := parseAttributeValue(reader)
134 if !ok {
135 return Attribute{}, false
136 }
137 if bytes.Equal(name, attrNameClass) {
138 if _, ok = value.([]byte); !ok {
139 return Attribute{}, false
140 }
141 }
142 return Attribute{Name: name, Value: value}, true
143 }
144
145 func parseAttributeValue(reader text.Reader) (interface{}, bool) {
146 reader.SkipSpaces()
147 c := reader.Peek()
148 var value interface{}
149 var ok bool
150 switch c {
151 case text.EOF:
152 return Attribute{}, false
153 case '{':
154 value, ok = ParseAttributes(reader)
155 case '[':
156 value, ok = parseAttributeArray(reader)
157 case '"':
158 value, ok = parseAttributeString(reader)
159 default:
160 if c == '-' || c == '+' || util.IsNumeric(c) {
161 value, ok = parseAttributeNumber(reader)
162 } else {
163 value, ok = parseAttributeOthers(reader)
164 }
165 }
166 if !ok {
167 return nil, false
168 }
169 return value, true
170 }
171
172 func parseAttributeArray(reader text.Reader) ([]interface{}, bool) {
173 reader.Advance(1)
174 ret := []interface{}{}
175 for i := 0; ; i++ {
176 c := reader.Peek()
177 comma := false
178 if i != 0 && c == ',' {
179 reader.Advance(1)
180 comma = true
181 }
182 if c == ']' {
183 if !comma {
184 reader.Advance(1)
185 return ret, true
186 }
187 return nil, false
188 }
189 reader.SkipSpaces()
190 value, ok := parseAttributeValue(reader)
191 if !ok {
192 return nil, false
193 }
194 ret = append(ret, value)
195 reader.SkipSpaces()
196 }
197 }
198
199 func parseAttributeString(reader text.Reader) ([]byte, bool) {
200 reader.Advance(1)
201 line, _ := reader.PeekLine()
202 i := 0
203 l := len(line)
204 var buf bytes.Buffer
205 for i < l {
206 c := line[i]
207 if c == '\\' && i != l-1 {
208 n := line[i+1]
209 switch n {
210 case '"', '/', '\\':
211 buf.WriteByte(n)
212 i += 2
213 case 'b':
214 buf.WriteString("\b")
215 i += 2
216 case 'f':
217 buf.WriteString("\f")
218 i += 2
219 case 'n':
220 buf.WriteString("\n")
221 i += 2
222 case 'r':
223 buf.WriteString("\r")
224 i += 2
225 case 't':
226 buf.WriteString("\t")
227 i += 2
228 default:
229 buf.WriteByte('\\')
230 i++
231 }
232 continue
233 }
234 if c == '"' {
235 reader.Advance(i + 1)
236 return buf.Bytes(), true
237 }
238 buf.WriteByte(c)
239 i++
240 }
241 return nil, false
242 }
243
244 func scanAttributeDecimal(reader text.Reader, w io.ByteWriter) {
245 for {
246 c := reader.Peek()
247 if util.IsNumeric(c) {
248 _ = w.WriteByte(c)
249 } else {
250 return
251 }
252 reader.Advance(1)
253 }
254 }
255
256 func parseAttributeNumber(reader text.Reader) (float64, bool) {
257 sign := 1
258 c := reader.Peek()
259 if c == '-' {
260 sign = -1
261 reader.Advance(1)
262 } else if c == '+' {
263 reader.Advance(1)
264 }
265 var buf bytes.Buffer
266 if !util.IsNumeric(reader.Peek()) {
267 return 0, false
268 }
269 scanAttributeDecimal(reader, &buf)
270 if buf.Len() == 0 {
271 return 0, false
272 }
273 c = reader.Peek()
274 if c == '.' {
275 buf.WriteByte(c)
276 reader.Advance(1)
277 scanAttributeDecimal(reader, &buf)
278 }
279 c = reader.Peek()
280 if c == 'e' || c == 'E' {
281 buf.WriteByte(c)
282 reader.Advance(1)
283 c = reader.Peek()
284 if c == '-' || c == '+' {
285 buf.WriteByte(c)
286 reader.Advance(1)
287 }
288 scanAttributeDecimal(reader, &buf)
289 }
290 f, err := strconv.ParseFloat(buf.String(), 64)
291 if err != nil {
292 return 0, false
293 }
294 return float64(sign) * f, true
295 }
296
297 var bytesTrue = []byte("true")
298 var bytesFalse = []byte("false")
299 var bytesNull = []byte("null")
300
301 func parseAttributeOthers(reader text.Reader) (interface{}, bool) {
302 line, _ := reader.PeekLine()
303 c := line[0]
304 if !((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') ||
305 c == '_' || c == ':') {
306 return nil, false
307 }
308 i := 0
309 for ; i < len(line); i++ {
310 c := line[i]
311 if !((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') ||
312 (c >= '0' && c <= '9') ||
313 c == '_' || c == ':' || c == '.' || c == '-') {
314 break
315 }
316 }
317 value := line[:i]
318 reader.Advance(i)
319 if bytes.Equal(value, bytesTrue) {
320 return true, true
321 }
322 if bytes.Equal(value, bytesFalse) {
323 return false, true
324 }
325 if bytes.Equal(value, bytesNull) {
326 return nil, true
327 }
328 return value, true
329 }
330
View as plain text