UNPKG

30.5 kBJavaScriptView Raw
1// Generated by CoffeeScript 1.6.0
2(function() {
3 var BOM, BOOL, CALLABLE, CODE, COFFEE_ALIASES, COFFEE_ALIAS_MAP, COFFEE_KEYWORDS, COMMENT, COMPARE, COMPOUND_ASSIGN, HEREDOC, HEREDOC_ILLEGAL, HEREDOC_INDENT, HEREGEX, HEREGEX_OMIT, IDENTIFIER, INDEXABLE, INVERSES, JSTOKEN, JS_FORBIDDEN, JS_KEYWORDS, LINE_BREAK, LINE_CONTINUER, LITERATE, LOGIC, Lexer, MATH, MULTILINER, MULTI_DENT, NOT_REGEX, NOT_SPACED_REGEX, NUMBER, OPERATOR, REGEX, RELATION, RESERVED, Rewriter, SHIFT, SIMPLESTR, STRICT_PROSCRIBED, TRAILING_SPACES, UNARY, WHITESPACE, compact, count, key, last, locationDataToString, starts, _ref, _ref1,
4 __indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; };
5
6 _ref = require('./rewriter'), Rewriter = _ref.Rewriter, INVERSES = _ref.INVERSES;
7
8 _ref1 = require('./helpers'), count = _ref1.count, starts = _ref1.starts, compact = _ref1.compact, last = _ref1.last, locationDataToString = _ref1.locationDataToString;
9
10 exports.Lexer = Lexer = (function() {
11
12 function Lexer() {}
13
14 Lexer.prototype.tokenize = function(code, opts) {
15 var consumed, i, tag, _ref2;
16 if (opts == null) {
17 opts = {};
18 }
19 this.literate = opts.literate;
20 this.indent = 0;
21 this.indebt = 0;
22 this.outdebt = 0;
23 this.indents = [];
24 this.ends = [];
25 this.tokens = [];
26 this.chunkLine = opts.line || 0;
27 this.chunkColumn = opts.column || 0;
28 code = this.clean(code);
29 i = 0;
30 while (this.chunk = code.slice(i)) {
31 consumed = this.identifierToken() || this.commentToken() || this.whitespaceToken() || this.lineToken() || this.heredocToken() || this.stringToken() || this.numberToken() || this.regexToken() || this.jsToken() || this.literalToken();
32 _ref2 = this.getLineAndColumnFromChunk(consumed), this.chunkLine = _ref2[0], this.chunkColumn = _ref2[1];
33 i += consumed;
34 }
35 this.closeIndentation();
36 if (tag = this.ends.pop()) {
37 this.error("missing " + tag);
38 }
39 if (opts.rewrite === false) {
40 return this.tokens;
41 }
42 return (new Rewriter).rewrite(this.tokens);
43 };
44
45 Lexer.prototype.clean = function(code) {
46 var line, lines, match;
47 if (code.charCodeAt(0) === BOM) {
48 code = code.slice(1);
49 }
50 code = code.replace(/\r/g, '').replace(TRAILING_SPACES, '');
51 if (WHITESPACE.test(code)) {
52 code = "\n" + code;
53 this.chunkLine--;
54 }
55 if (this.literate) {
56 lines = (function() {
57 var _i, _len, _ref2, _results;
58 _ref2 = code.split('\n');
59 _results = [];
60 for (_i = 0, _len = _ref2.length; _i < _len; _i++) {
61 line = _ref2[_i];
62 if (match = LITERATE.exec(line)) {
63 _results.push(line.slice(match[0].length));
64 } else {
65 _results.push('# ' + line);
66 }
67 }
68 return _results;
69 })();
70 code = lines.join('\n');
71 }
72 return code;
73 };
74
75 Lexer.prototype.identifierToken = function() {
76 var colon, colonOffset, forcedIdentifier, id, idLength, input, match, poppedToken, prev, tag, tagToken, _ref2, _ref3, _ref4;
77 if (!(match = IDENTIFIER.exec(this.chunk))) {
78 return 0;
79 }
80 input = match[0], id = match[1], colon = match[2];
81 idLength = id.length;
82 poppedToken = void 0;
83 if (id === 'own' && this.tag() === 'FOR') {
84 this.token('OWN', id);
85 return id.length;
86 }
87 forcedIdentifier = colon || (prev = last(this.tokens)) && (((_ref2 = prev[0]) === '.' || _ref2 === '?.' || _ref2 === '::' || _ref2 === '?::') || !prev.spaced && prev[0] === '@');
88 tag = 'IDENTIFIER';
89 if (!forcedIdentifier && (__indexOf.call(JS_KEYWORDS, id) >= 0 || __indexOf.call(COFFEE_KEYWORDS, id) >= 0)) {
90 tag = id.toUpperCase();
91 if (tag === 'WHEN' && (_ref3 = this.tag(), __indexOf.call(LINE_BREAK, _ref3) >= 0)) {
92 tag = 'LEADING_WHEN';
93 } else if (tag === 'FOR') {
94 this.seenFor = true;
95 } else if (tag === 'UNLESS') {
96 tag = 'IF';
97 } else if (__indexOf.call(UNARY, tag) >= 0) {
98 tag = 'UNARY';
99 } else if (__indexOf.call(RELATION, tag) >= 0) {
100 if (tag !== 'INSTANCEOF' && this.seenFor) {
101 tag = 'FOR' + tag;
102 this.seenFor = false;
103 } else {
104 tag = 'RELATION';
105 if (this.value() === '!') {
106 poppedToken = this.tokens.pop();
107 id = '!' + id;
108 }
109 }
110 }
111 }
112 if (__indexOf.call(JS_FORBIDDEN, id) >= 0) {
113 if (forcedIdentifier) {
114 tag = 'IDENTIFIER';
115 id = new String(id);
116 id.reserved = true;
117 } else if (__indexOf.call(RESERVED, id) >= 0) {
118 this.error("reserved word \"" + id + "\"");
119 }
120 }
121 if (!forcedIdentifier) {
122 if (__indexOf.call(COFFEE_ALIASES, id) >= 0) {
123 id = COFFEE_ALIAS_MAP[id];
124 }
125 tag = (function() {
126 switch (id) {
127 case '!':
128 return 'UNARY';
129 case '==':
130 case '!=':
131 return 'COMPARE';
132 case '&&':
133 case '||':
134 return 'LOGIC';
135 case 'true':
136 case 'false':
137 return 'BOOL';
138 case 'break':
139 case 'continue':
140 return 'STATEMENT';
141 default:
142 return tag;
143 }
144 })();
145 }
146 tagToken = this.token(tag, id, 0, idLength);
147 if (poppedToken) {
148 _ref4 = [poppedToken[2].first_line, poppedToken[2].first_column], tagToken[2].first_line = _ref4[0], tagToken[2].first_column = _ref4[1];
149 }
150 if (colon) {
151 colonOffset = input.lastIndexOf(':');
152 this.token(':', ':', colonOffset, colon.length);
153 }
154 return input.length;
155 };
156
157 Lexer.prototype.numberToken = function() {
158 var binaryLiteral, lexedLength, match, number, octalLiteral;
159 if (!(match = NUMBER.exec(this.chunk))) {
160 return 0;
161 }
162 number = match[0];
163 if (/^0[BOX]/.test(number)) {
164 this.error("radix prefix '" + number + "' must be lowercase");
165 } else if (/E/.test(number) && !/^0x/.test(number)) {
166 this.error("exponential notation '" + number + "' must be indicated with a lowercase 'e'");
167 } else if (/^0\d*[89]/.test(number)) {
168 this.error("decimal literal '" + number + "' must not be prefixed with '0'");
169 } else if (/^0\d+/.test(number)) {
170 this.error("octal literal '" + number + "' must be prefixed with '0o'");
171 }
172 lexedLength = number.length;
173 if (octalLiteral = /^0o([0-7]+)/.exec(number)) {
174 number = '0x' + (parseInt(octalLiteral[1], 8)).toString(16);
175 }
176 if (binaryLiteral = /^0b([01]+)/.exec(number)) {
177 number = '0x' + (parseInt(binaryLiteral[1], 2)).toString(16);
178 }
179 this.token('NUMBER', number, 0, lexedLength);
180 return lexedLength;
181 };
182
183 Lexer.prototype.stringToken = function() {
184 var match, octalEsc, string;
185 switch (this.chunk.charAt(0)) {
186 case "'":
187 if (!(match = SIMPLESTR.exec(this.chunk))) {
188 return 0;
189 }
190 string = match[0];
191 this.token('STRING', string.replace(MULTILINER, '\\\n'), 0, string.length);
192 break;
193 case '"':
194 if (!(string = this.balancedString(this.chunk, '"'))) {
195 return 0;
196 }
197 if (0 < string.indexOf('#{', 1)) {
198 this.interpolateString(string.slice(1, -1), {
199 strOffset: 1,
200 lexedLength: string.length
201 });
202 } else {
203 this.token('STRING', this.escapeLines(string, 0, string.length));
204 }
205 break;
206 default:
207 return 0;
208 }
209 if (octalEsc = /^(?:\\.|[^\\])*\\(?:0[0-7]|[1-7])/.test(string)) {
210 this.error("octal escape sequences " + string + " are not allowed");
211 }
212 return string.length;
213 };
214
215 Lexer.prototype.heredocToken = function() {
216 var doc, heredoc, match, quote;
217 if (!(match = HEREDOC.exec(this.chunk))) {
218 return 0;
219 }
220 heredoc = match[0];
221 quote = heredoc.charAt(0);
222 doc = this.sanitizeHeredoc(match[2], {
223 quote: quote,
224 indent: null
225 });
226 if (quote === '"' && 0 <= doc.indexOf('#{')) {
227 this.interpolateString(doc, {
228 heredoc: true,
229 strOffset: 3,
230 lexedLength: heredoc.length
231 });
232 } else {
233 this.token('STRING', this.makeString(doc, quote, true), 0, heredoc.length);
234 }
235 return heredoc.length;
236 };
237
238 Lexer.prototype.commentToken = function() {
239 var comment, here, match;
240 if (!(match = this.chunk.match(COMMENT))) {
241 return 0;
242 }
243 comment = match[0], here = match[1];
244 if (here) {
245 this.token('HERECOMMENT', this.sanitizeHeredoc(here, {
246 herecomment: true,
247 indent: Array(this.indent + 1).join(' ')
248 }), 0, comment.length);
249 }
250 return comment.length;
251 };
252
253 Lexer.prototype.jsToken = function() {
254 var match, script;
255 if (!(this.chunk.charAt(0) === '`' && (match = JSTOKEN.exec(this.chunk)))) {
256 return 0;
257 }
258 this.token('JS', (script = match[0]).slice(1, -1), 0, script.length);
259 return script.length;
260 };
261
262 Lexer.prototype.regexToken = function() {
263 var flags, length, match, prev, regex, _ref2, _ref3;
264 if (this.chunk.charAt(0) !== '/') {
265 return 0;
266 }
267 if (match = HEREGEX.exec(this.chunk)) {
268 length = this.heregexToken(match);
269 return length;
270 }
271 prev = last(this.tokens);
272 if (prev && (_ref2 = prev[0], __indexOf.call((prev.spaced ? NOT_REGEX : NOT_SPACED_REGEX), _ref2) >= 0)) {
273 return 0;
274 }
275 if (!(match = REGEX.exec(this.chunk))) {
276 return 0;
277 }
278 _ref3 = match, match = _ref3[0], regex = _ref3[1], flags = _ref3[2];
279 if (regex.slice(0, 2) === '/*') {
280 this.error('regular expressions cannot begin with `*`');
281 }
282 if (regex === '//') {
283 regex = '/(?:)/';
284 }
285 this.token('REGEX', "" + regex + flags, 0, match.length);
286 return match.length;
287 };
288
289 Lexer.prototype.heregexToken = function(match) {
290 var body, flags, flagsOffset, heregex, plusToken, prev, re, tag, token, tokens, value, _i, _len, _ref2, _ref3, _ref4;
291 heregex = match[0], body = match[1], flags = match[2];
292 if (0 > body.indexOf('#{')) {
293 re = body.replace(HEREGEX_OMIT, '').replace(/\//g, '\\/');
294 if (re.match(/^\*/)) {
295 this.error('regular expressions cannot begin with `*`');
296 }
297 this.token('REGEX', "/" + (re || '(?:)') + "/" + flags, 0, heregex.length);
298 return heregex.length;
299 }
300 this.token('IDENTIFIER', 'RegExp', 0, 0);
301 this.token('CALL_START', '(', 0, 0);
302 tokens = [];
303 _ref2 = this.interpolateString(body, {
304 regex: true
305 });
306 for (_i = 0, _len = _ref2.length; _i < _len; _i++) {
307 token = _ref2[_i];
308 tag = token[0], value = token[1];
309 if (tag === 'TOKENS') {
310 tokens.push.apply(tokens, value);
311 } else if (tag === 'NEOSTRING') {
312 if (!(value = value.replace(HEREGEX_OMIT, ''))) {
313 continue;
314 }
315 value = value.replace(/\\/g, '\\\\');
316 token[0] = 'STRING';
317 token[1] = this.makeString(value, '"', true);
318 tokens.push(token);
319 } else {
320 this.error("Unexpected " + tag);
321 }
322 prev = last(this.tokens);
323 plusToken = ['+', '+'];
324 plusToken[2] = prev[2];
325 tokens.push(plusToken);
326 }
327 tokens.pop();
328 if (((_ref3 = tokens[0]) != null ? _ref3[0] : void 0) !== 'STRING') {
329 this.token('STRING', '""', 0, 0);
330 this.token('+', '+', 0, 0);
331 }
332 (_ref4 = this.tokens).push.apply(_ref4, tokens);
333 if (flags) {
334 flagsOffset = heregex.lastIndexOf(flags);
335 this.token(',', ',', flagsOffset, 0);
336 this.token('STRING', '"' + flags + '"', flagsOffset, flags.length);
337 }
338 this.token(')', ')', heregex.length - 1, 0);
339 return heregex.length;
340 };
341
342 Lexer.prototype.lineToken = function() {
343 var diff, indent, match, noNewlines, size;
344 if (!(match = MULTI_DENT.exec(this.chunk))) {
345 return 0;
346 }
347 indent = match[0];
348 this.seenFor = false;
349 size = indent.length - 1 - indent.lastIndexOf('\n');
350 noNewlines = this.unfinished();
351 if (size - this.indebt === this.indent) {
352 if (noNewlines) {
353 this.suppressNewlines();
354 } else {
355 this.newlineToken(0);
356 }
357 return indent.length;
358 }
359 if (size > this.indent) {
360 if (noNewlines) {
361 this.indebt = size - this.indent;
362 this.suppressNewlines();
363 return indent.length;
364 }
365 diff = size - this.indent + this.outdebt;
366 this.token('INDENT', diff, 0, indent.length);
367 this.indents.push(diff);
368 this.ends.push('OUTDENT');
369 this.outdebt = this.indebt = 0;
370 } else {
371 this.indebt = 0;
372 this.outdentToken(this.indent - size, noNewlines, indent.length);
373 }
374 this.indent = size;
375 return indent.length;
376 };
377
378 Lexer.prototype.outdentToken = function(moveOut, noNewlines, outdentLength) {
379 var dent, len;
380 while (moveOut > 0) {
381 len = this.indents.length - 1;
382 if (this.indents[len] === void 0) {
383 moveOut = 0;
384 } else if (this.indents[len] === this.outdebt) {
385 moveOut -= this.outdebt;
386 this.outdebt = 0;
387 } else if (this.indents[len] < this.outdebt) {
388 this.outdebt -= this.indents[len];
389 moveOut -= this.indents[len];
390 } else {
391 dent = this.indents.pop() + this.outdebt;
392 moveOut -= dent;
393 this.outdebt = 0;
394 this.pair('OUTDENT');
395 this.token('OUTDENT', dent, 0, outdentLength);
396 }
397 }
398 if (dent) {
399 this.outdebt -= moveOut;
400 }
401 while (this.value() === ';') {
402 this.tokens.pop();
403 }
404 if (!(this.tag() === 'TERMINATOR' || noNewlines)) {
405 this.token('TERMINATOR', '\n', outdentLength, 0);
406 }
407 return this;
408 };
409
410 Lexer.prototype.whitespaceToken = function() {
411 var match, nline, prev;
412 if (!((match = WHITESPACE.exec(this.chunk)) || (nline = this.chunk.charAt(0) === '\n'))) {
413 return 0;
414 }
415 prev = last(this.tokens);
416 if (prev) {
417 prev[match ? 'spaced' : 'newLine'] = true;
418 }
419 if (match) {
420 return match[0].length;
421 } else {
422 return 0;
423 }
424 };
425
426 Lexer.prototype.newlineToken = function(offset) {
427 while (this.value() === ';') {
428 this.tokens.pop();
429 }
430 if (this.tag() !== 'TERMINATOR') {
431 this.token('TERMINATOR', '\n', offset, 0);
432 }
433 return this;
434 };
435
436 Lexer.prototype.suppressNewlines = function() {
437 if (this.value() === '\\') {
438 this.tokens.pop();
439 }
440 return this;
441 };
442
443 Lexer.prototype.literalToken = function() {
444 var match, prev, tag, value, _ref2, _ref3, _ref4, _ref5;
445 if (match = OPERATOR.exec(this.chunk)) {
446 value = match[0];
447 if (CODE.test(value)) {
448 this.tagParameters();
449 }
450 } else {
451 value = this.chunk.charAt(0);
452 }
453 tag = value;
454 prev = last(this.tokens);
455 if (value === '=' && prev) {
456 if (!prev[1].reserved && (_ref2 = prev[1], __indexOf.call(JS_FORBIDDEN, _ref2) >= 0)) {
457 this.error("reserved word \"" + (this.value()) + "\" can't be assigned");
458 }
459 if ((_ref3 = prev[1]) === '||' || _ref3 === '&&') {
460 prev[0] = 'COMPOUND_ASSIGN';
461 prev[1] += '=';
462 return value.length;
463 }
464 }
465 if (value === ';') {
466 this.seenFor = false;
467 tag = 'TERMINATOR';
468 } else if (__indexOf.call(MATH, value) >= 0) {
469 tag = 'MATH';
470 } else if (__indexOf.call(COMPARE, value) >= 0) {
471 tag = 'COMPARE';
472 } else if (__indexOf.call(COMPOUND_ASSIGN, value) >= 0) {
473 tag = 'COMPOUND_ASSIGN';
474 } else if (__indexOf.call(UNARY, value) >= 0) {
475 tag = 'UNARY';
476 } else if (__indexOf.call(SHIFT, value) >= 0) {
477 tag = 'SHIFT';
478 } else if (__indexOf.call(LOGIC, value) >= 0 || value === '?' && (prev != null ? prev.spaced : void 0)) {
479 tag = 'LOGIC';
480 } else if (prev && !prev.spaced) {
481 if (value === '(' && (_ref4 = prev[0], __indexOf.call(CALLABLE, _ref4) >= 0)) {
482 if (prev[0] === '?') {
483 prev[0] = 'FUNC_EXIST';
484 }
485 tag = 'CALL_START';
486 } else if (value === '[' && (_ref5 = prev[0], __indexOf.call(INDEXABLE, _ref5) >= 0)) {
487 tag = 'INDEX_START';
488 switch (prev[0]) {
489 case '?':
490 prev[0] = 'INDEX_SOAK';
491 }
492 }
493 }
494 switch (value) {
495 case '(':
496 case '{':
497 case '[':
498 this.ends.push(INVERSES[value]);
499 break;
500 case ')':
501 case '}':
502 case ']':
503 this.pair(value);
504 }
505 this.token(tag, value);
506 return value.length;
507 };
508
509 Lexer.prototype.sanitizeHeredoc = function(doc, options) {
510 var attempt, herecomment, indent, match, _ref2;
511 indent = options.indent, herecomment = options.herecomment;
512 if (herecomment) {
513 if (HEREDOC_ILLEGAL.test(doc)) {
514 this.error("block comment cannot contain \"*/\", starting");
515 }
516 if (doc.indexOf('\n') < 0) {
517 return doc;
518 }
519 } else {
520 while (match = HEREDOC_INDENT.exec(doc)) {
521 attempt = match[1];
522 if (indent === null || (0 < (_ref2 = attempt.length) && _ref2 < indent.length)) {
523 indent = attempt;
524 }
525 }
526 }
527 if (indent) {
528 doc = doc.replace(RegExp("\\n" + indent, "g"), '\n');
529 }
530 if (this.literate) {
531 doc = doc.replace(/\n# \n/g, '\n\n');
532 }
533 if (!herecomment) {
534 doc = doc.replace(/^\n/, '');
535 }
536 return doc;
537 };
538
539 Lexer.prototype.tagParameters = function() {
540 var i, stack, tok, tokens;
541 if (this.tag() !== ')') {
542 return this;
543 }
544 stack = [];
545 tokens = this.tokens;
546 i = tokens.length;
547 tokens[--i][0] = 'PARAM_END';
548 while (tok = tokens[--i]) {
549 switch (tok[0]) {
550 case ')':
551 stack.push(tok);
552 break;
553 case '(':
554 case 'CALL_START':
555 if (stack.length) {
556 stack.pop();
557 } else if (tok[0] === '(') {
558 tok[0] = 'PARAM_START';
559 return this;
560 } else {
561 return this;
562 }
563 }
564 }
565 return this;
566 };
567
568 Lexer.prototype.closeIndentation = function() {
569 return this.outdentToken(this.indent);
570 };
571
572 Lexer.prototype.balancedString = function(str, end) {
573 var continueCount, i, letter, match, prev, stack, _i, _ref2;
574 continueCount = 0;
575 stack = [end];
576 for (i = _i = 1, _ref2 = str.length; 1 <= _ref2 ? _i < _ref2 : _i > _ref2; i = 1 <= _ref2 ? ++_i : --_i) {
577 if (continueCount) {
578 --continueCount;
579 continue;
580 }
581 switch (letter = str.charAt(i)) {
582 case '\\':
583 ++continueCount;
584 continue;
585 case end:
586 stack.pop();
587 if (!stack.length) {
588 return str.slice(0, +i + 1 || 9e9);
589 }
590 end = stack[stack.length - 1];
591 continue;
592 }
593 if (end === '}' && (letter === '"' || letter === "'")) {
594 stack.push(end = letter);
595 } else if (end === '}' && letter === '/' && (match = HEREGEX.exec(str.slice(i)) || REGEX.exec(str.slice(i)))) {
596 continueCount += match[0].length - 1;
597 } else if (end === '}' && letter === '{') {
598 stack.push(end = '}');
599 } else if (end === '"' && prev === '#' && letter === '{') {
600 stack.push(end = '}');
601 }
602 prev = letter;
603 }
604 return this.error("missing " + (stack.pop()) + ", starting");
605 };
606
607 Lexer.prototype.interpolateString = function(str, options) {
608 var column, expr, heredoc, i, inner, interpolated, len, letter, lexedLength, line, locationToken, nested, offsetInChunk, pi, plusToken, popped, regex, strOffset, tag, token, tokens, value, _i, _len, _ref2, _ref3, _ref4;
609 if (options == null) {
610 options = {};
611 }
612 heredoc = options.heredoc, regex = options.regex, offsetInChunk = options.offsetInChunk, strOffset = options.strOffset, lexedLength = options.lexedLength;
613 offsetInChunk = offsetInChunk || 0;
614 strOffset = strOffset || 0;
615 lexedLength = lexedLength || str.length;
616 if (heredoc && str.length > 0 && str[0] === '\n') {
617 str = str.slice(1);
618 strOffset++;
619 }
620 tokens = [];
621 pi = 0;
622 i = -1;
623 while (letter = str.charAt(i += 1)) {
624 if (letter === '\\') {
625 i += 1;
626 continue;
627 }
628 if (!(letter === '#' && str.charAt(i + 1) === '{' && (expr = this.balancedString(str.slice(i + 1), '}')))) {
629 continue;
630 }
631 if (pi < i) {
632 tokens.push(this.makeToken('NEOSTRING', str.slice(pi, i), strOffset + pi));
633 }
634 inner = expr.slice(1, -1);
635 if (inner.length) {
636 _ref2 = this.getLineAndColumnFromChunk(strOffset + i + 1), line = _ref2[0], column = _ref2[1];
637 nested = new Lexer().tokenize(inner, {
638 line: line,
639 column: column,
640 rewrite: false
641 });
642 popped = nested.pop();
643 if (((_ref3 = nested[0]) != null ? _ref3[0] : void 0) === 'TERMINATOR') {
644 popped = nested.shift();
645 }
646 if (len = nested.length) {
647 if (len > 1) {
648 nested.unshift(this.makeToken('(', '(', strOffset + i + 1, 0));
649 nested.push(this.makeToken(')', ')', strOffset + i + 1 + inner.length, 0));
650 }
651 tokens.push(['TOKENS', nested]);
652 }
653 }
654 i += expr.length;
655 pi = i + 1;
656 }
657 if ((i > pi && pi < str.length)) {
658 tokens.push(this.makeToken('NEOSTRING', str.slice(pi), strOffset + pi));
659 }
660 if (regex) {
661 return tokens;
662 }
663 if (!tokens.length) {
664 return this.token('STRING', '""', offsetInChunk, lexedLength);
665 }
666 if (tokens[0][0] !== 'NEOSTRING') {
667 tokens.unshift(this.makeToken('NEOSTRING', '', offsetInChunk));
668 }
669 if (interpolated = tokens.length > 1) {
670 this.token('(', '(', offsetInChunk, 0);
671 }
672 for (i = _i = 0, _len = tokens.length; _i < _len; i = ++_i) {
673 token = tokens[i];
674 tag = token[0], value = token[1];
675 if (i) {
676 if (i) {
677 plusToken = this.token('+', '+');
678 }
679 locationToken = tag === 'TOKENS' ? value[0] : token;
680 plusToken[2] = {
681 first_line: locationToken[2].first_line,
682 first_column: locationToken[2].first_column,
683 last_line: locationToken[2].first_line,
684 last_column: locationToken[2].first_column
685 };
686 }
687 if (tag === 'TOKENS') {
688 (_ref4 = this.tokens).push.apply(_ref4, value);
689 } else if (tag === 'NEOSTRING') {
690 token[0] = 'STRING';
691 token[1] = this.makeString(value, '"', heredoc);
692 this.tokens.push(token);
693 } else {
694 this.error("Unexpected " + tag);
695 }
696 }
697 if (interpolated) {
698 this.token(')', ')', offsetInChunk + lexedLength, 0);
699 }
700 return tokens;
701 };
702
703 Lexer.prototype.pair = function(tag) {
704 var size, wanted;
705 if (tag !== (wanted = last(this.ends))) {
706 if ('OUTDENT' !== wanted) {
707 this.error("unmatched " + tag);
708 }
709 this.indent -= size = last(this.indents);
710 this.outdentToken(size, true);
711 return this.pair(tag);
712 }
713 return this.ends.pop();
714 };
715
716 Lexer.prototype.getLineAndColumnFromChunk = function(offset) {
717 var column, lineCount, lines, string;
718 if (offset === 0) {
719 return [this.chunkLine, this.chunkColumn];
720 }
721 if (offset >= this.chunk.length) {
722 string = this.chunk;
723 } else {
724 string = this.chunk.slice(0, +(offset - 1) + 1 || 9e9);
725 }
726 lineCount = count(string, '\n');
727 column = this.chunkColumn;
728 if (lineCount > 0) {
729 lines = string.split('\n');
730 column = (last(lines)).length;
731 } else {
732 column += string.length;
733 }
734 return [this.chunkLine + lineCount, column];
735 };
736
737 Lexer.prototype.makeToken = function(tag, value, offsetInChunk, length) {
738 var lastCharacter, locationData, token, _ref2, _ref3;
739 if (offsetInChunk == null) {
740 offsetInChunk = 0;
741 }
742 if (length == null) {
743 length = value.length;
744 }
745 locationData = {};
746 _ref2 = this.getLineAndColumnFromChunk(offsetInChunk), locationData.first_line = _ref2[0], locationData.first_column = _ref2[1];
747 lastCharacter = Math.max(0, length - 1);
748 _ref3 = this.getLineAndColumnFromChunk(offsetInChunk + (length - 1)), locationData.last_line = _ref3[0], locationData.last_column = _ref3[1];
749 token = [tag, value, locationData];
750 return token;
751 };
752
753 Lexer.prototype.token = function(tag, value, offsetInChunk, length) {
754 var token;
755 token = this.makeToken(tag, value, offsetInChunk, length);
756 this.tokens.push(token);
757 return token;
758 };
759
760 Lexer.prototype.tag = function(index, tag) {
761 var tok;
762 return (tok = last(this.tokens, index)) && (tag ? tok[0] = tag : tok[0]);
763 };
764
765 Lexer.prototype.value = function(index, val) {
766 var tok;
767 return (tok = last(this.tokens, index)) && (val ? tok[1] = val : tok[1]);
768 };
769
770 Lexer.prototype.unfinished = function() {
771 var _ref2;
772 return LINE_CONTINUER.test(this.chunk) || ((_ref2 = this.tag()) === '\\' || _ref2 === '.' || _ref2 === '?.' || _ref2 === '?::' || _ref2 === 'UNARY' || _ref2 === 'MATH' || _ref2 === '+' || _ref2 === '-' || _ref2 === 'SHIFT' || _ref2 === 'RELATION' || _ref2 === 'COMPARE' || _ref2 === 'LOGIC' || _ref2 === 'THROW' || _ref2 === 'EXTENDS');
773 };
774
775 Lexer.prototype.escapeLines = function(str, heredoc) {
776 return str.replace(MULTILINER, heredoc ? '\\n' : '');
777 };
778
779 Lexer.prototype.makeString = function(body, quote, heredoc) {
780 if (!body) {
781 return quote + quote;
782 }
783 body = body.replace(/\\([\s\S])/g, function(match, contents) {
784 if (contents === '\n' || contents === quote) {
785 return contents;
786 } else {
787 return match;
788 }
789 });
790 body = body.replace(RegExp("" + quote, "g"), '\\$&');
791 return quote + this.escapeLines(body, heredoc) + quote;
792 };
793
794 Lexer.prototype.error = function(message) {
795 throw SyntaxError("" + message + " on line " + (this.chunkLine + 1));
796 };
797
798 return Lexer;
799
800 })();
801
802 JS_KEYWORDS = ['true', 'false', 'null', 'this', 'new', 'delete', 'typeof', 'in', 'instanceof', 'return', 'throw', 'break', 'continue', 'debugger', 'if', 'else', 'switch', 'for', 'while', 'do', 'try', 'catch', 'finally', 'class', 'extends', 'super'];
803
804 COFFEE_KEYWORDS = ['undefined', 'then', 'unless', 'until', 'loop', 'of', 'by', 'when'];
805
806 COFFEE_ALIAS_MAP = {
807 and: '&&',
808 or: '||',
809 is: '==',
810 isnt: '!=',
811 not: '!',
812 yes: 'true',
813 no: 'false',
814 on: 'true',
815 off: 'false'
816 };
817
818 COFFEE_ALIASES = (function() {
819 var _results;
820 _results = [];
821 for (key in COFFEE_ALIAS_MAP) {
822 _results.push(key);
823 }
824 return _results;
825 })();
826
827 COFFEE_KEYWORDS = COFFEE_KEYWORDS.concat(COFFEE_ALIASES);
828
829 RESERVED = ['case', 'default', 'function', 'var', 'void', 'with', 'const', 'let', 'enum', 'export', 'import', 'native', '__hasProp', '__extends', '__slice', '__bind', '__indexOf', 'implements', 'interface', 'package', 'private', 'protected', 'public', 'static', 'yield'];
830
831 STRICT_PROSCRIBED = ['arguments', 'eval'];
832
833 JS_FORBIDDEN = JS_KEYWORDS.concat(RESERVED).concat(STRICT_PROSCRIBED);
834
835 exports.RESERVED = RESERVED.concat(JS_KEYWORDS).concat(COFFEE_KEYWORDS).concat(STRICT_PROSCRIBED);
836
837 exports.STRICT_PROSCRIBED = STRICT_PROSCRIBED;
838
839 BOM = 65279;
840
841 IDENTIFIER = /^([$A-Za-z_\x7f-\uffff][$\w\x7f-\uffff]*)([^\n\S]*:(?!:))?/;
842
843 NUMBER = /^0b[01]+|^0o[0-7]+|^0x[\da-f]+|^\d*\.?\d+(?:e[+-]?\d+)?/i;
844
845 HEREDOC = /^("""|''')([\s\S]*?)(?:\n[^\n\S]*)?\1/;
846
847 OPERATOR = /^(?:[-=]>|[-+*\/%<>&|^!?=]=|>>>=?|([-+:])\1|([&|<>])\2=?|\?(\.|::)|\.{2,3})/;
848
849 WHITESPACE = /^[^\n\S]+/;
850
851 COMMENT = /^###([^#][\s\S]*?)(?:###[^\n\S]*|(?:###)$)|^(?:\s*#(?!##[^#]).*)+/;
852
853 LITERATE = /^([ ]{4}|\t)/;
854
855 CODE = /^[-=]>/;
856
857 MULTI_DENT = /^(?:\n[^\n\S]*)+/;
858
859 SIMPLESTR = /^'[^\\']*(?:\\.[^\\']*)*'/;
860
861 JSTOKEN = /^`[^\\`]*(?:\\.[^\\`]*)*`/;
862
863 REGEX = /^(\/(?![\s=])[^[\/\n\\]*(?:(?:\\[\s\S]|\[[^\]\n\\]*(?:\\[\s\S][^\]\n\\]*)*])[^[\/\n\\]*)*\/)([imgy]{0,4})(?!\w)/;
864
865 HEREGEX = /^\/{3}([\s\S]+?)\/{3}([imgy]{0,4})(?!\w)/;
866
867 HEREGEX_OMIT = /\s+(?:#.*)?/g;
868
869 MULTILINER = /\n/g;
870
871 HEREDOC_INDENT = /\n+([^\n\S]*)/g;
872
873 HEREDOC_ILLEGAL = /\*\//;
874
875 LINE_CONTINUER = /^\s*(?:,|\??\.(?![.\d])|::)/;
876
877 TRAILING_SPACES = /\s+$/;
878
879 COMPOUND_ASSIGN = ['-=', '+=', '/=', '*=', '%=', '||=', '&&=', '?=', '<<=', '>>=', '>>>=', '&=', '^=', '|='];
880
881 UNARY = ['!', '~', 'NEW', 'TYPEOF', 'DELETE', 'DO'];
882
883 LOGIC = ['&&', '||', '&', '|', '^'];
884
885 SHIFT = ['<<', '>>', '>>>'];
886
887 COMPARE = ['==', '!=', '<', '>', '<=', '>='];
888
889 MATH = ['*', '/', '%'];
890
891 RELATION = ['IN', 'OF', 'INSTANCEOF'];
892
893 BOOL = ['TRUE', 'FALSE'];
894
895 NOT_REGEX = ['NUMBER', 'REGEX', 'BOOL', 'NULL', 'UNDEFINED', '++', '--', ']'];
896
897 NOT_SPACED_REGEX = NOT_REGEX.concat(')', '}', 'THIS', 'IDENTIFIER', 'STRING');
898
899 CALLABLE = ['IDENTIFIER', 'STRING', 'REGEX', ')', ']', '}', '?', '::', '@', 'THIS', 'SUPER'];
900
901 INDEXABLE = CALLABLE.concat('NUMBER', 'BOOL', 'NULL', 'UNDEFINED');
902
903 LINE_BREAK = ['INDENT', 'OUTDENT', 'TERMINATOR'];
904
905}).call(this);