nodes.go 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285
  1. package parser
  2. import (
  3. "regexp"
  4. "strings"
  5. )
  6. var selfClosingTags = [...]string{
  7. "meta",
  8. "img",
  9. "link",
  10. "input",
  11. "source",
  12. "area",
  13. "base",
  14. "col",
  15. "br",
  16. "hr",
  17. }
  18. var doctypes = map[string]string{
  19. "5": `<!DOCTYPE html>`,
  20. "default": `<!DOCTYPE html>`,
  21. "xml": `<?xml version="1.0" encoding="utf-8" ?>`,
  22. "transitional": `<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">`,
  23. "strict": `<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">`,
  24. "frameset": `<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Frameset//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd">`,
  25. "1.1": `<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">`,
  26. "basic": `<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML Basic 1.1//EN" "http://www.w3.org/TR/xhtml-basic/xhtml-basic11.dtd">`,
  27. "mobile": `<!DOCTYPE html PUBLIC "-//WAPFORUM//DTD XHTML Mobile 1.2//EN" "http://www.openmobilealliance.org/tech/DTD/xhtml-mobile12.dtd">`,
  28. }
  29. type Node interface {
  30. Pos() SourcePosition
  31. }
  32. type SourcePosition struct {
  33. LineNum int
  34. ColNum int
  35. TokenLength int
  36. Filename string
  37. }
  38. func (s *SourcePosition) Pos() SourcePosition {
  39. return *s
  40. }
  41. type Doctype struct {
  42. SourcePosition
  43. Value string
  44. }
  45. func newDoctype(value string) *Doctype {
  46. dt := new(Doctype)
  47. dt.Value = value
  48. return dt
  49. }
  50. func (d *Doctype) String() string {
  51. if defined := doctypes[d.Value]; len(defined) != 0 {
  52. return defined
  53. }
  54. return `<!DOCTYPE ` + d.Value + `>`
  55. }
  56. type Comment struct {
  57. SourcePosition
  58. Value string
  59. Block *Block
  60. Silent bool
  61. }
  62. func newComment(value string) *Comment {
  63. dt := new(Comment)
  64. dt.Value = value
  65. dt.Block = nil
  66. dt.Silent = false
  67. return dt
  68. }
  69. type Text struct {
  70. SourcePosition
  71. Value string
  72. Raw bool
  73. }
  74. func newText(value string, raw bool) *Text {
  75. dt := new(Text)
  76. dt.Value = value
  77. dt.Raw = raw
  78. return dt
  79. }
  80. type Block struct {
  81. SourcePosition
  82. Children []Node
  83. }
  84. func newBlock() *Block {
  85. block := new(Block)
  86. block.Children = make([]Node, 0)
  87. return block
  88. }
  89. func (b *Block) push(node Node) {
  90. b.Children = append(b.Children, node)
  91. }
  92. func (b *Block) pushFront(node Node) {
  93. b.Children = append([]Node{node}, b.Children...)
  94. }
  95. func (b *Block) CanInline() bool {
  96. if len(b.Children) == 0 {
  97. return true
  98. }
  99. allText := true
  100. for _, child := range b.Children {
  101. if txt, ok := child.(*Text); !ok || txt.Raw {
  102. allText = false
  103. break
  104. }
  105. }
  106. return allText
  107. }
  108. const (
  109. NamedBlockDefault = iota
  110. NamedBlockAppend
  111. NamedBlockPrepend
  112. )
  113. type NamedBlock struct {
  114. Block
  115. Name string
  116. Modifier int
  117. }
  118. func newNamedBlock(name string) *NamedBlock {
  119. bb := new(NamedBlock)
  120. bb.Name = name
  121. bb.Block.Children = make([]Node, 0)
  122. bb.Modifier = NamedBlockDefault
  123. return bb
  124. }
  125. type Attribute struct {
  126. SourcePosition
  127. Name string
  128. Value string
  129. IsRaw bool
  130. Condition string
  131. }
  132. type Tag struct {
  133. SourcePosition
  134. Block *Block
  135. Name string
  136. IsInterpolated bool
  137. Attributes []Attribute
  138. }
  139. func newTag(name string) *Tag {
  140. tag := new(Tag)
  141. tag.Block = nil
  142. tag.Name = name
  143. tag.Attributes = make([]Attribute, 0)
  144. tag.IsInterpolated = false
  145. return tag
  146. }
  147. func (t *Tag) IsSelfClosing() bool {
  148. for _, tag := range selfClosingTags {
  149. if tag == t.Name {
  150. return true
  151. }
  152. }
  153. return false
  154. }
  155. func (t *Tag) IsRawText() bool {
  156. return t.Name == "style" || t.Name == "script"
  157. }
  158. type Condition struct {
  159. SourcePosition
  160. Positive *Block
  161. Negative *Block
  162. Expression string
  163. }
  164. func newCondition(exp string) *Condition {
  165. cond := new(Condition)
  166. cond.Expression = exp
  167. return cond
  168. }
  169. type Each struct {
  170. SourcePosition
  171. X string
  172. Y string
  173. Expression string
  174. Block *Block
  175. }
  176. func newEach(exp string) *Each {
  177. each := new(Each)
  178. each.Expression = exp
  179. return each
  180. }
  181. type Assignment struct {
  182. SourcePosition
  183. X string
  184. Expression string
  185. }
  186. func newAssignment(x, expression string) *Assignment {
  187. assgn := new(Assignment)
  188. assgn.X = x
  189. assgn.Expression = expression
  190. return assgn
  191. }
  192. type Mixin struct {
  193. SourcePosition
  194. Block *Block
  195. Name string
  196. Args []string
  197. }
  198. func newMixin(name, args string) *Mixin {
  199. mixin := new(Mixin)
  200. mixin.Name = name
  201. delExp := regexp.MustCompile(`,\s`)
  202. mixin.Args = delExp.Split(args, -1)
  203. for i := 0; i < len(mixin.Args); i++ {
  204. mixin.Args[i] = strings.TrimSpace(mixin.Args[i])
  205. if mixin.Args[i] == "" {
  206. mixin.Args = append(mixin.Args[:i], mixin.Args[i+1:]...)
  207. i--
  208. }
  209. }
  210. return mixin
  211. }
  212. type MixinCall struct {
  213. SourcePosition
  214. Name string
  215. Args []string
  216. }
  217. func newMixinCall(name, args string) *MixinCall {
  218. mixinCall := new(MixinCall)
  219. mixinCall.Name = name
  220. if args != "" {
  221. const t = "%s"
  222. quoteExp := regexp.MustCompile(`"(.*?)"`)
  223. delExp := regexp.MustCompile(`,\s`)
  224. quotes := quoteExp.FindAllString(args, -1)
  225. replaced := quoteExp.ReplaceAllString(args, t)
  226. mixinCall.Args = delExp.Split(replaced, -1)
  227. qi := 0
  228. for i, arg := range mixinCall.Args {
  229. if arg == t {
  230. mixinCall.Args[i] = quotes[qi]
  231. qi++
  232. }
  233. }
  234. }
  235. return mixinCall
  236. }