UNPKG

4.42 kBJavaScriptView Raw
1var assert = require('assert');
2
3var FUNCTION_TYPE_RE = /Function/;
4var PAREN_KEYWORD_TYPE_RE = /Statement$|^CatchClause$/;
5var NO_PAREN_KEYWORD_TYPE_RE = /^ExpressionStatement$|^ReturnStatement$|^ThrowStatement$/;
6var QUASI_STATEMENT_TYPE_RE = /Statement$|Declaration$/;
7
8function nodeContains(node, token) {
9 var currentNode = token.parentElement;
10 while (currentNode) {
11 if (currentNode === node) {
12 return true;
13 }
14 currentNode = currentNode.parentElement;
15 }
16 return false;
17}
18
19/**
20 * Returns the type of AST node or ECMAScript production in which the provided
21 * open parenthesis token is included.
22 *
23 * @param {Object} token
24 * @returns {String}
25 */
26exports.categorizeOpenParen = function(token) {
27 assert(token.value === '(', 'Input token must be a parenthesis');
28 var node = token.parentElement;
29 var nodeType = node.type;
30 var prevToken = token.getPreviousCodeToken();
31
32 // Outermost grouping parenthesis
33 if (!prevToken) {
34 return 'ParenthesizedExpression';
35 }
36
37 // Part of a parentheses-bearing statement (if, with, while, switch, etc.)
38 if (prevToken.type === 'Keyword' && PAREN_KEYWORD_TYPE_RE.test(nodeType) &&
39 !NO_PAREN_KEYWORD_TYPE_RE.test(nodeType)) {
40
41 return 'Statement';
42 }
43
44 // Part of a function definition (declaration, expression, method, etc.)
45 if (FUNCTION_TYPE_RE.test(nodeType) &&
46
47 // Name is optional for function expressions
48 (prevToken.type === 'Identifier' || prevToken.value === 'function')) {
49
50 return 'Function';
51 }
52
53 // Part of a call expression
54 var prevNode = prevToken.parentElement;
55 if ((nodeType === 'CallExpression' || nodeType === 'NewExpression') &&
56
57 // Must not be inside an arguments list or other grouping parentheses
58 prevToken.value !== ',' && prevToken.value !== '(' &&
59
60 // If the callee is parenthesized (e.g., `(foo.bar)()`), prevNode will match node
61 // Otherwise (e.g., `foo.bar()`), prevToken must be the last token of the callee node
62 (prevNode === node || prevToken === node.callee.getLastToken())) {
63
64 return 'CallExpression';
65 }
66
67 // All remaining cases are grouping parentheses
68 return 'ParenthesizedExpression';
69};
70
71/**
72 * Returns the type of AST node or ECMAScript production in which the provided
73 * close parenthesis token is included.
74 *
75 * @param {Object} token
76 * @returns {String}
77 */
78exports.categorizeCloseParen = function(token) {
79 assert(token.value === ')', 'Input token must be a parenthesis');
80 var node = token.parentElement;
81 var nodeType = node.type;
82 var nextToken = token.getNextCodeToken();
83
84 // Terminal statement
85 if (nextToken.type === 'EOF') {
86 switch (nodeType) {
87 case 'DoWhileStatement':
88 return 'Statement';
89 case 'CallExpression':
90 case 'NewExpression':
91 return 'CallExpression';
92 default:
93 return 'ParenthesizedExpression';
94 }
95 }
96
97 // Part of a parentheses-bearing statement (if, with, while, switch, etc.)
98 if (PAREN_KEYWORD_TYPE_RE.test(nodeType) && !NO_PAREN_KEYWORD_TYPE_RE.test(nodeType)) {
99 // Closing parentheses for `switch` and `catch` must be followed by "{"
100 // Closing parentheses for `do..while` may be the last punctuation inside a block
101 if (nextToken.value === '{' || nextToken.value === '}') {
102 return 'Statement';
103 }
104
105 // Closing parentheses for other statements must be followed by a statement or declaration
106 var nextNode = nextToken.parentElement;
107 while (!nodeContains(nextNode, token)) {
108 if (QUASI_STATEMENT_TYPE_RE.test(nextNode.type)) {
109 return 'Statement';
110 }
111 nextNode = nextNode.parentElement;
112 }
113 }
114
115 // Part of a function definition (declaration, expression, method, etc.)
116 if (nextToken.value === '{' && FUNCTION_TYPE_RE.test(nodeType)) {
117 return 'Function';
118 }
119
120 // Part of a call expression
121 if ((nodeType === 'CallExpression' || nodeType === 'NewExpression')) {
122 var openParen = node.callee.getNextToken();
123 if (openParen.parentElement === node && node.lastChild === token) {
124 return 'CallExpression';
125 }
126 }
127
128 // All remaining cases are grouping parentheses
129 return 'ParenthesizedExpression';
130};