@makano/rew 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (58) hide show
  1. package/bin/rew +9 -0
  2. package/bin/ui +0 -0
  3. package/bin/webkit_app +0 -0
  4. package/lib/coffeescript/browser.js +151 -0
  5. package/lib/coffeescript/cake.js +135 -0
  6. package/lib/coffeescript/coffeescript.js +409 -0
  7. package/lib/coffeescript/command.js +750 -0
  8. package/lib/coffeescript/grammar.js +2496 -0
  9. package/lib/coffeescript/helpers.js +477 -0
  10. package/lib/coffeescript/index.js +217 -0
  11. package/lib/coffeescript/lexer.js +1943 -0
  12. package/lib/coffeescript/nodes.js +9204 -0
  13. package/lib/coffeescript/optparse.js +230 -0
  14. package/lib/coffeescript/parser.js +1344 -0
  15. package/lib/coffeescript/register.js +100 -0
  16. package/lib/coffeescript/repl.js +305 -0
  17. package/lib/coffeescript/rewriter.js +1138 -0
  18. package/lib/coffeescript/scope.js +187 -0
  19. package/lib/coffeescript/sourcemap.js +229 -0
  20. package/lib/rew/cli/cli.js +117 -0
  21. package/lib/rew/cli/log.js +40 -0
  22. package/lib/rew/cli/run.js +20 -0
  23. package/lib/rew/cli/utils.js +122 -0
  24. package/lib/rew/const/default.js +35 -0
  25. package/lib/rew/const/files.js +15 -0
  26. package/lib/rew/css/theme.css +3 -0
  27. package/lib/rew/functions/core.js +85 -0
  28. package/lib/rew/functions/emitter.js +57 -0
  29. package/lib/rew/functions/export.js +9 -0
  30. package/lib/rew/functions/future.js +22 -0
  31. package/lib/rew/functions/id.js +13 -0
  32. package/lib/rew/functions/import.js +57 -0
  33. package/lib/rew/functions/map.js +17 -0
  34. package/lib/rew/functions/match.js +34 -0
  35. package/lib/rew/functions/sleep.js +5 -0
  36. package/lib/rew/functions/types.js +96 -0
  37. package/lib/rew/html/ui.html +223 -0
  38. package/lib/rew/main.js +17 -0
  39. package/lib/rew/models/enum.js +14 -0
  40. package/lib/rew/models/struct.js +41 -0
  41. package/lib/rew/modules/compiler.js +17 -0
  42. package/lib/rew/modules/context.js +50 -0
  43. package/lib/rew/modules/fs.js +19 -0
  44. package/lib/rew/modules/runtime.js +24 -0
  45. package/lib/rew/modules/utils.js +0 -0
  46. package/lib/rew/modules/yaml.js +36 -0
  47. package/lib/rew/pkgs/conf.js +92 -0
  48. package/lib/rew/pkgs/data.js +8 -0
  49. package/lib/rew/pkgs/date.js +98 -0
  50. package/lib/rew/pkgs/modules/data/bintree.js +66 -0
  51. package/lib/rew/pkgs/modules/data/doublylinked.js +100 -0
  52. package/lib/rew/pkgs/modules/data/linkedList.js +88 -0
  53. package/lib/rew/pkgs/modules/data/queue.js +28 -0
  54. package/lib/rew/pkgs/modules/data/stack.js +27 -0
  55. package/lib/rew/pkgs/modules/ui/classes.js +171 -0
  56. package/lib/rew/pkgs/pkgs.js +13 -0
  57. package/lib/rew/pkgs/ui.js +108 -0
  58. package/package.json +41 -0
