UNPKG

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