mirror of
https://github.com/jashkenas/coffeescript.git
synced 2022-11-09 12:23:24 -05:00
first draft of coco's executable class bodies ... cleanups and tests to follow.
This commit is contained in:
parent
2c7f6d8bfe
commit
7fda0cd7d9
11 changed files with 948 additions and 1153 deletions
|
@ -44,8 +44,6 @@
|
|||
return $2;
|
||||
}), o('INDENT OUTDENT', function() {
|
||||
return new Expressions;
|
||||
}), o('TERMINATOR Comment', function() {
|
||||
return Expressions.wrap([$2]);
|
||||
})
|
||||
],
|
||||
Identifier: [
|
||||
|
@ -204,45 +202,22 @@
|
|||
})
|
||||
],
|
||||
Class: [
|
||||
o('CLASS SimpleAssignable', function() {
|
||||
o('CLASS', function() {
|
||||
return new Class;
|
||||
}), o('CLASS Block', function() {
|
||||
return new Class(null, null, $2);
|
||||
}), o('CLASS EXTENDS Value', function() {
|
||||
return new Class(null, $3);
|
||||
}), o('CLASS EXTENDS Value Block', function() {
|
||||
return new Class(null, $3, $4);
|
||||
}), o('CLASS SimpleAssignable', function() {
|
||||
return new Class($2);
|
||||
}), o('CLASS SimpleAssignable Block', function() {
|
||||
return new Class($2, null, $3);
|
||||
}), o('CLASS SimpleAssignable EXTENDS Value', function() {
|
||||
return new Class($2, $4);
|
||||
}), o('CLASS SimpleAssignable\
|
||||
INDENT ClassBody OUTDENT', function() {
|
||||
return new Class($2, null, $4);
|
||||
}), o('CLASS SimpleAssignable EXTENDS Value\
|
||||
INDENT ClassBody OUTDENT', function() {
|
||||
return new Class($2, $4, $6);
|
||||
}), o('CLASS INDENT ClassBody OUTDENT', function() {
|
||||
return new Class(null, null, $3);
|
||||
}), o('CLASS', function() {
|
||||
return new Class(null, null, new Expressions);
|
||||
}), o('CLASS EXTENDS Value', function() {
|
||||
return new Class(null, $3, new Expressions);
|
||||
}), o('CLASS EXTENDS Value\
|
||||
INDENT ClassBody OUTDENT', function() {
|
||||
return new Class(null, $3, $5);
|
||||
})
|
||||
],
|
||||
ClassAssign: [
|
||||
o('AssignObj', function() {
|
||||
return $1;
|
||||
}), o('ThisProperty : Expression', function() {
|
||||
return new Assign(new Value($1), $3, 'this');
|
||||
}), o('ThisProperty : INDENT Expression OUTDENT', function() {
|
||||
return new Assign(new Value($1), $4, 'this');
|
||||
})
|
||||
],
|
||||
ClassBody: [
|
||||
o('', function() {
|
||||
return [];
|
||||
}), o('ClassAssign', function() {
|
||||
return [$1];
|
||||
}), o('ClassBody TERMINATOR ClassAssign', function() {
|
||||
return $1.concat($3);
|
||||
}), o('{ ClassBody }', function() {
|
||||
return $2;
|
||||
}), o('CLASS SimpleAssignable EXTENDS Value Block', function() {
|
||||
return new Class($2, $4, $5);
|
||||
})
|
||||
],
|
||||
Invocation: [
|
||||
|
|
11
lib/lexer.js
11
lib/lexer.js
|
@ -8,13 +8,8 @@
|
|||
};
|
||||
Rewriter = require('./rewriter').Rewriter;
|
||||
_ref = require('./helpers'), count = _ref.count, starts = _ref.starts, compact = _ref.compact, last = _ref.last;
|
||||
exports.Lexer = (function() {
|
||||
Lexer = (function() {
|
||||
function Lexer() {
|
||||
return this;
|
||||
}
|
||||
return Lexer;
|
||||
})();
|
||||
exports.Lexer = Lexer = function() {
|
||||
function Lexer() {}
|
||||
Lexer.prototype.tokenize = function(code, opts) {
|
||||
var i;
|
||||
opts == null && (opts = {});
|
||||
|
@ -595,7 +590,7 @@
|
|||
return quote + this.escapeLines(body, heredoc) + quote;
|
||||
};
|
||||
return Lexer;
|
||||
})();
|
||||
}();
|
||||
JS_KEYWORDS = ['true', 'false', 'null', 'this', 'new', 'delete', 'typeof', 'in', 'instanceof', 'return', 'throw', 'break', 'continue', 'debugger', 'if', 'else', 'switch', 'for', 'while', 'try', 'catch', 'finally', 'class', 'extends', 'super'];
|
||||
COFFEE_KEYWORDS = ['undefined', 'then', 'unless', 'until', 'loop', 'of', 'by', 'when'];
|
||||
for (op in COFFEE_ALIASES = {
|
||||
|
|
734
lib/nodes.js
734
lib/nodes.js
File diff suppressed because it is too large
Load diff
|
@ -1,14 +1,10 @@
|
|||
(function() {
|
||||
var LONG_FLAG, MULTI_FLAG, OPTIONAL, OptionParser, SHORT_FLAG, buildRule, buildRules, normalizeArguments;
|
||||
exports.OptionParser = (function() {
|
||||
OptionParser = (function() {
|
||||
function OptionParser(rules, _arg) {
|
||||
this.banner = _arg;
|
||||
this.rules = buildRules(rules);
|
||||
return this;
|
||||
}
|
||||
return OptionParser;
|
||||
})();
|
||||
exports.OptionParser = OptionParser = function() {
|
||||
function OptionParser(rules, _arg) {
|
||||
this.banner = _arg;
|
||||
this.rules = buildRules(rules);
|
||||
}
|
||||
OptionParser.prototype.parse = function(args) {
|
||||
var arg, i, isOption, matchedRule, options, rule, value, _i, _len, _len2, _ref;
|
||||
options = {
|
||||
|
@ -56,7 +52,7 @@
|
|||
return "\n" + (lines.join('\n')) + "\n";
|
||||
};
|
||||
return OptionParser;
|
||||
})();
|
||||
}();
|
||||
LONG_FLAG = /^(--\w[\w\-]+)/;
|
||||
SHORT_FLAG = /^(-\w)/;
|
||||
MULTI_FLAG = /^-(\w{2,})/;
|
||||
|
|
350
lib/parser.js
350
lib/parser.js
File diff suppressed because one or more lines are too long
620
lib/rewriter.js
620
lib/rewriter.js
|
@ -1,335 +1,333 @@
|
|||
(function() {
|
||||
var BALANCED_PAIRS, EXPRESSION_CLOSE, EXPRESSION_END, EXPRESSION_START, IMPLICIT_BLOCK, IMPLICIT_CALL, IMPLICIT_END, IMPLICIT_FUNC, IMPLICIT_UNSPACED_CALL, INVERSES, LINEBREAKS, SINGLE_CLOSERS, SINGLE_LINERS, left, rite, _i, _len, _ref;
|
||||
var BALANCED_PAIRS, EXPRESSION_CLOSE, EXPRESSION_END, EXPRESSION_START, IMPLICIT_BLOCK, IMPLICIT_CALL, IMPLICIT_END, IMPLICIT_FUNC, IMPLICIT_UNSPACED_CALL, INVERSES, LINEBREAKS, Rewriter, SINGLE_CLOSERS, SINGLE_LINERS, left, rite, _i, _len, _ref;
|
||||
var __indexOf = Array.prototype.indexOf || function(item) {
|
||||
for (var i = 0, l = this.length; i < l; i++) {
|
||||
if (this[i] === item) return i;
|
||||
}
|
||||
return -1;
|
||||
}, __slice = Array.prototype.slice;
|
||||
exports.Rewriter = (function() {
|
||||
function Rewriter() {
|
||||
return this;
|
||||
}
|
||||
return Rewriter;
|
||||
})();
|
||||
exports.Rewriter.prototype.rewrite = function(_arg) {
|
||||
this.tokens = _arg;
|
||||
this.removeLeadingNewlines();
|
||||
this.removeMidExpressionNewlines();
|
||||
this.closeOpenCalls();
|
||||
this.closeOpenIndexes();
|
||||
this.addImplicitIndentation();
|
||||
this.tagPostfixConditionals();
|
||||
this.addImplicitBraces();
|
||||
this.addImplicitParentheses();
|
||||
this.ensureBalance(BALANCED_PAIRS);
|
||||
this.rewriteClosingParens();
|
||||
return this.tokens;
|
||||
};
|
||||
exports.Rewriter.prototype.scanTokens = function(block) {
|
||||
var i, token, tokens;
|
||||
tokens = this.tokens;
|
||||
i = 0;
|
||||
while (token = tokens[i]) {
|
||||
i += block.call(this, token, i, tokens);
|
||||
}
|
||||
return true;
|
||||
};
|
||||
exports.Rewriter.prototype.detectEnd = function(i, condition, action) {
|
||||
var levels, token, tokens, _ref, _ref2;
|
||||
tokens = this.tokens;
|
||||
levels = 0;
|
||||
while (token = tokens[i]) {
|
||||
if (levels === 0 && condition.call(this, token, i)) {
|
||||
return action.call(this, token, i);
|
||||
}
|
||||
if (!token || levels < 0) {
|
||||
return action.call(this, token, i - 1);
|
||||
}
|
||||
if (_ref = token[0], __indexOf.call(EXPRESSION_START, _ref) >= 0) {
|
||||
levels += 1;
|
||||
} else if (_ref2 = token[0], __indexOf.call(EXPRESSION_END, _ref2) >= 0) {
|
||||
levels -= 1;
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
return i - 1;
|
||||
};
|
||||
exports.Rewriter.prototype.removeLeadingNewlines = function() {
|
||||
var i, tag, _len;
|
||||
for (i = 0, _len = this.tokens.length; i < _len; i++) {
|
||||
tag = this.tokens[i][0];
|
||||
if (tag !== 'TERMINATOR') {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (i) {
|
||||
return this.tokens.splice(0, i);
|
||||
}
|
||||
};
|
||||
exports.Rewriter.prototype.removeMidExpressionNewlines = function() {
|
||||
return this.scanTokens(function(token, i, tokens) {
|
||||
var _ref;
|
||||
if (!(token[0] === 'TERMINATOR' && (_ref = this.tag(i + 1), __indexOf.call(EXPRESSION_CLOSE, _ref) >= 0))) {
|
||||
return 1;
|
||||
}
|
||||
tokens.splice(i, 1);
|
||||
return 0;
|
||||
});
|
||||
};
|
||||
exports.Rewriter.prototype.closeOpenCalls = function() {
|
||||
var action, condition;
|
||||
condition = function(token, i) {
|
||||
var _ref;
|
||||
return ((_ref = token[0]) === ')' || _ref === 'CALL_END') || token[0] === 'OUTDENT' && this.tag(i - 1) === ')';
|
||||
exports.Rewriter = Rewriter = function() {
|
||||
function Rewriter() {}
|
||||
Rewriter.prototype.rewrite = function(_arg) {
|
||||
this.tokens = _arg;
|
||||
this.removeLeadingNewlines();
|
||||
this.removeMidExpressionNewlines();
|
||||
this.closeOpenCalls();
|
||||
this.closeOpenIndexes();
|
||||
this.addImplicitIndentation();
|
||||
this.tagPostfixConditionals();
|
||||
this.addImplicitBraces();
|
||||
this.addImplicitParentheses();
|
||||
this.ensureBalance(BALANCED_PAIRS);
|
||||
this.rewriteClosingParens();
|
||||
return this.tokens;
|
||||
};
|
||||
action = function(token, i) {
|
||||
return this.tokens[token[0] === 'OUTDENT' ? i - 1 : i][0] = 'CALL_END';
|
||||
Rewriter.prototype.scanTokens = function(block) {
|
||||
var i, token, tokens;
|
||||
tokens = this.tokens;
|
||||
i = 0;
|
||||
while (token = tokens[i]) {
|
||||
i += block.call(this, token, i, tokens);
|
||||
}
|
||||
return true;
|
||||
};
|
||||
return this.scanTokens(function(token, i) {
|
||||
if (token[0] === 'CALL_START') {
|
||||
this.detectEnd(i + 1, condition, action);
|
||||
}
|
||||
return 1;
|
||||
});
|
||||
};
|
||||
exports.Rewriter.prototype.closeOpenIndexes = function() {
|
||||
var action, condition;
|
||||
condition = function(token, i) {
|
||||
var _ref;
|
||||
return (_ref = token[0]) === ']' || _ref === 'INDEX_END';
|
||||
};
|
||||
action = function(token, i) {
|
||||
return token[0] = 'INDEX_END';
|
||||
};
|
||||
return this.scanTokens(function(token, i) {
|
||||
if (token[0] === 'INDEX_START') {
|
||||
this.detectEnd(i + 1, condition, action);
|
||||
}
|
||||
return 1;
|
||||
});
|
||||
};
|
||||
exports.Rewriter.prototype.addImplicitBraces = function() {
|
||||
var action, condition, stack, start;
|
||||
stack = [];
|
||||
start = null;
|
||||
condition = function(token, i) {
|
||||
var one, tag, three, two, _ref, _ref2;
|
||||
if ('HERECOMMENT' === this.tag(i + 1) || 'HERECOMMENT' === this.tag(i - 1)) {
|
||||
return false;
|
||||
}
|
||||
_ref = this.tokens, one = _ref[i + 1], two = _ref[i + 2], three = _ref[i + 3];
|
||||
tag = token[0];
|
||||
return (tag === 'TERMINATOR' || tag === 'OUTDENT') && !((two != null ? two[0] : void 0) === ':' || (one != null ? one[0] : void 0) === '@' && (three != null ? three[0] : void 0) === ':') || tag === ',' && one && ((_ref2 = one[0]) !== 'IDENTIFIER' && _ref2 !== 'NUMBER' && _ref2 !== 'STRING' && _ref2 !== '@' && _ref2 !== 'TERMINATOR' && _ref2 !== 'OUTDENT' && _ref2 !== '(');
|
||||
};
|
||||
action = function(token, i) {
|
||||
return this.tokens.splice(i, 0, ['}', '}', token[2]]);
|
||||
};
|
||||
return this.scanTokens(function(token, i, tokens) {
|
||||
var ago1, ago2, idx, tag, tok, _ref, _ref2;
|
||||
if (_ref = (tag = token[0]), __indexOf.call(EXPRESSION_START, _ref) >= 0) {
|
||||
stack.push([(tag === 'INDENT' && this.tag(i - 1) === '{' ? '{' : tag), i]);
|
||||
return 1;
|
||||
}
|
||||
if (__indexOf.call(EXPRESSION_END, tag) >= 0) {
|
||||
start = stack.pop();
|
||||
return 1;
|
||||
}
|
||||
if (!(tag === ':' && ((ago2 = this.tag(i - 2)) === ':' || (ago1 = this.tag(i - 1)) === ')' && this.tag(start[1] - 1) === ':' || ((_ref2 = stack[stack.length - 1]) != null ? _ref2[0] : void 0) !== '{'))) {
|
||||
return 1;
|
||||
}
|
||||
stack.push(['{']);
|
||||
idx = ago1 === ')' ? start[1] : ago2 === '@' ? i - 2 : i - 1;
|
||||
if (this.tag(idx - 2) === 'HERECOMMENT') {
|
||||
idx -= 2;
|
||||
}
|
||||
tok = ['{', '{', token[2]];
|
||||
tok.generated = true;
|
||||
tokens.splice(idx, 0, tok);
|
||||
this.detectEnd(i + 2, condition, action);
|
||||
return 2;
|
||||
});
|
||||
};
|
||||
exports.Rewriter.prototype.addImplicitParentheses = function() {
|
||||
var action, classLine;
|
||||
classLine = false;
|
||||
action = function(token, i) {
|
||||
var idx;
|
||||
idx = token[0] === 'OUTDENT' ? i + 1 : i;
|
||||
return this.tokens.splice(idx, 0, ['CALL_END', ')', token[2]]);
|
||||
};
|
||||
return this.scanTokens(function(token, i, tokens) {
|
||||
var callObject, next, prev, seenSingle, tag, _ref, _ref2;
|
||||
tag = token[0];
|
||||
if (tag === 'CLASS') {
|
||||
classLine = true;
|
||||
}
|
||||
prev = tokens[i - 1], next = tokens[i + 1];
|
||||
callObject = !classLine && tag === 'INDENT' && next && next.generated && next[0] === '{' && prev && (_ref = prev[0], __indexOf.call(IMPLICIT_FUNC, _ref) >= 0);
|
||||
seenSingle = false;
|
||||
if (__indexOf.call(LINEBREAKS, tag) >= 0) {
|
||||
classLine = false;
|
||||
}
|
||||
if (prev && !prev.spaced && tag === '?') {
|
||||
token.call = true;
|
||||
}
|
||||
if (!(callObject || (prev != null ? prev.spaced : void 0) && (prev.call || (_ref2 = prev[0], __indexOf.call(IMPLICIT_FUNC, _ref2) >= 0)) && (__indexOf.call(IMPLICIT_CALL, tag) >= 0 || !(token.spaced || token.newLine) && __indexOf.call(IMPLICIT_UNSPACED_CALL, tag) >= 0))) {
|
||||
return 1;
|
||||
}
|
||||
tokens.splice(i, 0, ['CALL_START', '(', token[2]]);
|
||||
this.detectEnd(i + (callObject ? 2 : 1), function(token, i) {
|
||||
var post, _ref;
|
||||
if (!seenSingle && token.fromThen) {
|
||||
return true;
|
||||
Rewriter.prototype.detectEnd = function(i, condition, action) {
|
||||
var levels, token, tokens, _ref, _ref2;
|
||||
tokens = this.tokens;
|
||||
levels = 0;
|
||||
while (token = tokens[i]) {
|
||||
if (levels === 0 && condition.call(this, token, i)) {
|
||||
return action.call(this, token, i);
|
||||
}
|
||||
tag = token[0];
|
||||
if (tag === 'IF' || tag === 'ELSE' || tag === 'UNLESS' || tag === '->' || tag === '=>') {
|
||||
seenSingle = true;
|
||||
if (!token || levels < 0) {
|
||||
return action.call(this, token, i - 1);
|
||||
}
|
||||
if ((tag === '.' || tag === '?.' || tag === '::') && this.tag(i - 1) === 'OUTDENT') {
|
||||
return true;
|
||||
if (_ref = token[0], __indexOf.call(EXPRESSION_START, _ref) >= 0) {
|
||||
levels += 1;
|
||||
} else if (_ref2 = token[0], __indexOf.call(EXPRESSION_END, _ref2) >= 0) {
|
||||
levels -= 1;
|
||||
}
|
||||
return !token.generated && this.tag(i - 1) !== ',' && __indexOf.call(IMPLICIT_END, tag) >= 0 && (tag !== 'INDENT' || (this.tag(i - 2) !== 'CLASS' && (_ref = this.tag(i - 1), __indexOf.call(IMPLICIT_BLOCK, _ref) < 0) && !((post = this.tokens[i + 1]) && post.generated && post[0] === '{')));
|
||||
}, action);
|
||||
if (prev[0] === '?') {
|
||||
prev[0] = 'FUNC_EXIST';
|
||||
i += 1;
|
||||
}
|
||||
return 2;
|
||||
});
|
||||
};
|
||||
exports.Rewriter.prototype.addImplicitIndentation = function() {
|
||||
return this.scanTokens(function(token, i, tokens) {
|
||||
var action, condition, indent, outdent, starter, tag, _ref, _ref2;
|
||||
tag = token[0];
|
||||
if (tag === 'TERMINATOR' && this.tag(i + 1) === 'THEN') {
|
||||
return i - 1;
|
||||
};
|
||||
Rewriter.prototype.removeLeadingNewlines = function() {
|
||||
var i, tag, _len;
|
||||
for (i = 0, _len = this.tokens.length; i < _len; i++) {
|
||||
tag = this.tokens[i][0];
|
||||
if (tag !== 'TERMINATOR') {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (i) {
|
||||
return this.tokens.splice(0, i);
|
||||
}
|
||||
};
|
||||
Rewriter.prototype.removeMidExpressionNewlines = function() {
|
||||
return this.scanTokens(function(token, i, tokens) {
|
||||
var _ref;
|
||||
if (!(token[0] === 'TERMINATOR' && (_ref = this.tag(i + 1), __indexOf.call(EXPRESSION_CLOSE, _ref) >= 0))) {
|
||||
return 1;
|
||||
}
|
||||
tokens.splice(i, 1);
|
||||
return 0;
|
||||
}
|
||||
if (tag === 'ELSE' && this.tag(i - 1) !== 'OUTDENT') {
|
||||
tokens.splice.apply(tokens, [i, 0].concat(__slice.call(this.indentation(token))));
|
||||
return 2;
|
||||
}
|
||||
if (tag === 'CATCH' && ((_ref = this.tag(i + 2)) === 'OUTDENT' || _ref === 'TERMINATOR' || _ref === 'FINALLY')) {
|
||||
tokens.splice.apply(tokens, [i + 2, 0].concat(__slice.call(this.indentation(token))));
|
||||
return 4;
|
||||
}
|
||||
if (__indexOf.call(SINGLE_LINERS, tag) >= 0 && this.tag(i + 1) !== 'INDENT' && !(tag === 'ELSE' && this.tag(i + 1) === 'IF')) {
|
||||
starter = tag;
|
||||
_ref2 = this.indentation(token), indent = _ref2[0], outdent = _ref2[1];
|
||||
if (starter === 'THEN') {
|
||||
indent.fromThen = true;
|
||||
}
|
||||
indent.generated = outdent.generated = true;
|
||||
tokens.splice(i + 1, 0, indent);
|
||||
condition = function(token, i) {
|
||||
var _ref;
|
||||
return token[1] !== ';' && (_ref = token[0], __indexOf.call(SINGLE_CLOSERS, _ref) >= 0) && !(token[0] === 'ELSE' && (starter !== 'IF' && starter !== 'THEN'));
|
||||
};
|
||||
action = function(token, i) {
|
||||
return this.tokens.splice((this.tag(i - 1) === ',' ? i - 1 : i), 0, outdent);
|
||||
};
|
||||
this.detectEnd(i + 2, condition, action);
|
||||
if (tag === 'THEN') {
|
||||
tokens.splice(i, 1);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
return 1;
|
||||
});
|
||||
};
|
||||
exports.Rewriter.prototype.tagPostfixConditionals = function() {
|
||||
var condition;
|
||||
condition = function(token, i) {
|
||||
var _ref;
|
||||
return (_ref = token[0]) === 'TERMINATOR' || _ref === 'INDENT';
|
||||
};
|
||||
return this.scanTokens(function(token, i) {
|
||||
var original, _ref;
|
||||
if ((_ref = token[0]) !== 'IF' && _ref !== 'UNLESS') {
|
||||
return 1;
|
||||
}
|
||||
original = token;
|
||||
this.detectEnd(i + 1, condition, function(token, i) {
|
||||
if (token[0] !== 'INDENT') {
|
||||
return original[0] = 'POST_' + original[0];
|
||||
}
|
||||
});
|
||||
return 1;
|
||||
});
|
||||
};
|
||||
exports.Rewriter.prototype.ensureBalance = function(pairs) {
|
||||
var close, level, levels, open, openLine, tag, token, _i, _j, _len, _len2, _ref, _ref2;
|
||||
levels = {};
|
||||
openLine = {};
|
||||
_ref = this.tokens;
|
||||
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
|
||||
token = _ref[_i];
|
||||
tag = token[0];
|
||||
for (_j = 0, _len2 = pairs.length; _j < _len2; _j++) {
|
||||
_ref2 = pairs[_j], open = _ref2[0], close = _ref2[1];
|
||||
levels[open] |= 0;
|
||||
if (tag === open) {
|
||||
if (levels[open]++ === 0) {
|
||||
openLine[open] = token[2];
|
||||
};
|
||||
Rewriter.prototype.closeOpenCalls = function() {
|
||||
var action, condition;
|
||||
condition = function(token, i) {
|
||||
var _ref;
|
||||
return ((_ref = token[0]) === ')' || _ref === 'CALL_END') || token[0] === 'OUTDENT' && this.tag(i - 1) === ')';
|
||||
};
|
||||
action = function(token, i) {
|
||||
return this.tokens[token[0] === 'OUTDENT' ? i - 1 : i][0] = 'CALL_END';
|
||||
};
|
||||
return this.scanTokens(function(token, i) {
|
||||
if (token[0] === 'CALL_START') {
|
||||
this.detectEnd(i + 1, condition, action);
|
||||
}
|
||||
return 1;
|
||||
});
|
||||
};
|
||||
Rewriter.prototype.closeOpenIndexes = function() {
|
||||
var action, condition;
|
||||
condition = function(token, i) {
|
||||
var _ref;
|
||||
return (_ref = token[0]) === ']' || _ref === 'INDEX_END';
|
||||
};
|
||||
action = function(token, i) {
|
||||
return token[0] = 'INDEX_END';
|
||||
};
|
||||
return this.scanTokens(function(token, i) {
|
||||
if (token[0] === 'INDEX_START') {
|
||||
this.detectEnd(i + 1, condition, action);
|
||||
}
|
||||
return 1;
|
||||
});
|
||||
};
|
||||
Rewriter.prototype.addImplicitBraces = function() {
|
||||
var action, condition, stack, start;
|
||||
stack = [];
|
||||
start = null;
|
||||
condition = function(token, i) {
|
||||
var one, tag, three, two, _ref, _ref2;
|
||||
if ('HERECOMMENT' === this.tag(i + 1) || 'HERECOMMENT' === this.tag(i - 1)) {
|
||||
return false;
|
||||
}
|
||||
_ref = this.tokens, one = _ref[i + 1], two = _ref[i + 2], three = _ref[i + 3];
|
||||
tag = token[0];
|
||||
return (tag === 'TERMINATOR' || tag === 'OUTDENT') && !((two != null ? two[0] : void 0) === ':' || (one != null ? one[0] : void 0) === '@' && (three != null ? three[0] : void 0) === ':') || tag === ',' && one && ((_ref2 = one[0]) !== 'IDENTIFIER' && _ref2 !== 'NUMBER' && _ref2 !== 'STRING' && _ref2 !== '@' && _ref2 !== 'TERMINATOR' && _ref2 !== 'OUTDENT' && _ref2 !== '(');
|
||||
};
|
||||
action = function(token, i) {
|
||||
return this.tokens.splice(i, 0, ['}', '}', token[2]]);
|
||||
};
|
||||
return this.scanTokens(function(token, i, tokens) {
|
||||
var ago1, ago2, idx, tag, tok, _ref, _ref2;
|
||||
if (_ref = (tag = token[0]), __indexOf.call(EXPRESSION_START, _ref) >= 0) {
|
||||
stack.push([(tag === 'INDENT' && this.tag(i - 1) === '{' ? '{' : tag), i]);
|
||||
return 1;
|
||||
}
|
||||
if (__indexOf.call(EXPRESSION_END, tag) >= 0) {
|
||||
start = stack.pop();
|
||||
return 1;
|
||||
}
|
||||
if (!(tag === ':' && ((ago2 = this.tag(i - 2)) === ':' || (ago1 = this.tag(i - 1)) === ')' && this.tag(start[1] - 1) === ':' || ((_ref2 = stack[stack.length - 1]) != null ? _ref2[0] : void 0) !== '{'))) {
|
||||
return 1;
|
||||
}
|
||||
stack.push(['{']);
|
||||
idx = ago1 === ')' ? start[1] : ago2 === '@' ? i - 2 : i - 1;
|
||||
if (this.tag(idx - 2) === 'HERECOMMENT') {
|
||||
idx -= 2;
|
||||
}
|
||||
tok = ['{', '{', token[2]];
|
||||
tok.generated = true;
|
||||
tokens.splice(idx, 0, tok);
|
||||
this.detectEnd(i + 2, condition, action);
|
||||
return 2;
|
||||
});
|
||||
};
|
||||
Rewriter.prototype.addImplicitParentheses = function() {
|
||||
var action, classLine;
|
||||
classLine = false;
|
||||
action = function(token, i) {
|
||||
var idx;
|
||||
idx = token[0] === 'OUTDENT' ? i + 1 : i;
|
||||
return this.tokens.splice(idx, 0, ['CALL_END', ')', token[2]]);
|
||||
};
|
||||
return this.scanTokens(function(token, i, tokens) {
|
||||
var callObject, next, prev, seenSingle, tag, _ref, _ref2;
|
||||
tag = token[0];
|
||||
if (tag === 'CLASS') {
|
||||
classLine = true;
|
||||
}
|
||||
prev = tokens[i - 1], next = tokens[i + 1];
|
||||
callObject = !classLine && tag === 'INDENT' && next && next.generated && next[0] === '{' && prev && (_ref = prev[0], __indexOf.call(IMPLICIT_FUNC, _ref) >= 0);
|
||||
seenSingle = false;
|
||||
if (__indexOf.call(LINEBREAKS, tag) >= 0) {
|
||||
classLine = false;
|
||||
}
|
||||
if (prev && !prev.spaced && tag === '?') {
|
||||
token.call = true;
|
||||
}
|
||||
if (!(callObject || (prev != null ? prev.spaced : void 0) && (prev.call || (_ref2 = prev[0], __indexOf.call(IMPLICIT_FUNC, _ref2) >= 0)) && (__indexOf.call(IMPLICIT_CALL, tag) >= 0 || !(token.spaced || token.newLine) && __indexOf.call(IMPLICIT_UNSPACED_CALL, tag) >= 0))) {
|
||||
return 1;
|
||||
}
|
||||
tokens.splice(i, 0, ['CALL_START', '(', token[2]]);
|
||||
this.detectEnd(i + (callObject ? 2 : 1), function(token, i) {
|
||||
var post, _ref;
|
||||
if (!seenSingle && token.fromThen) {
|
||||
return true;
|
||||
}
|
||||
tag = token[0];
|
||||
if (tag === 'IF' || tag === 'ELSE' || tag === 'UNLESS' || tag === '->' || tag === '=>') {
|
||||
seenSingle = true;
|
||||
}
|
||||
if ((tag === '.' || tag === '?.' || tag === '::') && this.tag(i - 1) === 'OUTDENT') {
|
||||
return true;
|
||||
}
|
||||
return !token.generated && this.tag(i - 1) !== ',' && __indexOf.call(IMPLICIT_END, tag) >= 0 && (tag !== 'INDENT' || (this.tag(i - 2) !== 'CLASS' && (_ref = this.tag(i - 1), __indexOf.call(IMPLICIT_BLOCK, _ref) < 0) && !((post = this.tokens[i + 1]) && post.generated && post[0] === '{')));
|
||||
}, action);
|
||||
if (prev[0] === '?') {
|
||||
prev[0] = 'FUNC_EXIST';
|
||||
}
|
||||
return 2;
|
||||
});
|
||||
};
|
||||
Rewriter.prototype.addImplicitIndentation = function() {
|
||||
return this.scanTokens(function(token, i, tokens) {
|
||||
var action, condition, indent, outdent, starter, tag, _ref, _ref2;
|
||||
tag = token[0];
|
||||
if (tag === 'TERMINATOR' && this.tag(i + 1) === 'THEN') {
|
||||
tokens.splice(i, 1);
|
||||
return 0;
|
||||
}
|
||||
if (tag === 'ELSE' && this.tag(i - 1) !== 'OUTDENT') {
|
||||
tokens.splice.apply(tokens, [i, 0].concat(__slice.call(this.indentation(token))));
|
||||
return 2;
|
||||
}
|
||||
if (tag === 'CATCH' && ((_ref = this.tag(i + 2)) === 'OUTDENT' || _ref === 'TERMINATOR' || _ref === 'FINALLY')) {
|
||||
tokens.splice.apply(tokens, [i + 2, 0].concat(__slice.call(this.indentation(token))));
|
||||
return 4;
|
||||
}
|
||||
if (__indexOf.call(SINGLE_LINERS, tag) >= 0 && this.tag(i + 1) !== 'INDENT' && !(tag === 'ELSE' && this.tag(i + 1) === 'IF')) {
|
||||
starter = tag;
|
||||
_ref2 = this.indentation(token), indent = _ref2[0], outdent = _ref2[1];
|
||||
if (starter === 'THEN') {
|
||||
indent.fromThen = true;
|
||||
}
|
||||
indent.generated = outdent.generated = true;
|
||||
tokens.splice(i + 1, 0, indent);
|
||||
condition = function(token, i) {
|
||||
var _ref;
|
||||
return token[1] !== ';' && (_ref = token[0], __indexOf.call(SINGLE_CLOSERS, _ref) >= 0) && !(token[0] === 'ELSE' && (starter !== 'IF' && starter !== 'THEN'));
|
||||
};
|
||||
action = function(token, i) {
|
||||
return this.tokens.splice((this.tag(i - 1) === ',' ? i - 1 : i), 0, outdent);
|
||||
};
|
||||
this.detectEnd(i + 2, condition, action);
|
||||
if (tag === 'THEN') {
|
||||
tokens.splice(i, 1);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
return 1;
|
||||
});
|
||||
};
|
||||
Rewriter.prototype.tagPostfixConditionals = function() {
|
||||
var condition;
|
||||
condition = function(token, i) {
|
||||
var _ref;
|
||||
return (_ref = token[0]) === 'TERMINATOR' || _ref === 'INDENT';
|
||||
};
|
||||
return this.scanTokens(function(token, i) {
|
||||
var original, _ref;
|
||||
if ((_ref = token[0]) !== 'IF' && _ref !== 'UNLESS') {
|
||||
return 1;
|
||||
}
|
||||
original = token;
|
||||
this.detectEnd(i + 1, condition, function(token, i) {
|
||||
if (token[0] !== 'INDENT') {
|
||||
return original[0] = 'POST_' + original[0];
|
||||
}
|
||||
});
|
||||
return 1;
|
||||
});
|
||||
};
|
||||
Rewriter.prototype.ensureBalance = function(pairs) {
|
||||
var close, level, levels, open, openLine, tag, token, _i, _j, _len, _len2, _ref, _ref2;
|
||||
levels = {};
|
||||
openLine = {};
|
||||
_ref = this.tokens;
|
||||
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
|
||||
token = _ref[_i];
|
||||
tag = token[0];
|
||||
for (_j = 0, _len2 = pairs.length; _j < _len2; _j++) {
|
||||
_ref2 = pairs[_j], open = _ref2[0], close = _ref2[1];
|
||||
levels[open] |= 0;
|
||||
if (tag === open) {
|
||||
if (levels[open]++ === 0) {
|
||||
openLine[open] = token[2];
|
||||
}
|
||||
} else if (tag === close && --levels[open] < 0) {
|
||||
throw Error("too many " + token[1] + " on line " + (token[2] + 1));
|
||||
}
|
||||
} else if (tag === close && --levels[open] < 0) {
|
||||
throw Error("too many " + token[1] + " on line " + (token[2] + 1));
|
||||
}
|
||||
}
|
||||
}
|
||||
for (open in levels) {
|
||||
level = levels[open];
|
||||
if (level > 0) {
|
||||
throw Error("unclosed " + open + " on line " + (openLine[open] + 1));
|
||||
for (open in levels) {
|
||||
level = levels[open];
|
||||
if (level > 0) {
|
||||
throw Error("unclosed " + open + " on line " + (openLine[open] + 1));
|
||||
}
|
||||
}
|
||||
}
|
||||
return this;
|
||||
};
|
||||
exports.Rewriter.prototype.rewriteClosingParens = function() {
|
||||
var debt, key, stack;
|
||||
stack = [];
|
||||
debt = {};
|
||||
for (key in INVERSES) {
|
||||
debt[key] = 0;
|
||||
}
|
||||
return this.scanTokens(function(token, i, tokens) {
|
||||
var inv, match, mtag, oppos, tag, val, _ref;
|
||||
if (_ref = (tag = token[0]), __indexOf.call(EXPRESSION_START, _ref) >= 0) {
|
||||
stack.push(token);
|
||||
return this;
|
||||
};
|
||||
Rewriter.prototype.rewriteClosingParens = function() {
|
||||
var debt, key, stack;
|
||||
stack = [];
|
||||
debt = {};
|
||||
for (key in INVERSES) {
|
||||
debt[key] = 0;
|
||||
}
|
||||
return this.scanTokens(function(token, i, tokens) {
|
||||
var inv, match, mtag, oppos, tag, val, _ref;
|
||||
if (_ref = (tag = token[0]), __indexOf.call(EXPRESSION_START, _ref) >= 0) {
|
||||
stack.push(token);
|
||||
return 1;
|
||||
}
|
||||
if (__indexOf.call(EXPRESSION_END, tag) < 0) {
|
||||
return 1;
|
||||
}
|
||||
if (debt[inv = INVERSES[tag]] > 0) {
|
||||
debt[inv] -= 1;
|
||||
tokens.splice(i, 1);
|
||||
return 0;
|
||||
}
|
||||
match = stack.pop();
|
||||
mtag = match[0];
|
||||
oppos = INVERSES[mtag];
|
||||
if (tag === oppos) {
|
||||
return 1;
|
||||
}
|
||||
debt[mtag] += 1;
|
||||
val = [oppos, mtag === 'INDENT' ? match[1] : oppos];
|
||||
if (this.tag(i + 2) === mtag) {
|
||||
tokens.splice(i + 3, 0, val);
|
||||
stack.push(match);
|
||||
} else {
|
||||
tokens.splice(i, 0, val);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
if (__indexOf.call(EXPRESSION_END, tag) < 0) {
|
||||
return 1;
|
||||
}
|
||||
if (debt[inv = INVERSES[tag]] > 0) {
|
||||
debt[inv] -= 1;
|
||||
tokens.splice(i, 1);
|
||||
return 0;
|
||||
}
|
||||
match = stack.pop();
|
||||
mtag = match[0];
|
||||
oppos = INVERSES[mtag];
|
||||
if (tag === oppos) {
|
||||
return 1;
|
||||
}
|
||||
debt[mtag] += 1;
|
||||
val = [oppos, mtag === 'INDENT' ? match[1] : oppos];
|
||||
if (this.tag(i + 2) === mtag) {
|
||||
tokens.splice(i + 3, 0, val);
|
||||
stack.push(match);
|
||||
} else {
|
||||
tokens.splice(i, 0, val);
|
||||
}
|
||||
return 1;
|
||||
});
|
||||
};
|
||||
exports.Rewriter.prototype.indentation = function(token) {
|
||||
return [['INDENT', 2, token[2]], ['OUTDENT', 2, token[2]]];
|
||||
};
|
||||
exports.Rewriter.prototype.tag = function(i) {
|
||||
var _ref;
|
||||
return (_ref = this.tokens[i]) != null ? _ref[0] : void 0;
|
||||
};
|
||||
});
|
||||
};
|
||||
Rewriter.prototype.indentation = function(token) {
|
||||
return [['INDENT', 2, token[2]], ['OUTDENT', 2, token[2]]];
|
||||
};
|
||||
Rewriter.prototype.tag = function(i) {
|
||||
var _ref;
|
||||
return (_ref = this.tokens[i]) != null ? _ref[0] : void 0;
|
||||
};
|
||||
return Rewriter;
|
||||
}();
|
||||
BALANCED_PAIRS = [['(', ')'], ['[', ']'], ['{', '}'], ['INDENT', 'OUTDENT'], ['CALL_START', 'CALL_END'], ['PARAM_START', 'PARAM_END'], ['INDEX_START', 'INDEX_END']];
|
||||
INVERSES = {};
|
||||
EXPRESSION_START = [];
|
||||
|
|
40
lib/scope.js
40
lib/scope.js
|
@ -1,29 +1,25 @@
|
|||
(function() {
|
||||
var Scope, extend, last, _ref;
|
||||
_ref = require('./helpers'), extend = _ref.extend, last = _ref.last;
|
||||
exports.Scope = (function() {
|
||||
Scope = (function() {
|
||||
function Scope(_arg, _arg2, _arg3) {
|
||||
this.parent = _arg;
|
||||
this.expressions = _arg2;
|
||||
this.method = _arg3;
|
||||
this.variables = [
|
||||
{
|
||||
name: 'arguments',
|
||||
type: 'arguments'
|
||||
}
|
||||
];
|
||||
this.positions = {};
|
||||
if (this.parent) {
|
||||
this.garbage = this.parent.garbage;
|
||||
} else {
|
||||
this.garbage = [];
|
||||
Scope.root = this;
|
||||
exports.Scope = Scope = function() {
|
||||
function Scope(_arg, _arg2, _arg3) {
|
||||
this.parent = _arg;
|
||||
this.expressions = _arg2;
|
||||
this.method = _arg3;
|
||||
this.variables = [
|
||||
{
|
||||
name: 'arguments',
|
||||
type: 'arguments'
|
||||
}
|
||||
return this;
|
||||
];
|
||||
this.positions = {};
|
||||
if (this.parent) {
|
||||
this.garbage = this.parent.garbage;
|
||||
} else {
|
||||
this.garbage = [];
|
||||
Scope.root = this;
|
||||
}
|
||||
return Scope;
|
||||
})();
|
||||
}
|
||||
Scope.root = null;
|
||||
Scope.prototype.add = function(name, type) {
|
||||
var pos;
|
||||
|
@ -159,5 +155,5 @@
|
|||
return this.assignedVariables().join(', ');
|
||||
};
|
||||
return Scope;
|
||||
}).call(this);
|
||||
}();
|
||||
}).call(this);
|
||||
|
|
|
@ -109,7 +109,6 @@ grammar =
|
|||
Block: [
|
||||
o 'INDENT Body OUTDENT', -> $2
|
||||
o 'INDENT OUTDENT', -> new Expressions
|
||||
o 'TERMINATOR Comment', -> Expressions.wrap [$2]
|
||||
]
|
||||
|
||||
# A literal identifier, a variable name or property.
|
||||
|
@ -275,32 +274,14 @@ grammar =
|
|||
# Class definitions have optional bodies of prototype property assignments,
|
||||
# and optional references to the superclass.
|
||||
Class: [
|
||||
o 'CLASS SimpleAssignable', -> new Class $2
|
||||
o 'CLASS SimpleAssignable EXTENDS Value', -> new Class $2, $4
|
||||
o 'CLASS SimpleAssignable
|
||||
INDENT ClassBody OUTDENT', -> new Class $2, null, $4
|
||||
o 'CLASS SimpleAssignable EXTENDS Value
|
||||
INDENT ClassBody OUTDENT', -> new Class $2, $4, $6
|
||||
o 'CLASS INDENT ClassBody OUTDENT', -> new Class null, null, $3
|
||||
o 'CLASS', -> new Class null, null, new Expressions
|
||||
o 'CLASS EXTENDS Value', -> new Class null, $3 , new Expressions
|
||||
o 'CLASS EXTENDS Value
|
||||
INDENT ClassBody OUTDENT', -> new Class null, $3, $5
|
||||
]
|
||||
|
||||
# Assignments that can happen directly inside a class declaration.
|
||||
ClassAssign: [
|
||||
o 'AssignObj', -> $1
|
||||
o 'ThisProperty : Expression', -> new Assign new Value($1), $3, 'this'
|
||||
o 'ThisProperty : INDENT Expression OUTDENT', -> new Assign new Value($1), $4, 'this'
|
||||
]
|
||||
|
||||
# A list of assignments to a class.
|
||||
ClassBody: [
|
||||
o '', -> []
|
||||
o 'ClassAssign', -> [$1]
|
||||
o 'ClassBody TERMINATOR ClassAssign', -> $1.concat $3
|
||||
o '{ ClassBody }', -> $2
|
||||
o 'CLASS', -> new Class
|
||||
o 'CLASS Block', -> new Class null, null, $2
|
||||
o 'CLASS EXTENDS Value', -> new Class null, $3
|
||||
o 'CLASS EXTENDS Value Block', -> new Class null, $3, $4
|
||||
o 'CLASS SimpleAssignable', -> new Class $2
|
||||
o 'CLASS SimpleAssignable Block', -> new Class $2, null, $3
|
||||
o 'CLASS SimpleAssignable EXTENDS Value', -> new Class $2, $4
|
||||
o 'CLASS SimpleAssignable EXTENDS Value Block', -> new Class $2, $4, $5
|
||||
]
|
||||
|
||||
# Ordinary function invocation, or a chained series of calls.
|
||||
|
|
238
src/nodes.coffee
238
src/nodes.coffee
|
@ -11,13 +11,10 @@
|
|||
exports.extend = extend # for parser
|
||||
|
||||
# Constant functions for nodes that don't need customization.
|
||||
YES = -> yes
|
||||
NO = -> no
|
||||
THIS = -> this
|
||||
|
||||
# Default implementations of common functions.
|
||||
RETURN = -> @returns = yes; this
|
||||
NEGATE = -> @negated = not @negated; this
|
||||
YES = -> yes
|
||||
NO = -> no
|
||||
THIS = -> this
|
||||
NEGATE = -> @negated = not @negated; this
|
||||
|
||||
#### Base
|
||||
|
||||
|
@ -160,7 +157,7 @@ exports.Expressions = class Expressions extends Base
|
|||
|
||||
children: ['expressions']
|
||||
|
||||
constructor: (nodes) ->
|
||||
(nodes) ->
|
||||
@expressions = compact flatten nodes or []
|
||||
|
||||
# Tack an expression on to the end of this expression list.
|
||||
|
@ -249,7 +246,7 @@ exports.Expressions = class Expressions extends Base
|
|||
|
||||
# Wrap up the given nodes as an **Expressions**, unless it already happens
|
||||
# to be one.
|
||||
@wrap: (nodes) ->
|
||||
@wrap = (nodes) ->
|
||||
return nodes[0] if nodes.length is 1 and nodes[0] instanceof Expressions
|
||||
new Expressions nodes
|
||||
|
||||
|
@ -260,7 +257,7 @@ exports.Expressions = class Expressions extends Base
|
|||
# `true`, `false`, `null`...
|
||||
exports.Literal = class Literal extends Base
|
||||
|
||||
constructor: (@value) ->
|
||||
(@value) ->
|
||||
|
||||
makeReturn: ->
|
||||
if @isPureStatement() then this else new Return this
|
||||
|
@ -295,7 +292,7 @@ exports.Return = class Return extends Base
|
|||
isStatement : YES
|
||||
isPureStatement: YES
|
||||
|
||||
constructor: (@expression) ->
|
||||
(@expression) ->
|
||||
|
||||
makeReturn: THIS
|
||||
|
||||
|
@ -316,7 +313,7 @@ exports.Value = class Value extends Base
|
|||
children: ['base', 'properties']
|
||||
|
||||
# A **Value** has a base and a list of property accesses.
|
||||
constructor: (base, props, tag) ->
|
||||
(base, props, tag) ->
|
||||
return base if not props and base instanceof Value
|
||||
@base = base
|
||||
@properties = props or []
|
||||
|
@ -404,7 +401,7 @@ exports.Value = class Value extends Base
|
|||
# at the same position.
|
||||
exports.Comment = class Comment extends Base
|
||||
|
||||
constructor: (@comment) ->
|
||||
(@comment) ->
|
||||
|
||||
isPureStatement: YES
|
||||
isStatement: YES
|
||||
|
@ -423,7 +420,7 @@ exports.Call = class Call extends Base
|
|||
|
||||
children: ['variable', 'args']
|
||||
|
||||
constructor: (variable, @args = [], @soak) ->
|
||||
(variable, @args = [], @soak) ->
|
||||
@isNew = false
|
||||
@isSuper = variable is 'super'
|
||||
@variable = if @isSuper then null else variable
|
||||
|
@ -448,7 +445,7 @@ exports.Call = class Call extends Base
|
|||
unfoldSoak: (o) ->
|
||||
if @soak
|
||||
if @variable
|
||||
return ifn if ifn = If.unfoldSoak o, this, 'variable'
|
||||
return ifn if ifn = unfoldSoak o, this, 'variable'
|
||||
[left, rite] = new Value(@variable).cacheReference o
|
||||
else
|
||||
left = new Literal @superReference o
|
||||
|
@ -473,7 +470,7 @@ exports.Call = class Call extends Base
|
|||
call.variable = ifn
|
||||
else
|
||||
call.variable.base = ifn
|
||||
ifn = If.unfoldSoak o, call, 'variable'
|
||||
ifn = unfoldSoak o, call, 'variable'
|
||||
ifn
|
||||
|
||||
# Compile a vanilla function call.
|
||||
|
@ -525,7 +522,7 @@ exports.Extends = class Extends extends Base
|
|||
|
||||
children: ['child', 'parent']
|
||||
|
||||
constructor: (@child, @parent) ->
|
||||
(@child, @parent) ->
|
||||
|
||||
# Hooks one constructor into another's prototype chain.
|
||||
compile: (o) ->
|
||||
|
@ -539,7 +536,7 @@ exports.Accessor = class Accessor extends Base
|
|||
|
||||
children: ['name']
|
||||
|
||||
constructor: (@name, tag) ->
|
||||
(@name, tag) ->
|
||||
@proto = if tag is 'proto' then '.prototype' else ''
|
||||
@soak = tag is 'soak'
|
||||
|
||||
|
@ -556,7 +553,7 @@ exports.Index = class Index extends Base
|
|||
|
||||
children: ['index']
|
||||
|
||||
constructor: (@index) ->
|
||||
(@index) ->
|
||||
|
||||
compile: (o) ->
|
||||
(if @proto then '.prototype' else '') + "[#{ @index.compile o, LEVEL_PAREN }]"
|
||||
|
@ -571,7 +568,7 @@ exports.Obj = class Obj extends Base
|
|||
|
||||
children: ['properties']
|
||||
|
||||
constructor: (props) ->
|
||||
(props) ->
|
||||
@objects = @properties = props or []
|
||||
|
||||
compileNode: (o) ->
|
||||
|
@ -635,7 +632,7 @@ exports.Arr = class Arr extends Base
|
|||
|
||||
children: ['objects']
|
||||
|
||||
constructor: (objs) ->
|
||||
(objs) ->
|
||||
@objects = objs or []
|
||||
|
||||
compileNode: (o) ->
|
||||
|
@ -657,78 +654,62 @@ exports.Arr = class Arr extends Base
|
|||
# The CoffeeScript class definition.
|
||||
exports.Class = class Class extends Base
|
||||
|
||||
children: ['variable', 'parent', 'properties']
|
||||
|
||||
isStatement: YES
|
||||
makeReturn: RETURN
|
||||
children: ['variable', 'parent', 'body']
|
||||
|
||||
# Initialize a **Class** with its name, an optional superclass, and a
|
||||
# list of prototype property assignments.
|
||||
constructor: (@variable, @parent, @properties = []) ->
|
||||
(@variable, @parent, @body = new Expressions) ->
|
||||
|
||||
# Instead of generating the JavaScript string directly, we build up the
|
||||
# equivalent syntax tree and compile that, in pieces. You can see the
|
||||
# constructor, property assignments, and inheritance getting built out below.
|
||||
compileNode: (o) ->
|
||||
variable = @variable or new Literal o.scope.freeVariable 'ctor'
|
||||
extension = @parent and new Extends variable, @parent
|
||||
props = new Expressions
|
||||
me = null
|
||||
className = variable.compile o
|
||||
constScope = null
|
||||
if @variable
|
||||
decl = if tail = last @variable.properties
|
||||
tail instanceof Accessor and tail.name.value
|
||||
else
|
||||
@variable.base.value
|
||||
decl and= IDENTIFIER.test(decl) and decl
|
||||
|
||||
if @parent
|
||||
applied = new Value @parent, [new Accessor new Literal 'apply']
|
||||
ctor = new Code [], new Expressions [
|
||||
new Call applied, [new Literal('this'), new Literal('arguments')]
|
||||
]
|
||||
else
|
||||
ctor = new Code [], new Expressions [new Return new Literal 'this']
|
||||
name = decl or @name or '_Class'
|
||||
lname = new Literal name
|
||||
@body.traverseChildren false, (node) ->
|
||||
if node instanceof Literal and node.value is 'this'
|
||||
node.value = name
|
||||
else if node instanceof Code
|
||||
node.klass = name
|
||||
node.context = name if node.bound
|
||||
|
||||
for prop in @properties
|
||||
{variable: pvar, value: func} = prop
|
||||
if pvar and pvar.base.value is 'constructor'
|
||||
if func not instanceof Code
|
||||
[func, ref] = func.cache o
|
||||
props.push func if func isnt ref
|
||||
apply = new Call new Value(ref, [new Accessor new Literal 'apply']),
|
||||
[new Literal('this'), new Literal('arguments')]
|
||||
func = new Code [], new Expressions([apply])
|
||||
throw SyntaxError 'cannot define a constructor as a bound function.' if func.bound
|
||||
func.name = className
|
||||
func.body.push new Return new Literal 'this'
|
||||
variable = new Value variable
|
||||
variable.namespaced = 0 < className.indexOf '.'
|
||||
ctor = func
|
||||
ctor.comment = props.expressions.pop() if last(props.expressions) instanceof Comment
|
||||
continue
|
||||
if func instanceof Code and func.bound
|
||||
if prop.context is 'this'
|
||||
func.context = className
|
||||
else
|
||||
func.bound = false
|
||||
constScope or= new Scope o.scope, ctor.body, ctor
|
||||
me or= constScope.freeVariable 'this'
|
||||
pname = pvar.compile o
|
||||
ctor.body.push new Return new Literal 'this' if ctor.body.isEmpty()
|
||||
ret = "return #{className}.prototype.#{pname}.apply(#{me}, arguments);"
|
||||
ctor.body.unshift new Literal "this.#{pname} = function() { #{ret} }"
|
||||
if pvar
|
||||
access = if prop.context is 'this'
|
||||
then pvar.properties[0]
|
||||
else new Accessor pvar, 'proto'
|
||||
val = new Value variable, [access]
|
||||
prop = new Assign val, func
|
||||
props.push prop
|
||||
for node, i in exps = @body.expressions
|
||||
if node instanceof Value and node.isObject()
|
||||
assigns = for assign in node.base.properties
|
||||
assign.variable = new Value(lname, [new Accessor(assign.variable.base, 'proto')])
|
||||
delete assign.context
|
||||
assign
|
||||
exps[i] = assigns
|
||||
else if node instanceof Code
|
||||
if ctor
|
||||
throw new Error 'cannot define more than one constructor in a class'
|
||||
if node.bound
|
||||
throw new Error 'cannot define a constructor as a bound function'
|
||||
ctor = node
|
||||
exps.splice(i, 1)
|
||||
exps.unshift ctor
|
||||
|
||||
ctor.className = className.match /[$\w]+$/
|
||||
ctor.body.unshift new Literal "#{me} = this" if me
|
||||
o.sharedScope = constScope
|
||||
code = @tab + new Assign(variable, ctor).compile(o) + ';'
|
||||
code += '\n' + @tab + extension.compile(o) + ';' if extension
|
||||
code += '\n' + props.compile o if !props.isEmpty()
|
||||
code += '\n' + new Return(variable).compile o if @returns
|
||||
code
|
||||
@body.expressions = exps = flatten exps
|
||||
unless ctor
|
||||
exps.unshift ctor = new Code
|
||||
if @parent
|
||||
ctor.body.push new Call 'super', [new Splat new Literal 'arguments']
|
||||
ctor.ctor = ctor.name = name
|
||||
ctor.klass = null
|
||||
ctor.noReturn = yes
|
||||
exps.splice 1, 0, new Extends lname, @parent if @parent
|
||||
exps.push lname
|
||||
klass = new Parens new Call(new Code [], @body), true
|
||||
klass = new Assign new Value(lname), klass if decl and @variable?.isComplex()
|
||||
klass = new Assign @variable, klass if @variable
|
||||
klass.compile o
|
||||
|
||||
#### Assign
|
||||
|
||||
|
@ -737,17 +718,17 @@ exports.Class = class Class extends Base
|
|||
exports.Assign = class Assign extends Base
|
||||
|
||||
# Matchers for detecting class/method names
|
||||
METHOD_DEF: /^(?:(\S+)\.prototype\.)?([$A-Za-z_][$\w]*)$/
|
||||
METHOD_DEF: /^(?:(\S+)\.prototype\.|\S+?)?\b([$A-Za-z_][$\w]*)$/
|
||||
|
||||
children: ['variable', 'value']
|
||||
|
||||
constructor: (@variable, @value, @context) ->
|
||||
(@variable, @value, @context) ->
|
||||
|
||||
assigns: (name) ->
|
||||
@[if @context is 'object' then 'value' else 'variable'].assigns name
|
||||
|
||||
unfoldSoak: (o) ->
|
||||
If.unfoldSoak o, this, 'variable'
|
||||
unfoldSoak o, this, 'variable'
|
||||
|
||||
# Compile an assignment, delegating to `compilePatternMatch` or
|
||||
# `compileSplice` if appropriate. Keep track of the name of the base object
|
||||
|
@ -760,7 +741,7 @@ exports.Assign = class Assign extends Base
|
|||
name = @variable.compile o, LEVEL_LIST
|
||||
if @value instanceof Code and match = @METHOD_DEF.exec name
|
||||
@value.name = match[2]
|
||||
@value.klass = match[1]
|
||||
@value.klass = match[1] if match[1]
|
||||
val = @value.compile o, LEVEL_LIST
|
||||
return "#{name}: #{val}" if @context is 'object'
|
||||
unless @variable.isAssignable()
|
||||
|
@ -856,10 +837,14 @@ exports.Code = class Code extends Base
|
|||
|
||||
children: ['params', 'body']
|
||||
|
||||
constructor: (@params = [], @body = new Expressions, tag) ->
|
||||
(params, body, tag) ->
|
||||
@params = params or []
|
||||
@body = body or new Expressions
|
||||
@bound = tag is 'boundfunc'
|
||||
@context = 'this' if @bound
|
||||
|
||||
isStatement: -> !!@ctor
|
||||
|
||||
# Compilation creates a new scope unless explicitly asked to share with the
|
||||
# outer scope. Handles splat parameters in the parameter list by peeking at
|
||||
# the JavaScript `arguments` objects. If the function is bound with the `=>`
|
||||
|
@ -892,23 +877,18 @@ exports.Code = class Code extends Base
|
|||
scope.startLevel()
|
||||
wasEmpty = @body.isEmpty()
|
||||
exprs.unshift splats if splats
|
||||
@body.expressions.splice 0, 0, exprs... if exprs.length
|
||||
@body.makeReturn() unless wasEmpty or @noReturn
|
||||
@body.expressions.unshift exprs... if exprs.length
|
||||
scope.parameter vars[i] = v.compile o for v, i in vars unless splats
|
||||
comm = if @comment then @comment.compile(o) + '\n' else ''
|
||||
idt = o.indent
|
||||
o.indent += TAB if @className
|
||||
code = if @body.isEmpty() then '' else "\n#{ @body.compileWithDeclarations o }\n"
|
||||
if @className
|
||||
open = "(function() {\n#{comm}#{idt}function #{@className}("
|
||||
close = "#{ code and idt }}\n#{idt}return #{@className};\n#{@tab}})()"
|
||||
else
|
||||
open = 'function('
|
||||
close = "#{ code and @tab }}"
|
||||
func = "#{open}#{ vars.join ', ' }) {#{code}#{close}"
|
||||
scope.endLevel()
|
||||
return "#{ utility 'bind' }(#{func}, #{@context})" if @bound
|
||||
if @front then "(#{func})" else func
|
||||
@body.makeReturn() unless wasEmpty or @noReturn
|
||||
idt = o.indent
|
||||
code = 'function'
|
||||
code += ' ' + @name if @ctor
|
||||
code += '(' + vars.join(', ') + ') {'
|
||||
code += "\n#{ @body.compileWithDeclarations o }\n#{@tab}" unless @body.isEmpty()
|
||||
code += '}'
|
||||
return @tab + code if @ctor
|
||||
return utility('bind') + "(#{code}, #{@context})" if @bound
|
||||
if @front then "(#{code})" else code
|
||||
|
||||
# Short-circuit `traverseChildren` method to prevent it from crossing scope boundaries
|
||||
# unless `crossScope` is `true`.
|
||||
|
@ -924,7 +904,7 @@ exports.Param = class Param extends Base
|
|||
|
||||
children: ['name', 'value']
|
||||
|
||||
constructor: (@name, @value, @splat) ->
|
||||
(@name, @value, @splat) ->
|
||||
|
||||
compile: (o) ->
|
||||
@name.compile o, LEVEL_LIST
|
||||
|
@ -949,7 +929,7 @@ exports.Splat = class Splat extends Base
|
|||
|
||||
isAssignable: YES
|
||||
|
||||
constructor: (name) ->
|
||||
(name) ->
|
||||
@name = if name.compile then name else new Literal name
|
||||
|
||||
assigns: (name) ->
|
||||
|
@ -960,7 +940,7 @@ exports.Splat = class Splat extends Base
|
|||
|
||||
# Utility function that converts arbitrary number of elements, mixed with
|
||||
# splats, to a proper array.
|
||||
@compileSplattedArray: (o, list, apply) ->
|
||||
@compileSplattedArray = (o, list, apply) ->
|
||||
index = -1
|
||||
continue while (node = list[++index]) and node not instanceof Splat
|
||||
return '' if index >= list.length
|
||||
|
@ -988,12 +968,15 @@ exports.While = class While extends Base
|
|||
children: ['condition', 'guard', 'body']
|
||||
|
||||
isStatement: YES
|
||||
makeReturn: RETURN
|
||||
|
||||
constructor: (condition, options) ->
|
||||
(condition, options) ->
|
||||
@condition = if options?.invert then condition.invert() else condition
|
||||
@guard = options?.guard
|
||||
|
||||
makeReturn: ->
|
||||
@returns = yes
|
||||
this
|
||||
|
||||
addBody: (@body) ->
|
||||
this
|
||||
|
||||
|
@ -1050,7 +1033,7 @@ exports.Op = class Op extends Base
|
|||
|
||||
children: ['first', 'second']
|
||||
|
||||
constructor: (op, first, second, flip) ->
|
||||
(op, first, second, flip) ->
|
||||
return new In first, second if op is 'in'
|
||||
if op is 'new'
|
||||
return first.newInstance() if first instanceof Call
|
||||
|
@ -1078,7 +1061,7 @@ exports.Op = class Op extends Base
|
|||
new Op '!', this
|
||||
|
||||
unfoldSoak: (o) ->
|
||||
@operator in ['++', '--', 'delete'] and If.unfoldSoak o, this, 'first'
|
||||
@operator in ['++', '--', 'delete'] and unfoldSoak o, this, 'first'
|
||||
|
||||
compileNode: (o) ->
|
||||
return @compileUnary o if @isUnary()
|
||||
|
@ -1127,7 +1110,7 @@ exports.In = class In extends Base
|
|||
|
||||
invert: NEGATE
|
||||
|
||||
constructor: (@object, @array) ->
|
||||
(@object, @array) ->
|
||||
|
||||
compileNode: (o) ->
|
||||
if @array instanceof Value and @array.isArray()
|
||||
|
@ -1163,7 +1146,7 @@ exports.Try = class Try extends Base
|
|||
|
||||
isStatement: YES
|
||||
|
||||
constructor: (@attempt, @error, @recovery, @ensure) ->
|
||||
(@attempt, @error, @recovery, @ensure) ->
|
||||
|
||||
makeReturn: ->
|
||||
@attempt = @attempt .makeReturn() if @attempt
|
||||
|
@ -1194,7 +1177,7 @@ exports.Throw = class Throw extends Base
|
|||
|
||||
isStatement: YES
|
||||
|
||||
constructor: (@expression) ->
|
||||
(@expression) ->
|
||||
|
||||
# A **Throw** is already a return, of sorts...
|
||||
makeReturn: THIS
|
||||
|
@ -1213,7 +1196,7 @@ exports.Existence = class Existence extends Base
|
|||
|
||||
invert: NEGATE
|
||||
|
||||
constructor: (@expression) ->
|
||||
(@expression) ->
|
||||
|
||||
compileNode: (o) ->
|
||||
code = @expression.compile o, LEVEL_OP
|
||||
|
@ -1238,7 +1221,7 @@ exports.Parens = class Parens extends Base
|
|||
|
||||
children: ['expression']
|
||||
|
||||
constructor: (@expression) ->
|
||||
(@expression) ->
|
||||
|
||||
unwrap : -> @expression
|
||||
isComplex : -> @expression.isComplex()
|
||||
|
@ -1267,9 +1250,8 @@ exports.For = class For extends Base
|
|||
children: ['body', 'source', 'guard', 'step', 'from', 'to']
|
||||
|
||||
isStatement: YES
|
||||
makeReturn: RETURN
|
||||
|
||||
constructor: (body, head) ->
|
||||
(body, head) ->
|
||||
if head.index instanceof Value
|
||||
throw SyntaxError 'index cannot be a pattern matching expression'
|
||||
extend this, head
|
||||
|
@ -1277,6 +1259,10 @@ exports.For = class For extends Base
|
|||
@pattern = @name instanceof Value
|
||||
@returns = false
|
||||
|
||||
makeReturn: ->
|
||||
@returns = yes
|
||||
this
|
||||
|
||||
containsPureStatement: While::containsPureStatement
|
||||
|
||||
compileReturnValue: (val, o) ->
|
||||
|
@ -1399,7 +1385,7 @@ exports.Switch = class Switch extends Base
|
|||
|
||||
isStatement: YES
|
||||
|
||||
constructor: (@subject, @cases, @otherwise) ->
|
||||
(@subject, @cases, @otherwise) ->
|
||||
|
||||
makeReturn: ->
|
||||
pair[1].makeReturn() for pair in @cases
|
||||
|
@ -1433,7 +1419,7 @@ exports.If = class If extends Base
|
|||
|
||||
children: ['condition', 'body', 'elseBody']
|
||||
|
||||
constructor: (condition, @body, options = {}) ->
|
||||
(condition, @body, options = {}) ->
|
||||
@condition = if options.invert then condition.invert() else condition
|
||||
@elseBody = null
|
||||
@isChain = false
|
||||
|
@ -1497,13 +1483,6 @@ exports.If = class If extends Base
|
|||
unfoldSoak: ->
|
||||
@soak and this
|
||||
|
||||
# Unfold a node's child if soak, then tuck the node under created `If`
|
||||
@unfoldSoak: (o, parent, name) ->
|
||||
return unless ifn = parent[name].unfoldSoak o
|
||||
parent[name] = ifn.body
|
||||
ifn.body = new Value parent
|
||||
ifn
|
||||
|
||||
# Faux-Nodes
|
||||
# ----------
|
||||
# Faux-nodes are never created by the grammar, but are used during code
|
||||
|
@ -1545,6 +1524,13 @@ Closure =
|
|||
literalThis: (node) -> node instanceof Literal and node.value is 'this' or
|
||||
node instanceof Code and node.bound
|
||||
|
||||
# Unfold a node's child if soak, then tuck the node under created `If`
|
||||
unfoldSoak = (o, parent, name) ->
|
||||
return unless ifn = parent[name].unfoldSoak o
|
||||
parent[name] = ifn.body
|
||||
ifn.body = new Value parent
|
||||
ifn
|
||||
|
||||
# Constants
|
||||
# ---------
|
||||
|
||||
|
|
|
@ -13,7 +13,7 @@ exports.OptionParser = class OptionParser
|
|||
# [short-flag, long-flag, description]
|
||||
#
|
||||
# Along with an an optional banner for the usage help.
|
||||
constructor: (rules, @banner) ->
|
||||
(rules, @banner) ->
|
||||
@rules = buildRules rules
|
||||
|
||||
# Parse the list of arguments, populating an `options` object with all of the
|
||||
|
|
|
@ -11,13 +11,13 @@
|
|||
exports.Scope = class Scope
|
||||
|
||||
# The top-level **Scope** object.
|
||||
@root: null
|
||||
@root = null
|
||||
|
||||
# Initialize a scope with its parent, for lookups up the chain,
|
||||
# as well as a reference to the **Expressions** node is belongs to, which is
|
||||
# where it should declare its variables, and a reference to the function that
|
||||
# it wraps.
|
||||
constructor: (@parent, @expressions, @method) ->
|
||||
(@parent, @expressions, @method) ->
|
||||
@variables = [{name: 'arguments', type: 'arguments'}]
|
||||
@positions = {}
|
||||
if @parent
|
||||
|
|
Loading…
Reference in a new issue