UNPKG

7.01 kBJavaScriptView Raw
1/**
2 * @fileoverview Object to handle access and retrieval of tokens.
3 * @author Brandon Mills
4 */
5"use strict";
6
7//------------------------------------------------------------------------------
8// Implementation
9//------------------------------------------------------------------------------
10
11module.exports = function(tokens) {
12 const api = {},
13 starts = Object.create(null),
14 ends = Object.create(null),
15 length = tokens.length;
16
17 /**
18 * Gets tokens in a given interval.
19 * @param {int} start Inclusive index of the first token. 0 if negative.
20 * @param {int} end Exclusive index of the last token.
21 * @returns {Token[]} Tokens in the interval.
22 */
23 function get(start, end) {
24 const result = [];
25
26 for (let i = Math.max(0, start); i < end && i < length; i++) {
27 result.push(tokens[i]);
28 }
29
30 return result;
31 }
32
33 /**
34 * Gets the index in the tokens array of the last token belonging to a node.
35 * Usually a node ends exactly at a token, but due to ASI, sometimes a
36 * node's range extends beyond its last token.
37 * @param {ASTNode} node The node for which to find the last token's index.
38 * @returns {int} Index in the tokens array of the node's last token.
39 */
40 function lastTokenIndex(node) {
41 const end = node.range[1];
42 let cursor = ends[end];
43
44 // If the node extends beyond its last token, get the token before the
45 // next token
46 if (typeof cursor === "undefined") {
47 cursor = starts[end] - 1;
48 }
49
50 // If there isn't a next token, the desired token is the last one in the
51 // array
52 if (isNaN(cursor)) {
53 cursor = length - 1;
54 }
55
56 return cursor;
57 }
58
59 // Map tokens' start and end range to the index in the tokens array
60 for (let i = 0; i < length; i++) {
61 const range = tokens[i].range;
62
63 starts[range[0]] = i;
64 ends[range[1]] = i;
65 }
66
67 /**
68 * Gets a number of tokens that precede a given node or token in the token
69 * stream.
70 * @param {(ASTNode|Token)} node The AST node or token.
71 * @param {int} [beforeCount=0] The number of tokens before the node or
72 * token to retrieve.
73 * @returns {Token[]} Array of objects representing tokens.
74 */
75 api.getTokensBefore = function(node, beforeCount) {
76 const first = starts[node.range[0]];
77
78 return get(first - (beforeCount || 0), first);
79 };
80
81 /**
82 * Gets the token that precedes a given node or token in the token stream.
83 * @param {(ASTNode|Token)} node The AST node or token.
84 * @param {int} [skip=0] A number of tokens to skip before the given node or
85 * token.
86 * @returns {Token} An object representing the token.
87 */
88 api.getTokenBefore = function(node, skip) {
89 return tokens[starts[node.range[0]] - (skip || 0) - 1];
90 };
91
92 /**
93 * Gets a number of tokens that follow a given node or token in the token
94 * stream.
95 * @param {(ASTNode|Token)} node The AST node or token.
96 * @param {int} [afterCount=0] The number of tokens after the node or token
97 * to retrieve.
98 * @returns {Token[]} Array of objects representing tokens.
99 */
100 api.getTokensAfter = function(node, afterCount) {
101 const start = lastTokenIndex(node) + 1;
102
103 return get(start, start + (afterCount || 0));
104 };
105
106 /**
107 * Gets the token that follows a given node or token in the token stream.
108 * @param {(ASTNode|Token)} node The AST node or token.
109 * @param {int} [skip=0] A number of tokens to skip after the given node or
110 * token.
111 * @returns {Token} An object representing the token.
112 */
113 api.getTokenAfter = function(node, skip) {
114 return tokens[lastTokenIndex(node) + (skip || 0) + 1];
115 };
116
117 /**
118 * Gets all tokens that are related to the given node.
119 * @param {ASTNode} node The AST node.
120 * @param {int} [beforeCount=0] The number of tokens before the node to retrieve.
121 * @param {int} [afterCount=0] The number of tokens after the node to retrieve.
122 * @returns {Token[]} Array of objects representing tokens.
123 */
124 api.getTokens = function(node, beforeCount, afterCount) {
125 return get(
126 starts[node.range[0]] - (beforeCount || 0),
127 lastTokenIndex(node) + (afterCount || 0) + 1
128 );
129 };
130
131 /**
132 * Gets the first `count` tokens of the given node's token stream.
133 * @param {ASTNode} node The AST node.
134 * @param {int} [count=0] The number of tokens of the node to retrieve.
135 * @returns {Token[]} Array of objects representing tokens.
136 */
137 api.getFirstTokens = function(node, count) {
138 const first = starts[node.range[0]];
139
140 return get(
141 first,
142 Math.min(lastTokenIndex(node) + 1, first + (count || 0))
143 );
144 };
145
146 /**
147 * Gets the first token of the given node's token stream.
148 * @param {ASTNode} node The AST node.
149 * @param {int} [skip=0] A number of tokens to skip.
150 * @returns {Token} An object representing the token.
151 */
152 api.getFirstToken = function(node, skip) {
153 return tokens[starts[node.range[0]] + (skip || 0)];
154 };
155
156 /**
157 * Gets the last `count` tokens of the given node.
158 * @param {ASTNode} node The AST node.
159 * @param {int} [count=0] The number of tokens of the node to retrieve.
160 * @returns {Token[]} Array of objects representing tokens.
161 */
162 api.getLastTokens = function(node, count) {
163 const last = lastTokenIndex(node) + 1;
164
165 return get(Math.max(starts[node.range[0]], last - (count || 0)), last);
166 };
167
168 /**
169 * Gets the last token of the given node's token stream.
170 * @param {ASTNode} node The AST node.
171 * @param {int} [skip=0] A number of tokens to skip.
172 * @returns {Token} An object representing the token.
173 */
174 api.getLastToken = function(node, skip) {
175 return tokens[lastTokenIndex(node) - (skip || 0)];
176 };
177
178 /**
179 * Gets all of the tokens between two non-overlapping nodes.
180 * @param {ASTNode} left Node before the desired token range.
181 * @param {ASTNode} right Node after the desired token range.
182 * @param {int} [padding=0] Number of extra tokens on either side of center.
183 * @returns {Token[]} Tokens between left and right plus padding.
184 */
185 api.getTokensBetween = function(left, right, padding) {
186 padding = padding || 0;
187 return get(
188 lastTokenIndex(left) + 1 - padding,
189 starts[right.range[0]] + padding
190 );
191 };
192
193 /**
194 * Gets the token starting at the specified index.
195 * @param {int} startIndex Index of the start of the token's range.
196 * @returns {Token} The token starting at index, or null if no such token.
197 */
198 api.getTokenByRangeStart = function(startIndex) {
199 return (tokens[starts[startIndex]] || null);
200 };
201
202 return api;
203};