parser.go 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409
  1. package parser
  2. import (
  3. "errors"
  4. "fmt"
  5. "regexp"
  6. "strings"
  7. "github.com/gorilla/css/scanner"
  8. "github.com/aymerick/douceur/css"
  9. )
  10. const (
  11. importantSuffixRegexp = `(?i)\s*!important\s*$`
  12. )
  13. var (
  14. importantRegexp *regexp.Regexp
  15. )
  16. // Parser represents a CSS parser
  17. type Parser struct {
  18. scan *scanner.Scanner // Tokenizer
  19. // Tokens parsed but not consumed yet
  20. tokens []*scanner.Token
  21. // Rule embedding level
  22. embedLevel int
  23. }
  24. func init() {
  25. importantRegexp = regexp.MustCompile(importantSuffixRegexp)
  26. }
  27. // NewParser instanciates a new parser
  28. func NewParser(txt string) *Parser {
  29. return &Parser{
  30. scan: scanner.New(txt),
  31. }
  32. }
  33. // Parse parses a whole stylesheet
  34. func Parse(text string) (*css.Stylesheet, error) {
  35. result, err := NewParser(text).ParseStylesheet()
  36. if err != nil {
  37. return nil, err
  38. }
  39. return result, nil
  40. }
  41. // ParseDeclarations parses CSS declarations
  42. func ParseDeclarations(text string) ([]*css.Declaration, error) {
  43. result, err := NewParser(text).ParseDeclarations()
  44. if err != nil {
  45. return nil, err
  46. }
  47. return result, nil
  48. }
  49. // ParseStylesheet parses a stylesheet
  50. func (parser *Parser) ParseStylesheet() (*css.Stylesheet, error) {
  51. result := css.NewStylesheet()
  52. // Parse BOM
  53. if _, err := parser.parseBOM(); err != nil {
  54. return result, err
  55. }
  56. // Parse list of rules
  57. rules, err := parser.ParseRules()
  58. if err != nil {
  59. return result, err
  60. }
  61. result.Rules = rules
  62. return result, nil
  63. }
  64. // ParseRules parses a list of rules
  65. func (parser *Parser) ParseRules() ([]*css.Rule, error) {
  66. result := []*css.Rule{}
  67. inBlock := false
  68. if parser.tokenChar("{") {
  69. // parsing a block of rules
  70. inBlock = true
  71. parser.embedLevel++
  72. parser.shiftToken()
  73. }
  74. for parser.tokenParsable() {
  75. if parser.tokenIgnorable() {
  76. parser.shiftToken()
  77. } else if parser.tokenChar("}") {
  78. if !inBlock {
  79. errMsg := fmt.Sprintf("Unexpected } character: %s", parser.nextToken().String())
  80. return result, errors.New(errMsg)
  81. }
  82. parser.shiftToken()
  83. parser.embedLevel--
  84. // finished
  85. break
  86. } else {
  87. rule, err := parser.ParseRule()
  88. if err != nil {
  89. return result, err
  90. }
  91. rule.EmbedLevel = parser.embedLevel
  92. result = append(result, rule)
  93. }
  94. }
  95. return result, parser.err()
  96. }
  97. // ParseRule parses a rule
  98. func (parser *Parser) ParseRule() (*css.Rule, error) {
  99. if parser.tokenAtKeyword() {
  100. return parser.parseAtRule()
  101. }
  102. return parser.parseQualifiedRule()
  103. }
  104. // ParseDeclarations parses a list of declarations
  105. func (parser *Parser) ParseDeclarations() ([]*css.Declaration, error) {
  106. result := []*css.Declaration{}
  107. if parser.tokenChar("{") {
  108. parser.shiftToken()
  109. }
  110. for parser.tokenParsable() {
  111. if parser.tokenIgnorable() {
  112. parser.shiftToken()
  113. } else if parser.tokenChar("}") {
  114. // end of block
  115. parser.shiftToken()
  116. break
  117. } else {
  118. declaration, err := parser.ParseDeclaration()
  119. if err != nil {
  120. return result, err
  121. }
  122. result = append(result, declaration)
  123. }
  124. }
  125. return result, parser.err()
  126. }
  127. // ParseDeclaration parses a declaration
  128. func (parser *Parser) ParseDeclaration() (*css.Declaration, error) {
  129. result := css.NewDeclaration()
  130. curValue := ""
  131. for parser.tokenParsable() {
  132. if parser.tokenChar(":") {
  133. result.Property = strings.TrimSpace(curValue)
  134. curValue = ""
  135. parser.shiftToken()
  136. } else if parser.tokenChar(";") || parser.tokenChar("}") {
  137. if result.Property == "" {
  138. errMsg := fmt.Sprintf("Unexpected ; character: %s", parser.nextToken().String())
  139. return result, errors.New(errMsg)
  140. }
  141. if importantRegexp.MatchString(curValue) {
  142. result.Important = true
  143. curValue = importantRegexp.ReplaceAllString(curValue, "")
  144. }
  145. result.Value = strings.TrimSpace(curValue)
  146. if parser.tokenChar(";") {
  147. parser.shiftToken()
  148. }
  149. // finished
  150. break
  151. } else {
  152. token := parser.shiftToken()
  153. curValue += token.Value
  154. }
  155. }
  156. // log.Printf("[parsed] Declaration: %s", result.String())
  157. return result, parser.err()
  158. }
  159. // Parse an At Rule
  160. func (parser *Parser) parseAtRule() (*css.Rule, error) {
  161. // parse rule name (eg: "@import")
  162. token := parser.shiftToken()
  163. result := css.NewRule(css.AtRule)
  164. result.Name = token.Value
  165. for parser.tokenParsable() {
  166. if parser.tokenChar(";") {
  167. parser.shiftToken()
  168. // finished
  169. break
  170. } else if parser.tokenChar("{") {
  171. if result.EmbedsRules() {
  172. // parse rules block
  173. rules, err := parser.ParseRules()
  174. if err != nil {
  175. return result, err
  176. }
  177. result.Rules = rules
  178. } else {
  179. // parse declarations block
  180. declarations, err := parser.ParseDeclarations()
  181. if err != nil {
  182. return result, err
  183. }
  184. result.Declarations = declarations
  185. }
  186. // finished
  187. break
  188. } else {
  189. // parse prelude
  190. prelude, err := parser.parsePrelude()
  191. if err != nil {
  192. return result, err
  193. }
  194. result.Prelude = prelude
  195. }
  196. }
  197. // log.Printf("[parsed] Rule: %s", result.String())
  198. return result, parser.err()
  199. }
  200. // Parse a Qualified Rule
  201. func (parser *Parser) parseQualifiedRule() (*css.Rule, error) {
  202. result := css.NewRule(css.QualifiedRule)
  203. for parser.tokenParsable() {
  204. if parser.tokenChar("{") {
  205. if result.Prelude == "" {
  206. errMsg := fmt.Sprintf("Unexpected { character: %s", parser.nextToken().String())
  207. return result, errors.New(errMsg)
  208. }
  209. // parse declarations block
  210. declarations, err := parser.ParseDeclarations()
  211. if err != nil {
  212. return result, err
  213. }
  214. result.Declarations = declarations
  215. // finished
  216. break
  217. } else {
  218. // parse prelude
  219. prelude, err := parser.parsePrelude()
  220. if err != nil {
  221. return result, err
  222. }
  223. result.Prelude = prelude
  224. }
  225. }
  226. result.Selectors = strings.Split(result.Prelude, ",")
  227. for i, sel := range result.Selectors {
  228. result.Selectors[i] = strings.TrimSpace(sel)
  229. }
  230. // log.Printf("[parsed] Rule: %s", result.String())
  231. return result, parser.err()
  232. }
  233. // Parse Rule prelude
  234. func (parser *Parser) parsePrelude() (string, error) {
  235. result := ""
  236. for parser.tokenParsable() && !parser.tokenEndOfPrelude() {
  237. token := parser.shiftToken()
  238. result += token.Value
  239. }
  240. result = strings.TrimSpace(result)
  241. // log.Printf("[parsed] prelude: %s", result)
  242. return result, parser.err()
  243. }
  244. // Parse BOM
  245. func (parser *Parser) parseBOM() (bool, error) {
  246. if parser.nextToken().Type == scanner.TokenBOM {
  247. parser.shiftToken()
  248. return true, nil
  249. }
  250. return false, parser.err()
  251. }
  252. // Returns next token without removing it from tokens buffer
  253. func (parser *Parser) nextToken() *scanner.Token {
  254. if len(parser.tokens) == 0 {
  255. // fetch next token
  256. nextToken := parser.scan.Next()
  257. // log.Printf("[token] %s => %v", nextToken.Type.String(), nextToken.Value)
  258. // queue it
  259. parser.tokens = append(parser.tokens, nextToken)
  260. }
  261. return parser.tokens[0]
  262. }
  263. // Returns next token and remove it from the tokens buffer
  264. func (parser *Parser) shiftToken() *scanner.Token {
  265. var result *scanner.Token
  266. result, parser.tokens = parser.tokens[0], parser.tokens[1:]
  267. return result
  268. }
  269. // Returns tokenizer error, or nil if no error
  270. func (parser *Parser) err() error {
  271. if parser.tokenError() {
  272. token := parser.nextToken()
  273. return fmt.Errorf("Tokenizer error: %s", token.String())
  274. }
  275. return nil
  276. }
  277. // Returns true if next token is Error
  278. func (parser *Parser) tokenError() bool {
  279. return parser.nextToken().Type == scanner.TokenError
  280. }
  281. // Returns true if next token is EOF
  282. func (parser *Parser) tokenEOF() bool {
  283. return parser.nextToken().Type == scanner.TokenEOF
  284. }
  285. // Returns true if next token is a whitespace
  286. func (parser *Parser) tokenWS() bool {
  287. return parser.nextToken().Type == scanner.TokenS
  288. }
  289. // Returns true if next token is a comment
  290. func (parser *Parser) tokenComment() bool {
  291. return parser.nextToken().Type == scanner.TokenComment
  292. }
  293. // Returns true if next token is a CDO or a CDC
  294. func (parser *Parser) tokenCDOorCDC() bool {
  295. switch parser.nextToken().Type {
  296. case scanner.TokenCDO, scanner.TokenCDC:
  297. return true
  298. default:
  299. return false
  300. }
  301. }
  302. // Returns true if next token is ignorable
  303. func (parser *Parser) tokenIgnorable() bool {
  304. return parser.tokenWS() || parser.tokenComment() || parser.tokenCDOorCDC()
  305. }
  306. // Returns true if next token is parsable
  307. func (parser *Parser) tokenParsable() bool {
  308. return !parser.tokenEOF() && !parser.tokenError()
  309. }
  310. // Returns true if next token is an At Rule keyword
  311. func (parser *Parser) tokenAtKeyword() bool {
  312. return parser.nextToken().Type == scanner.TokenAtKeyword
  313. }
  314. // Returns true if next token is given character
  315. func (parser *Parser) tokenChar(value string) bool {
  316. token := parser.nextToken()
  317. return (token.Type == scanner.TokenChar) && (token.Value == value)
  318. }
  319. // Returns true if next token marks the end of a prelude
  320. func (parser *Parser) tokenEndOfPrelude() bool {
  321. return parser.tokenChar(";") || parser.tokenChar("{")
  322. }