lval.js 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159
  1. import {flowParseAssignableListItemTypes} from "../plugins/flow";
  2. import {tsParseAssignableListItemTypes, tsParseModifiers} from "../plugins/typescript";
  3. import {
  4. eat,
  5. IdentifierRole,
  6. match,
  7. next,
  8. popTypeContext,
  9. pushTypeContext,
  10. } from "../tokenizer/index";
  11. import {ContextualKeyword} from "../tokenizer/keywords";
  12. import {TokenType, TokenType as tt} from "../tokenizer/types";
  13. import {isFlowEnabled, isTypeScriptEnabled, state} from "./base";
  14. import {parseIdentifier, parseMaybeAssign, parseObj} from "./expression";
  15. import {expect, unexpected} from "./util";
  16. export function parseSpread() {
  17. next();
  18. parseMaybeAssign(false);
  19. }
  20. export function parseRest(isBlockScope) {
  21. next();
  22. parseBindingAtom(isBlockScope);
  23. }
  24. export function parseBindingIdentifier(isBlockScope) {
  25. parseIdentifier();
  26. markPriorBindingIdentifier(isBlockScope);
  27. }
  28. export function parseImportedIdentifier() {
  29. parseIdentifier();
  30. state.tokens[state.tokens.length - 1].identifierRole = IdentifierRole.ImportDeclaration;
  31. }
  32. export function markPriorBindingIdentifier(isBlockScope) {
  33. let identifierRole;
  34. if (state.scopeDepth === 0) {
  35. identifierRole = IdentifierRole.TopLevelDeclaration;
  36. } else if (isBlockScope) {
  37. identifierRole = IdentifierRole.BlockScopedDeclaration;
  38. } else {
  39. identifierRole = IdentifierRole.FunctionScopedDeclaration;
  40. }
  41. state.tokens[state.tokens.length - 1].identifierRole = identifierRole;
  42. }
  43. // Parses lvalue (assignable) atom.
  44. export function parseBindingAtom(isBlockScope) {
  45. switch (state.type) {
  46. case tt._this: {
  47. // In TypeScript, "this" may be the name of a parameter, so allow it.
  48. const oldIsType = pushTypeContext(0);
  49. next();
  50. popTypeContext(oldIsType);
  51. return;
  52. }
  53. case tt._yield:
  54. case tt.name: {
  55. state.type = tt.name;
  56. parseBindingIdentifier(isBlockScope);
  57. return;
  58. }
  59. case tt.bracketL: {
  60. next();
  61. parseBindingList(tt.bracketR, isBlockScope, true /* allowEmpty */);
  62. return;
  63. }
  64. case tt.braceL:
  65. parseObj(true, isBlockScope);
  66. return;
  67. default:
  68. unexpected();
  69. }
  70. }
  71. export function parseBindingList(
  72. close,
  73. isBlockScope,
  74. allowEmpty = false,
  75. allowModifiers = false,
  76. contextId = 0,
  77. ) {
  78. let first = true;
  79. let hasRemovedComma = false;
  80. const firstItemTokenIndex = state.tokens.length;
  81. while (!eat(close) && !state.error) {
  82. if (first) {
  83. first = false;
  84. } else {
  85. expect(tt.comma);
  86. state.tokens[state.tokens.length - 1].contextId = contextId;
  87. // After a "this" type in TypeScript, we need to set the following comma (if any) to also be
  88. // a type token so that it will be removed.
  89. if (!hasRemovedComma && state.tokens[firstItemTokenIndex].isType) {
  90. state.tokens[state.tokens.length - 1].isType = true;
  91. hasRemovedComma = true;
  92. }
  93. }
  94. if (allowEmpty && match(tt.comma)) {
  95. // Empty item; nothing further to parse for this item.
  96. } else if (eat(close)) {
  97. break;
  98. } else if (match(tt.ellipsis)) {
  99. parseRest(isBlockScope);
  100. parseAssignableListItemTypes();
  101. // Support rest element trailing commas allowed by TypeScript <2.9.
  102. eat(TokenType.comma);
  103. expect(close);
  104. break;
  105. } else {
  106. parseAssignableListItem(allowModifiers, isBlockScope);
  107. }
  108. }
  109. }
  110. function parseAssignableListItem(allowModifiers, isBlockScope) {
  111. if (allowModifiers) {
  112. tsParseModifiers([
  113. ContextualKeyword._public,
  114. ContextualKeyword._protected,
  115. ContextualKeyword._private,
  116. ContextualKeyword._readonly,
  117. ContextualKeyword._override,
  118. ]);
  119. }
  120. parseMaybeDefault(isBlockScope);
  121. parseAssignableListItemTypes();
  122. parseMaybeDefault(isBlockScope, true /* leftAlreadyParsed */);
  123. }
  124. function parseAssignableListItemTypes() {
  125. if (isFlowEnabled) {
  126. flowParseAssignableListItemTypes();
  127. } else if (isTypeScriptEnabled) {
  128. tsParseAssignableListItemTypes();
  129. }
  130. }
  131. // Parses assignment pattern around given atom if possible.
  132. export function parseMaybeDefault(isBlockScope, leftAlreadyParsed = false) {
  133. if (!leftAlreadyParsed) {
  134. parseBindingAtom(isBlockScope);
  135. }
  136. if (!eat(tt.eq)) {
  137. return;
  138. }
  139. const eqIndex = state.tokens.length - 1;
  140. parseMaybeAssign();
  141. state.tokens[eqIndex].rhsEndIndex = state.tokens.length;
  142. }