attribute.go 6.9 KB

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