Condition.js 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123
  1. import {
  2. WhiteSpace,
  3. Comment,
  4. Ident,
  5. LeftParenthesis,
  6. RightParenthesis,
  7. Function as FunctionToken,
  8. Colon,
  9. EOF
  10. } from '../../tokenizer/index.js';
  11. const likelyFeatureToken = new Set([Colon, RightParenthesis, EOF]);
  12. export const name = 'Condition';
  13. export const structure = {
  14. kind: String,
  15. children: [[
  16. 'Identifier',
  17. 'Feature',
  18. 'FeatureFunction',
  19. 'FeatureRange',
  20. 'SupportsDeclaration'
  21. ]]
  22. };
  23. function featureOrRange(kind) {
  24. if (this.lookupTypeNonSC(1) === Ident &&
  25. likelyFeatureToken.has(this.lookupTypeNonSC(2))) {
  26. return this.Feature(kind);
  27. }
  28. return this.FeatureRange(kind);
  29. }
  30. const parentheses = {
  31. media: featureOrRange,
  32. container: featureOrRange,
  33. supports() {
  34. return this.SupportsDeclaration();
  35. }
  36. };
  37. export function parse(kind = 'media') {
  38. const children = this.createList();
  39. scan: while (!this.eof) {
  40. switch (this.tokenType) {
  41. case Comment:
  42. case WhiteSpace:
  43. this.next();
  44. continue;
  45. case Ident:
  46. children.push(this.Identifier());
  47. break;
  48. case LeftParenthesis: {
  49. let term = this.parseWithFallback(
  50. () => parentheses[kind].call(this, kind),
  51. () => null
  52. );
  53. if (!term) {
  54. term = this.parseWithFallback(
  55. () => {
  56. this.eat(LeftParenthesis);
  57. const res = this.Condition(kind);
  58. this.eat(RightParenthesis);
  59. return res;
  60. },
  61. () => {
  62. return this.GeneralEnclosed(kind);
  63. }
  64. );
  65. }
  66. children.push(term);
  67. break;
  68. }
  69. case FunctionToken: {
  70. let term = this.parseWithFallback(
  71. () => this.FeatureFunction(kind),
  72. () => null
  73. );
  74. if (!term) {
  75. term = this.GeneralEnclosed(kind);
  76. }
  77. children.push(term);
  78. break;
  79. }
  80. default:
  81. break scan;
  82. }
  83. }
  84. if (children.isEmpty) {
  85. this.error('Condition is expected');
  86. }
  87. return {
  88. type: 'Condition',
  89. loc: this.getLocationFromList(children),
  90. kind,
  91. children
  92. };
  93. }
  94. export function generate(node) {
  95. node.children.forEach(child => {
  96. if (child.type === 'Condition') {
  97. this.token(LeftParenthesis, '(');
  98. this.node(child);
  99. this.token(RightParenthesis, ')');
  100. } else {
  101. this.node(child);
  102. }
  103. });
  104. }