UNPKG

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