attribute.go 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328
  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] == '_' || line[i] == '-' || line[i] == ':' || line[i] == '.'); i++ {
  88. }
  89. name := attrNameClass
  90. if c == '#' {
  91. name = attrNameID
  92. }
  93. reader.Advance(i)
  94. return Attribute{Name: name, Value: line[0:i]}, true
  95. }
  96. line, _ := reader.PeekLine()
  97. if len(line) == 0 {
  98. return Attribute{}, false
  99. }
  100. c = line[0]
  101. if !((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') ||
  102. c == '_' || c == ':') {
  103. return Attribute{}, false
  104. }
  105. i := 0
  106. for ; i < len(line); i++ {
  107. c = line[i]
  108. if !((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') ||
  109. (c >= '0' && c <= '9') ||
  110. c == '_' || c == ':' || c == '.' || c == '-') {
  111. break
  112. }
  113. }
  114. name := line[:i]
  115. reader.Advance(i)
  116. reader.SkipSpaces()
  117. c = reader.Peek()
  118. if c != '=' {
  119. return Attribute{}, false
  120. }
  121. reader.Advance(1)
  122. reader.SkipSpaces()
  123. value, ok := parseAttributeValue(reader)
  124. if !ok {
  125. return Attribute{}, false
  126. }
  127. if bytes.Equal(name, attrNameClass) {
  128. if _, ok = value.([]byte); !ok {
  129. return Attribute{}, false
  130. }
  131. }
  132. return Attribute{Name: name, Value: value}, true
  133. }
  134. func parseAttributeValue(reader text.Reader) (interface{}, bool) {
  135. reader.SkipSpaces()
  136. c := reader.Peek()
  137. var value interface{}
  138. ok := false
  139. switch c {
  140. case text.EOF:
  141. return Attribute{}, false
  142. case '{':
  143. value, ok = ParseAttributes(reader)
  144. case '[':
  145. value, ok = parseAttributeArray(reader)
  146. case '"':
  147. value, ok = parseAttributeString(reader)
  148. default:
  149. if c == '-' || c == '+' || util.IsNumeric(c) {
  150. value, ok = parseAttributeNumber(reader)
  151. } else {
  152. value, ok = parseAttributeOthers(reader)
  153. }
  154. }
  155. if !ok {
  156. return nil, false
  157. }
  158. return value, true
  159. }
  160. func parseAttributeArray(reader text.Reader) ([]interface{}, bool) {
  161. reader.Advance(1) // skip [
  162. ret := []interface{}{}
  163. for i := 0; ; i++ {
  164. c := reader.Peek()
  165. comma := false
  166. if i != 0 && c == ',' {
  167. reader.Advance(1)
  168. comma = true
  169. }
  170. if c == ']' {
  171. if !comma {
  172. reader.Advance(1)
  173. return ret, true
  174. }
  175. return nil, false
  176. }
  177. reader.SkipSpaces()
  178. value, ok := parseAttributeValue(reader)
  179. if !ok {
  180. return nil, false
  181. }
  182. ret = append(ret, value)
  183. reader.SkipSpaces()
  184. }
  185. }
  186. func parseAttributeString(reader text.Reader) ([]byte, bool) {
  187. reader.Advance(1) // skip "
  188. line, _ := reader.PeekLine()
  189. i := 0
  190. l := len(line)
  191. var buf bytes.Buffer
  192. for i < l {
  193. c := line[i]
  194. if c == '\\' && i != l-1 {
  195. n := line[i+1]
  196. switch n {
  197. case '"', '/', '\\':
  198. buf.WriteByte(n)
  199. i += 2
  200. case 'b':
  201. buf.WriteString("\b")
  202. i += 2
  203. case 'f':
  204. buf.WriteString("\f")
  205. i += 2
  206. case 'n':
  207. buf.WriteString("\n")
  208. i += 2
  209. case 'r':
  210. buf.WriteString("\r")
  211. i += 2
  212. case 't':
  213. buf.WriteString("\t")
  214. i += 2
  215. default:
  216. buf.WriteByte('\\')
  217. i++
  218. }
  219. continue
  220. }
  221. if c == '"' {
  222. reader.Advance(i + 1)
  223. return buf.Bytes(), true
  224. }
  225. buf.WriteByte(c)
  226. i++
  227. }
  228. return nil, false
  229. }
  230. func scanAttributeDecimal(reader text.Reader, w io.ByteWriter) {
  231. for {
  232. c := reader.Peek()
  233. if util.IsNumeric(c) {
  234. w.WriteByte(c)
  235. } else {
  236. return
  237. }
  238. reader.Advance(1)
  239. }
  240. }
  241. func parseAttributeNumber(reader text.Reader) (float64, bool) {
  242. sign := 1
  243. c := reader.Peek()
  244. if c == '-' {
  245. sign = -1
  246. reader.Advance(1)
  247. } else if c == '+' {
  248. reader.Advance(1)
  249. }
  250. var buf bytes.Buffer
  251. if !util.IsNumeric(reader.Peek()) {
  252. return 0, false
  253. }
  254. scanAttributeDecimal(reader, &buf)
  255. if buf.Len() == 0 {
  256. return 0, false
  257. }
  258. c = reader.Peek()
  259. if c == '.' {
  260. buf.WriteByte(c)
  261. reader.Advance(1)
  262. scanAttributeDecimal(reader, &buf)
  263. }
  264. c = reader.Peek()
  265. if c == 'e' || c == 'E' {
  266. buf.WriteByte(c)
  267. reader.Advance(1)
  268. c = reader.Peek()
  269. if c == '-' || c == '+' {
  270. buf.WriteByte(c)
  271. reader.Advance(1)
  272. }
  273. scanAttributeDecimal(reader, &buf)
  274. }
  275. f, err := strconv.ParseFloat(buf.String(), 10)
  276. if err != nil {
  277. return 0, false
  278. }
  279. return float64(sign) * f, true
  280. }
  281. var bytesTrue = []byte("true")
  282. var bytesFalse = []byte("false")
  283. var bytesNull = []byte("null")
  284. func parseAttributeOthers(reader text.Reader) (interface{}, bool) {
  285. line, _ := reader.PeekLine()
  286. c := line[0]
  287. if !((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') ||
  288. c == '_' || c == ':') {
  289. return nil, false
  290. }
  291. i := 0
  292. for ; i < len(line); i++ {
  293. c := line[i]
  294. if !((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') ||
  295. (c >= '0' && c <= '9') ||
  296. c == '_' || c == ':' || c == '.' || c == '-') {
  297. break
  298. }
  299. }
  300. value := line[:i]
  301. reader.Advance(i)
  302. if bytes.Equal(value, bytesTrue) {
  303. return true, true
  304. }
  305. if bytes.Equal(value, bytesFalse) {
  306. return false, true
  307. }
  308. if bytes.Equal(value, bytesNull) {
  309. return nil, true
  310. }
  311. return value, true
  312. }