Condition.cjs 2.9 KB

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