.gitignore added
[dotfiles/.git] / .config / coc / extensions / node_modules / coc-prettier / node_modules / espree / lib / token-translator.js
1 /**
2  * @fileoverview Translates tokens between Acorn format and Esprima format.
3  * @author Nicholas C. Zakas
4  */
5 /* eslint no-underscore-dangle: 0 */
6
7 "use strict";
8
9 //------------------------------------------------------------------------------
10 // Requirements
11 //------------------------------------------------------------------------------
12
13 // none!
14
15 //------------------------------------------------------------------------------
16 // Private
17 //------------------------------------------------------------------------------
18
19
20 // Esprima Token Types
21 const Token = {
22     Boolean: "Boolean",
23     EOF: "<end>",
24     Identifier: "Identifier",
25     Keyword: "Keyword",
26     Null: "Null",
27     Numeric: "Numeric",
28     Punctuator: "Punctuator",
29     String: "String",
30     RegularExpression: "RegularExpression",
31     Template: "Template",
32     JSXIdentifier: "JSXIdentifier",
33     JSXText: "JSXText"
34 };
35
36 /**
37  * Converts part of a template into an Esprima token.
38  * @param {AcornToken[]} tokens The Acorn tokens representing the template.
39  * @param {string} code The source code.
40  * @returns {EsprimaToken} The Esprima equivalent of the template token.
41  * @private
42  */
43 function convertTemplatePart(tokens, code) {
44     const firstToken = tokens[0],
45         lastTemplateToken = tokens[tokens.length - 1];
46
47     const token = {
48         type: Token.Template,
49         value: code.slice(firstToken.start, lastTemplateToken.end)
50     };
51
52     if (firstToken.loc) {
53         token.loc = {
54             start: firstToken.loc.start,
55             end: lastTemplateToken.loc.end
56         };
57     }
58
59     if (firstToken.range) {
60         token.start = firstToken.range[0];
61         token.end = lastTemplateToken.range[1];
62         token.range = [token.start, token.end];
63     }
64
65     return token;
66 }
67
68 /**
69  * Contains logic to translate Acorn tokens into Esprima tokens.
70  * @param {Object} acornTokTypes The Acorn token types.
71  * @param {string} code The source code Acorn is parsing. This is necessary
72  *      to correct the "value" property of some tokens.
73  * @constructor
74  */
75 function TokenTranslator(acornTokTypes, code) {
76
77     // token types
78     this._acornTokTypes = acornTokTypes;
79
80     // token buffer for templates
81     this._tokens = [];
82
83     // track the last curly brace
84     this._curlyBrace = null;
85
86     // the source code
87     this._code = code;
88
89 }
90
91 TokenTranslator.prototype = {
92     constructor: TokenTranslator,
93
94     /**
95      * Translates a single Esprima token to a single Acorn token. This may be
96      * inaccurate due to how templates are handled differently in Esprima and
97      * Acorn, but should be accurate for all other tokens.
98      * @param {AcornToken} token The Acorn token to translate.
99      * @param {Object} extra Espree extra object.
100      * @returns {EsprimaToken} The Esprima version of the token.
101      */
102     translate(token, extra) {
103
104         const type = token.type,
105             tt = this._acornTokTypes;
106
107         if (type === tt.name) {
108             token.type = Token.Identifier;
109
110             // TODO: See if this is an Acorn bug
111             if (token.value === "static") {
112                 token.type = Token.Keyword;
113             }
114
115             if (extra.ecmaVersion > 5 && (token.value === "yield" || token.value === "let")) {
116                 token.type = Token.Keyword;
117             }
118
119         } else if (type === tt.semi || type === tt.comma ||
120                  type === tt.parenL || type === tt.parenR ||
121                  type === tt.braceL || type === tt.braceR ||
122                  type === tt.dot || type === tt.bracketL ||
123                  type === tt.colon || type === tt.question ||
124                  type === tt.bracketR || type === tt.ellipsis ||
125                  type === tt.arrow || type === tt.jsxTagStart ||
126                  type === tt.incDec || type === tt.starstar ||
127                  type === tt.jsxTagEnd || type === tt.prefix ||
128                  type === tt.questionDot ||
129                  (type.binop && !type.keyword) ||
130                  type.isAssign) {
131
132             token.type = Token.Punctuator;
133             token.value = this._code.slice(token.start, token.end);
134         } else if (type === tt.jsxName) {
135             token.type = Token.JSXIdentifier;
136         } else if (type.label === "jsxText" || type === tt.jsxAttrValueToken) {
137             token.type = Token.JSXText;
138         } else if (type.keyword) {
139             if (type.keyword === "true" || type.keyword === "false") {
140                 token.type = Token.Boolean;
141             } else if (type.keyword === "null") {
142                 token.type = Token.Null;
143             } else {
144                 token.type = Token.Keyword;
145             }
146         } else if (type === tt.num) {
147             token.type = Token.Numeric;
148             token.value = this._code.slice(token.start, token.end);
149         } else if (type === tt.string) {
150
151             if (extra.jsxAttrValueToken) {
152                 extra.jsxAttrValueToken = false;
153                 token.type = Token.JSXText;
154             } else {
155                 token.type = Token.String;
156             }
157
158             token.value = this._code.slice(token.start, token.end);
159         } else if (type === tt.regexp) {
160             token.type = Token.RegularExpression;
161             const value = token.value;
162
163             token.regex = {
164                 flags: value.flags,
165                 pattern: value.pattern
166             };
167             token.value = `/${value.pattern}/${value.flags}`;
168         }
169
170         return token;
171     },
172
173     /**
174      * Function to call during Acorn's onToken handler.
175      * @param {AcornToken} token The Acorn token.
176      * @param {Object} extra The Espree extra object.
177      * @returns {void}
178      */
179     onToken(token, extra) {
180
181         const that = this,
182             tt = this._acornTokTypes,
183             tokens = extra.tokens,
184             templateTokens = this._tokens;
185
186         /**
187          * Flushes the buffered template tokens and resets the template
188          * tracking.
189          * @returns {void}
190          * @private
191          */
192         function translateTemplateTokens() {
193             tokens.push(convertTemplatePart(that._tokens, that._code));
194             that._tokens = [];
195         }
196
197         if (token.type === tt.eof) {
198
199             // might be one last curlyBrace
200             if (this._curlyBrace) {
201                 tokens.push(this.translate(this._curlyBrace, extra));
202             }
203
204             return;
205         }
206
207         if (token.type === tt.backQuote) {
208
209             // if there's already a curly, it's not part of the template
210             if (this._curlyBrace) {
211                 tokens.push(this.translate(this._curlyBrace, extra));
212                 this._curlyBrace = null;
213             }
214
215             templateTokens.push(token);
216
217             // it's the end
218             if (templateTokens.length > 1) {
219                 translateTemplateTokens();
220             }
221
222             return;
223         }
224         if (token.type === tt.dollarBraceL) {
225             templateTokens.push(token);
226             translateTemplateTokens();
227             return;
228         }
229         if (token.type === tt.braceR) {
230
231             // if there's already a curly, it's not part of the template
232             if (this._curlyBrace) {
233                 tokens.push(this.translate(this._curlyBrace, extra));
234             }
235
236             // store new curly for later
237             this._curlyBrace = token;
238             return;
239         }
240         if (token.type === tt.template || token.type === tt.invalidTemplate) {
241             if (this._curlyBrace) {
242                 templateTokens.push(this._curlyBrace);
243                 this._curlyBrace = null;
244             }
245
246             templateTokens.push(token);
247             return;
248         }
249
250         if (this._curlyBrace) {
251             tokens.push(this.translate(this._curlyBrace, extra));
252             this._curlyBrace = null;
253         }
254
255         tokens.push(this.translate(token, extra));
256     }
257 };
258
259 //------------------------------------------------------------------------------
260 // Public
261 //------------------------------------------------------------------------------
262
263 module.exports = TokenTranslator;