@@ -0,0 +1,1943 @@
1
+ // Generated by CoffeeScript 2.7.0
2
+ (function() {
3
+ // The CoffeeScript Lexer. Uses a series of token-matching regexes to attempt
4
+ // matches against the beginning of the source code. When a match is found,
5
+ // a token is produced, we consume the match, and start again. Tokens are in the
6
+ // form:
7
+
8
+ // [tag, value, locationData]
9
+
10
+ // where locationData is {first_line, first_column, last_line, last_column, last_line_exclusive, last_column_exclusive}, which is a
11
+ // format that can be fed directly into [Jison](https://github.com/zaach/jison). These
12
+ // are read by jison in the `parser.lexer` function defined in coffeescript.coffee.
13
+ var BOM, BOOL, CALLABLE, CODE, COFFEE_ALIASES, COFFEE_ALIAS_MAP, COFFEE_KEYWORDS, COMMENT, COMPARABLE_LEFT_SIDE, COMPARE, COMPOUND_ASSIGN, HERECOMMENT_ILLEGAL, HEREDOC_DOUBLE, HEREDOC_INDENT, HEREDOC_SINGLE, HEREGEX, HEREGEX_COMMENT, HERE_JSTOKEN, IDENTIFIER, INDENTABLE_CLOSERS, INDEXABLE, INSIDE_JSX, INVERSES, JSTOKEN, JSX_ATTRIBUTE, JSX_FRAGMENT_IDENTIFIER, JSX_IDENTIFIER, JSX_IDENTIFIER_PART, JSX_INTERPOLATION, JS_KEYWORDS, LINE_BREAK, LINE_CONTINUER, Lexer, MATH, MULTI_DENT, NOT_REGEX, NUMBER, OPERATOR, POSSIBLY_DIVISION, REGEX, REGEX_FLAGS, REGEX_ILLEGAL, REGEX_INVALID_ESCAPE, RELATION, RESERVED, Rewriter, SHIFT, STRICT_PROSCRIBED, STRING_DOUBLE, STRING_INVALID_ESCAPE, STRING_SINGLE, STRING_START, TRAILING_SPACES, UNARY, UNARY_MATH, UNFINISHED, VALID_FLAGS, WHITESPACE, addTokenData, attachCommentsToNode, compact, count, flatten, invertLiterate, isForFrom, isUnassignable, key, locationDataToString, merge, parseNumber, repeat, replaceUnicodeCodePointEscapes, starts, throwSyntaxError,
14
+ indexOf = [].indexOf,
15
+ slice = [].slice;
16
+
17
+ ({Rewriter, INVERSES, UNFINISHED} = require('./rewriter'));
18
+
19
+ // Import the helpers we need.
20
+ ({count, starts, compact, repeat, invertLiterate, merge, attachCommentsToNode, locationDataToString, throwSyntaxError, replaceUnicodeCodePointEscapes, flatten, parseNumber} = require('./helpers'));
21
+
22
+ // The Lexer Class
23
+ // ---------------
24
+
25
+ // The Lexer class reads a stream of CoffeeScript and divvies it up into tagged
26
+ // tokens. Some potential ambiguity in the grammar has been avoided by
27
+ // pushing some extra smarts into the Lexer.
28
+ exports.Lexer = Lexer = class Lexer {
29
+ constructor() {
30
+ // Throws an error at either a given offset from the current chunk or at the
31
+ // location of a token (`token[2]`).
32
+ this.error = this.error.bind(this);
33
+ }
34
+
35
+ // **tokenize** is the Lexer's main method. Scan by attempting to match tokens
36
+ // one at a time, using a regular expression anchored at the start of the
37
+ // remaining code, or a custom recursive token-matching method
38
+ // (for interpolations). When the next token has been recorded, we move forward
39
+ // within the code past the token, and begin again.
40
+
41
+ // Each tokenizing method is responsible for returning the number of characters
42
+ // it has consumed.
43
+
44
+ // Before returning the token stream, run it through the [Rewriter](rewriter.html).
45
+ tokenize(code, opts = {}) {
46
+ var consumed, end, i, ref;
47
+ this.literate = opts.literate; // Are we lexing literate CoffeeScript?
48
+ this.indent = 0; // The current indentation level.
49
+ this.baseIndent = 0; // The overall minimum indentation level.
50
+ this.continuationLineAdditionalIndent = 0; // The over-indentation at the current level.
51
+ this.outdebt = 0; // The under-outdentation at the current level.
52
+ this.indents = []; // The stack of all current indentation levels.
53
+ this.indentLiteral = ''; // The indentation.
54
+ this.ends = []; // The stack for pairing up tokens.
55
+ this.tokens = []; // Stream of parsed tokens in the form `['TYPE', value, location data]`.
56
+ this.seenFor = false; // Used to recognize `FORIN`, `FOROF` and `FORFROM` tokens.
57
+ this.seenImport = false; // Used to recognize `IMPORT FROM? AS?` tokens.
58
+ this.seenExport = false; // Used to recognize `EXPORT FROM? AS?` tokens.
59
+ this.importSpecifierList = false; // Used to identify when in an `IMPORT {...} FROM? ...`.
60
+ this.exportSpecifierList = false; // Used to identify when in an `EXPORT {...} FROM? ...`.
61
+ this.jsxDepth = 0; // Used to optimize JSX checks, how deep in JSX we are.
62
+ this.jsxObjAttribute = {}; // Used to detect if JSX attributes is wrapped in {} (<div {props...} />).
63
+ this.chunkLine = opts.line || 0; // The start line for the current @chunk.
64
+ this.chunkColumn = opts.column || 0; // The start column of the current @chunk.
65
+ this.chunkOffset = opts.offset || 0; // The start offset for the current @chunk.
66
+ this.locationDataCompensations = opts.locationDataCompensations || {};
67
+ code = this.clean(code); // The stripped, cleaned original source code.
68
+
69
+ // At every position, run through this list of attempted matches,
70
+ // short-circuiting if any of them succeed. Their order determines precedence:
71
+ // `@literalToken` is the fallback catch-all.
72
+ i = 0;
73
+ while (this.chunk = code.slice(i)) {
74
+ consumed = this.identifierToken() || this.commentToken() || this.whitespaceToken() || this.lineToken() || this.stringToken() || this.numberToken() || this.jsxToken() || this.regexToken() || this.jsToken() || this.literalToken();
75
+ // Update position.
76
+ [this.chunkLine, this.chunkColumn, this.chunkOffset] = this.getLineAndColumnFromChunk(consumed);
77
+ i += consumed;
78
+ if (opts.untilBalanced && this.ends.length === 0) {
79
+ return {
80
+ tokens: this.tokens,
81
+ index: i
82
+ };
83
+ }
84
+ }
85
+ this.closeIndentation();
86
+ if (end = this.ends.pop()) {
87
+ this.error(`missing ${end.tag}`, ((ref = end.origin) != null ? ref : end)[2]);
88
+ }
89
+ if (opts.rewrite === false) {
90
+ return this.tokens;
91
+ }
92
+ return (new Rewriter()).rewrite(this.tokens);
93
+ }
94
+
95
+ // Preprocess the code to remove leading and trailing whitespace, carriage
96
+ // returns, etc. If we’re lexing literate CoffeeScript, strip external Markdown
97
+ // by removing all lines that aren’t indented by at least four spaces or a tab.
98
+ clean(code) {
99
+ var base, thusFar;
100
+ thusFar = 0;
101
+ if (code.charCodeAt(0) === BOM) {
102
+ code = code.slice(1);
103
+ this.locationDataCompensations[0] = 1;
104
+ thusFar += 1;
105
+ }
106
+ if (WHITESPACE.test(code)) {
107
+ code = `\n${code}`;
108
+ this.chunkLine--;
109
+ if ((base = this.locationDataCompensations)[0] == null) {
110
+ base[0] = 0;
111
+ }
112
+ this.locationDataCompensations[0] -= 1;
113
+ }
114
+ code = code.replace(/\r/g, (match, offset) => {
115
+ this.locationDataCompensations[thusFar + offset] = 1;
116
+ return '';
117
+ }).replace(TRAILING_SPACES, '');
118
+ if (this.literate) {
119
+ code = invertLiterate(code);
120
+ }
121
+ return code;
122
+ }
123
+
124
+ // Tokenizers
125
+ // ----------
126
+
127
+ // Matches identifying literals: variables, keywords, method names, etc.
128
+ // Check to ensure that JavaScript reserved words aren’t being used as
129
+ // identifiers. Because CoffeeScript reserves a handful of keywords that are
130
+ // allowed in JavaScript, we’re careful not to tag them as keywords when
131
+ // referenced as property names here, so you can still do `jQuery.is()` even
132
+ // though `is` means `===` otherwise.
133
+ identifierToken() {
134
+ var alias, colon, colonOffset, colonToken, id, idLength, inJSXTag, input, match, poppedToken, prev, prevprev, ref, ref1, ref10, ref11, ref12, ref2, ref3, ref4, ref5, ref6, ref7, ref8, ref9, regExSuper, regex, sup, tag, tagToken, tokenData;
135
+ inJSXTag = this.atJSXTag();
136
+ regex = inJSXTag ? JSX_ATTRIBUTE : IDENTIFIER;
137
+ if (!(match = regex.exec(this.chunk))) {
138
+ return 0;
139
+ }
140
+ [input, id, colon] = match;
141
+ // Preserve length of id for location data
142
+ idLength = id.length;
143
+ poppedToken = void 0;
144
+ if (id === 'own' && this.tag() === 'FOR') {
145
+ this.token('OWN', id);
146
+ return id.length;
147
+ }
148
+ if (id === 'from' && this.tag() === 'YIELD') {
149
+ this.token('FROM', id);
150
+ return id.length;
151
+ }
152
+ if (id === 'as' && this.seenImport) {
153
+ if (this.value() === '*') {
154
+ this.tokens[this.tokens.length - 1][0] = 'IMPORT_ALL';
155
+ } else if (ref = this.value(true), indexOf.call(COFFEE_KEYWORDS, ref) >= 0) {
156
+ prev = this.prev();
157
+ [prev[0], prev[1]] = ['IDENTIFIER', this.value(true)];
158
+ }
159
+ if ((ref1 = this.tag()) === 'DEFAULT' || ref1 === 'IMPORT_ALL' || ref1 === 'IDENTIFIER') {
160
+ this.token('AS', id);
161
+ return id.length;
162
+ }
163
+ }
164
+ if (id === 'as' && this.seenExport) {
165
+ if ((ref2 = this.tag()) === 'IDENTIFIER' || ref2 === 'DEFAULT') {
166
+ this.token('AS', id);
167
+ return id.length;
168
+ }
169
+ if (ref3 = this.value(true), indexOf.call(COFFEE_KEYWORDS, ref3) >= 0) {
170
+ prev = this.prev();
171
+ [prev[0], prev[1]] = ['IDENTIFIER', this.value(true)];
172
+ this.token('AS', id);
173
+ return id.length;
174
+ }
175
+ }
176
+ if (id === 'default' && this.seenExport && ((ref4 = this.tag()) === 'EXPORT' || ref4 === 'AS')) {
177
+ this.token('DEFAULT', id);
178
+ return id.length;
179
+ }
180
+ if (id === 'assert' && (this.seenImport || this.seenExport) && this.tag() === 'STRING') {
181
+ this.token('ASSERT', id);
182
+ return id.length;
183
+ }
184
+ if (id === 'do' && (regExSuper = /^(\s*super)(?!\(\))/.exec(this.chunk.slice(3)))) {
185
+ this.token('SUPER', 'super');
186
+ this.token('CALL_START', '(');
187
+ this.token('CALL_END', ')');
188
+ [input, sup] = regExSuper;
189
+ return sup.length + 3;
190
+ }
191
+ prev = this.prev();
192
+ tag = colon || (prev != null) && (((ref5 = prev[0]) === '.' || ref5 === '?.' || ref5 === '::' || ref5 === '?::') || !prev.spaced && prev[0] === '@') ? 'PROPERTY' : 'IDENTIFIER';
193
+ tokenData = {};
194
+ if (tag === 'IDENTIFIER' && (indexOf.call(JS_KEYWORDS, id) >= 0 || indexOf.call(COFFEE_KEYWORDS, id) >= 0) && !(this.exportSpecifierList && indexOf.call(COFFEE_KEYWORDS, id) >= 0)) {
195
+ tag = id.toUpperCase();
196
+ if (tag === 'WHEN' && (ref6 = this.tag(), indexOf.call(LINE_BREAK, ref6) >= 0)) {
197
+ tag = 'LEADING_WHEN';
198
+ } else if (tag === 'FOR') {
199
+ this.seenFor = {
200
+ endsLength: this.ends.length
201
+ };
202
+ } else if (tag === 'UNLESS') {
203
+ tag = 'IF';
204
+ } else if (tag === 'IMPORT') {
205
+ this.seenImport = true;
206
+ } else if (tag === 'EXPORT') {
207
+ this.seenExport = true;
208
+ } else if (indexOf.call(UNARY, tag) >= 0) {
209
+ tag = 'UNARY';
210
+ } else if (indexOf.call(RELATION, tag) >= 0) {
211
+ if (tag !== 'INSTANCEOF' && this.seenFor) {
212
+ tag = 'FOR' + tag;
213
+ this.seenFor = false;
214
+ } else {
215
+ tag = 'RELATION';
216
+ if (this.value() === '!') {
217
+ poppedToken = this.tokens.pop();
218
+ tokenData.invert = (ref7 = (ref8 = poppedToken.data) != null ? ref8.original : void 0) != null ? ref7 : poppedToken[1];
219
+ }
220
+ }
221
+ }
222
+ } else if (tag === 'IDENTIFIER' && this.seenFor && id === 'from' && isForFrom(prev)) {
223
+ tag = 'FORFROM';
224
+ this.seenFor = false;
225
+ // Throw an error on attempts to use `get` or `set` as keywords, or
226
+ // what CoffeeScript would normally interpret as calls to functions named
227
+ // `get` or `set`, i.e. `get({foo: function () {}})`.
228
+ } else if (tag === 'PROPERTY' && prev) {
229
+ if (prev.spaced && (ref9 = prev[0], indexOf.call(CALLABLE, ref9) >= 0) && /^[gs]et$/.test(prev[1]) && this.tokens.length > 1 && ((ref10 = this.tokens[this.tokens.length - 2][0]) !== '.' && ref10 !== '?.' && ref10 !== '@')) {
230
+ this.error(`'${prev[1]}' cannot be used as a keyword, or as a function call without parentheses`, prev[2]);
231
+ } else if (prev[0] === '.' && this.tokens.length > 1 && (prevprev = this.tokens[this.tokens.length - 2])[0] === 'UNARY' && prevprev[1] === 'new') {
232
+ prevprev[0] = 'NEW_TARGET';
233
+ } else if (prev[0] === '.' && this.tokens.length > 1 && (prevprev = this.tokens[this.tokens.length - 2])[0] === 'IMPORT' && prevprev[1] === 'import') {
234
+ this.seenImport = false;
235
+ prevprev[0] = 'IMPORT_META';
236
+ } else if (this.tokens.length > 2) {
237
+ prevprev = this.tokens[this.tokens.length - 2];
238
+ if (((ref11 = prev[0]) === '@' || ref11 === 'THIS') && prevprev && prevprev.spaced && /^[gs]et$/.test(prevprev[1]) && ((ref12 = this.tokens[this.tokens.length - 3][0]) !== '.' && ref12 !== '?.' && ref12 !== '@')) {
239
+ this.error(`'${prevprev[1]}' cannot be used as a keyword, or as a function call without parentheses`, prevprev[2]);
240
+ }
241
+ }
242
+ }
243
+ if (tag === 'IDENTIFIER' && indexOf.call(RESERVED, id) >= 0 && !inJSXTag) {
244
+ this.error(`reserved word '${id}'`, {
245
+ length: id.length
246
+ });
247
+ }
248
+ if (!(tag === 'PROPERTY' || this.exportSpecifierList || this.importSpecifierList)) {
249
+ if (indexOf.call(COFFEE_ALIASES, id) >= 0) {
250
+ alias = id;
251
+ id = COFFEE_ALIAS_MAP[id];
252
+ tokenData.original = alias;
253
+ }
254
+ tag = (function() {
255
+ switch (id) {
256
+ case '!':
257
+ return 'UNARY';
258
+ case '==':
259
+ case '!=':
260
+ return 'COMPARE';
261
+ case 'true':
262
+ case 'false':
263
+ return 'BOOL';
264
+ case 'break':
265
+ case 'continue':
266
+ case 'debugger':
267
+ return 'STATEMENT';
268
+ case '&&':
269
+ case '||':
270
+ return id;
271
+ default:
272
+ return tag;
273
+ }
274
+ })();
275
+ }
276
+ tagToken = this.token(tag, id, {
277
+ length: idLength,
278
+ data: tokenData
279
+ });
280
+ if (alias) {
281
+ tagToken.origin = [tag, alias, tagToken[2]];
282
+ }
283
+ if (poppedToken) {
284
+ [tagToken[2].first_line, tagToken[2].first_column, tagToken[2].range[0]] = [poppedToken[2].first_line, poppedToken[2].first_column, poppedToken[2].range[0]];
285
+ }
286
+ if (colon) {
287
+ colonOffset = input.lastIndexOf(inJSXTag ? '=' : ':');
288
+ colonToken = this.token(':', ':', {
289
+ offset: colonOffset
290
+ });
291
+ if (inJSXTag) { // used by rewriter
292
+ colonToken.jsxColon = true;
293
+ }
294
+ }
295
+ if (inJSXTag && tag === 'IDENTIFIER' && prev[0] !== ':') {
296
+ this.token(',', ',', {
297
+ length: 0,
298
+ origin: tagToken,
299
+ generated: true
300
+ });
301
+ }
302
+ return input.length;
303
+ }
304
+
305
+ // Matches numbers, including decimals, hex, and exponential notation.
306
+ // Be careful not to interfere with ranges in progress.
307
+ numberToken() {
308
+ var lexedLength, match, number, parsedValue, tag, tokenData;
309
+ if (!(match = NUMBER.exec(this.chunk))) {
310
+ return 0;
311
+ }
312
+ number = match[0];
313
+ lexedLength = number.length;
314
+ switch (false) {
315
+ case !/^0[BOX]/.test(number):
316
+ this.error(`radix prefix in '${number}' must be lowercase`, {
317
+ offset: 1
318
+ });
319
+ break;
320
+ case !/^0\d*[89]/.test(number):
321
+ this.error(`decimal literal '${number}' must not be prefixed with '0'`, {
322
+ length: lexedLength
323
+ });
324
+ break;
325
+ case !/^0\d+/.test(number):
326
+ this.error(`octal literal '${number}' must be prefixed with '0o'`, {
327
+ length: lexedLength
328
+ });
329
+ }
330
+ parsedValue = parseNumber(number);
331
+ tokenData = {parsedValue};
332
+ tag = parsedValue === 2e308 ? 'INFINITY' : 'NUMBER';
333
+ if (tag === 'INFINITY') {
334
+ tokenData.original = number;
335
+ }
336
+ this.token(tag, number, {
337
+ length: lexedLength,
338
+ data: tokenData
339
+ });
340
+ return lexedLength;
341
+ }
342
+
343
+ // Matches strings, including multiline strings, as well as heredocs, with or without
344
+ // interpolation.
345
+ stringToken() {
346
+ var attempt, delimiter, doc, end, heredoc, i, indent, match, prev, quote, ref, regex, token, tokens;
347
+ [quote] = STRING_START.exec(this.chunk) || [];
348
+ if (!quote) {
349
+ return 0;
350
+ }
351
+ // If the preceding token is `from` and this is an import or export statement,
352
+ // properly tag the `from`.
353
+ prev = this.prev();
354
+ if (prev && this.value() === 'from' && (this.seenImport || this.seenExport)) {
355
+ prev[0] = 'FROM';
356
+ }
357
+ regex = (function() {
358
+ switch (quote) {
359
+ case "'":
360
+ return STRING_SINGLE;
361
+ case '"':
362
+ return STRING_DOUBLE;
363
+ case "'''":
364
+ return HEREDOC_SINGLE;
365
+ case '"""':
366
+ return HEREDOC_DOUBLE;
367
+ }
368
+ })();
369
+ ({
370
+ tokens,
371
+ index: end
372
+ } = this.matchWithInterpolations(regex, quote));
373
+ heredoc = quote.length === 3;
374
+ if (heredoc) {
375
+ // Find the smallest indentation. It will be removed from all lines later.
376
+ indent = null;
377
+ doc = ((function() {
378
+ var j, len, results;
379
+ results = [];
380
+ for (i = j = 0, len = tokens.length; j < len; i = ++j) {
381
+ token = tokens[i];
382
+ if (token[0] === 'NEOSTRING') {
383
+ results.push(token[1]);
384
+ }
385
+ }
386
+ return results;
387
+ })()).join('#{}');
388
+ while (match = HEREDOC_INDENT.exec(doc)) {
389
+ attempt = match[1];
390
+ if (indent === null || (0 < (ref = attempt.length) && ref < indent.length)) {
391
+ indent = attempt;
392
+ }
393
+ }
394
+ }
395
+ delimiter = quote.charAt(0);
396
+ this.mergeInterpolationTokens(tokens, {
397
+ quote,
398
+ indent,
399
+ endOffset: end
400
+ }, (value) => {
401
+ return this.validateUnicodeCodePointEscapes(value, {
402
+ delimiter: quote
403
+ });
404
+ });
405
+ if (this.atJSXTag()) {
406
+ this.token(',', ',', {
407
+ length: 0,
408
+ origin: this.prev,
409
+ generated: true
410
+ });
411
+ }
412
+ return end;
413
+ }
414
+
415
+ // Matches and consumes comments. The comments are taken out of the token
416
+ // stream and saved for later, to be reinserted into the output after
417
+ // everything has been parsed and the JavaScript code generated.
418
+ commentToken(chunk = this.chunk, {heregex, returnCommentTokens = false, offsetInChunk = 0} = {}) {
419
+ var commentAttachment, commentAttachments, commentWithSurroundingWhitespace, content, contents, getIndentSize, hasSeenFirstCommentLine, hereComment, hereLeadingWhitespace, hereTrailingWhitespace, i, indentSize, leadingNewline, leadingNewlineOffset, leadingNewlines, leadingWhitespace, length, lineComment, match, matchIllegal, noIndent, nonInitial, placeholderToken, precededByBlankLine, precedingNonCommentLines, prev;
420
+ if (!(match = chunk.match(COMMENT))) {
421
+ return 0;
422
+ }
423
+ [commentWithSurroundingWhitespace, hereLeadingWhitespace, hereComment, hereTrailingWhitespace, lineComment] = match;
424
+ contents = null;
425
+ // Does this comment follow code on the same line?
426
+ leadingNewline = /^\s*\n+\s*#/.test(commentWithSurroundingWhitespace);
427
+ if (hereComment) {
428
+ matchIllegal = HERECOMMENT_ILLEGAL.exec(hereComment);
429
+ if (matchIllegal) {
430
+ this.error(`block comments cannot contain ${matchIllegal[0]}`, {
431
+ offset: '###'.length + matchIllegal.index,
432
+ length: matchIllegal[0].length
433
+ });
434
+ }
435
+ // Parse indentation or outdentation as if this block comment didn’t exist.
436
+ chunk = chunk.replace(`###${hereComment}###`, '');
437
+ // Remove leading newlines, like `Rewriter::removeLeadingNewlines`, to
438
+ // avoid the creation of unwanted `TERMINATOR` tokens.
439
+ chunk = chunk.replace(/^\n+/, '');
440
+ this.lineToken({chunk});
441
+ // Pull out the ###-style comment’s content, and format it.
442
+ content = hereComment;
443
+ contents = [
444
+ {
445
+ content,
446
+ length: commentWithSurroundingWhitespace.length - hereLeadingWhitespace.length - hereTrailingWhitespace.length,
447
+ leadingWhitespace: hereLeadingWhitespace
448
+ }
449
+ ];
450
+ } else {
451
+ // The `COMMENT` regex captures successive line comments as one token.
452
+ // Remove any leading newlines before the first comment, but preserve
453
+ // blank lines between line comments.
454
+ leadingNewlines = '';
455
+ content = lineComment.replace(/^(\n*)/, function(leading) {
456
+ leadingNewlines = leading;
457
+ return '';
458
+ });
459
+ precedingNonCommentLines = '';
460
+ hasSeenFirstCommentLine = false;
461
+ contents = content.split('\n').map(function(line, index) {
462
+ var comment, leadingWhitespace;
463
+ if (!(line.indexOf('#') > -1)) {
464
+ precedingNonCommentLines += `\n${line}`;
465
+ return;
466
+ }
467
+ leadingWhitespace = '';
468
+ content = line.replace(/^([ |\t]*)#/, function(_, whitespace) {
469
+ leadingWhitespace = whitespace;
470
+ return '';
471
+ });
472
+ comment = {
473
+ content,
474
+ length: '#'.length + content.length,
475
+ leadingWhitespace: `${!hasSeenFirstCommentLine ? leadingNewlines : ''}${precedingNonCommentLines}${leadingWhitespace}`,
476
+ precededByBlankLine: !!precedingNonCommentLines
477
+ };
478
+ hasSeenFirstCommentLine = true;
479
+ precedingNonCommentLines = '';
480
+ return comment;
481
+ }).filter(function(comment) {
482
+ return comment;
483
+ });
484
+ }
485
+ getIndentSize = function({leadingWhitespace, nonInitial}) {
486
+ var lastNewlineIndex;
487
+ lastNewlineIndex = leadingWhitespace.lastIndexOf('\n');
488
+ if ((hereComment != null) || !nonInitial) {
489
+ if (!(lastNewlineIndex > -1)) {
490
+ return null;
491
+ }
492
+ } else {
493
+ if (lastNewlineIndex == null) {
494
+ lastNewlineIndex = -1;
495
+ }
496
+ }
497
+ return leadingWhitespace.length - 1 - lastNewlineIndex;
498
+ };
499
+ commentAttachments = (function() {
500
+ var j, len, results;
501
+ results = [];
502
+ for (i = j = 0, len = contents.length; j < len; i = ++j) {
503
+ ({content, length, leadingWhitespace, precededByBlankLine} = contents[i]);
504
+ nonInitial = i !== 0;
505
+ leadingNewlineOffset = nonInitial ? 1 : 0;
506
+ offsetInChunk += leadingNewlineOffset + leadingWhitespace.length;
507
+ indentSize = getIndentSize({leadingWhitespace, nonInitial});
508
+ noIndent = (indentSize == null) || indentSize === -1;
509
+ commentAttachment = {
510
+ content,
511
+ here: hereComment != null,
512
+ newLine: leadingNewline || nonInitial, // Line comments after the first one start new lines, by definition.
513
+ locationData: this.makeLocationData({offsetInChunk, length}),
514
+ precededByBlankLine,
515
+ indentSize,
516
+ indented: !noIndent && indentSize > this.indent,
517
+ outdented: !noIndent && indentSize < this.indent
518
+ };
519
+ if (heregex) {
520
+ commentAttachment.heregex = true;
521
+ }
522
+ offsetInChunk += length;
523
+ results.push(commentAttachment);
524
+ }
525
+ return results;
526
+ }).call(this);
527
+ prev = this.prev();
528
+ if (!prev) {
529
+ // If there’s no previous token, create a placeholder token to attach
530
+ // this comment to; and follow with a newline.
531
+ commentAttachments[0].newLine = true;
532
+ this.lineToken({
533
+ chunk: this.chunk.slice(commentWithSurroundingWhitespace.length),
534
+ offset: commentWithSurroundingWhitespace.length // Set the indent.
535
+ });
536
+ placeholderToken = this.makeToken('JS', '', {
537
+ offset: commentWithSurroundingWhitespace.length,
538
+ generated: true
539
+ });
540
+ placeholderToken.comments = commentAttachments;
541
+ this.tokens.push(placeholderToken);
542
+ this.newlineToken(commentWithSurroundingWhitespace.length);
543
+ } else {
544
+ attachCommentsToNode(commentAttachments, prev);
545
+ }
546
+ if (returnCommentTokens) {
547
+ return commentAttachments;
548
+ }
549
+ return commentWithSurroundingWhitespace.length;
550
+ }
551
+
552
+ // Matches JavaScript interpolated directly into the source via backticks.
553
+ jsToken() {
554
+ var length, match, matchedHere, script;
555
+ if (!(this.chunk.charAt(0) === '`' && (match = (matchedHere = HERE_JSTOKEN.exec(this.chunk)) || JSTOKEN.exec(this.chunk)))) {
556
+ return 0;
557
+ }
558
+ // Convert escaped backticks to backticks, and escaped backslashes
559
+ // just before escaped backticks to backslashes
560
+ script = match[1];
561
+ ({length} = match[0]);
562
+ this.token('JS', script, {
563
+ length,
564
+ data: {
565
+ here: !!matchedHere
566
+ }
567
+ });
568
+ return length;
569
+ }
570
+
571
+ // Matches regular expression literals, as well as multiline extended ones.
572
+ // Lexing regular expressions is difficult to distinguish from division, so we
573
+ // borrow some basic heuristics from JavaScript and Ruby.
574
+ regexToken() {
575
+ var body, closed, comment, commentIndex, commentOpts, commentTokens, comments, delimiter, end, flags, fullMatch, index, leadingWhitespace, match, matchedComment, origin, prev, ref, ref1, regex, tokens;
576
+ switch (false) {
577
+ case !(match = REGEX_ILLEGAL.exec(this.chunk)):
578
+ this.error(`regular expressions cannot begin with ${match[2]}`, {
579
+ offset: match.index + match[1].length
580
+ });
581
+ break;
582
+ case !(match = this.matchWithInterpolations(HEREGEX, '///')):
583
+ ({tokens, index} = match);
584
+ comments = [];
585
+ while (matchedComment = HEREGEX_COMMENT.exec(this.chunk.slice(0, index))) {
586
+ ({
587
+ index: commentIndex
588
+ } = matchedComment);
589
+ [fullMatch, leadingWhitespace, comment] = matchedComment;
590
+ comments.push({
591
+ comment,
592
+ offsetInChunk: commentIndex + leadingWhitespace.length
593
+ });
594
+ }
595
+ commentTokens = flatten((function() {
596
+ var j, len, results;
597
+ results = [];
598
+ for (j = 0, len = comments.length; j < len; j++) {
599
+ commentOpts = comments[j];
600
+ results.push(this.commentToken(commentOpts.comment, Object.assign(commentOpts, {
601
+ heregex: true,
602
+ returnCommentTokens: true
603
+ })));
604
+ }
605
+ return results;
606
+ }).call(this));
607
+ break;
608
+ case !(match = REGEX.exec(this.chunk)):
609
+ [regex, body, closed] = match;
610
+ this.validateEscapes(body, {
611
+ isRegex: true,
612
+ offsetInChunk: 1
613
+ });
614
+ index = regex.length;
615
+ prev = this.prev();
616
+ if (prev) {
617
+ if (prev.spaced && (ref = prev[0], indexOf.call(CALLABLE, ref) >= 0)) {
618
+ if (!closed || POSSIBLY_DIVISION.test(regex)) {
619
+ return 0;
620
+ }
621
+ } else if (ref1 = prev[0], indexOf.call(NOT_REGEX, ref1) >= 0) {
622
+ return 0;
623
+ }
624
+ }
625
+ if (!closed) {
626
+ this.error('missing / (unclosed regex)');
627
+ }
628
+ break;
629
+ default:
630
+ return 0;
631
+ }
632
+ [flags] = REGEX_FLAGS.exec(this.chunk.slice(index));
633
+ end = index + flags.length;
634
+ origin = this.makeToken('REGEX', null, {
635
+ length: end
636
+ });
637
+ switch (false) {
638
+ case !!VALID_FLAGS.test(flags):
639
+ this.error(`invalid regular expression flags ${flags}`, {
640
+ offset: index,
641
+ length: flags.length
642
+ });
643
+ break;
644
+ case !(regex || tokens.length === 1):
645
+ delimiter = body ? '/' : '///';
646
+ if (body == null) {
647
+ body = tokens[0][1];
648
+ }
649
+ this.validateUnicodeCodePointEscapes(body, {delimiter});
650
+ this.token('REGEX', `/${body}/${flags}`, {
651
+ length: end,
652
+ origin,
653
+ data: {delimiter}
654
+ });
655
+ break;
656
+ default:
657
+ this.token('REGEX_START', '(', {
658
+ length: 0,
659
+ origin,
660
+ generated: true
661
+ });
662
+ this.token('IDENTIFIER', 'RegExp', {
663
+ length: 0,
664
+ generated: true
665
+ });
666
+ this.token('CALL_START', '(', {
667
+ length: 0,
668
+ generated: true
669
+ });
670
+ this.mergeInterpolationTokens(tokens, {
671
+ double: true,
672
+ heregex: {flags},
673
+ endOffset: end - flags.length,
674
+ quote: '///'
675
+ }, (str) => {
676
+ return this.validateUnicodeCodePointEscapes(str, {delimiter});
677
+ });
678
+ if (flags) {
679
+ this.token(',', ',', {
680
+ offset: index - 1,
681
+ length: 0,
682
+ generated: true
683
+ });
684
+ this.token('STRING', '"' + flags + '"', {
685
+ offset: index,
686
+ length: flags.length
687
+ });
688
+ }
689
+ this.token(')', ')', {
690
+ offset: end,
691
+ length: 0,
692
+ generated: true
693
+ });
694
+ this.token('REGEX_END', ')', {
695
+ offset: end,
696
+ length: 0,
697
+ generated: true
698
+ });
699
+ }
700
+ // Explicitly attach any heregex comments to the REGEX/REGEX_END token.
701
+ if (commentTokens != null ? commentTokens.length : void 0) {
702
+ addTokenData(this.tokens[this.tokens.length - 1], {
703
+ heregexCommentTokens: commentTokens
704
+ });
705
+ }
706
+ return end;
707
+ }
708
+
709
+ // Matches newlines, indents, and outdents, and determines which is which.
710
+ // If we can detect that the current line is continued onto the next line,
711
+ // then the newline is suppressed:
712
+
713
+ // elements
714
+ // .each( ... )
715
+ // .map( ... )
716
+
717
+ // Keeps track of the level of indentation, because a single outdent token
718
+ // can close multiple indents, so we need to know how far in we happen to be.
719
+ lineToken({chunk = this.chunk, offset = 0} = {}) {
720
+ var backslash, diff, endsContinuationLineIndentation, indent, match, minLiteralLength, newIndentLiteral, noNewlines, prev, ref, size;
721
+ if (!(match = MULTI_DENT.exec(chunk))) {
722
+ return 0;
723
+ }
724
+ indent = match[0];
725
+ prev = this.prev();
726
+ backslash = (prev != null ? prev[0] : void 0) === '\\';
727
+ if (!((backslash || ((ref = this.seenFor) != null ? ref.endsLength : void 0) < this.ends.length) && this.seenFor)) {
728
+ this.seenFor = false;
729
+ }
730
+ if (!((backslash && this.seenImport) || this.importSpecifierList)) {
731
+ this.seenImport = false;
732
+ }
733
+ if (!((backslash && this.seenExport) || this.exportSpecifierList)) {
734
+ this.seenExport = false;
735
+ }
736
+ size = indent.length - 1 - indent.lastIndexOf('\n');
737
+ noNewlines = this.unfinished();
738
+ newIndentLiteral = size > 0 ? indent.slice(-size) : '';
739
+ if (!/^(.?)\1*$/.exec(newIndentLiteral)) {
740
+ this.error('mixed indentation', {
741
+ offset: indent.length
742
+ });
743
+ return indent.length;
744
+ }
745
+ minLiteralLength = Math.min(newIndentLiteral.length, this.indentLiteral.length);
746
+ if (newIndentLiteral.slice(0, minLiteralLength) !== this.indentLiteral.slice(0, minLiteralLength)) {
747
+ this.error('indentation mismatch', {
748
+ offset: indent.length
749
+ });
750
+ return indent.length;
751
+ }
752
+ if (size - this.continuationLineAdditionalIndent === this.indent) {
753
+ if (noNewlines) {
754
+ this.suppressNewlines();
755
+ } else {
756
+ this.newlineToken(offset);
757
+ }
758
+ return indent.length;
759
+ }
760
+ if (size > this.indent) {
761
+ if (noNewlines) {
762
+ if (!backslash) {
763
+ this.continuationLineAdditionalIndent = size - this.indent;
764
+ }
765
+ if (this.continuationLineAdditionalIndent) {
766
+ prev.continuationLineIndent = this.indent + this.continuationLineAdditionalIndent;
767
+ }
768
+ this.suppressNewlines();
769
+ return indent.length;
770
+ }
771
+ if (!this.tokens.length) {
772
+ this.baseIndent = this.indent = size;
773
+ this.indentLiteral = newIndentLiteral;
774
+ return indent.length;
775
+ }
776
+ diff = size - this.indent + this.outdebt;
777
+ this.token('INDENT', diff, {
778
+ offset: offset + indent.length - size,
779
+ length: size
780
+ });
781
+ this.indents.push(diff);
782
+ this.ends.push({
783
+ tag: 'OUTDENT'
784
+ });
785
+ this.outdebt = this.continuationLineAdditionalIndent = 0;
786
+ this.indent = size;
787
+ this.indentLiteral = newIndentLiteral;
788
+ } else if (size < this.baseIndent) {
789
+ this.error('missing indentation', {
790
+ offset: offset + indent.length
791
+ });
792
+ } else {
793
+ endsContinuationLineIndentation = this.continuationLineAdditionalIndent > 0;
794
+ this.continuationLineAdditionalIndent = 0;
795
+ this.outdentToken({
796
+ moveOut: this.indent - size,
797
+ noNewlines,
798
+ outdentLength: indent.length,
799
+ offset,
800
+ indentSize: size,
801
+ endsContinuationLineIndentation
802
+ });
803
+ }
804
+ return indent.length;
805
+ }
806
+
807
+ // Record an outdent token or multiple tokens, if we happen to be moving back
808
+ // inwards past several recorded indents. Sets new @indent value.
809
+ outdentToken({moveOut, noNewlines, outdentLength = 0, offset = 0, indentSize, endsContinuationLineIndentation}) {
810
+ var decreasedIndent, dent, lastIndent, ref, terminatorToken;
811
+ decreasedIndent = this.indent - moveOut;
812
+ while (moveOut > 0) {
813
+ lastIndent = this.indents[this.indents.length - 1];
814
+ if (!lastIndent) {
815
+ this.outdebt = moveOut = 0;
816
+ } else if (this.outdebt && moveOut <= this.outdebt) {
817
+ this.outdebt -= moveOut;
818
+ moveOut = 0;
819
+ } else {
820
+ dent = this.indents.pop() + this.outdebt;
821
+ if (outdentLength && (ref = this.chunk[outdentLength], indexOf.call(INDENTABLE_CLOSERS, ref) >= 0)) {
822
+ decreasedIndent -= dent - moveOut;
823
+ moveOut = dent;
824
+ }
825
+ this.outdebt = 0;
826
+ // pair might call outdentToken, so preserve decreasedIndent
827
+ this.pair('OUTDENT');
828
+ this.token('OUTDENT', moveOut, {
829
+ length: outdentLength,
830
+ indentSize: indentSize + moveOut - dent
831
+ });
832
+ moveOut -= dent;
833
+ }
834
+ }
835
+ if (dent) {
836
+ this.outdebt -= moveOut;
837
+ }
838
+ this.suppressSemicolons();
839
+ if (!(this.tag() === 'TERMINATOR' || noNewlines)) {
840
+ terminatorToken = this.token('TERMINATOR', '\n', {
841
+ offset: offset + outdentLength,
842
+ length: 0
843
+ });
844
+ if (endsContinuationLineIndentation) {
845
+ terminatorToken.endsContinuationLineIndentation = {
846
+ preContinuationLineIndent: this.indent
847
+ };
848
+ }
849
+ }
850
+ this.indent = decreasedIndent;
851
+ this.indentLiteral = this.indentLiteral.slice(0, decreasedIndent);
852
+ return this;
853
+ }
854
+
855
+ // Matches and consumes non-meaningful whitespace. Tag the previous token
856
+ // as being “spaced”, because there are some cases where it makes a difference.
857
+ whitespaceToken() {
858
+ var match, nline, prev;
859
+ if (!((match = WHITESPACE.exec(this.chunk)) || (nline = this.chunk.charAt(0) === '\n'))) {
860
+ return 0;
861
+ }
862
+ prev = this.prev();
863
+ if (prev) {
864
+ prev[match ? 'spaced' : 'newLine'] = true;
865
+ }
866
+ if (match) {
867
+ return match[0].length;
868
+ } else {
869
+ return 0;
870
+ }
871
+ }
872
+
873
+ // Generate a newline token. Consecutive newlines get merged together.
874
+ newlineToken(offset) {
875
+ this.suppressSemicolons();
876
+ if (this.tag() !== 'TERMINATOR') {
877
+ this.token('TERMINATOR', '\n', {
878
+ offset,
879
+ length: 0
880
+ });
881
+ }
882
+ return this;
883
+ }
884
+
885
+ // Use a `\` at a line-ending to suppress the newline.
886
+ // The slash is removed here once its job is done.
887
+ suppressNewlines() {
888
+ var prev;
889
+ prev = this.prev();
890
+ if (prev[1] === '\\') {
891
+ if (prev.comments && this.tokens.length > 1) {
892
+ // `@tokens.length` should be at least 2 (some code, then `\`).
893
+ // If something puts a `\` after nothing, they deserve to lose any
894
+ // comments that trail it.
895
+ attachCommentsToNode(prev.comments, this.tokens[this.tokens.length - 2]);
896
+ }
897
+ this.tokens.pop();
898
+ }
899
+ return this;
900
+ }
901
+
902
+ jsxToken() {
903
+ var afterTag, end, endToken, firstChar, fullId, fullTagName, id, input, j, jsxTag, len, match, offset, openingTagToken, prev, prevChar, properties, property, ref, tagToken, token, tokens;
904
+ firstChar = this.chunk[0];
905
+ // Check the previous token to detect if attribute is spread.
906
+ prevChar = this.tokens.length > 0 ? this.tokens[this.tokens.length - 1][0] : '';
907
+ if (firstChar === '<') {
908
+ match = JSX_IDENTIFIER.exec(this.chunk.slice(1)) || JSX_FRAGMENT_IDENTIFIER.exec(this.chunk.slice(1));
909
+ // Not the right hand side of an unspaced comparison (i.e. `a<b`).
910
+ if (!(match && (this.jsxDepth > 0 || !(prev = this.prev()) || prev.spaced || (ref = prev[0], indexOf.call(COMPARABLE_LEFT_SIDE, ref) < 0)))) {
911
+ return 0;
912
+ }
913
+ [input, id] = match;
914
+ fullId = id;
915
+ if (indexOf.call(id, '.') >= 0) {
916
+ [id, ...properties] = id.split('.');
917
+ } else {
918
+ properties = [];
919
+ }
920
+ tagToken = this.token('JSX_TAG', id, {
921
+ length: id.length + 1,
922
+ data: {
923
+ openingBracketToken: this.makeToken('<', '<'),
924
+ tagNameToken: this.makeToken('IDENTIFIER', id, {
925
+ offset: 1
926
+ })
927
+ }
928
+ });
929
+ offset = id.length + 1;
930
+ for (j = 0, len = properties.length; j < len; j++) {
931
+ property = properties[j];
932
+ this.token('.', '.', {offset});
933
+ offset += 1;
934
+ this.token('PROPERTY', property, {offset});
935
+ offset += property.length;
936
+ }
937
+ this.token('CALL_START', '(', {
938
+ generated: true
939
+ });
940
+ this.token('[', '[', {
941
+ generated: true
942
+ });
943
+ this.ends.push({
944
+ tag: '/>',
945
+ origin: tagToken,
946
+ name: id,
947
+ properties
948
+ });
949
+ this.jsxDepth++;
950
+ return fullId.length + 1;
951
+ } else if (jsxTag = this.atJSXTag()) {
952
+ if (this.chunk.slice(0, 2) === '/>') { // Self-closing tag.
953
+ this.pair('/>');
954
+ this.token(']', ']', {
955
+ length: 2,
956
+ generated: true
957
+ });
958
+ this.token('CALL_END', ')', {
959
+ length: 2,
960
+ generated: true,
961
+ data: {
962
+ selfClosingSlashToken: this.makeToken('/', '/'),
963
+ closingBracketToken: this.makeToken('>', '>', {
964
+ offset: 1
965
+ })
966
+ }
967
+ });
968
+ this.jsxDepth--;
969
+ return 2;
970
+ } else if (firstChar === '{') {
971
+ if (prevChar === ':') {
972
+ // This token represents the start of a JSX attribute value
973
+ // that’s an expression (e.g. the `{b}` in `<div a={b} />`).
974
+ // Our grammar represents the beginnings of expressions as `(`
975
+ // tokens, so make this into a `(` token that displays as `{`.
976
+ token = this.token('(', '{');
977
+ this.jsxObjAttribute[this.jsxDepth] = false;
978
+ // tag attribute name as JSX
979
+ addTokenData(this.tokens[this.tokens.length - 3], {
980
+ jsx: true
981
+ });
982
+ } else {
983
+ token = this.token('{', '{');
984
+ this.jsxObjAttribute[this.jsxDepth] = true;
985
+ }
986
+ this.ends.push({
987
+ tag: '}',
988
+ origin: token
989
+ });
990
+ return 1;
991
+ } else if (firstChar === '>') { // end of opening tag
992
+ ({
993
+ // Ignore terminators inside a tag.
994
+ origin: openingTagToken
995
+ } = this.pair('/>')); // As if the current tag was self-closing.
996
+ this.token(']', ']', {
997
+ generated: true,
998
+ data: {
999
+ closingBracketToken: this.makeToken('>', '>')
1000
+ }
1001
+ });
1002
+ this.token(',', 'JSX_COMMA', {
1003
+ generated: true
1004
+ });
1005
+ ({
1006
+ tokens,
1007
+ index: end
1008
+ } = this.matchWithInterpolations(INSIDE_JSX, '>', '</', JSX_INTERPOLATION));
1009
+ this.mergeInterpolationTokens(tokens, {
1010
+ endOffset: end,
1011
+ jsx: true
1012
+ }, (value) => {
1013
+ return this.validateUnicodeCodePointEscapes(value, {
1014
+ delimiter: '>'
1015
+ });
1016
+ });
1017
+ match = JSX_IDENTIFIER.exec(this.chunk.slice(end)) || JSX_FRAGMENT_IDENTIFIER.exec(this.chunk.slice(end));
1018
+ if (!match || match[1] !== `${jsxTag.name}${((function() {
1019
+ var k, len1, ref1, results;
1020
+ ref1 = jsxTag.properties;
1021
+ results = [];
1022
+ for (k = 0, len1 = ref1.length; k < len1; k++) {
1023
+ property = ref1[k];
1024
+ results.push(`.${property}`);
1025
+ }
1026
+ return results;
1027
+ })()).join('')}`) {
1028
+ this.error(`expected corresponding JSX closing tag for ${jsxTag.name}`, jsxTag.origin.data.tagNameToken[2]);
1029
+ }
1030
+ [, fullTagName] = match;
1031
+ afterTag = end + fullTagName.length;
1032
+ if (this.chunk[afterTag] !== '>') {
1033
+ this.error("missing closing > after tag name", {
1034
+ offset: afterTag,
1035
+ length: 1
1036
+ });
1037
+ }
1038
+ // -2/+2 for the opening `</` and +1 for the closing `>`.
1039
+ endToken = this.token('CALL_END', ')', {
1040
+ offset: end - 2,
1041
+ length: fullTagName.length + 3,
1042
+ generated: true,
1043
+ data: {
1044
+ closingTagOpeningBracketToken: this.makeToken('<', '<', {
1045
+ offset: end - 2
1046
+ }),
1047
+ closingTagSlashToken: this.makeToken('/', '/', {
1048
+ offset: end - 1
1049
+ }),
1050
+ // TODO: individual tokens for complex tag name? eg < / A . B >
1051
+ closingTagNameToken: this.makeToken('IDENTIFIER', fullTagName, {
1052
+ offset: end
1053
+ }),
1054
+ closingTagClosingBracketToken: this.makeToken('>', '>', {
1055
+ offset: end + fullTagName.length
1056
+ })
1057
+ }
1058
+ });
1059
+ // make the closing tag location data more easily accessible to the grammar
1060
+ addTokenData(openingTagToken, endToken.data);
1061
+ this.jsxDepth--;
1062
+ return afterTag + 1;
1063
+ } else {
1064
+ return 0;
1065
+ }
1066
+ } else if (this.atJSXTag(1)) {
1067
+ if (firstChar === '}') {
1068
+ this.pair(firstChar);
1069
+ if (this.jsxObjAttribute[this.jsxDepth]) {
1070
+ this.token('}', '}');
1071
+ this.jsxObjAttribute[this.jsxDepth] = false;
1072
+ } else {
1073
+ this.token(')', '}');
1074
+ }
1075
+ this.token(',', ',', {
1076
+ generated: true
1077
+ });
1078
+ return 1;
1079
+ } else {
1080
+ return 0;
1081
+ }
1082
+ } else {
1083
+ return 0;
1084
+ }
1085
+ }
1086
+
1087
+ atJSXTag(depth = 0) {
1088
+ var i, last, ref;
1089
+ if (this.jsxDepth === 0) {
1090
+ return false;
1091
+ }
1092
+ i = this.ends.length - 1;
1093
+ while (((ref = this.ends[i]) != null ? ref.tag : void 0) === 'OUTDENT' || depth-- > 0) { // Ignore indents.
1094
+ i--;
1095
+ }
1096
+ last = this.ends[i];
1097
+ return (last != null ? last.tag : void 0) === '/>' && last;
1098
+ }
1099
+
1100
+ // We treat all other single characters as a token. E.g.: `( ) , . !`
1101
+ // Multi-character operators are also literal tokens, so that Jison can assign
1102
+ // the proper order of operations. There are some symbols that we tag specially
1103
+ // here. `;` and newlines are both treated as a `TERMINATOR`, we distinguish
1104
+ // parentheses that indicate a method call from regular parentheses, and so on.
1105
+ literalToken() {
1106
+ var match, message, origin, prev, ref, ref1, ref2, ref3, ref4, ref5, skipToken, tag, token, value;
1107
+ if (match = OPERATOR.exec(this.chunk)) {
1108
+ [value] = match;
1109
+ if (CODE.test(value)) {
1110
+ this.tagParameters();
1111
+ }
1112
+ } else {
1113
+ value = this.chunk.charAt(0);
1114
+ }
1115
+ tag = value;
1116
+ prev = this.prev();
1117
+ if (prev && indexOf.call(['=', ...COMPOUND_ASSIGN], value) >= 0) {
1118
+ skipToken = false;
1119
+ if (value === '=' && ((ref = prev[1]) === '||' || ref === '&&') && !prev.spaced) {
1120
+ prev[0] = 'COMPOUND_ASSIGN';
1121
+ prev[1] += '=';
1122
+ if ((ref1 = prev.data) != null ? ref1.original : void 0) {
1123
+ prev.data.original += '=';
1124
+ }
1125
+ prev[2].range = [prev[2].range[0], prev[2].range[1] + 1];
1126
+ prev[2].last_column += 1;
1127
+ prev[2].last_column_exclusive += 1;
1128
+ prev = this.tokens[this.tokens.length - 2];
1129
+ skipToken = true;
1130
+ }
1131
+ if (prev && prev[0] !== 'PROPERTY') {
1132
+ origin = (ref2 = prev.origin) != null ? ref2 : prev;
1133
+ message = isUnassignable(prev[1], origin[1]);
1134
+ if (message) {
1135
+ this.error(message, origin[2]);
1136
+ }
1137
+ }
1138
+ if (skipToken) {
1139
+ return value.length;
1140
+ }
1141
+ }
1142
+ if (value === '(' && (prev != null ? prev[0] : void 0) === 'IMPORT') {
1143
+ prev[0] = 'DYNAMIC_IMPORT';
1144
+ }
1145
+ if (value === '{' && this.seenImport) {
1146
+ this.importSpecifierList = true;
1147
+ } else if (this.importSpecifierList && value === '}') {
1148
+ this.importSpecifierList = false;
1149
+ } else if (value === '{' && (prev != null ? prev[0] : void 0) === 'EXPORT') {
1150
+ this.exportSpecifierList = true;
1151
+ } else if (this.exportSpecifierList && value === '}') {
1152
+ this.exportSpecifierList = false;
1153
+ }
1154
+ if (value === ';') {
1155
+ if (ref3 = prev != null ? prev[0] : void 0, indexOf.call(['=', ...UNFINISHED], ref3) >= 0) {
1156
+ this.error('unexpected ;');
1157
+ }
1158
+ this.seenFor = this.seenImport = this.seenExport = false;
1159
+ tag = 'TERMINATOR';
1160
+ } else if (value === '*' && (prev != null ? prev[0] : void 0) === 'EXPORT') {
1161
+ tag = 'EXPORT_ALL';
1162
+ } else if (indexOf.call(MATH, value) >= 0) {
1163
+ tag = 'MATH';
1164
+ } else if (indexOf.call(COMPARE, value) >= 0) {
1165
+ tag = 'COMPARE';
1166
+ } else if (indexOf.call(COMPOUND_ASSIGN, value) >= 0) {
1167
+ tag = 'COMPOUND_ASSIGN';
1168
+ } else if (indexOf.call(UNARY, value) >= 0) {
1169
+ tag = 'UNARY';
1170
+ } else if (indexOf.call(UNARY_MATH, value) >= 0) {
1171
+ tag = 'UNARY_MATH';
1172
+ } else if (indexOf.call(SHIFT, value) >= 0) {
1173
+ tag = 'SHIFT';
1174
+ } else if (value === '?' && (prev != null ? prev.spaced : void 0)) {
1175
+ tag = 'BIN?';
1176
+ } else if (prev) {
1177
+ if (value === '(' && !prev.spaced && (ref4 = prev[0], indexOf.call(CALLABLE, ref4) >= 0)) {
1178
+ if (prev[0] === '?') {
1179
+ prev[0] = 'FUNC_EXIST';
1180
+ }
1181
+ tag = 'CALL_START';
1182
+ } else if (value === '[' && (((ref5 = prev[0], indexOf.call(INDEXABLE, ref5) >= 0) && !prev.spaced) || (prev[0] === '::'))) { // `.prototype` can’t be a method you can call.
1183
+ tag = 'INDEX_START';
1184
+ switch (prev[0]) {
1185
+ case '?':
1186
+ prev[0] = 'INDEX_SOAK';
1187
+ }
1188
+ }
1189
+ }
1190
+ token = this.makeToken(tag, value);
1191
+ switch (value) {
1192
+ case '(':
1193
+ case '{':
1194
+ case '[':
1195
+ this.ends.push({
1196
+ tag: INVERSES[value],
1197
+ origin: token
1198
+ });
1199
+ break;
1200
+ case ')':
1201
+ case '}':
1202
+ case ']':
1203
+ this.pair(value);
1204
+ }
1205
+ this.tokens.push(this.makeToken(tag, value));
1206
+ return value.length;
1207
+ }
1208
+
1209
+ // Token Manipulators
1210
+ // ------------------
1211
+
1212
+ // A source of ambiguity in our grammar used to be parameter lists in function
1213
+ // definitions versus argument lists in function calls. Walk backwards, tagging
1214
+ // parameters specially in order to make things easier for the parser.
1215
+ tagParameters() {
1216
+ var i, paramEndToken, stack, tok, tokens;
1217
+ if (this.tag() !== ')') {
1218
+ return this.tagDoIife();
1219
+ }
1220
+ stack = [];
1221
+ ({tokens} = this);
1222
+ i = tokens.length;
1223
+ paramEndToken = tokens[--i];
1224
+ paramEndToken[0] = 'PARAM_END';
1225
+ while (tok = tokens[--i]) {
1226
+ switch (tok[0]) {
1227
+ case ')':
1228
+ stack.push(tok);
1229
+ break;
1230
+ case '(':
1231
+ case 'CALL_START':
1232
+ if (stack.length) {
1233
+ stack.pop();
1234
+ } else if (tok[0] === '(') {
1235
+ tok[0] = 'PARAM_START';
1236
+ return this.tagDoIife(i - 1);
1237
+ } else {
1238
+ paramEndToken[0] = 'CALL_END';
1239
+ return this;
1240
+ }
1241
+ }
1242
+ }
1243
+ return this;
1244
+ }
1245
+
1246
+ // Tag `do` followed by a function differently than `do` followed by eg an
1247
+ // identifier to allow for different grammar precedence
1248
+ tagDoIife(tokenIndex) {
1249
+ var tok;
1250
+ tok = this.tokens[tokenIndex != null ? tokenIndex : this.tokens.length - 1];
1251
+ if ((tok != null ? tok[0] : void 0) !== 'DO') {
1252
+ return this;
1253
+ }
1254
+ tok[0] = 'DO_IIFE';
1255
+ return this;
1256
+ }
1257
+
1258
+ // Close up all remaining open blocks at the end of the file.
1259
+ closeIndentation() {
1260
+ return this.outdentToken({
1261
+ moveOut: this.indent,
1262
+ indentSize: 0
1263
+ });
1264
+ }
1265
+
1266
+ // Match the contents of a delimited token and expand variables and expressions
1267
+ // inside it using Ruby-like notation for substitution of arbitrary
1268
+ // expressions.
1269
+
1270
+ // "Hello #{name.capitalize()}."
1271
+
1272
+ // If it encounters an interpolation, this method will recursively create a new
1273
+ // Lexer and tokenize until the `{` of `#{` is balanced with a `}`.
1274
+
1275
+ // - `regex` matches the contents of a token (but not `delimiter`, and not
1276
+ // `#{` if interpolations are desired).
1277
+ // - `delimiter` is the delimiter of the token. Examples are `'`, `"`, `'''`,
1278
+ // `"""` and `///`.
1279
+ // - `closingDelimiter` is different from `delimiter` only in JSX
1280
+ // - `interpolators` matches the start of an interpolation, for JSX it's both
1281
+ // `{` and `<` (i.e. nested JSX tag)
1282
+
1283
+ // This method allows us to have strings within interpolations within strings,
1284
+ // ad infinitum.
1285
+ matchWithInterpolations(regex, delimiter, closingDelimiter = delimiter, interpolators = /^#\{/) {
1286
+ var braceInterpolator, close, column, index, interpolationOffset, interpolator, line, match, nested, offset, offsetInChunk, open, ref, ref1, rest, str, strPart, tokens;
1287
+ tokens = [];
1288
+ offsetInChunk = delimiter.length;
1289
+ if (this.chunk.slice(0, offsetInChunk) !== delimiter) {
1290
+ return null;
1291
+ }
1292
+ str = this.chunk.slice(offsetInChunk);
1293
+ while (true) {
1294
+ [strPart] = regex.exec(str);
1295
+ this.validateEscapes(strPart, {
1296
+ isRegex: delimiter.charAt(0) === '/',
1297
+ offsetInChunk
1298
+ });
1299
+ // Push a fake `'NEOSTRING'` token, which will get turned into a real string later.
1300
+ tokens.push(this.makeToken('NEOSTRING', strPart, {
1301
+ offset: offsetInChunk
1302
+ }));
1303
+ str = str.slice(strPart.length);
1304
+ offsetInChunk += strPart.length;
1305
+ if (!(match = interpolators.exec(str))) {
1306
+ break;
1307
+ }
1308
+ [interpolator] = match;
1309
+ // To remove the `#` in `#{`.
1310
+ interpolationOffset = interpolator.length - 1;
1311
+ [line, column, offset] = this.getLineAndColumnFromChunk(offsetInChunk + interpolationOffset);
1312
+ rest = str.slice(interpolationOffset);
1313
+ ({
1314
+ tokens: nested,
1315
+ index
1316
+ } = new Lexer().tokenize(rest, {
1317
+ line,
1318
+ column,
1319
+ offset,
1320
+ untilBalanced: true,
1321
+ locationDataCompensations: this.locationDataCompensations
1322
+ }));
1323
+ // Account for the `#` in `#{`.
1324
+ index += interpolationOffset;
1325
+ braceInterpolator = str[index - 1] === '}';
1326
+ if (braceInterpolator) {
1327
+ // Turn the leading and trailing `{` and `}` into parentheses. Unnecessary
1328
+ // parentheses will be removed later.
1329
+ [open] = nested, [close] = slice.call(nested, -1);
1330
+ open[0] = 'INTERPOLATION_START';
1331
+ open[1] = '(';
1332
+ open[2].first_column -= interpolationOffset;
1333
+ open[2].range = [open[2].range[0] - interpolationOffset, open[2].range[1]];
1334
+ close[0] = 'INTERPOLATION_END';
1335
+ close[1] = ')';
1336
+ close.origin = ['', 'end of interpolation', close[2]];
1337
+ }
1338
+ if (((ref = nested[1]) != null ? ref[0] : void 0) === 'TERMINATOR') {
1339
+ // Remove leading `'TERMINATOR'` (if any).
1340
+ nested.splice(1, 1);
1341
+ }
1342
+ if (((ref1 = nested[nested.length - 3]) != null ? ref1[0] : void 0) === 'INDENT' && nested[nested.length - 2][0] === 'OUTDENT') {
1343
+ // Remove trailing `'INDENT'/'OUTDENT'` pair (if any).
1344
+ nested.splice(-3, 2);
1345
+ }
1346
+ if (!braceInterpolator) {
1347
+ // We are not using `{` and `}`, so wrap the interpolated tokens instead.
1348
+ open = this.makeToken('INTERPOLATION_START', '(', {
1349
+ offset: offsetInChunk,
1350
+ length: 0,
1351
+ generated: true
1352
+ });
1353
+ close = this.makeToken('INTERPOLATION_END', ')', {
1354
+ offset: offsetInChunk + index,
1355
+ length: 0,
1356
+ generated: true
1357
+ });
1358
+ nested = [open, ...nested, close];
1359
+ }
1360
+ // Push a fake `'TOKENS'` token, which will get turned into real tokens later.
1361
+ tokens.push(['TOKENS', nested]);
1362
+ str = str.slice(index);
1363
+ offsetInChunk += index;
1364
+ }
1365
+ if (str.slice(0, closingDelimiter.length) !== closingDelimiter) {
1366
+ this.error(`missing ${closingDelimiter}`, {
1367
+ length: delimiter.length
1368
+ });
1369
+ }
1370
+ return {
1371
+ tokens,
1372
+ index: offsetInChunk + closingDelimiter.length
1373
+ };
1374
+ }
1375
+
1376
+ // Merge the array `tokens` of the fake token types `'TOKENS'` and `'NEOSTRING'`
1377
+ // (as returned by `matchWithInterpolations`) into the token stream. The value
1378
+ // of `'NEOSTRING'`s are converted using `fn` and turned into strings using
1379
+ // `options` first.
1380
+ mergeInterpolationTokens(tokens, options, fn) {
1381
+ var $, converted, double, endOffset, firstIndex, heregex, i, indent, j, jsx, k, lastToken, len, len1, locationToken, lparen, placeholderToken, quote, ref, ref1, rparen, tag, token, tokensToPush, val, value;
1382
+ ({quote, indent, double, heregex, endOffset, jsx} = options);
1383
+ if (tokens.length > 1) {
1384
+ lparen = this.token('STRING_START', '(', {
1385
+ length: (ref = quote != null ? quote.length : void 0) != null ? ref : 0,
1386
+ data: {quote},
1387
+ generated: !(quote != null ? quote.length : void 0)
1388
+ });
1389
+ }
1390
+ firstIndex = this.tokens.length;
1391
+ $ = tokens.length - 1;
1392
+ for (i = j = 0, len = tokens.length; j < len; i = ++j) {
1393
+ token = tokens[i];
1394
+ [tag, value] = token;
1395
+ switch (tag) {
1396
+ case 'TOKENS':
1397
+ // There are comments (and nothing else) in this interpolation.
1398
+ if (value.length === 2 && (value[0].comments || value[1].comments)) {
1399
+ placeholderToken = this.makeToken('JS', '', {
1400
+ generated: true
1401
+ });
1402
+ // Use the same location data as the first parenthesis.
1403
+ placeholderToken[2] = value[0][2];
1404
+ for (k = 0, len1 = value.length; k < len1; k++) {
1405
+ val = value[k];
1406
+ if (!val.comments) {
1407
+ continue;
1408
+ }
1409
+ if (placeholderToken.comments == null) {
1410
+ placeholderToken.comments = [];
1411
+ }
1412
+ placeholderToken.comments.push(...val.comments);
1413
+ }
1414
+ value.splice(1, 0, placeholderToken);
1415
+ }
1416
+ // Push all the tokens in the fake `'TOKENS'` token. These already have
1417
+ // sane location data.
1418
+ locationToken = value[0];
1419
+ tokensToPush = value;
1420
+ break;
1421
+ case 'NEOSTRING':
1422
+ // Convert `'NEOSTRING'` into `'STRING'`.
1423
+ converted = fn.call(this, token[1], i);
1424
+ if (i === 0) {
1425
+ addTokenData(token, {
1426
+ initialChunk: true
1427
+ });
1428
+ }
1429
+ if (i === $) {
1430
+ addTokenData(token, {
1431
+ finalChunk: true
1432
+ });
1433
+ }
1434
+ addTokenData(token, {indent, quote, double});
1435
+ if (heregex) {
1436
+ addTokenData(token, {heregex});
1437
+ }
1438
+ if (jsx) {
1439
+ addTokenData(token, {jsx});
1440
+ }
1441
+ token[0] = 'STRING';
1442
+ token[1] = '"' + converted + '"';
1443
+ if (tokens.length === 1 && (quote != null)) {
1444
+ token[2].first_column -= quote.length;
1445
+ if (token[1].substr(-2, 1) === '\n') {
1446
+ token[2].last_line += 1;
1447
+ token[2].last_column = quote.length - 1;
1448
+ } else {
1449
+ token[2].last_column += quote.length;
1450
+ if (token[1].length === 2) {
1451
+ token[2].last_column -= 1;
1452
+ }
1453
+ }
1454
+ token[2].last_column_exclusive += quote.length;
1455
+ token[2].range = [token[2].range[0] - quote.length, token[2].range[1] + quote.length];
1456
+ }
1457
+ locationToken = token;
1458
+ tokensToPush = [token];
1459
+ }
1460
+ this.tokens.push(...tokensToPush);
1461
+ }
1462
+ if (lparen) {
1463
+ [lastToken] = slice.call(tokens, -1);
1464
+ lparen.origin = [
1465
+ 'STRING',
1466
+ null,
1467
+ {
1468
+ first_line: lparen[2].first_line,
1469
+ first_column: lparen[2].first_column,
1470
+ last_line: lastToken[2].last_line,
1471
+ last_column: lastToken[2].last_column,
1472
+ last_line_exclusive: lastToken[2].last_line_exclusive,
1473
+ last_column_exclusive: lastToken[2].last_column_exclusive,
1474
+ range: [lparen[2].range[0],
1475
+ lastToken[2].range[1]]
1476
+ }
1477
+ ];
1478
+ if (!(quote != null ? quote.length : void 0)) {
1479
+ lparen[2] = lparen.origin[2];
1480
+ }
1481
+ return rparen = this.token('STRING_END', ')', {
1482
+ offset: endOffset - (quote != null ? quote : '').length,
1483
+ length: (ref1 = quote != null ? quote.length : void 0) != null ? ref1 : 0,
1484
+ generated: !(quote != null ? quote.length : void 0)
1485
+ });
1486
+ }
1487
+ }
1488
+
1489
+ // Pairs up a closing token, ensuring that all listed pairs of tokens are
1490
+ // correctly balanced throughout the course of the token stream.
1491
+ pair(tag) {
1492
+ var lastIndent, prev, ref, ref1, wanted;
1493
+ ref = this.ends, [prev] = slice.call(ref, -1);
1494
+ if (tag !== (wanted = prev != null ? prev.tag : void 0)) {
1495
+ if ('OUTDENT' !== wanted) {
1496
+ this.error(`unmatched ${tag}`);
1497
+ }
1498
+ // Auto-close `INDENT` to support syntax like this:
1499
+
1500
+ // el.click((event) ->
1501
+ // el.hide())
1502
+
1503
+ ref1 = this.indents, [lastIndent] = slice.call(ref1, -1);
1504
+ this.outdentToken({
1505
+ moveOut: lastIndent,
1506
+ noNewlines: true
1507
+ });
1508
+ return this.pair(tag);
1509
+ }
1510
+ return this.ends.pop();
1511
+ }
1512
+
1513
+ // Helpers
1514
+ // -------
1515
+
1516
+ // Compensate for the things we strip out initially (e.g. carriage returns)
1517
+ // so that location data stays accurate with respect to the original source file.
1518
+ getLocationDataCompensation(start, end) {
1519
+ var compensation, current, initialEnd, totalCompensation;
1520
+ totalCompensation = 0;
1521
+ initialEnd = end;
1522
+ current = start;
1523
+ while (current <= end) {
1524
+ if (current === end && start !== initialEnd) {
1525
+ break;
1526
+ }
1527
+ compensation = this.locationDataCompensations[current];
1528
+ if (compensation != null) {
1529
+ totalCompensation += compensation;
1530
+ end += compensation;
1531
+ }
1532
+ current++;
1533
+ }
1534
+ return totalCompensation;
1535
+ }
1536
+
1537
+ // Returns the line and column number from an offset into the current chunk.
1538
+
1539
+ // `offset` is a number of characters into `@chunk`.
1540
+ getLineAndColumnFromChunk(offset) {
1541
+ var column, columnCompensation, compensation, lastLine, lineCount, previousLinesCompensation, ref, string;
1542
+ compensation = this.getLocationDataCompensation(this.chunkOffset, this.chunkOffset + offset);
1543
+ if (offset === 0) {
1544
+ return [this.chunkLine, this.chunkColumn + compensation, this.chunkOffset + compensation];
1545
+ }
1546
+ if (offset >= this.chunk.length) {
1547
+ string = this.chunk;
1548
+ } else {
1549
+ string = this.chunk.slice(0, +(offset - 1) + 1 || 9e9);
1550
+ }
1551
+ lineCount = count(string, '\n');
1552
+ column = this.chunkColumn;
1553
+ if (lineCount > 0) {
1554
+ ref = string.split('\n'), [lastLine] = slice.call(ref, -1);
1555
+ column = lastLine.length;
1556
+ previousLinesCompensation = this.getLocationDataCompensation(this.chunkOffset, this.chunkOffset + offset - column);
1557
+ if (previousLinesCompensation < 0) {
1558
+ // Don't recompensate for initially inserted newline.
1559
+ previousLinesCompensation = 0;
1560
+ }
1561
+ columnCompensation = this.getLocationDataCompensation(this.chunkOffset + offset + previousLinesCompensation - column, this.chunkOffset + offset + previousLinesCompensation);
1562
+ } else {
1563
+ column += string.length;
1564
+ columnCompensation = compensation;
1565
+ }
1566
+ return [this.chunkLine + lineCount, column + columnCompensation, this.chunkOffset + offset + compensation];
1567
+ }
1568
+
1569
+ makeLocationData({offsetInChunk, length}) {
1570
+ var endOffset, lastCharacter, locationData;
1571
+ locationData = {
1572
+ range: []
1573
+ };
1574
+ [locationData.first_line, locationData.first_column, locationData.range[0]] = this.getLineAndColumnFromChunk(offsetInChunk);
1575
+ // Use length - 1 for the final offset - we’re supplying the last_line and the last_column,
1576
+ // so if last_column == first_column, then we’re looking at a character of length 1.
1577
+ lastCharacter = length > 0 ? length - 1 : 0;
1578
+ [locationData.last_line, locationData.last_column, endOffset] = this.getLineAndColumnFromChunk(offsetInChunk + lastCharacter);
1579
+ [locationData.last_line_exclusive, locationData.last_column_exclusive] = this.getLineAndColumnFromChunk(offsetInChunk + lastCharacter + (length > 0 ? 1 : 0));
1580
+ locationData.range[1] = length > 0 ? endOffset + 1 : endOffset;
1581
+ return locationData;
1582
+ }
1583
+
1584
+ // Same as `token`, except this just returns the token without adding it
1585
+ // to the results.
1586
+ makeToken(tag, value, {
1587
+ offset: offsetInChunk = 0,
1588
+ length = value.length,
1589
+ origin,
1590
+ generated,
1591
+ indentSize
1592
+ } = {}) {
1593
+ var token;
1594
+ token = [tag, value, this.makeLocationData({offsetInChunk, length})];
1595
+ if (origin) {
1596
+ token.origin = origin;
1597
+ }
1598
+ if (generated) {
1599
+ token.generated = true;
1600
+ }
1601
+ if (indentSize != null) {
1602
+ token.indentSize = indentSize;
1603
+ }
1604
+ return token;
1605
+ }
1606
+
1607
+ // Add a token to the results.
1608
+ // `offset` is the offset into the current `@chunk` where the token starts.
1609
+ // `length` is the length of the token in the `@chunk`, after the offset. If
1610
+ // not specified, the length of `value` will be used.
1611
+
1612
+ // Returns the new token.
1613
+ token(tag, value, {offset, length, origin, data, generated, indentSize} = {}) {
1614
+ var token;
1615
+ token = this.makeToken(tag, value, {offset, length, origin, generated, indentSize});
1616
+ if (data) {
1617
+ addTokenData(token, data);
1618
+ }
1619
+ this.tokens.push(token);
1620
+ return token;
1621
+ }
1622
+
1623
+ // Peek at the last tag in the token stream.
1624
+ tag() {
1625
+ var ref, token;
1626
+ ref = this.tokens, [token] = slice.call(ref, -1);
1627
+ return token != null ? token[0] : void 0;
1628
+ }
1629
+
1630
+ // Peek at the last value in the token stream.
1631
+ value(useOrigin = false) {
1632
+ var ref, token;
1633
+ ref = this.tokens, [token] = slice.call(ref, -1);
1634
+ if (useOrigin && ((token != null ? token.origin : void 0) != null)) {
1635
+ return token.origin[1];
1636
+ } else {
1637
+ return token != null ? token[1] : void 0;
1638
+ }
1639
+ }
1640
+
1641
+ // Get the previous token in the token stream.
1642
+ prev() {
1643
+ return this.tokens[this.tokens.length - 1];
1644
+ }
1645
+
1646
+ // Are we in the midst of an unfinished expression?
1647
+ unfinished() {
1648
+ var ref;
1649
+ return LINE_CONTINUER.test(this.chunk) || (ref = this.tag(), indexOf.call(UNFINISHED, ref) >= 0);
1650
+ }
1651
+
1652
+ validateUnicodeCodePointEscapes(str, options) {
1653
+ return replaceUnicodeCodePointEscapes(str, merge(options, {error: this.error}));
1654
+ }
1655
+
1656
+ // Validates escapes in strings and regexes.
1657
+ validateEscapes(str, options = {}) {
1658
+ var before, hex, invalidEscape, invalidEscapeRegex, match, message, octal, ref, unicode, unicodeCodePoint;
1659
+ invalidEscapeRegex = options.isRegex ? REGEX_INVALID_ESCAPE : STRING_INVALID_ESCAPE;
1660
+ match = invalidEscapeRegex.exec(str);
1661
+ if (!match) {
1662
+ return;
1663
+ }
1664
+ match[0], before = match[1], octal = match[2], hex = match[3], unicodeCodePoint = match[4], unicode = match[5];
1665
+ message = octal ? "octal escape sequences are not allowed" : "invalid escape sequence";
1666
+ invalidEscape = `\\${octal || hex || unicodeCodePoint || unicode}`;
1667
+ return this.error(`${message} ${invalidEscape}`, {
1668
+ offset: ((ref = options.offsetInChunk) != null ? ref : 0) + match.index + before.length,
1669
+ length: invalidEscape.length
1670
+ });
1671
+ }
1672
+
1673
+ suppressSemicolons() {
1674
+ var ref, ref1, results;
1675
+ results = [];
1676
+ while (this.value() === ';') {
1677
+ this.tokens.pop();
1678
+ if (ref = (ref1 = this.prev()) != null ? ref1[0] : void 0, indexOf.call(['=', ...UNFINISHED], ref) >= 0) {
1679
+ results.push(this.error('unexpected ;'));
1680
+ } else {
1681
+ results.push(void 0);
1682
+ }
1683
+ }
1684
+ return results;
1685
+ }
1686
+
1687
+ error(message, options = {}) {
1688
+ var first_column, first_line, location, ref, ref1;
1689
+ location = 'first_line' in options ? options : ([first_line, first_column] = this.getLineAndColumnFromChunk((ref = options.offset) != null ? ref : 0), {
1690
+ first_line,
1691
+ first_column,
1692
+ last_column: first_column + ((ref1 = options.length) != null ? ref1 : 1) - 1
1693
+ });
1694
+ return throwSyntaxError(message, location);
1695
+ }
1696
+
1697
+ };
1698
+
1699
+ // Helper functions
1700
+ // ----------------
1701
+ isUnassignable = function(name, displayName = name) {
1702
+ switch (false) {
1703
+ case indexOf.call([...JS_KEYWORDS, ...COFFEE_KEYWORDS], name) < 0:
1704
+ return `keyword '${displayName}' can't be assigned`;
1705
+ case indexOf.call(STRICT_PROSCRIBED, name) < 0:
1706
+ return `'${displayName}' can't be assigned`;
1707
+ case indexOf.call(RESERVED, name) < 0:
1708
+ return `reserved word '${displayName}' can't be assigned`;
1709
+ default:
1710
+ return false;
1711
+ }
1712
+ };
1713
+
1714
+ exports.isUnassignable = isUnassignable;
1715
+
1716
+ // `from` isn’t a CoffeeScript keyword, but it behaves like one in `import` and
1717
+ // `export` statements (handled above) and in the declaration line of a `for`
1718
+ // loop. Try to detect when `from` is a variable identifier and when it is this
1719
+ // “sometimes” keyword.
1720
+ isForFrom = function(prev) {
1721
+ var ref;
1722
+ // `for i from iterable`
1723
+ if (prev[0] === 'IDENTIFIER') {
1724
+ return true;
1725
+ // `for from…`
1726
+ } else if (prev[0] === 'FOR') {
1727
+ return false;
1728
+ // `for {from}…`, `for [from]…`, `for {a, from}…`, `for {a: from}…`
1729
+ } else if ((ref = prev[1]) === '{' || ref === '[' || ref === ',' || ref === ':') {
1730
+ return false;
1731
+ } else {
1732
+ return true;
1733
+ }
1734
+ };
1735
+
1736
+ addTokenData = function(token, data) {
1737
+ return Object.assign((token.data != null ? token.data : token.data = {}), data);
1738
+ };
1739
+
1740
+ // Constants
1741
+ // ---------
1742
+
1743
+ // Keywords that CoffeeScript shares in common with JavaScript.
1744
+ JS_KEYWORDS = ['true', 'false', 'null', 'this', 'new', 'delete', 'typeof', 'in', 'instanceof', 'return', 'throw', 'break', 'continue', 'debugger', 'yield', 'await', 'if', 'else', 'switch', 'for', 'while', 'do', 'try', 'catch', 'finally', 'class', 'extends', 'super', 'import', 'export', 'default'];
1745
+
1746
+ // CoffeeScript-only keywords.
1747
+ COFFEE_KEYWORDS = ['undefined', 'Infinity', 'NaN', 'then', 'unless', 'until', 'loop', 'of', 'by', 'when'];
1748
+
1749
+ COFFEE_ALIAS_MAP = {
1750
+ and: '&&',
1751
+ or: '||',
1752
+ is: '==',
1753
+ isnt: '!=',
1754
+ not: '!',
1755
+ yes: 'true',
1756
+ no: 'false',
1757
+ on: 'true',
1758
+ off: 'false'
1759
+ };
1760
+
1761
+ COFFEE_ALIASES = (function() {
1762
+ var results;
1763
+ results = [];
1764
+ for (key in COFFEE_ALIAS_MAP) {
1765
+ results.push(key);
1766
+ }
1767
+ return results;
1768
+ })();
1769
+
1770
+ COFFEE_KEYWORDS = COFFEE_KEYWORDS.concat(COFFEE_ALIASES);
1771
+
1772
+ // The list of keywords that are reserved by JavaScript, but not used, or are
1773
+ // used by CoffeeScript internally. We throw an error when these are encountered,
1774
+ // to avoid having a JavaScript error at runtime.
1775
+ RESERVED = ['case', 'function', 'var', 'void', 'with', 'const', 'let', 'enum', 'native', 'implements', 'interface', 'package', 'private', 'protected', 'public', 'static'];
1776
+
1777
+ STRICT_PROSCRIBED = ['arguments', 'eval'];
1778
+
1779
+ // The superset of both JavaScript keywords and reserved words, none of which may
1780
+ // be used as identifiers or properties.
1781
+ exports.JS_FORBIDDEN = JS_KEYWORDS.concat(RESERVED).concat(STRICT_PROSCRIBED);
1782
+
1783
+ // The character code of the nasty Microsoft madness otherwise known as the BOM.
1784
+ BOM = 65279;
1785
+
1786
+ // Token matching regexes.
1787
+ IDENTIFIER = /^(?!\d)((?:(?!\s)[$\w\x7f-\uffff])+)([^\n\S]*:(?!:))?/; // Is this a property name?
1788
+
1789
+ // Like `IDENTIFIER`, but includes `-`s
1790
+ JSX_IDENTIFIER_PART = /(?:(?!\s)[\-$\w\x7f-\uffff])+/.source;
1791
+
1792
+ // In https://facebook.github.io/jsx/ spec, JSXElementName can be
1793
+ // JSXIdentifier, JSXNamespacedName (JSXIdentifier : JSXIdentifier), or
1794
+ // JSXMemberExpression (two or more JSXIdentifier connected by `.`s).
1795
+ JSX_IDENTIFIER = RegExp(`^(?![\\d<])(${JSX_IDENTIFIER_PART // Must not start with `<`.
1796
+ // JSXNamespacedName
1797
+ // JSXMemberExpression
1798
+ }(?:\\s*:\\s*${JSX_IDENTIFIER_PART}|(?:\\s*\\.\\s*${JSX_IDENTIFIER_PART})+)?)`);
1799
+
1800
+ // Fragment: <></>
1801
+ JSX_FRAGMENT_IDENTIFIER = /^()>/; // Ends immediately with `>`.
1802
+
1803
+ // In https://facebook.github.io/jsx/ spec, JSXAttributeName can be either
1804
+ // JSXIdentifier or JSXNamespacedName which is JSXIdentifier : JSXIdentifier
1805
+ JSX_ATTRIBUTE = RegExp(`^(?!\\d)(${JSX_IDENTIFIER_PART // JSXNamespacedName
1806
+ // Is this an attribute with a value?
1807
+ }(?:\\s*:\\s*${JSX_IDENTIFIER_PART})?)([^\\S]*=(?!=))?`);
1808
+
1809
+ NUMBER = /^0b[01](?:_?[01])*n?|^0o[0-7](?:_?[0-7])*n?|^0x[\da-f](?:_?[\da-f])*n?|^\d+(?:_\d+)*n|^(?:\d+(?:_\d+)*)?\.?\d+(?:_\d+)*(?:e[+-]?\d+(?:_\d+)*)?/i; // binary
1810
+ // octal
1811
+ // hex
1812
+ // decimal bigint
1813
+ // decimal
1814
+ // decimal without support for numeric literal separators for reference:
1815
+ // \d*\.?\d+ (?:e[+-]?\d+)?
1816
+
1817
+ OPERATOR = /^(?:[-=]>|[-+*\/%<>&|^!?=]=|>>>=?|([-+:])\1|([&|<>*\/%])\2=?|\?(\.|::)|\.{2,3})/; // function
1818
+ // compound assign / compare
1819
+ // zero-fill right shift
1820
+ // doubles
1821
+ // logic / shift / power / floor division / modulo
1822
+ // soak access
1823
+ // range or splat
1824
+
1825
+ WHITESPACE = /^[^\n\S]+/;
1826
+
1827
+ COMMENT = /^(\s*)###([^#][\s\S]*?)(?:###([^\n\S]*)|###$)|^((?:\s*#(?!##[^#]).*)+)/;
1828
+
1829
+ CODE = /^[-=]>/;
1830
+
1831
+ MULTI_DENT = /^(?:\n[^\n\S]*)+/;
1832
+
1833
+ JSTOKEN = /^`(?!``)((?:[^`\\]|\\[\s\S])*)`/;
1834
+
1835
+ HERE_JSTOKEN = /^```((?:[^`\\]|\\[\s\S]|`(?!``))*)```/;
1836
+
1837
+ // String-matching-regexes.
1838
+ STRING_START = /^(?:'''|"""|'|")/;
1839
+
1840
+ STRING_SINGLE = /^(?:[^\\']|\\[\s\S])*/;
1841
+
1842
+ STRING_DOUBLE = /^(?:[^\\"#]|\\[\s\S]|\#(?!\{))*/;
1843
+
1844
+ HEREDOC_SINGLE = /^(?:[^\\']|\\[\s\S]|'(?!''))*/;
1845
+
1846
+ HEREDOC_DOUBLE = /^(?:[^\\"#]|\\[\s\S]|"(?!"")|\#(?!\{))*/;
1847
+
1848
+ INSIDE_JSX = /^(?:[^\{<])*/; // Start of CoffeeScript interpolation. // Similar to `HEREDOC_DOUBLE` but there is no escaping.
1849
+ // Maybe JSX tag (`<` not allowed even if bare).
1850
+
1851
+ JSX_INTERPOLATION = /^(?:\{|<(?!\/))/; // CoffeeScript interpolation.
1852
+ // JSX opening tag.
1853
+
1854
+ HEREDOC_INDENT = /\n+([^\n\S]*)(?=\S)/g;
1855
+
1856
+ // Regex-matching-regexes.
1857
+ REGEX = /^\/(?!\/)((?:[^[\/\n\\]|\\[^\n]|\[(?:\\[^\n]|[^\]\n\\])*\])*)(\/)?/; // Every other thing.
1858
+ // Anything but newlines escaped.
1859
+ // Character class.
1860
+
1861
+ REGEX_FLAGS = /^\w*/;
1862
+
1863
+ VALID_FLAGS = /^(?!.*(.).*\1)[gimsuy]*$/;
1864
+
1865
+ HEREGEX = /^(?:[^\\\/#\s]|\\[\s\S]|\/(?!\/\/)|\#(?!\{)|\s+(?:#(?!\{).*)?)*/; // Match any character, except those that need special handling below.
1866
+ // Match `\` followed by any character.
1867
+ // Match any `/` except `///`.
1868
+ // Match `#` which is not part of interpolation, e.g. `#{}`.
1869
+ // Comments consume everything until the end of the line, including `///`.
1870
+
1871
+ HEREGEX_COMMENT = /(\s+)(#(?!{).*)/gm;
1872
+
1873
+ REGEX_ILLEGAL = /^(\/|\/{3}\s*)(\*)/;
1874
+
1875
+ POSSIBLY_DIVISION = /^\/=?\s/;
1876
+
1877
+ // Other regexes.
1878
+ HERECOMMENT_ILLEGAL = /\*\//;
1879
+
1880
+ LINE_CONTINUER = /^\s*(?:,|\??\.(?![.\d])|\??::)/;
1881
+
1882
+ STRING_INVALID_ESCAPE = /((?:^|[^\\])(?:\\\\)*)\\(?:(0\d|[1-7])|(x(?![\da-fA-F]{2}).{0,2})|(u\{(?![\da-fA-F]{1,}\})[^}]*\}?)|(u(?!\{|[\da-fA-F]{4}).{0,4}))/; // Make sure the escape isn’t escaped.
1883
+ // octal escape
1884
+ // hex escape
1885
+ // unicode code point escape
1886
+ // unicode escape
1887
+
1888
+ REGEX_INVALID_ESCAPE = /((?:^|[^\\])(?:\\\\)*)\\(?:(0\d)|(x(?![\da-fA-F]{2}).{0,2})|(u\{(?![\da-fA-F]{1,}\})[^}]*\}?)|(u(?!\{|[\da-fA-F]{4}).{0,4}))/; // Make sure the escape isn’t escaped.
1889
+ // octal escape
1890
+ // hex escape
1891
+ // unicode code point escape
1892
+ // unicode escape
1893
+
1894
+ TRAILING_SPACES = /\s+$/;
1895
+
1896
+ // Compound assignment tokens.
1897
+ COMPOUND_ASSIGN = ['-=', '+=', '/=', '*=', '%=', '||=', '&&=', '?=', '<<=', '>>=', '>>>=', '&=', '^=', '|=', '**=', '//=', '%%='];
1898
+
1899
+ // Unary tokens.
1900
+ UNARY = ['NEW', 'TYPEOF', 'DELETE'];
1901
+
1902
+ UNARY_MATH = ['!', '~'];
1903
+
1904
+ // Bit-shifting tokens.
1905
+ SHIFT = ['<<', '>>', '>>>'];
1906
+
1907
+ // Comparison tokens.
1908
+ COMPARE = ['==', '!=', '<', '>', '<=', '>='];
1909
+
1910
+ // Mathematical tokens.
1911
+ MATH = ['*', '/', '%', '//', '%%'];
1912
+
1913
+ // Relational tokens that are negatable with `not` prefix.
1914
+ RELATION = ['IN', 'OF', 'INSTANCEOF'];
1915
+
1916
+ // Boolean tokens.
1917
+ BOOL = ['TRUE', 'FALSE'];
1918
+
1919
+ // Tokens which could legitimately be invoked or indexed. An opening
1920
+ // parentheses or bracket following these tokens will be recorded as the start
1921
+ // of a function invocation or indexing operation.
1922
+ CALLABLE = ['IDENTIFIER', 'PROPERTY', ')', ']', '?', '@', 'THIS', 'SUPER', 'DYNAMIC_IMPORT'];
1923
+
1924
+ INDEXABLE = CALLABLE.concat(['NUMBER', 'INFINITY', 'NAN', 'STRING', 'STRING_END', 'REGEX', 'REGEX_END', 'BOOL', 'NULL', 'UNDEFINED', '}', '::']);
1925
+
1926
+ // Tokens which can be the left-hand side of a less-than comparison, i.e. `a<b`.
1927
+ COMPARABLE_LEFT_SIDE = ['IDENTIFIER', ')', ']', 'NUMBER'];
1928
+
1929
+ // Tokens which a regular expression will never immediately follow (except spaced
1930
+ // CALLABLEs in some cases), but which a division operator can.
1931
+
1932
+ // See: http://www-archive.mozilla.org/js/language/js20-2002-04/rationale/syntax.html#regular-expressions
1933
+ NOT_REGEX = INDEXABLE.concat(['++', '--']);
1934
+
1935
+ // Tokens that, when immediately preceding a `WHEN`, indicate that the `WHEN`
1936
+ // occurs at the start of a line. We disambiguate these from trailing whens to
1937
+ // avoid an ambiguity in the grammar.
1938
+ LINE_BREAK = ['INDENT', 'OUTDENT', 'TERMINATOR'];
1939
+
1940
+ // Additional indent in front of these is ignored.
1941
+ INDENTABLE_CLOSERS = [')', '}', ']'];
1942
+
1943
+ }).call(this);