first draft of coco's executable class bodies ... cleanups and tests to follow.

This commit is contained in:
Jeremy Ashkenas 2010-11-11 21:48:08 -05:00
parent 2c7f6d8bfe
commit 7fda0cd7d9
11 changed files with 948 additions and 1153 deletions

View File

@ -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: [

View File

@ -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 = {

File diff suppressed because it is too large Load Diff

View File

@ -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,})/;

File diff suppressed because one or more lines are too long

View File

@ -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 = [];

View File

@ -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);

View File

@ -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.

View File

@ -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
# ---------

View File

@ -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

View File

@ -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