Brak opisu

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