coco
Version:
Unfancy CoffeeScript
1,516 lines • 42.5 kB
JavaScript
var string, TABS, unlines, enlines, enslash, reslash, camelize, character, KEYWORDS_SHARED, KEYWORDS_UNUSED, KEYWORDS, ID, SYMBOL, SPACE, MULTIDENT, SIMPLESTR, BSTOKEN, JSTOKEN, NUMBER, NUMBER_OMIT, REGEX, HEREGEX_OMIT, LASTDENT, INLINEDENT, NONASCII, OPENERS, CLOSERS, INVERSES, CHAIN, ARG, BLOCK_USERS, slice$ = [].slice;
exports.lex = function(code, options){
return (clone$(exports)).tokenize(code || '', options || {});
};
exports.rewrite = function(it){
var ref$;
it || (it = this.tokens);
addImplicitIndentation(it);
rewriteBlockless(it);
addImplicitParentheses(it);
addImplicitBraces(it);
expandLiterals(it);
if (((ref$ = it[0]) != null ? ref$[0] : void 8) === 'NEWLINE') {
it.shift();
}
return it;
};
exports.tokenize = function(code, o){
var i, c, that;
this.inter || (code = code.replace(/[\r\u2028\u2029\uFEFF]/g, ''));
code = '\n' + code;
this.tokens = [this.last = ['NEWLINE', '\n', 0]];
this.line = ~-o.line;
this.dents = [];
this.closes = [];
this.parens = [];
this.flags = [];
i = 0;
while (c = code.charAt(i)) {
switch (c) {
case ' ':
i += this.doSpace(code, i);
break;
case '\n':
i += this.doLine(code, i);
break;
case '\\':
i += this.doBackslash(code, i);
break;
case '\'':
case '"':
i += this.doString(code, i, c);
break;
case "0":
case "1":
case "2":
case "3":
case "4":
case "5":
case "6":
case "7":
case "8":
case "9":
i += this.doNumber(code, i);
break;
case '/':
switch (code.charAt(i + 1)) {
case '*':
i += this.doComment(code, i);
break;
case '/':
i += this.doHeregex(code, i);
break;
default:
i += this.doRegex(code, i) || this.doLiteral(code, i);
}
break;
case '`':
i += this.doJS(code, i);
break;
default:
i += this.doID(code, i) || this.doLiteral(code, i) || this.doSpace(code, i);
}
}
this.dedent(this.dent);
if (that = this.closes.pop()) {
this.carp("missing `" + that + "`");
}
if (this.inter) {
this.rest == null && this.carp('unterminated interpolation');
} else {
this.last.spaced = true;
this.newline();
}
o.raw || this.rewrite();
return this.tokens;
};
exports.dent = 0;
exports.doID = function(code, index){
var match, input, id, e, last, ref$, tag, ref1$, ref2$, ref3$, ref4$, that, ref5$;
input = (match = (ID.lastIndex = index, ID).exec(code))[0];
if (!input) {
return 0;
}
id = camelize(match[1]);
if (NONASCII.test(id)) {
try {
Function("var " + id);
} catch (e$) {
e = e$;
this.carp("invalid identifier \"" + id + "\"");
}
}
last = this.last;
if (match[2] || last[0] === 'DOT' || this.adi()) {
this.token('ID', of$(id, KEYWORDS) ? (ref$ = Object(id), ref$.reserved = true, ref$) : id);
if (match[2]) {
this.token(':', ':');
}
return input.length;
}
switch (id) {
case 'true':
case 'false':
case 'null':
case 'void':
case 'arguments':
case 'debugger':
tag = 'LITERAL';
break;
case 'new':
case 'do':
case 'typeof':
case 'delete':
tag = 'UNARY';
break;
case 'return':
case 'throw':
tag = 'HURL';
break;
case 'break':
case 'continue':
tag = 'JUMP';
break;
case 'this':
case 'eval':
case 'super':
return this.token('LITERAL', id, true).length;
case 'for':
id = [];
this.fset('for', true);
this.fset('to', false);
break;
case 'then':
this.fset('for', false);
this.fset('to', false);
break;
case 'catch':
case 'function':
id = '';
break;
case 'in':
case 'of':
if (this.fget('for')) {
this.fset('for', false);
if (id === 'of') {
this.fset('by', true);
id = '';
if (last[0] === 'ID' && ((ref1$ = (ref2$ = this.tokens)[ref2$.length - 2][0]) === ',' || ref1$ === ']' || ref1$ === '}')) {
id = this.tokens.pop()[1];
if ((ref3$ = this.tokens)[ref3$.length - 1][0] === ',') {
this.tokens.pop();
}
}
}
break;
}
// fallthrough
case 'instanceof':
if (last[1] === '!') {
id = this.tokens.pop()[1] + id;
}
tag = 'RELATION';
break;
case 'not':
if (last.alias && last[1] === '===') {
return last[1] = '!==', 3;
}
tag = 'UNARY';
id = '!';
break;
case 'and':
case 'or':
case 'is':
this.unline();
if (id === 'is') {
this.token('COMPARE', '===');
} else {
this.token('LOGIC', id === 'or' ? '||' : '&&');
}
this.last.alias = true;
return id.length;
case 'unless':
tag = 'IF';
break;
case 'until':
tag = 'WHILE';
break;
case 'import':
if (able(this.tokens)) {
id = '<<<';
break;
}
// fallthrough
case 'export':
case 'const':
case 'var':
tag = 'DECL';
break;
case 'let':
case 'own':
if (last[0] === 'FOR' && !of$(id, last[1])) {
last[1].push(id);
return 3;
}
// fallthrough
default:
if (of$(id, KEYWORDS_SHARED)) {
break;
}
if (of$(id, KEYWORDS_UNUSED)) {
this.carp("reserved word \"" + id + "\"");
}
if (!last[1] && ((ref4$ = last[0]) === 'CATCH' || ref4$ === 'FUNCTION' || ref4$ === 'LABEL')) {
last[1] = id;
last.spaced = false;
return input.length;
}
tag = 'ID';
switch (id) {
case 'all':
if (that = last[1] === '<<<' && '<' || last[1] === 'import' && 'All') {
last[1] += that;
return 3;
}
break;
case 'from':
this.forange() && (tag = 'FROM');
break;
case 'to':
case 'til':
this.forange() && this.tokens.push(['FROM', '', this.line], ['STRNUM', '0', this.line]);
if (this.fget('from')) {
this.fset('from', false);
this.fset('by', true);
tag = 'TO';
} else if (last[0] === 'STRNUM' && !last.callable) {
last[0] = 'RANGE';
last.op = id;
return id.length;
}
break;
case 'by':
if (last[0] === 'STRNUM' && (ref5$ = this.tokens)[ref5$.length - 2][0] === 'RANGE') {
tag = 'RANGE_BY';
} else if (this.fget('by')) {
tag = 'BY';
this.fset('by', false);
}
break;
case 'ever':
if (last[0] === 'FOR') {
this.fset('for', false);
last[0] = 'WHILE';
tag = 'LITERAL';
id = 'true';
}
}
}
tag || (tag = match[1].toUpperCase());
if (tag === 'RELATION' || tag === 'THEN' || tag === 'ELSE' || tag === 'CASE' || tag === 'DEFAULT' || tag === 'CATCH' || tag === 'FINALLY' || tag === 'IN' || tag === 'OF' || tag === 'FROM' || tag === 'TO' || tag === 'BY' || tag === 'EXTENDS' || tag === 'IMPLEMENTS') {
this.unline();
}
if (tag === 'THEN' || tag === 'IF' || tag === 'WHILE') {
this.fset('for', false);
this.fset('by', false);
}
this.token(tag, id);
return input.length;
};
exports.doNumber = function(code, lastIndex){
var input, match, last, radix, num, rnum, ref$;
NUMBER.lastIndex = lastIndex;
if (!(input = (match = NUMBER.exec(code))[0])) {
return 0;
}
last = this.last;
if (match[5] && (last[0] === 'DOT' || this.adi())) {
this.token('STRNUM', match[4].replace(NUMBER_OMIT, ''));
return match[4].length;
}
if (radix = match[1]) {
num = parseInt(rnum = match[2].replace(NUMBER_OMIT, ''), radix);
if (isNaN(num) || num === parseInt(rnum.slice(0, -1), radix)) {
this.carp("invalid number " + rnum + " in base " + radix);
}
num += '';
} else {
num = (match[3] || input).replace(NUMBER_OMIT, '');
if (match[3] && num.charAt() === '0' && ((ref$ = num.charAt(1)) !== '' && ref$ !== '.')) {
this.carp("deprecated octal literal " + match[4]);
}
}
if (!last.spaced && last[0] === '+-') {
last[0] = 'STRNUM';
last[1] += num;
return input.length;
}
this.strnum(num);
return input.length;
};
exports.doString = function(code, index, q){
var parts, str;
if (q === code.charAt(index + 1)) {
return q === code.charAt(index + 2)
? this.doHeredoc(code, index, q)
: (this.strnum(q + q), 2);
}
if (q === '"') {
parts = this.interpolate(code, index, q);
this.addInterpolated(parts, unlines);
return 1 + parts.size;
}
str = (SIMPLESTR.lastIndex = index, SIMPLESTR).exec(code)[0] || this.carp('unterminated string');
this.strnum(unlines(this.string(q, str.slice(1, -1))));
return this.countLines(str).length;
};
exports.doHeredoc = function(code, index, q){
var end, raw, doc, parts, tabs, i, len$, t;
if (q === '\'') {
~(end = code.indexOf(q + q + q, index + 3)) || this.carp('unterminated heredoc');
raw = code.slice(index + 3, end);
doc = raw.replace(LASTDENT, '');
this.strnum(enlines(this.string(q, lchomp(detab(doc, heretabs(doc))))));
return this.countLines(raw).length + 6;
}
parts = this.interpolate(code, index, q + q + q);
tabs = heretabs(code.slice(index + 3, index + parts.size).replace(LASTDENT, ''));
for (i = 0, len$ = parts.length; i < len$; ++i) {
t = parts[i];
if (t[0] === 'S') {
if (i + 1 === parts.length) {
t[1] = t[1].replace(LASTDENT, '');
}
t[1] = detab(t[1], tabs);
if (i === 0) {
t[1] = lchomp(t[1]);
}
}
}
this.addInterpolated(parts, enlines);
return 3 + parts.size;
};
exports.doComment = function(code, index){
var comment, end, ref$;
comment = ~(end = code.indexOf('*/', index + 2))
? code.slice(index, end + 2)
: code.slice(index) + '*/';
if ((ref$ = this.last[0]) === 'NEWLINE' || ref$ === 'INDENT' || ref$ === 'THEN') {
this.token('COMMENT', detab(comment, this.dent));
this.token('NEWLINE', '\n');
}
return this.countLines(comment).length;
};
exports.doJS = function(code, lastIndex){
var ref$, lit, js, ref1$;
JSTOKEN.lastIndex = lastIndex;
ref$ = JSTOKEN.exec(code), lit = ref$[0], js = ref$[2];
lit || this.carp('unterminated JS literal');
this.token('LITERAL', (ref1$ = Object(detab(js, this.dent)), ref1$.js = true, ref1$), true);
return this.countLines(lit).length;
};
exports.doRegex = function(code, index){
var divisible, ref$, ref1$, input, body, flag;
if (divisible = able(this.tokens) || this.last[0] === 'CREMENT') {
if (!this.last.spaced || ((ref$ = code.charAt(index + 1)) === ' ' || ref$ === '=')) {
return 0;
}
}
ref1$ = (REGEX.lastIndex = index, REGEX).exec(code), input = ref1$[0], body = ref1$[1], flag = ref1$[2];
if (input) {
this.regex(body, flag);
} else {
divisible || this.carp('unterminated regex');
}
return input.length;
};
exports.doHeregex = function(code, index){
var tokens, last, parts, rest, flag, i, t, dynaflag, len$, val, one;
tokens = this.tokens, last = this.last;
parts = this.interpolate(code, index, '//');
rest = code.slice(index + 2 + parts.size);
flag = this.validate(/^(?:[gimy]{1,4}|[?$]?)/.exec(rest)[0]);
if (parts[1]) {
if (flag === '$') {
this.adi();
this.token('(', '"');
} else {
tokens.push(['ID', 'RegExp', last[2]], ['CALL(', '', last[2]]);
if (flag === '?') {
for (i = parts.length - 1; i >= 0; --i) {
t = parts[i];
if (t[0] === 'TOKENS') {
dynaflag = parts.splice(i, 1)[0][1];
break;
}
}
}
}
for (i = 0, len$ = parts.length; i < len$; ++i) {
t = parts[i];
if (t[0] === 'TOKENS') {
tokens.push.apply(tokens, t[1]);
} else {
val = t[1].replace(HEREGEX_OMIT, '');
if (one && !val) {
continue;
}
one = tokens.push((t[0] = 'STRNUM', t[1] = this.string('\'', enslash(val)), t));
}
tokens.push(['+-', '+', tokens[tokens.length - 1][2]]);
}
--tokens.length;
if (dynaflag || flag >= 'g') {
this.token(',', ',');
if (dynaflag) {
tokens.push.apply(tokens, dynaflag);
} else {
this.token('STRNUM', "'" + flag + "'");
}
}
this.token(flag === '$' ? ')' : ')CALL', '');
} else {
this.regex(reslash(parts[0][1].replace(HEREGEX_OMIT, '')), flag);
}
return 2 + parts.size + flag.length;
};
exports.doBackslash = function(code, lastIndex){
var ref$, input, word;
BSTOKEN.lastIndex = lastIndex;
ref$ = BSTOKEN.exec(code), input = ref$[0], word = ref$[1];
if (word) {
this.strnum(this.string('\'', word));
} else {
this.countLines(input);
}
return input.length;
};
exports.doLine = function(code, index){
var ref$, input, tabs, length, last, that, delta, tag, val, ref1$;
ref$ = (MULTIDENT.lastIndex = index, MULTIDENT).exec(code), input = ref$[0], tabs = ref$[1];
length = this.countLines(input).length;
last = this.last;
last.eol = true;
last.spaced = true;
if (index + length >= code.length) {
return length;
}
if (that = tabs && (this.emender || (this.emender = RegExp('[^' + tabs.charAt() + ']'))).exec(tabs)) {
this.carp("contaminated indent " + escape(that));
}
if (0 > (delta = tabs.length - this.dent)) {
this.dedent(-delta);
this.newline();
} else {
tag = last[0], val = last[1];
if (tag === 'ASSIGN' && ((ref1$ = val + '') !== '=' && ref1$ !== ':=' && ref1$ !== '+=') || tag === 'BITWISE' && val !== '&' || (tag === '+-' || tag === 'DOT' || tag === 'LOGIC' || tag === 'MATH' || tag === 'COMPARE' || tag === 'RELATION' || tag === 'SHIFT' || tag === 'IN' || tag === 'OF' || tag === 'TO' || tag === 'BY' || tag === 'FROM' || tag === 'EXTENDS' || tag === 'IMPLEMENTS')) {
return length;
}
if (delta) {
this.indent(delta);
} else {
this.newline();
}
}
this.fset('for', false);
this.fset('by', false);
return length;
};
exports.doSpace = function(code, lastIndex){
var input;
SPACE.lastIndex = lastIndex;
if (input = SPACE.exec(code)[0]) {
this.last.spaced = true;
}
return input.length;
};
exports.doLiteral = function(code, index){
var sym, tag, val, ref$, ref1$, ref2$, that, ref3$, up;
if (!(sym = (SYMBOL.lastIndex = index, SYMBOL).exec(code)[0])) {
return 0;
}
switch (tag = val = sym) {
case '+':
case '-':
tag = '+-';
break;
case '&&':
case '||':
tag = 'LOGIC';
break;
case '?':
case '!?':
if (this.last.spaced) {
tag = 'LOGIC';
}
break;
case '/':
case '%':
case '**':
tag = 'MATH';
break;
case '++':
case '--':
tag = 'CREMENT';
break;
case '<<<':
case '<<<<':
tag = 'IMPORT';
break;
case ';':
this.fset('by', false);
tag = 'NEWLINE';
break;
case '.':
if (this.last[1] === '?') {
this.last[0] = '?';
}
tag = 'DOT';
break;
case ',':
switch (this.last[0]) {
case ',':
case '[':
case '(':
case 'CALL(':
this.token('LITERAL', 'void');
break;
case 'FOR':
case 'OWN':
this.token('ID', '');
}
break;
case '!=':
if (!(able(this.tokens) || this.last[0] === 'CREMENT')) {
this.tokens.push(['UNARY', '!', this.line], ['ASSIGN', '=', this.line]);
return 2;
}
// fallthrough
case '===':
case '!==':
case '<':
case '>':
case '<=':
case '>=':
case '==':
tag = 'COMPARE';
break;
case '<<':
case '>>':
case '>>>':
case '<?':
case '>?':
tag = 'SHIFT';
break;
case '(':
if (!(((ref$ = this.last[0]) === 'FUNCTION' || ref$ === 'LET') || this.able(true))) {
this.token('(', '(');
this.closes.push(')');
this.parens.push(this.last);
return 1;
}
tag = 'CALL(';
this.closes.push(')CALL');
break;
case '[':
case '{':
this.adi();
this.closes.push(']}'.charAt(val === '{'));
break;
case '}':
if (this.inter && val !== (ref1$ = this.closes)[ref1$.length - 1]) {
this.rest = code.slice(index + 1);
return 9e9;
}
// fallthrough
case ']':
case ')':
if (')' === (tag = val = this.pair(val))) {
if (this.last === (this.lpar = this.parens.pop())) {
this.last[0] = 'CALL(';
tag = ')CALL';
}
}
break;
case ':':
switch (this.last[0]) {
case 'ID':
case 'STRNUM':
case ')':
break;
case '...':
this.last[0] = 'STRNUM';
break;
default:
tag = 'LABEL';
val = '';
}
break;
case '=':
case ':=':
case '+=':
case '-=':
case '*=':
case '/=':
case '%=':
case '&=':
case '^=':
case '|=':
case '<<=':
case '>>=':
case '>>>=':
case '<?=':
case '>?=':
case '**=':
if (this.last[1] === '.' || this.last[0] === '?' && this.adi()) {
this.last[1] += val;
return val.length;
}
if (this.last[0] === 'LOGIC') {
(val = Object(val)).logic = this.tokens.pop()[1];
} else if ((val === '+=' || val === '-=' || val === '^=') && !able(this.tokens) && ((ref2$ = this.last[0]) !== '+-' && ref2$ !== '^' && ref2$ !== 'UNARY' && ref2$ !== 'LABEL')) {
this.token('UNARY', val.charAt());
val = '=';
}
tag = 'ASSIGN';
break;
case '*':
if (that = ((ref3$ = this.last[0]) === 'NEWLINE' || ref3$ === 'INDENT' || ref3$ === 'THEN') && this.doInlinedent(code, index + 1, 'list')) {
return that;
}
tag = able(this.tokens) || this.last[0] === 'CREMENT' && able(this.tokens, this.tokens.length - 1) ? 'MATH' : 'STRNUM';
break;
case '@':
case '@@':
this.dotcat(val) || (val === '@'
? this.token('LITERAL', 'this', true)
: this.token('LITERAL', 'arguments'));
return val.length;
case '!':
switch (false) {
default:
if (!this.last.spaced) {
if (able(this.tokens, null, true)) {
this.token('CALL(', '!');
this.token(')CALL', ')');
} else if (this.last[1] === 'typeof') {
this.last[1] = 'classof';
} else {
break;
}
return 1;
}
}
tag = 'UNARY';
break;
case '<>':
this.token('LITERAL', '<>', true);
return 2;
case '&':
if (!able(this.tokens)) {
this.token('LITERAL', '&', true);
return 1;
}
// fallthrough
case '|':
tag = 'BITWISE';
break;
case '~':
if (this.dotcat(val)) {
return 1;
}
tag = 'UNARY';
break;
case '->':
case '~>':
up = '->';
// fallthrough
case '<-':
case '<~':
this.parameters(tag = up || '<-');
break;
case '::':
up = 'prototype';
// fallthrough
case '..':
this.adi();
tag = 'ID';
val = up || 'constructor';
break;
case '=>':
this.unline();
this.fset('for', false);
if (that = this.doInlinedent(code, index + 2)) {
return 1 + that;
}
tag = 'THEN';
break;
default:
if ('<' === val.charAt(0)) {
if (val.length < 4) {
this.carp('unterminated words');
}
this.token('WORDS', val, this.adi());
return val.length;
}
}
if (tag === ',' || tag === '|>' || tag === 'DOT' || tag === 'LOGIC' || tag === 'COMPARE' || tag === 'MATH' || tag === 'IMPORT' || tag === 'SHIFT' || tag === 'BITWISE') {
this.unline();
}
this.token(tag, val);
return sym.length;
};
exports.doInlinedent = function(code, index, list){
var d;
if (!(d = (INLINEDENT.lastIndex = index, INLINEDENT).exec(code)[0].length)) {
return 0;
}
list && this.tokens.push(['LITERAL', 'void', this.line], ['ASSIGN', '=', this.line]);
this.indent(index + d - this.dent - 2 - code.lastIndexOf('\n', index));
return d;
};
exports.token = function(tag, value, callable){
this.tokens.push(this.last = [tag, value, this.line]);
if (callable) {
this.last.callable = true;
}
return value;
};
exports.indent = function(delta){
this.dent += delta;
this.dents.push(this.token('INDENT', delta));
this.closes.push('DEDENT');
};
exports.dedent = function(debt){
var dent;
this.dent -= debt;
while (debt > 0 && (dent = this.dents.pop())) {
if (debt < dent && !this.inter) {
this.carp("unmatched dedent (" + debt + " for " + dent + ")");
}
this.pair('DEDENT');
debt -= typeof dent === 'number' ? this.token('DEDENT', dent) : dent;
}
};
exports.newline = function(){
var ref$;
this.last[1] === '\n' || this.tokens.push(this.last = (ref$ = ['NEWLINE', '\n', this.line], ref$.spaced = true, ref$));
};
exports.unline = function(){
var ref$, ref1$;
if (!this.tokens[1]) {
return;
}
switch (this.last[0]) {
case 'INDENT':
(ref$ = this.dents)[ref$.length - 1] += '';
break;
case 'NEWLINE':
if (this.last[1] === ';') {
return;
}
break;
default:
return;
}
this.last = (ref1$ = this.tokens)[--ref1$.length - 1];
};
exports.parameters = function(arrow){
var i, ref$, t, ref1$;
if (this.last[0] === ')' && ')' === this.last[1]) {
this.lpar[0] = 'PARAM(';
this.last[0] = ')PARAM';
return;
}
if (arrow === '->') {
this.token('PARAM(', '');
} else {
for (i = (ref$ = this.tokens).length - 1; i >= 0; --i) {
t = ref$[i];
if ((ref1$ = t[0]) === 'NEWLINE' || ref1$ === 'INDENT' || ref1$ === 'THEN' || ref1$ === '(') {
break;
}
}
this.tokens.splice(i + 1, 0, ['PARAM(', '', t[2]]);
}
this.token(')PARAM', '');
};
exports.interpolate = function(str, idx, end){
var parts, end0, pos, i, ch, c1, id, stringified, length, tag, e, delta, nested, clone, ref$, ref1$;
parts = [];
end0 = end.charAt(0);
pos = 0;
i = -1;
str = str.slice(idx + end.length);
while (ch = str.charAt(++i)) {
switch (ch) {
case end0:
if (end !== str.slice(i, i + end.length)) {
continue;
}
parts.push(['S', this.countLines(str.slice(0, i)), this.line]);
return parts.size = pos + i + end.length, parts;
case '#':
c1 = str.charAt(i + 1);
id = (c1 === '@' || c1 === '&') && c1 || c1 === '<' && '>' === str.charAt(i + 2) && '<>' || (ID.lastIndex = i + 1, ID).exec(str)[1];
if (!(id || c1 === '{')) {
continue;
}
break;
case '\\':
++i;
// fallthrough
default:
continue;
}
if (i || nested && !stringified) {
stringified = parts.push(['S', this.countLines(str.slice(0, i)), this.line]);
}
if (id) {
length = id.length;
if (id === '@') {
id = 'this';
}
if (id === 'this' || id === '&' || id === '<>') {
tag = 'LITERAL';
} else {
id = camelize(id);
try {
Function("'use strict'; var " + id);
} catch (e$) {
e = e$;
this.carp("invalid variable interpolation \"" + id + "\"");
}
tag = 'ID';
}
str = str.slice(delta = i + 1 + length);
parts.push(['TOKENS', nested = [[tag, id, this.line]]]);
} else {
clone = (ref$ = clone$(exports), ref$.inter = true, ref$.emender = this.emender, ref$);
nested = clone.tokenize(str.slice(i + 2), {
line: this.line,
raw: true
});
delta = str.length - clone.rest.length;
str = clone.rest, this.line = clone.line;
while (((ref1$ = nested[0]) != null ? ref1$[0] : void 8) === 'NEWLINE') {
nested.shift();
}
if (nested.length) {
nested.unshift(['(', '(', nested[0][2]]);
nested.push([')', ')', this.line]);
parts.push(['TOKENS', nested]);
}
}
pos += delta;
i = -1;
}
this.carp("missing `" + end + "`");
};
exports.addInterpolated = function(parts, nlines){
var tokens, last, ref$, left, right, joint, callable, i, len$, t;
if (!parts[1]) {
return this.strnum(nlines(this.string('"', parts[0][1])));
}
tokens = this.tokens, last = this.last;
ref$ = !last.spaced && last[1] === '%'
? (--tokens.length, this.last = last = tokens[tokens.length - 1], ['[', ']', [',', ',']])
: ['(', ')', ['+-', '+']], left = ref$[0], right = ref$[1], joint = ref$[2];
callable = this.adi();
tokens.push([left, '"', last[2]]);
for (i = 0, len$ = parts.length; i < len$; ++i) {
t = parts[i];
if (t[0] === 'TOKENS') {
tokens.push.apply(tokens, t[1]);
} else {
if (i > 1 && !t[1]) {
continue;
}
tokens.push(['STRNUM', nlines(this.string('"', t[1])), t[2]]);
}
tokens.push(joint.concat(tokens[tokens.length - 1][2]));
}
--tokens.length;
this.token(right, '', callable);
};
exports.strnum = function(it){
this.token('STRNUM', it, this.adi() || this.last[0] === 'DOT');
};
exports.regex = function(body, flag){
var e;
try {
RegExp(body);
} catch (e$) {
e = e$;
this.carp(e.message);
}
if (flag === '$') {
return this.strnum(this.string('\'', enslash(body)));
}
return this.token('LITERAL', "/" + (body || '(?:)') + "/" + this.validate(flag));
};
exports.adi = function(){
if (this.last.spaced) {
return;
}
if (this.last[0] === '!?') {
this.last[0] = 'CALL(';
this.token(')CALL', '');
this.token('?', '?');
} else if (!able(this.tokens)) {
return;
}
return this.token('DOT', '.');
};
exports.dotcat = function(it){
if (this.last[1] === '.' || this.adi()) {
return this.last[1] += it;
}
};
exports.pair = function(it){
var wanted, ref$, ref1$;
if (!(it === (wanted = (ref$ = this.closes)[ref$.length - 1]) || ')CALL' === wanted && it === ')')) {
if ('DEDENT' !== wanted) {
this.carp("unmatched `" + it + "`");
}
this.dedent((ref1$ = this.dents)[ref1$.length - 1]);
return this.pair(it);
}
this.unline();
return this.closes.pop();
};
exports.able = function(call){
return !this.last.spaced && able(this.tokens, null, call);
};
exports.countLines = function(it){
var pos;
while (pos = 1 + it.indexOf('\n', pos)) {
++this.line;
}
return it;
};
exports.forange = function(){
var ref$, ref1$, ref2$;
if (((ref$ = (ref1$ = this.tokens)[ref1$.length - 2 - ((ref2$ = this.last[0]) === 'NEWLINE' || ref2$ === 'INDENT')]) != null ? ref$[0] : void 8) === 'FOR') {
this.fset('for', false);
this.fset('from', true);
return true;
}
return false;
};
exports.validate = function(flag){
var that;
if (that = flag && /(.).*\1/.exec(flag)) {
this.carp("duplicate regex flag `" + that[1] + "`");
}
return flag;
};
exports.fget = function(key){
var ref$;
return (ref$ = this.flags[this.closes.length]) != null ? ref$[key] : void 8;
};
exports.fset = function(key, val){
var ref$, key$;
return ((ref$ = this.flags)[key$ = this.closes.length] || (ref$[key$] = {}))[key] = val;
};
exports.carp = function(it){
carp(it, this.line);
};
exports.string = function(q, body){
return string(q, body, this.line);
};
function carp(msg, lno){
throw SyntaxError(msg + " on line " + (-~lno));
}
function able(tokens, i, call){
var token, tag;
i == null && (i = tokens.length);
tag = (token = tokens[i - 1])[0];
if ((tag === 'ID' || tag === ']' || tag === '?') || tag === 'BITWISE' && token[1] === '&' && !token.spaced && tokens[i - 2].spaced) {
return true;
}
if (call) {
return token.callable || (tag === ')' || tag === ')CALL') && token[1];
} else {
return tag === '}' || tag === ')' || tag === ')CALL' || tag === 'STRNUM' || tag === 'LITERAL' || tag === 'WORDS';
}
}
string = (function(re){
return function(q, body, lno){
body = body.replace(re, function(it, oct, xu, rest){
if (it === q || it === '\\') {
return '\\' + it;
}
if (oct) {
return '\\x' + (0x100 + parseInt(oct, 8)).toString(16).slice(1);
}
if (xu) {
carp('malformed character escape sequence', lno);
}
if (!rest || q === rest) {
return it;
} else {
return rest;
}
});
return q + body + q;
};
}.call(this, /['"]|\\(?:([0-3]?[0-7]{2}|[1-7]|0(?=[89]))|x[\dA-Fa-f]{2}|u[\dA-Fa-f]{4}|([xu])|[\\0bfnrtv]|[^\n\S]|([\w\W]))?/g));
function heretabs(doc){
var dent, that, ref$;
dent = 0 / 0;
while (that = TABS.exec(doc)) {
dent <= (ref$ = that[0].length - 1) || (dent = ref$);
}
return dent;
}
TABS = /\n(?!$)[^\n\S]*/mg;
function detab(str, len){
if (len) {
return str.replace(detab[len] || (detab[len] = RegExp('\\n[^\\n\\S]{1,' + len + '}', 'g')), '\n');
} else {
return str;
}
}
function replacer(re, to){
return function(it){
return it.replace(re, to);
};
}
unlines = replacer(/\n[^\n\S]*/g, '');
enlines = replacer(/\n/g, '\\n');
enslash = replacer(/\\/g, '\\\\');
reslash = replacer(/(\\.)|\//g, function(){
return arguments[1] || '\\/';
});
camelize = replacer(/-[a-z]/ig, function(it){
return it.charAt(1).toUpperCase();
});
function lchomp(it){
return it.slice(1 + it.lastIndexOf('\n', 0));
}
function decode(val, lno){
if (!isNaN(val)) {
return [+val];
}
val = val.length > 8
? 'ng'
: Function('return' + val)();
val.length === 1 || carp('bad string in range', lno);
return [val.charCodeAt(), true];
}
function uxxxx(it){
return '"\\u' + ('000' + it.toString(16)).slice(-4) + '"';
}
character = typeof JSON == 'undefined' || JSON === null
? uxxxx
: function(it){
switch (it) {
case 0x2028:
case 0x2029:
return uxxxx(it);
default:
return JSON.stringify(String.fromCharCode(it));
}
};
function rewriteBlockless(tokens){
var i, len$, token, tag;
for (i = 0, len$ = tokens.length; i < len$; ++i) {
token = tokens[i], tag = token[0];
if (tag === 'IF' || tag === 'CLASS') {
detectEnd(tokens, i + 1, ok, go);
}
}
function ok(it){
var ref$;
return (ref$ = it[0]) === 'NEWLINE' || ref$ === 'INDENT';
}
function go(it, i){
var lno;
if (tag === 'IF') {
if (it[0] !== 'INDENT' || !it[1] && !it.then || of$(tokens[i - 1][0], BLOCK_USERS)) {
token[0] = 'POST_IF';
}
} else if (it[0] !== 'INDENT') {
tokens.splice(i, 0, ['INDENT', 0, lno = tokens[i - 1][2]], ['DEDENT', 0, lno]);
}
}
}
function addImplicitIndentation(tokens){
var i, token, tag, next, indent, dedent, ref$, ref1$, idx;
i = 0;
while (token = tokens[++i]) {
tag = token[0];
if (tag !== '->' && tag !== 'THEN' && tag !== 'ELSE' && tag !== 'DEFAULT' && tag !== 'TRY' && tag !== 'CATCH' && tag !== 'FINALLY' && tag !== 'DECL') {
continue;
}
switch (next = tokens[i + 1][0]) {
case 'IF':
if (tag === 'ELSE') {
continue;
}
break;
case 'INDENT':
case 'THEN':
if (tag === 'THEN') {
tokens.splice(i--, 1);
}
continue;
}
indent = ['INDENT', 0, token[2]];
dedent = ['DEDENT', 0];
if (tag === 'THEN') {
(tokens[i] = indent).then = true;
} else {
tokens.splice(++i, 0, indent);
}
switch (false) {
case tag !== 'DECL':
break;
case next !== 'DOT' && next !== '?' && next !== ',' && next !== '|>':
--i;
// fallthrough
case !((next === 'ID' || next === 'STRNUM' || next === 'LITERAL') && ',' === ((ref$ = tokens[i + 2]) != null ? ref$[0] : void 8)):
go(0, i += 2);
++i;
continue;
case !((next === '(' || next === '[' || next === '{') && ',' === ((ref1$ = tokens[idx = 1 + indexOfPair(tokens, i + 1)]) != null ? ref1$[0] : void 8)):
go(0, idx);
++i;
continue;
}
detectEnd(tokens, i + 1, ok, go);
}
function ok(token, i){
var t0, t;
t0 = token[0];
t = tag;
if (tag === t0 || tag === 'THEN' && t0 === 'SWITCH') {
tag = '';
}
switch (t0) {
case 'NEWLINE':
return token[1] !== ';';
case 'DOT':
case '?':
case ',':
case '|>':
return tokens[i - 1].eol;
case 'ELSE':
return t === 'THEN';
case 'CATCH':
return t === 'TRY';
case 'FINALLY':
return t === 'TRY' || t === 'CATCH' || t === 'THEN';
case 'CASE':
case 'DEFAULT':
return t === 'CASE' || t === 'THEN';
}
}
function go(arg$, i){
var prev;
prev = tokens[i - 1];
tokens.splice(prev[0] === ',' ? i - 1 : i, 0, (dedent[2] = prev[2], dedent));
}
}
function addImplicitParentheses(tokens){
var i, brackets, token, endi, ref$, tpair, tag, prev, ref1$, skipBlock, seenSwitch;
i = 0;
brackets = [];
while (token = tokens[++i]) {
if (token[1] === 'do' && tokens[i + 1][0] === 'INDENT') {
endi = indexOfPair(tokens, i + 1);
if (tokens[endi + 1][0] === 'NEWLINE' && ((ref$ = tokens[endi + 2]) != null ? ref$[0] : void 8) === 'WHILE') {
token[0] = 'DO';
tokens[endi + 2].done = true;
tokens.splice(endi + 1, 1);
} else {
(token = tokens[1 + i])[0] = '(';
(tpair = tokens[endi])[0] = ')';
token.doblock = true;
tokens.splice(i, 1);
}
}
tag = token[0];
prev = tokens[i - 1];
tag === '[' && brackets.push(prev[0] === 'DOT');
if (prev[0] === ']') {
if (brackets.pop()) {
prev.index = true;
} else {
continue;
}
}
if (tag === 'BITWISE' && token[1] === '&' && !exp(tokens[i + 1])) {
tag = token[0] = 'LITERAL';
}
if (!(((ref1$ = prev[0]) === 'FUNCTION' || ref1$ === 'LET') || prev.spaced && able(tokens, i, true))) {
continue;
}
if (token.doblock) {
token[0] = 'CALL(';
tpair[0] = ')CALL';
continue;
}
if (!exp(token)) {
continue;
}
if (tag === 'CREMENT') {
if (token.spaced || !of$(tokens[i + 1][0], CHAIN)) {
continue;
}
}
skipBlock = seenSwitch = false;
tokens.splice(i++, 0, ['CALL(', '', token[2]]);
detectEnd(tokens, i, ok, go);
}
function exp(token){
var tag;
tag = token[0];
return of$(tag, ARG) || !token.spaced && (tag === '+-' || tag === '^');
}
function ok(token, i){
var tag, ref$, pre;
tag = token[0];
if (tag === '|>' || tag === 'POST_IF') {
return true;
}
if (!skipBlock) {
if (token.alias && ((ref$ = token[1]) === '&&' || ref$ === '||') || (tag === 'TO' || tag === 'BY' || tag === 'IMPLEMENTS')) {
return true;
}
}
pre = tokens[i - 1];
switch (tag) {
case 'NEWLINE':
return pre[0] !== ',';
case 'DOT':
case '?':
return !skipBlock && (pre.spaced || pre[0] === 'DEDENT');
case 'SWITCH':
seenSwitch = true;
// fallthrough
case 'IF':
case 'CLASS':
case 'FUNCTION':
case 'LET':
case 'WITH':
skipBlock = true;
break;
case 'CASE':
if (seenSwitch) {
skipBlock = true;
} else {
return true;
}
break;
case 'INDENT':
if (skipBlock) {
return skipBlock = false;
}
return !of$(pre[0], BLOCK_USERS);
case 'WHILE':
if (token.done) {
return false;
}
// fallthrough
case 'FOR':
skipBlock = true;
return able(tokens, i) || pre[0] === 'CREMENT' || pre[0] === '...' && pre.spaced;
}
return false;
}
function go(token, i){
tokens.splice(i, 0, [')CALL', '', tokens[i - 1][2]]);
}
}
function addImplicitBraces(tokens){
var stack, i, token, tag, start, paren, index, pre, ref$, ref1$, inline, ref2$, ref3$;
stack = [];
i = 0;
while (token = tokens[++i]) {
if (':' !== (tag = token[0])) {
switch (false) {
case !of$(tag, CLOSERS):
start = stack.pop();
break;
case !of$(tag, OPENERS):
if (tag === 'INDENT' && tokens[i - 1][0] === '{') {
tag = '{';
}
stack.push([tag, i]);
}
continue;
}
paren = tokens[i - 1][0] === ')';
index = paren
? start[1]
: i - 1;
pre = tokens[index - 1];
if (!(((ref$ = pre[0]) === ':' || ref$ === 'ASSIGN' || ref$ === 'IMPORT') || ((ref1$ = stack[stack.length - 1]) != null ? ref1$[0] : void 8) !== '{')) {
continue;
}
stack.push(['{']);
inline = !pre.doblock && ((ref2$ = pre[0]) !== 'NEWLINE' && ref2$ !== 'INDENT');
while (((ref3$ = tokens[index - 2]) != null ? ref3$[0] : void 8) === 'COMMENT') {
index -= 2;
}
tokens.splice(index, 0, ['{', '{', tokens[index][2]]);
detectEnd(tokens, ++i + 1, ok, go);
}
function ok(token, i){
var tag, t1, ref$, ref1$;
switch (tag = token[0]) {
case ',':
break;
case 'NEWLINE':
if (inline) {
return true;
}
break;
case 'DEDENT':
return true;
case 'POST_IF':
case 'FOR':
case 'WHILE':
case '|>':
return inline;
default:
return false;
}
t1 = (ref$ = tokens[i + 1]) != null ? ref$[0] : void 8;
return t1 !== (tag === ',' ? 'NEWLINE' : 'COMMENT') && ':' !== ((ref1$ = tokens[t1 === '('
? 1 + indexOfPair(tokens, i + 1)
: i + 2]) != null ? ref1$[0] : void 8);
}
function go(token, i){
tokens.splice(i, 0, ['}', '', token[2]]);
}
}
function expandLiterals(tokens){
var i, token, sig, next, lno, ref$, from, char, ref1$, to, tochar, by, byp, ref2$, ref3$, ts, enc, add, n, i$, ref4$, len$, word, that, ref5$;
i = 0;
while (token = tokens[++i]) {
switch (token[0]) {
case 'STRNUM':
if (~'-+'.indexOf(sig = token[1].charAt(0))) {
token[1] = token[1].slice(1);
tokens.splice(i++, 0, ['+-', sig, token[2]]);
}
if (token.callable) {
continue;
}
break;
case 'RANGE':
next = tokens[i + 1];
lno = token[2];
ref$ = decode(token[1], lno), from = ref$[0], char = ref$[1];
ref1$ = next[0] === 'STRNUM' && decode(next[1], lno), to = ref1$[0], tochar = ref1$[1];
if (to == null || char ^ tochar) {
carp('bad "to" in range', lno);
}
by = 1;
if (byp = ((ref2$ = tokens[i + 2]) != null ? ref2$[0] : void 8) === 'RANGE_BY') {
if (!(by = +((ref3$ = tokens[i + 3]) != null ? ref3$[1] : void 8))) {
carp('bad "by" in range', tokens[i + 2][2]);
}
} else if (from > to) {
by = -1;
}
ts = [];
enc = char ? character : String;
add = fn$;
if (token.op === 'to') {
for (n = from; by < 0 ? n >= to : n <= to; n += by) {
add();
}
} else {
for (n = from; by < 0 ? n > to : n < to; n += by) {
add();
}
}
ts.pop() || carp('empty range', lno);
tokens.splice.apply(tokens, [i, 2 + 2 * byp].concat(slice$.call(ts)));
i += ts.length - 1;
break;
case 'WORDS':
ts = [['[', '[', lno = token[2]]];
for (i$ = 0, len$ = (ref4$ = token[1].slice(2, -2).match(/\S+/g) || '').length; i$ < len$; ++i$) {
word = ref4$[i$];
ts.push(['STRNUM', string('\'', word, lno), lno], [',', ',', lno]);
}
tokens.splice.apply(tokens, [i, 1].concat(slice$.call(ts), [[']', ']', lno]]));
i += ts.length;
break;
case 'INDENT':
if (that = tokens[i - 1]) {
if (that[1] === 'new') {
tokens.splice(i++, 0, ['PARAM(', '', token[2]], [')PARAM', '', token[2]], ['->', '', token[2]]);
} else if ((ref5$ = that[0]) === 'FUNCTION' || ref5$ === 'LET') {
tokens.splice(i, 0, ['CALL(', '', token[2]], [')CALL', '', token[2]]);
i += 2;
}
}
continue;
case 'LITERAL':
case '}':
case '!?':
break;
case ')':
case ')CALL':
if (token[1]) {
continue;
}
break;
case ']':
if (token.index) {
continue;
}
break;
case 'CREMENT':
if (!able(tokens, i)) {
continue;
}
break;
default:
continue;
}
if (token.spaced && of$(tokens[i + 1][0], ARG)) {
tokens.splice(++i, 0, [',', ',', token[2]]);
}
}
function fn$(){
if (0x10000 < ts.push(['STRNUM', enc(n), lno], [',', ',', lno])) {
carp('range limit exceeded', lno);
}
}
}
function detectEnd(tokens, i, ok, go){
var levels, token, tag;
levels = 0;
for (; token = tokens[i]; ++i) {
if (!levels && ok(token, i)) {
return go(token, i);
}
tag = token[0];
if (0 > (levels += of$(tag, OPENERS) || -of$(tag, CLOSERS))) {
return go(token, i);
}
}
}
function indexOfPair(tokens, i){
var level, end, start, that;
level = 1;
end = INVERSES[start = tokens[i][0]];
while (that = tokens[++i]) {
switch (that[0]) {
case start:
++level;
break;
case end:
if (!--level) {
return i;
}
}
}
return -1;
}
KEYWORDS_SHARED = ['true', 'false', 'null', 'this', 'void', 'super', 'return', 'throw', 'break', 'continue', 'if', 'else', 'for', 'while', 'switch', 'case', 'default', 'try', 'catch', 'finally', 'function', 'class', 'extends', 'implements', 'new', 'do', 'delete', 'typeof', 'in', 'instanceof', 'let', 'with', 'var', 'const', 'import', 'export', 'debugger'];
KEYWORDS_UNUSED = ['enum', 'interface', 'package', 'private', 'protected', 'public', 'static', 'yield'];
KEYWORDS = KEYWORDS_SHARED.concat(KEYWORDS_UNUSED);
ID = /((?!\s)[a-z_$\xAA-\uFFDC](?:(?!\s)[\w$\xAA-\uFFDC]|-[a-z])*)([^\n\S]*:(?![:=]))?|/ig;
SYMBOL = /[-+*\/%&|^:]=|\.{1,3}|([-+&|@:])\1|[-~=|]>|[!=]==?|<(?:<(?:=|<{0,2})|[-~>]|\[(?:[\s\S]*?\]>)?)|>>>?=?|[<>]\??=?|!\?|\*\*=?|[^\s#]?/g;
SPACE = /[^\n\S]*(?:#.*)?/g;
MULTIDENT = /(?:\s*#.*)*(?:\n([^\n\S]*))*/g;
SIMPLESTR = /'[^\\']*(?:\\[\s\S][^\\']*)*'|/g;
BSTOKEN = /\\(?:(\S[^\s,;)}\]]*)|\s*)/g;
JSTOKEN = /(`+)([^`][\s\S]*?)\1|/g;
NUMBER = /0x[\dA-Fa-f][\dA-Fa-f_]*|([2-9]|[12]\d|3[0-6])r([\dA-Za-z]\w*)|((\d[\d_]*)(\.\d[\d_]*)?(?:e[+-]?\d[\d_]*)?)[$\w]*|/g;
NUMBER_OMIT = /_+/g;
REGEX = /\/([^[\/\n\\]*(?:(?:\\.|\[[^\]\n\\]*(?:\\.[^\]\n\\]*)*\])[^[\/\n\\]*)*)\/([gimy]{1,4}|\$?)|/g;
HEREGEX_OMIT = /\s+(?:#.*)?/g;
LASTDENT = /\n[^\n\S]*$/;
INLINEDENT = /[^\n\S]*[^#\s]?/g;
NONASCII = /[\x80-\uFFFF]/;
OPENERS = ['(', '[', '{', 'CALL(', 'PARAM(', 'INDENT'];
CLOSERS = [')', ']', '}', ')CALL', ')PARAM', 'DEDENT'];
INVERSES = new function(){
var i, ref$, len$, o;
for (i = 0, len$ = (ref$ = OPENERS).length; i < len$; ++i) {
o = ref$[i];
this[this[o] = CLOSERS[i]] = o;
}
};
CHAIN = ['(', '{', '[', 'ID', 'STRNUM', 'LITERAL', 'LET', 'WITH', 'WORDS'];
ARG = CHAIN.concat(['...', 'UNARY', 'CREMENT', 'PARAM(', 'FUNCTION', 'IF', 'SWITCH', 'TRY', 'CLASS', 'RANGE', 'LABEL', 'DECL', 'DO']);
BLOCK_USERS = [',', ':', '->', 'ELSE', 'ASSIGN', 'IMPORT', 'UNARY', 'DEFAULT', 'TRY', 'CATCH', 'FINALLY', 'HURL', 'DECL', 'DO', 'LET', 'FUNCTION'];
function clone$(it){
function fun(){} fun.prototype = it;
return new fun;
}
function of$(x, arr){
var i = 0, l = arr.length >>> 0;
while (i < l) if (x === arr[i++]) return true;
return false;
}