grammar.pegjs 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102
  1. {
  2. function nth(n) { return { type: 'nth-child', index: { type: 'literal', value: n } }; }
  3. function nthLast(n) { return { type: 'nth-last-child', index: { type: 'literal', value: n } }; }
  4. function strUnescape(s) {
  5. return s.replace(/\\(.)/g, function(match, ch) {
  6. switch(ch) {
  7. case 'a': return '\a';
  8. case 'b': return '\b';
  9. case 'f': return '\f';
  10. case 'n': return '\n';
  11. case 'r': return '\r';
  12. case 't': return '\t';
  13. case 'v': return '\v';
  14. default: return ch;
  15. }
  16. });
  17. }
  18. }
  19. start
  20. = _ ss:selectors _ { return ss.length === 1 ? ss[0] : { type: 'matches', selectors: ss }; }
  21. / _ { return void 0; }
  22. _ = " "*
  23. identifierName = i:[^ [\],():#!=><~+.]+ { return i.join(''); }
  24. binaryOp
  25. = _ ">" _ { return 'child'; }
  26. / _ "~" _ { return 'sibling'; }
  27. / _ "+" _ { return 'adjacent'; }
  28. / " " _ { return 'descendant'; }
  29. selectors = s:selector ss:(_ "," _ selector)* {
  30. return [s].concat(ss.map(function (s) { return s[3]; }));
  31. }
  32. selector
  33. = a:sequence ops:(binaryOp sequence)* {
  34. return ops.reduce(function (memo, rhs) {
  35. return { type: rhs[0], left: memo, right: rhs[1] };
  36. }, a);
  37. }
  38. sequence
  39. = subject:"!"? as:atom+ {
  40. var b = as.length === 1 ? as[0] : { type: 'compound', selectors: as };
  41. if(subject) b.subject = true;
  42. return b;
  43. }
  44. atom
  45. = wildcard / identifier / attr / field / negation / matches
  46. / has / firstChild / lastChild / nthChild / nthLastChild / class
  47. wildcard = a:"*" { return { type: 'wildcard', value: a }; }
  48. identifier = "#"? i:identifierName { return { type: 'identifier', value: i }; }
  49. attr
  50. = "[" _ v:attrValue _ "]" { return v; }
  51. attrOps = a:[><!]? "=" { return a + '='; } / [><]
  52. attrEqOps = a:"!"? "=" { return a + '='; }
  53. attrName = i:(identifierName / ".")+ { return i.join(''); }
  54. attrValue
  55. = name:attrName _ op:attrEqOps _ value:(type / regex) {
  56. return { type: 'attribute', name: name, operator: op, value: value };
  57. }
  58. / name:attrName _ op:attrOps _ value:(string / number / path) {
  59. return { type: 'attribute', name: name, operator: op, value: value };
  60. }
  61. / name:attrName { return { type: 'attribute', name: name }; }
  62. string
  63. = "\"" d:([^\\"] / a:"\\" b:. { return a + b; })* "\"" {
  64. return { type: 'literal', value: strUnescape(d.join('')) };
  65. }
  66. / "'" d:([^\\'] / a:"\\" b:. { return a + b; })* "'" {
  67. return { type: 'literal', value: strUnescape(d.join('')) };
  68. }
  69. number
  70. = a:([0-9]* ".")? b:[0-9]+ {
  71. return { type: 'literal', value: parseFloat((a ? a.join('') : '') + b.join('')) };
  72. }
  73. path = i:identifierName { return { type: 'literal', value: i }; }
  74. type = "type(" _ t:[^ )]+ _ ")" { return { type: 'type', value: t.join('') }; }
  75. regex = "/" d:[^/]+ "/" { return { type: 'regexp', value: new RegExp(d.join('')) }; }
  76. field = "." i:identifierName is:("." identifierName)* {
  77. return { type: 'field', name: is.reduce(function(memo, p){ return memo + p[0] + p[1]; }, i)};
  78. }
  79. negation = ":not(" _ ss:selectors _ ")" { return { type: 'not', selectors: ss }; }
  80. matches = ":matches(" _ ss:selectors _ ")" { return { type: 'matches', selectors: ss }; }
  81. has = ":has(" _ ss:selectors _ ")" { return { type: 'has', selectors: ss }; }
  82. firstChild = ":first-child" { return nth(1); }
  83. lastChild = ":last-child" { return nthLast(1); }
  84. nthChild = ":nth-child(" _ n:[0-9]+ _ ")" { return nth(parseInt(n.join(''), 10)); }
  85. nthLastChild = ":nth-last-child(" _ n:[0-9]+ _ ")" { return nthLast(parseInt(n.join(''), 10)); }
  86. class = ":" c:("statement"i / "expression"i / "declaration"i / "function"i / "pattern"i) {
  87. return { type: 'class', name: c };
  88. }