mirror of
https://github.com/jashkenas/coffeescript.git
synced 2026-01-14 09:17:55 -05:00
Merge pull request #2712 from troels/implicit-object-implicit-call-stuff
Implicit object/implicit call interaction handling
This commit is contained in:
@@ -878,33 +878,6 @@
|
||||
return ifn;
|
||||
};
|
||||
|
||||
Call.prototype.filterImplicitObjects = function(list) {
|
||||
var node, nodes, obj, prop, properties, _i, _j, _len, _len1, _ref2;
|
||||
nodes = [];
|
||||
for (_i = 0, _len = list.length; _i < _len; _i++) {
|
||||
node = list[_i];
|
||||
if (!((typeof node.isObject === "function" ? node.isObject() : void 0) && node.base.generated)) {
|
||||
nodes.push(node);
|
||||
continue;
|
||||
}
|
||||
obj = null;
|
||||
_ref2 = node.base.properties;
|
||||
for (_j = 0, _len1 = _ref2.length; _j < _len1; _j++) {
|
||||
prop = _ref2[_j];
|
||||
if (prop instanceof Assign || prop instanceof Comment) {
|
||||
if (!obj) {
|
||||
nodes.push(obj = new Obj(properties = [], true));
|
||||
}
|
||||
properties.push(prop);
|
||||
} else {
|
||||
nodes.push(prop);
|
||||
obj = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
return nodes;
|
||||
};
|
||||
|
||||
Call.prototype.compileNode = function(o) {
|
||||
var arg, args, code, _ref2;
|
||||
if ((_ref2 = this.variable) != null) {
|
||||
@@ -913,16 +886,16 @@
|
||||
if (code = Splat.compileSplattedArray(o, this.args, true)) {
|
||||
return this.compileSplat(o, code);
|
||||
}
|
||||
args = this.filterImplicitObjects(this.args);
|
||||
args = ((function() {
|
||||
var _i, _len, _results;
|
||||
var _i, _len, _ref3, _results;
|
||||
_ref3 = this.args;
|
||||
_results = [];
|
||||
for (_i = 0, _len = args.length; _i < _len; _i++) {
|
||||
arg = args[_i];
|
||||
for (_i = 0, _len = _ref3.length; _i < _len; _i++) {
|
||||
arg = _ref3[_i];
|
||||
_results.push(arg.compile(o, LEVEL_LIST));
|
||||
}
|
||||
return _results;
|
||||
})()).join(', ');
|
||||
}).call(this)).join(', ');
|
||||
if (this.isSuper) {
|
||||
return this.superReference(o) + (".call(" + (this.superThis(o)) + (args && ', ' + args) + ")");
|
||||
} else {
|
||||
@@ -1240,27 +1213,25 @@
|
||||
|
||||
Arr.prototype.children = ['objects'];
|
||||
|
||||
Arr.prototype.filterImplicitObjects = Call.prototype.filterImplicitObjects;
|
||||
|
||||
Arr.prototype.compileNode = function(o) {
|
||||
var code, obj, objs;
|
||||
var code, obj;
|
||||
if (!this.objects.length) {
|
||||
return '[]';
|
||||
}
|
||||
o.indent += TAB;
|
||||
objs = this.filterImplicitObjects(this.objects);
|
||||
if (code = Splat.compileSplattedArray(o, objs)) {
|
||||
if (code = Splat.compileSplattedArray(o, this.objects)) {
|
||||
return code;
|
||||
}
|
||||
code = ((function() {
|
||||
var _i, _len, _results;
|
||||
var _i, _len, _ref2, _results;
|
||||
_ref2 = this.objects;
|
||||
_results = [];
|
||||
for (_i = 0, _len = objs.length; _i < _len; _i++) {
|
||||
obj = objs[_i];
|
||||
for (_i = 0, _len = _ref2.length; _i < _len; _i++) {
|
||||
obj = _ref2[_i];
|
||||
_results.push(obj.compile(o, LEVEL_LIST));
|
||||
}
|
||||
return _results;
|
||||
})()).join(', ');
|
||||
}).call(this)).join(', ');
|
||||
if (code.indexOf('\n') >= 0) {
|
||||
return "[\n" + o.indent + code + "\n" + this.tab + "]";
|
||||
} else {
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
var js;
|
||||
input = input.replace(/\uFF00/g, '\n');
|
||||
input = input.replace(/(^|[\r\n]+)(\s*)##?(?:[^#\r\n][^\r\n]*|)($|[\r\n])/, '$1$2$3');
|
||||
if (/^(\()?\s*(\n\))?$/.test(input)) {
|
||||
if (/^(\s*|\(\s*\))$/.test(input)) {
|
||||
return cb(null);
|
||||
}
|
||||
try {
|
||||
|
||||
@@ -1,9 +1,16 @@
|
||||
// Generated by CoffeeScript 1.5.0
|
||||
(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, SINGLE_CLOSERS, SINGLE_LINERS, generate, left, rite, _i, _len, _ref,
|
||||
__indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; },
|
||||
__slice = [].slice;
|
||||
|
||||
generate = function(tag, value) {
|
||||
var tok;
|
||||
tok = [tag, value];
|
||||
tok.generated = true;
|
||||
return tok;
|
||||
};
|
||||
|
||||
exports.Rewriter = (function() {
|
||||
|
||||
function Rewriter() {}
|
||||
@@ -16,8 +23,7 @@
|
||||
this.closeOpenIndexes();
|
||||
this.addImplicitIndentation();
|
||||
this.tagPostfixConditionals();
|
||||
this.addImplicitBraces();
|
||||
this.addImplicitParentheses();
|
||||
this.addImplicitBracesAndParens();
|
||||
this.addLocationDataToGeneratedTokens();
|
||||
return this.tokens;
|
||||
};
|
||||
@@ -112,140 +118,237 @@
|
||||
});
|
||||
};
|
||||
|
||||
Rewriter.prototype.addImplicitBraces = function() {
|
||||
var action, condition, sameLine, stack, start, startIndent, startIndex, startsLine;
|
||||
stack = [];
|
||||
start = null;
|
||||
startsLine = null;
|
||||
sameLine = true;
|
||||
startIndent = 0;
|
||||
startIndex = 0;
|
||||
condition = function(token, i) {
|
||||
var one, tag, three, two, _ref, _ref1;
|
||||
_ref = this.tokens.slice(i + 1, +(i + 3) + 1 || 9e9), one = _ref[0], two = _ref[1], three = _ref[2];
|
||||
if ('HERECOMMENT' === (one != null ? one[0] : void 0)) {
|
||||
Rewriter.prototype.matchTags = function() {
|
||||
var fuzz, i, j, pattern, _i, _ref, _ref1;
|
||||
i = arguments[0], pattern = 2 <= arguments.length ? __slice.call(arguments, 1) : [];
|
||||
fuzz = 0;
|
||||
for (j = _i = 0, _ref = pattern.length; 0 <= _ref ? _i < _ref : _i > _ref; j = 0 <= _ref ? ++_i : --_i) {
|
||||
while (this.tag(i + j + fuzz) === 'HERECOMMENT') {
|
||||
fuzz += 2;
|
||||
}
|
||||
if (pattern[j] == null) {
|
||||
continue;
|
||||
}
|
||||
if (typeof pattern[j] === 'string') {
|
||||
pattern[j] = [pattern[j]];
|
||||
}
|
||||
if (_ref1 = this.tag(i + j + fuzz), __indexOf.call(pattern[j], _ref1) < 0) {
|
||||
return false;
|
||||
}
|
||||
tag = token[0];
|
||||
if (__indexOf.call(LINEBREAKS, tag) >= 0) {
|
||||
sameLine = false;
|
||||
}
|
||||
return (((tag === 'TERMINATOR' || tag === 'OUTDENT') || (__indexOf.call(IMPLICIT_END, tag) >= 0 && sameLine && !(i - startIndex === 1))) && ((!startsLine && this.tag(i - 1) !== ',') || !((two != null ? two[0] : void 0) === ':' || (one != null ? one[0] : void 0) === '@' && (three != null ? three[0] : void 0) === ':'))) || (tag === ',' && one && ((_ref1 = one[0]) !== 'IDENTIFIER' && _ref1 !== 'NUMBER' && _ref1 !== 'STRING' && _ref1 !== '@' && _ref1 !== 'TERMINATOR' && _ref1 !== 'OUTDENT'));
|
||||
};
|
||||
action = function(token, i) {
|
||||
var tok;
|
||||
tok = this.generate('}', '}');
|
||||
return this.tokens.splice(i, 0, tok);
|
||||
};
|
||||
return this.scanTokens(function(token, i, tokens) {
|
||||
var ago, idx, prevTag, tag, tok, value, _ref, _ref1;
|
||||
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 === ':' && ((ago = this.tag(i - 2)) === ':' || ((_ref1 = stack[stack.length - 1]) != null ? _ref1[0] : void 0) !== '{'))) {
|
||||
return 1;
|
||||
}
|
||||
sameLine = true;
|
||||
startIndex = i + 1;
|
||||
stack.push(['{']);
|
||||
idx = ago === '@' ? i - 2 : i - 1;
|
||||
while (this.tag(idx - 2) === 'HERECOMMENT') {
|
||||
idx -= 2;
|
||||
}
|
||||
prevTag = this.tag(idx - 1);
|
||||
startsLine = !prevTag || (__indexOf.call(LINEBREAKS, prevTag) >= 0);
|
||||
value = new String('{');
|
||||
value.generated = true;
|
||||
tok = this.generate('{', value);
|
||||
tokens.splice(idx, 0, tok);
|
||||
this.detectEnd(i + 2, condition, action);
|
||||
return 2;
|
||||
});
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
Rewriter.prototype.addImplicitParentheses = function() {
|
||||
var action, callIndex, condition, noCall, seenControl, seenSingle;
|
||||
noCall = seenSingle = seenControl = false;
|
||||
callIndex = null;
|
||||
condition = function(token, i) {
|
||||
var post, tag, _ref, _ref1;
|
||||
tag = token[0];
|
||||
if (!seenSingle && token.fromThen) {
|
||||
return true;
|
||||
Rewriter.prototype.looksObjectish = function(j) {
|
||||
return this.matchTags(j, '@', null, ':') || this.matchTags(j, null, ':');
|
||||
};
|
||||
|
||||
Rewriter.prototype.findTagsBackwards = function(i, tags) {
|
||||
var backStack, _ref, _ref1, _ref2, _ref3, _ref4, _ref5;
|
||||
backStack = [];
|
||||
while (i >= 0 && (backStack.length || (_ref2 = this.tag(i), __indexOf.call(tags, _ref2) < 0) && ((_ref3 = this.tag(i), __indexOf.call(EXPRESSION_START, _ref3) < 0) || this.tokens[i].generated) && (_ref4 = this.tag(i), __indexOf.call(LINEBREAKS, _ref4) < 0))) {
|
||||
if (_ref = this.tag(i), __indexOf.call(EXPRESSION_END, _ref) >= 0) {
|
||||
backStack.push(this.tag(i));
|
||||
}
|
||||
if (tag === 'IF' || tag === 'ELSE' || tag === 'CATCH' || tag === '->' || tag === '=>' || tag === 'CLASS') {
|
||||
seenSingle = true;
|
||||
if ((_ref1 = this.tag(i), __indexOf.call(EXPRESSION_START, _ref1) >= 0) && backStack.length) {
|
||||
backStack.pop();
|
||||
}
|
||||
if (tag === 'IF' || tag === 'ELSE' || tag === 'SWITCH' || tag === 'TRY' || tag === '=') {
|
||||
seenControl = 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' && !seenControl)) && (tag !== 'INDENT' || (((_ref = this.tag(i - 2)) !== 'CLASS' && _ref !== 'EXTENDS') && (_ref1 = this.tag(i - 1), __indexOf.call(IMPLICIT_BLOCK, _ref1) < 0) && !(callIndex === i - 1 && (post = this.tokens[i + 1]) && post.generated && post[0] === '{')));
|
||||
};
|
||||
action = function(token, i) {
|
||||
return this.tokens.splice(i, 0, this.generate('CALL_END', ')'));
|
||||
};
|
||||
i -= 1;
|
||||
}
|
||||
return _ref5 = this.tag(i), __indexOf.call(tags, _ref5) >= 0;
|
||||
};
|
||||
|
||||
Rewriter.prototype.addImplicitBracesAndParens = function() {
|
||||
var stack;
|
||||
stack = [];
|
||||
return this.scanTokens(function(token, i, tokens) {
|
||||
var callObject, current, next, prev, tag, _ref, _ref1, _ref2;
|
||||
var endImplicitCall, endImplicitObject, forward, inImplicit, inImplicitCall, inImplicitControl, inImplicitObject, nextTag, prevTag, s, sameLine, stackIdx, stackTag, stackTop, startIdx, startImplicitCall, startImplicitObject, startsLine, tag, _ref, _ref1, _ref2, _ref3, _ref4, _ref5, _ref6;
|
||||
tag = token[0];
|
||||
if (tag === 'CLASS' || tag === 'IF' || tag === 'FOR' || tag === 'WHILE') {
|
||||
noCall = true;
|
||||
prevTag = (i > 0 ? tokens[i - 1] : [])[0];
|
||||
nextTag = (i < tokens.length - 1 ? tokens[i + 1] : [])[0];
|
||||
stackTop = function() {
|
||||
return stack[stack.length - 1];
|
||||
};
|
||||
startIdx = i;
|
||||
forward = function(n) {
|
||||
return i - startIdx + n;
|
||||
};
|
||||
inImplicit = function() {
|
||||
var _ref, _ref1;
|
||||
return (_ref = stackTop()) != null ? (_ref1 = _ref[2]) != null ? _ref1.ours : void 0 : void 0;
|
||||
};
|
||||
inImplicitCall = function() {
|
||||
var _ref;
|
||||
return inImplicit() && ((_ref = stackTop()) != null ? _ref[0] : void 0) === '(';
|
||||
};
|
||||
inImplicitObject = function() {
|
||||
var _ref;
|
||||
return inImplicit() && ((_ref = stackTop()) != null ? _ref[0] : void 0) === '{';
|
||||
};
|
||||
inImplicitControl = function() {
|
||||
var _ref;
|
||||
return inImplicit && ((_ref = stackTop()) != null ? _ref[0] : void 0) === 'CONTROL';
|
||||
};
|
||||
startImplicitCall = function(j) {
|
||||
var idx;
|
||||
idx = j != null ? j : i;
|
||||
stack.push([
|
||||
'(', idx, {
|
||||
ours: true
|
||||
}
|
||||
]);
|
||||
tokens.splice(idx, 0, generate('CALL_START', '('));
|
||||
if (j == null) {
|
||||
return i += 1;
|
||||
}
|
||||
};
|
||||
endImplicitCall = function() {
|
||||
stack.pop();
|
||||
tokens.splice(i, 0, generate('CALL_END', ')'));
|
||||
return i += 1;
|
||||
};
|
||||
startImplicitObject = function(j, startsLine) {
|
||||
var idx;
|
||||
if (startsLine == null) {
|
||||
startsLine = true;
|
||||
}
|
||||
idx = j != null ? j : i;
|
||||
stack.push([
|
||||
'{', idx, {
|
||||
sameLine: true,
|
||||
startsLine: startsLine,
|
||||
ours: true
|
||||
}
|
||||
]);
|
||||
tokens.splice(idx, 0, generate('{', generate(new String('{'))));
|
||||
if (j == null) {
|
||||
return i += 1;
|
||||
}
|
||||
};
|
||||
endImplicitObject = function() {
|
||||
stack.pop();
|
||||
tokens.splice(i, 0, generate('}', '}'));
|
||||
return i += 1;
|
||||
};
|
||||
if (inImplicitCall() && (tag === 'IF' || tag === 'CLASS' || tag === 'SWITCH' || tag === 'CATCH')) {
|
||||
stack.push([
|
||||
'CONTROL', i, {
|
||||
ours: true
|
||||
}
|
||||
]);
|
||||
return forward(1);
|
||||
}
|
||||
_ref = tokens.slice(i - 1, +(i + 1) + 1 || 9e9), prev = _ref[0], current = _ref[1], next = _ref[2];
|
||||
callObject = !noCall && tag === 'INDENT' && next && next.generated && next[0] === '{' && prev && (_ref1 = prev[0], __indexOf.call(IMPLICIT_FUNC, _ref1) >= 0);
|
||||
seenSingle = false;
|
||||
seenControl = false;
|
||||
if (__indexOf.call(LINEBREAKS, tag) >= 0) {
|
||||
noCall = false;
|
||||
if (tag === 'INDENT' && inImplicit()) {
|
||||
if (prevTag !== '=>' && prevTag !== '->' && prevTag !== '[' && prevTag !== '(' && prevTag !== ',' && prevTag !== '{' && prevTag !== 'TRY' && prevTag !== 'ELSE' && prevTag !== '=') {
|
||||
while (inImplicitCall()) {
|
||||
endImplicitCall();
|
||||
}
|
||||
}
|
||||
if (inImplicitControl()) {
|
||||
stack.pop();
|
||||
}
|
||||
stack.push([tag, i]);
|
||||
return forward(1);
|
||||
}
|
||||
if (prev && !prev.spaced && tag === '?') {
|
||||
token.call = true;
|
||||
if (__indexOf.call(EXPRESSION_START, tag) >= 0) {
|
||||
stack.push([tag, i]);
|
||||
return forward(1);
|
||||
}
|
||||
if (token.fromThen) {
|
||||
return 1;
|
||||
if (__indexOf.call(EXPRESSION_END, tag) >= 0) {
|
||||
while (inImplicit()) {
|
||||
if (inImplicitCall()) {
|
||||
endImplicitCall();
|
||||
} else if (inImplicitObject()) {
|
||||
endImplicitObject();
|
||||
} else {
|
||||
stack.pop();
|
||||
}
|
||||
}
|
||||
stack.pop();
|
||||
}
|
||||
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;
|
||||
if ((__indexOf.call(IMPLICIT_FUNC, tag) >= 0 && token.spaced || tag === '?' && i > 0 && !tokens[i - 1].spaced) && (__indexOf.call(IMPLICIT_CALL, nextTag) >= 0 || __indexOf.call(IMPLICIT_UNSPACED_CALL, nextTag) >= 0 && !((_ref = tokens[i + 1]) != null ? _ref.spaced : void 0) && !((_ref1 = tokens[i + 1]) != null ? _ref1.newLine : void 0))) {
|
||||
if (tag === '?') {
|
||||
tag = token[0] = 'FUNC_EXIST';
|
||||
}
|
||||
startImplicitCall(i + 1);
|
||||
return forward(2);
|
||||
}
|
||||
callIndex = i;
|
||||
tokens.splice(i, 0, this.generate('CALL_START', '(', token[2]));
|
||||
this.detectEnd(i + 1, condition, action);
|
||||
if (prev[0] === '?') {
|
||||
prev[0] = 'FUNC_EXIST';
|
||||
if (this.matchTags(i, IMPLICIT_FUNC, 'INDENT') && ((_ref2 = stackTop()) != null ? _ref2[0] : void 0) !== '[' && !this.findTagsBackwards(i, ['CLASS', 'EXTENDS', 'IF', 'CATCH', 'SWITCH', 'LEADING_WHEN', 'FOR', 'WHILE', 'UNTIL'])) {
|
||||
startImplicitCall(i + 1);
|
||||
stack.push(['INDENT', i + 2]);
|
||||
return forward(3);
|
||||
}
|
||||
return 2;
|
||||
if (tag === ':') {
|
||||
if (this.tag(i - 2) === '@') {
|
||||
s = i - 2;
|
||||
} else {
|
||||
s = i - 1;
|
||||
}
|
||||
while (this.tag(s - 2) === 'HERECOMMENT') {
|
||||
s -= 2;
|
||||
}
|
||||
startsLine = s === 0 || (_ref3 = this.tag(s - 1), __indexOf.call(LINEBREAKS, _ref3) >= 0) || tokens[s - 1].newLine;
|
||||
if (stackTop()) {
|
||||
_ref4 = stackTop(), stackTag = _ref4[0], stackIdx = _ref4[1];
|
||||
if ((stackTag === '{' || stackTag === 'INDENT' && this.tag(stackIdx - 1) === '{') && (startsLine || this.tag(s - 1) === ',' || this.tag(s - 1) === '{')) {
|
||||
return forward(1);
|
||||
}
|
||||
}
|
||||
startImplicitObject(s, !!startsLine);
|
||||
return forward(2);
|
||||
}
|
||||
if (prevTag === 'OUTDENT' && inImplicitCall() && (tag === '.' || tag === '?.' || tag === '::')) {
|
||||
endImplicitCall();
|
||||
return forward(1);
|
||||
}
|
||||
if (inImplicitObject() && __indexOf.call(LINEBREAKS, tag) >= 0) {
|
||||
stackTop()[2].sameLine = false;
|
||||
}
|
||||
if (__indexOf.call(IMPLICIT_END, tag) >= 0) {
|
||||
while (inImplicit()) {
|
||||
_ref5 = stackTop(), stackTag = _ref5[0], stackIdx = _ref5[1], (_ref6 = _ref5[2], sameLine = _ref6.sameLine, startsLine = _ref6.startsLine);
|
||||
if (inImplicitCall() && prevTag !== ',') {
|
||||
endImplicitCall();
|
||||
} else if (inImplicitObject() && sameLine && !startsLine) {
|
||||
endImplicitObject();
|
||||
} else if (inImplicitObject() && tag === 'TERMINATOR' && prevTag !== ',' && !(startsLine && this.looksObjectish(i + 1))) {
|
||||
endImplicitObject();
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (tag === ',' && !this.looksObjectish(i + 1) && inImplicitObject() && (nextTag !== 'TERMINATOR' || !this.looksObjectish(i + 2))) {
|
||||
if (nextTag === 'OUTDENT') {
|
||||
i += 1;
|
||||
}
|
||||
while (inImplicitObject()) {
|
||||
endImplicitObject();
|
||||
}
|
||||
}
|
||||
return forward(1);
|
||||
});
|
||||
};
|
||||
|
||||
Rewriter.prototype.addLocationDataToGeneratedTokens = function() {
|
||||
return this.scanTokens(function(token, i, tokens) {
|
||||
var prevToken, tag;
|
||||
tag = token[0];
|
||||
if ((token.generated || token.explicit) && (!token[2])) {
|
||||
if (i > 0) {
|
||||
prevToken = tokens[i - 1];
|
||||
token[2] = {
|
||||
first_line: prevToken[2].last_line,
|
||||
first_column: prevToken[2].last_column,
|
||||
last_line: prevToken[2].last_line,
|
||||
last_column: prevToken[2].last_column
|
||||
};
|
||||
} else {
|
||||
token[2] = {
|
||||
first_line: 0,
|
||||
first_column: 0,
|
||||
last_line: 0,
|
||||
last_column: 0
|
||||
};
|
||||
}
|
||||
var last_column, last_line, _ref, _ref1, _ref2;
|
||||
if (token[2]) {
|
||||
return 1;
|
||||
}
|
||||
if (!(token.generated || token.explicit)) {
|
||||
return 1;
|
||||
}
|
||||
_ref2 = (_ref = (_ref1 = tokens[i - 1]) != null ? _ref1[2] : void 0) != null ? _ref : {
|
||||
last_line: 0,
|
||||
last_column: 0
|
||||
}, last_line = _ref2.last_line, last_column = _ref2.last_column;
|
||||
token[2] = {
|
||||
first_line: last_line,
|
||||
first_column: last_column,
|
||||
last_line: last_line,
|
||||
last_column: last_column
|
||||
};
|
||||
return 1;
|
||||
});
|
||||
};
|
||||
@@ -330,12 +433,7 @@
|
||||
return [indent, outdent];
|
||||
};
|
||||
|
||||
Rewriter.prototype.generate = function(tag, value) {
|
||||
var tok;
|
||||
tok = [tag, value];
|
||||
tok.generated = true;
|
||||
return tok;
|
||||
};
|
||||
Rewriter.prototype.generate = generate;
|
||||
|
||||
Rewriter.prototype.tag = function(i) {
|
||||
var _ref;
|
||||
|
||||
@@ -566,31 +566,12 @@ exports.Call = class Call extends Base
|
||||
ifn = unfoldSoak o, call, 'variable'
|
||||
ifn
|
||||
|
||||
# Walk through the objects in the arguments, moving over simple values.
|
||||
# This allows syntax like `call a: b, c` into `call({a: b}, c);`
|
||||
filterImplicitObjects: (list) ->
|
||||
nodes = []
|
||||
for node in list
|
||||
unless node.isObject?() and node.base.generated
|
||||
nodes.push node
|
||||
continue
|
||||
obj = null
|
||||
for prop in node.base.properties
|
||||
if prop instanceof Assign or prop instanceof Comment
|
||||
nodes.push obj = new Obj properties = [], true if not obj
|
||||
properties.push prop
|
||||
else
|
||||
nodes.push prop
|
||||
obj = null
|
||||
nodes
|
||||
|
||||
# Compile a vanilla function call.
|
||||
compileNode: (o) ->
|
||||
@variable?.front = @front
|
||||
if code = Splat.compileSplattedArray o, @args, true
|
||||
return @compileSplat o, code
|
||||
args = @filterImplicitObjects @args
|
||||
args = (arg.compile o, LEVEL_LIST for arg in args).join ', '
|
||||
args = (arg.compile o, LEVEL_LIST for arg in @args).join ', '
|
||||
if @isSuper
|
||||
@superReference(o) + ".call(#{@superThis(o)}#{ args and ', ' + args })"
|
||||
else
|
||||
@@ -840,14 +821,11 @@ exports.Arr = class Arr extends Base
|
||||
|
||||
children: ['objects']
|
||||
|
||||
filterImplicitObjects: Call::filterImplicitObjects
|
||||
|
||||
compileNode: (o) ->
|
||||
return '[]' unless @objects.length
|
||||
o.indent += TAB
|
||||
objs = @filterImplicitObjects @objects
|
||||
return code if code = Splat.compileSplattedArray o, objs
|
||||
code = (obj.compile o, LEVEL_LIST for obj in objs).join ', '
|
||||
return code if code = Splat.compileSplattedArray o, @objects
|
||||
code = (obj.compile o, LEVEL_LIST for obj in @objects).join ', '
|
||||
if code.indexOf('\n') >= 0
|
||||
"[\n#{o.indent}#{code}\n#{@tab}]"
|
||||
else
|
||||
|
||||
@@ -5,6 +5,12 @@
|
||||
# shorthand into the unambiguous long form, add implicit indentation and
|
||||
# parentheses, and generally clean things up.
|
||||
|
||||
# Create a generated token: one that exists due to a use of implicit syntax.
|
||||
generate = (tag, value) ->
|
||||
tok = [tag, value]
|
||||
tok.generated = yes
|
||||
tok
|
||||
|
||||
# The **Rewriter** class is used by the [Lexer](lexer.html), directly against
|
||||
# its internal array of tokens.
|
||||
class exports.Rewriter
|
||||
@@ -24,8 +30,7 @@ class exports.Rewriter
|
||||
@closeOpenIndexes()
|
||||
@addImplicitIndentation()
|
||||
@tagPostfixConditionals()
|
||||
@addImplicitBraces()
|
||||
@addImplicitParentheses()
|
||||
@addImplicitBracesAndParens()
|
||||
@addLocationDataToGeneratedTokens()
|
||||
@tokens
|
||||
|
||||
@@ -71,7 +76,6 @@ class exports.Rewriter
|
||||
# its paired close. We have the mis-nested outdent case included here for
|
||||
# calls that close on the same line, just before their outdent.
|
||||
closeOpenCalls: ->
|
||||
|
||||
condition = (token, i) ->
|
||||
token[0] in [')', 'CALL_END'] or
|
||||
token[0] is 'OUTDENT' and @tag(i - 1) is ')'
|
||||
@@ -86,7 +90,6 @@ class exports.Rewriter
|
||||
# The lexer has tagged the opening parenthesis of an indexing operation call.
|
||||
# Match it with its paired close.
|
||||
closeOpenIndexes: ->
|
||||
|
||||
condition = (token, i) ->
|
||||
token[0] in [']', 'INDEX_END']
|
||||
|
||||
@@ -97,121 +100,237 @@ class exports.Rewriter
|
||||
@detectEnd i + 1, condition, action if token[0] is 'INDEX_START'
|
||||
1
|
||||
|
||||
# Object literals may be written with implicit braces, for simple cases.
|
||||
# Insert the missing braces here, so that the parser doesn't have to.
|
||||
addImplicitBraces: ->
|
||||
# Match tags in token stream starting at i with pattern, skipping HERECOMMENTs
|
||||
# Pattern may consist of strings (equality), an array of strings (one of)
|
||||
# or null (wildcard)
|
||||
matchTags: (i, pattern...) ->
|
||||
fuzz = 0
|
||||
for j in [0 ... pattern.length]
|
||||
fuzz += 2 while @tag(i + j + fuzz) is 'HERECOMMENT'
|
||||
continue if not pattern[j]?
|
||||
pattern[j] = [pattern[j]] if typeof pattern[j] is 'string'
|
||||
return no if @tag(i + j + fuzz) not in pattern[j]
|
||||
yes
|
||||
|
||||
stack = []
|
||||
start = null
|
||||
startsLine = null
|
||||
sameLine = yes
|
||||
startIndent = 0
|
||||
startIndex = 0
|
||||
# yes iff standing in front of something looking like
|
||||
# @<x>: or <x>:, skipping over 'HERECOMMENT's
|
||||
looksObjectish: (j) ->
|
||||
@matchTags(j, '@', null, ':') or @matchTags(j, null, ':')
|
||||
|
||||
condition = (token, i) ->
|
||||
[one, two, three] = @tokens[i + 1 .. i + 3]
|
||||
return no if 'HERECOMMENT' is one?[0]
|
||||
[tag] = token
|
||||
sameLine = no if tag in LINEBREAKS
|
||||
return (
|
||||
(tag in ['TERMINATOR', 'OUTDENT'] or
|
||||
(tag in IMPLICIT_END and sameLine and not (i - startIndex is 1))) and
|
||||
((!startsLine and @tag(i - 1) isnt ',') or
|
||||
not (two?[0] is ':' or one?[0] is '@' and three?[0] is ':'))) or
|
||||
(tag is ',' and one and
|
||||
one[0] not in ['IDENTIFIER', 'NUMBER', 'STRING', '@', 'TERMINATOR', 'OUTDENT']
|
||||
)
|
||||
# yes iff current line of tokens contain an element of tags on same
|
||||
# expression level. Stop searching at LINEBREAKS or explicit start of
|
||||
# containing balanced expression.
|
||||
findTagsBackwards: (i, tags) ->
|
||||
backStack = []
|
||||
while i >= 0 and (backStack.length or
|
||||
@tag(i) not in tags and
|
||||
(@tag(i) not in EXPRESSION_START or @tokens[i].generated) and
|
||||
@tag(i) not in LINEBREAKS)
|
||||
backStack.push @tag(i) if @tag(i) in EXPRESSION_END
|
||||
backStack.pop() if @tag(i) in EXPRESSION_START and backStack.length
|
||||
i -= 1
|
||||
@tag(i) in tags
|
||||
|
||||
action = (token, i) ->
|
||||
tok = @generate '}', '}'
|
||||
@tokens.splice i, 0, tok
|
||||
# Look for signs of implicit calls and objects in the token stream and
|
||||
# add them.
|
||||
addImplicitBracesAndParens: ->
|
||||
# Track current balancing depth (both implicit and explicit) on stack.
|
||||
stack = []
|
||||
|
||||
@scanTokens (token, i, tokens) ->
|
||||
if (tag = token[0]) in EXPRESSION_START
|
||||
stack.push [(if tag is 'INDENT' and @tag(i - 1) is '{' then '{' else tag), i]
|
||||
return 1
|
||||
[tag] = token
|
||||
[prevTag] = if i > 0 then tokens[i - 1] else []
|
||||
[nextTag] = if i < tokens.length - 1 then tokens[i + 1] else []
|
||||
stackTop = -> stack[stack.length - 1]
|
||||
startIdx = i
|
||||
|
||||
# Helper function, used for keeping track of the number of tokens consumed
|
||||
# and spliced, when returning for getting a new token.
|
||||
forward = (n) -> i - startIdx + n
|
||||
|
||||
# Helper functions
|
||||
inImplicit = -> stackTop()?[2]?.ours
|
||||
inImplicitCall = -> inImplicit() and stackTop()?[0] is '('
|
||||
inImplicitObject = -> inImplicit() and stackTop()?[0] is '{'
|
||||
# Unclosed control statement inside implicit parens (like
|
||||
# class declaration or if-conditionals)
|
||||
inImplicitControl = -> inImplicit and stackTop()?[0] is 'CONTROL'
|
||||
|
||||
startImplicitCall = (j) ->
|
||||
idx = j ? i
|
||||
stack.push ['(', idx, ours: yes]
|
||||
tokens.splice idx, 0, generate 'CALL_START', '('
|
||||
i += 1 if not j?
|
||||
|
||||
endImplicitCall = ->
|
||||
stack.pop()
|
||||
tokens.splice i, 0, generate 'CALL_END', ')'
|
||||
i += 1
|
||||
|
||||
startImplicitObject = (j, startsLine = yes) ->
|
||||
idx = j ? i
|
||||
stack.push ['{', idx, sameLine: yes, startsLine: startsLine, ours: yes]
|
||||
tokens.splice idx, 0, generate '{', generate(new String('{'))
|
||||
i += 1 if not j?
|
||||
|
||||
endImplicitObject = ->
|
||||
stack.pop()
|
||||
tokens.splice i, 0, generate '}', '}'
|
||||
i += 1
|
||||
|
||||
# Don't end an implicit call on next indent if any of these are in an argument
|
||||
if inImplicitCall() and tag in ['IF', 'CLASS', 'SWITCH', 'CATCH']
|
||||
stack.push ['CONTROL', i, ours: true]
|
||||
return forward(1)
|
||||
|
||||
if tag is 'INDENT' and inImplicit()
|
||||
# An INDENT closes an implicit call unless
|
||||
# 1. We have seen a CONTROL argument on the line.
|
||||
# 2. The last token before the indent is part of the list below
|
||||
if prevTag not in ['=>', '->', '[', '(', ',', '{', 'TRY', 'ELSE', '=']
|
||||
endImplicitCall() while inImplicitCall()
|
||||
stack.pop() if inImplicitControl()
|
||||
stack.push [tag, i]
|
||||
return forward(1)
|
||||
|
||||
# Straightforward start of explicit expression
|
||||
if tag in EXPRESSION_START
|
||||
stack.push [tag, i]
|
||||
return forward(1)
|
||||
|
||||
# Close all implicit expressions inside of explicitly closed expressions.
|
||||
if tag in EXPRESSION_END
|
||||
start = stack.pop()
|
||||
return 1
|
||||
return 1 unless tag is ':' and
|
||||
((ago = @tag i - 2) is ':' or stack[stack.length - 1]?[0] isnt '{')
|
||||
sameLine = yes
|
||||
startIndex = i + 1
|
||||
stack.push ['{']
|
||||
idx = if ago is '@' then i - 2 else i - 1
|
||||
idx -= 2 while @tag(idx - 2) is 'HERECOMMENT'
|
||||
prevTag = @tag(idx - 1)
|
||||
startsLine = not prevTag or (prevTag in LINEBREAKS)
|
||||
value = new String('{')
|
||||
value.generated = yes
|
||||
tok = @generate '{', value
|
||||
tokens.splice idx, 0, tok
|
||||
@detectEnd i + 2, condition, action
|
||||
2
|
||||
while inImplicit()
|
||||
if inImplicitCall()
|
||||
endImplicitCall()
|
||||
else if inImplicitObject()
|
||||
endImplicitObject()
|
||||
else
|
||||
stack.pop()
|
||||
stack.pop()
|
||||
|
||||
# Methods may be optionally called without parentheses, for simple cases.
|
||||
# Insert the implicit parentheses here, so that the parser doesn't have to
|
||||
# deal with them.
|
||||
addImplicitParentheses: ->
|
||||
# Recognize standard implicit calls like
|
||||
# f a, f() b, f? c, h[0] d etc.
|
||||
if (tag in IMPLICIT_FUNC and token.spaced or
|
||||
tag is '?' and i > 0 and not tokens[i - 1].spaced) and
|
||||
(nextTag in IMPLICIT_CALL or
|
||||
nextTag in IMPLICIT_UNSPACED_CALL and
|
||||
not tokens[i + 1]?.spaced and not tokens[i + 1]?.newLine)
|
||||
tag = token[0] = 'FUNC_EXIST' if tag is '?'
|
||||
startImplicitCall i + 1
|
||||
return forward(2)
|
||||
|
||||
noCall = seenSingle = seenControl = no
|
||||
callIndex = null
|
||||
# Implicit call taking an implicit indented object as first argument.
|
||||
# f
|
||||
# a: b
|
||||
# c: d
|
||||
# and
|
||||
# f
|
||||
# 1
|
||||
# a: b
|
||||
# b: c
|
||||
# Don't accept implicit calls of this type, when on the same line
|
||||
# as the control strucutures below as that may misinterpret constructs like:
|
||||
# if f
|
||||
# a: 1
|
||||
# as
|
||||
# if f(a: 1)
|
||||
# which is probably always unintended.
|
||||
# Furthermore don't allow this in literal arrays, as
|
||||
# that creates grammatical ambiguities.
|
||||
if @matchTags(i, IMPLICIT_FUNC, 'INDENT') and
|
||||
stackTop()?[0] isnt '[' and
|
||||
not @findTagsBackwards(i, ['CLASS', 'EXTENDS', 'IF', 'CATCH',
|
||||
'SWITCH', 'LEADING_WHEN', 'FOR', 'WHILE', 'UNTIL'])
|
||||
startImplicitCall i + 1
|
||||
stack.push ['INDENT', i + 2]
|
||||
return forward(3)
|
||||
|
||||
condition = (token, i) ->
|
||||
[tag] = token
|
||||
return yes if not seenSingle and token.fromThen
|
||||
seenSingle = yes if tag in ['IF', 'ELSE', 'CATCH', '->', '=>', 'CLASS']
|
||||
seenControl = yes if tag in ['IF', 'ELSE', 'SWITCH', 'TRY', '=']
|
||||
return yes if tag in ['.', '?.', '::'] and @tag(i - 1) is 'OUTDENT'
|
||||
not token.generated and @tag(i - 1) isnt ',' and (tag in IMPLICIT_END or
|
||||
(tag is 'INDENT' and not seenControl)) and
|
||||
(tag isnt 'INDENT' or
|
||||
(@tag(i - 2) not in ['CLASS', 'EXTENDS'] and @tag(i - 1) not in IMPLICIT_BLOCK and
|
||||
not (callIndex is i - 1 and (post = @tokens[i + 1]) and post.generated and post[0] is '{')))
|
||||
# Implicit objects start here
|
||||
if tag is ':'
|
||||
# Go back to the (implicit) start of the object
|
||||
if @tag(i - 2) is '@' then s = i - 2 else s = i - 1
|
||||
s -= 2 while @tag(s - 2) is 'HERECOMMENT'
|
||||
|
||||
action = (token, i) ->
|
||||
@tokens.splice i, 0, @generate 'CALL_END', ')'
|
||||
startsLine = s is 0 or @tag(s - 1) in LINEBREAKS or tokens[s - 1].newLine
|
||||
# Are we just continuing an already declared object?
|
||||
if stackTop()
|
||||
[stackTag, stackIdx] = stackTop()
|
||||
if (stackTag is '{' or stackTag is 'INDENT' and @tag(stackIdx - 1) is '{') and
|
||||
(startsLine or @tag(s - 1) is ',' or @tag(s - 1) is '{')
|
||||
return forward(1)
|
||||
|
||||
@scanTokens (token, i, tokens) ->
|
||||
tag = token[0]
|
||||
noCall = yes if tag in ['CLASS', 'IF', 'FOR', 'WHILE']
|
||||
[prev, current, next] = tokens[i - 1 .. i + 1]
|
||||
callObject = not noCall and tag is 'INDENT' and
|
||||
next and next.generated and next[0] is '{' and
|
||||
prev and prev[0] in IMPLICIT_FUNC
|
||||
seenSingle = no
|
||||
seenControl = no
|
||||
noCall = no if tag in LINEBREAKS
|
||||
token.call = yes if prev and not prev.spaced and tag is '?'
|
||||
return 1 if token.fromThen
|
||||
return 1 unless callObject or
|
||||
prev?.spaced and (prev.call or prev[0] in IMPLICIT_FUNC) and
|
||||
(tag in IMPLICIT_CALL or not (token.spaced or token.newLine) and tag in IMPLICIT_UNSPACED_CALL)
|
||||
callIndex = i
|
||||
tokens.splice i, 0, @generate 'CALL_START', '(', token[2]
|
||||
@detectEnd i + 1, condition, action
|
||||
prev[0] = 'FUNC_EXIST' if prev[0] is '?'
|
||||
2
|
||||
startImplicitObject(s, !!startsLine)
|
||||
return forward(2)
|
||||
|
||||
# End implicit calls when chaining method calls
|
||||
# like e.g.:
|
||||
# f ->
|
||||
# a
|
||||
# .g b, ->
|
||||
# c
|
||||
# .h a
|
||||
if prevTag is 'OUTDENT' and inImplicitCall() and tag in ['.', '?.', '::']
|
||||
endImplicitCall()
|
||||
return forward(1)
|
||||
|
||||
stackTop()[2].sameLine = no if inImplicitObject() and tag in LINEBREAKS
|
||||
|
||||
if tag in IMPLICIT_END
|
||||
while inImplicit()
|
||||
[stackTag, stackIdx, {sameLine, startsLine}] = stackTop()
|
||||
# Close implicit calls when reached end of argument list
|
||||
if inImplicitCall() and prevTag isnt ','
|
||||
endImplicitCall()
|
||||
# Close implicit objects such as:
|
||||
# return a: 1, b: 2 unless true
|
||||
else if inImplicitObject() and sameLine and not startsLine
|
||||
endImplicitObject()
|
||||
# Close implicit objects when at end of line, line didn't end with a comma
|
||||
# and the implicit object didn't start the line or the next line doesn't look like
|
||||
# the continuation of an object.
|
||||
else if inImplicitObject() and tag is 'TERMINATOR' and prevTag isnt ',' and
|
||||
not (startsLine and @looksObjectish(i + 1))
|
||||
endImplicitObject()
|
||||
else
|
||||
break
|
||||
|
||||
# Close implicit object if comma is the last character
|
||||
# and what comes after doesn't look like it belongs.
|
||||
# This is used for trailing commas and calls, like:
|
||||
# x =
|
||||
# a: b,
|
||||
# c: d,
|
||||
# e = 2
|
||||
#
|
||||
# and
|
||||
#
|
||||
# f a, b: c, d: e, f, g: h: i, j
|
||||
if tag is ',' and not @looksObjectish(i + 1) and inImplicitObject() and
|
||||
(nextTag isnt 'TERMINATOR' or not @looksObjectish(i + 2))
|
||||
# When nextTag is OUTDENT the comma is insignificant and
|
||||
# should just be ignored so embed it in the implicit object.
|
||||
#
|
||||
# When it isn't the comma go on to play a role in a call or
|
||||
# array further up the stack, so give it a chance.
|
||||
if nextTag is 'OUTDENT'
|
||||
i += 1
|
||||
while inImplicitObject()
|
||||
endImplicitObject()
|
||||
return forward(1)
|
||||
|
||||
# Add location data to all tokens generated by the rewriter.
|
||||
addLocationDataToGeneratedTokens: ->
|
||||
@scanTokens (token, i, tokens) ->
|
||||
tag = token[0]
|
||||
if (token.generated or token.explicit) and (not token[2])
|
||||
if i > 0
|
||||
prevToken = tokens[i-1]
|
||||
token[2] =
|
||||
first_line: prevToken[2].last_line
|
||||
first_column: prevToken[2].last_column
|
||||
last_line: prevToken[2].last_line
|
||||
last_column: prevToken[2].last_column
|
||||
else
|
||||
token[2] =
|
||||
first_line: 0
|
||||
first_column: 0
|
||||
last_line: 0
|
||||
last_column: 0
|
||||
return 1
|
||||
return 1 if token[2]
|
||||
return 1 unless token.generated or token.explicit
|
||||
{last_line, last_column} = tokens[i - 1]?[2] ? last_line: 0, last_column: 0
|
||||
token[2] =
|
||||
first_line: last_line
|
||||
first_column: last_column
|
||||
last_line: last_line
|
||||
last_column: last_column
|
||||
1
|
||||
|
||||
# Because our grammar is LALR(1), it can't handle some single-line
|
||||
# expressions that lack ending delimiters. The **Rewriter** adds the implicit
|
||||
@@ -276,11 +395,7 @@ class exports.Rewriter
|
||||
indent.explicit = outdent.explicit = yes if not implicit
|
||||
[indent, outdent]
|
||||
|
||||
# Create a generated token: one that exists due to a use of implicit syntax.
|
||||
generate: (tag, value) ->
|
||||
tok = [tag, value]
|
||||
tok.generated = yes
|
||||
tok
|
||||
generate: generate
|
||||
|
||||
# Look up a tag by token index.
|
||||
tag: (i) -> @tokens[i]?[0]
|
||||
@@ -330,7 +445,8 @@ IMPLICIT_UNSPACED_CALL = ['+', '-']
|
||||
IMPLICIT_BLOCK = ['->', '=>', '{', '[', ',']
|
||||
|
||||
# Tokens that always mark the end of an implicit call for single-liners.
|
||||
IMPLICIT_END = ['POST_IF', 'FOR', 'WHILE', 'UNTIL', 'WHEN', 'BY', 'LOOP', 'TERMINATOR']
|
||||
IMPLICIT_END = ['POST_IF', 'FOR', 'WHILE', 'UNTIL', 'WHEN', 'BY',
|
||||
'LOOP', 'TERMINATOR']
|
||||
|
||||
# Single-line flavors of block expressions that have unclosed endings.
|
||||
# The grammar can't disambiguate them, so we insert the implicit indentation.
|
||||
|
||||
@@ -696,3 +696,14 @@ test "#2359: constructors should not return an explicit value", ->
|
||||
return bar: 7
|
||||
baz()
|
||||
"""
|
||||
|
||||
test "#2319: fn class n extends o.p [INDENT] x = 123", ->
|
||||
first = ->
|
||||
|
||||
base = onebase: ->
|
||||
|
||||
first class OneKeeper extends base.onebase
|
||||
one = 1
|
||||
one: -> one
|
||||
|
||||
eq new OneKeeper().one(), 1
|
||||
@@ -422,7 +422,6 @@ test "Issue #997. Switch doesn't fallthrough.", ->
|
||||
|
||||
|
||||
test "Throw should be usable as an expression.", ->
|
||||
|
||||
try
|
||||
false or throw 'up'
|
||||
throw new Error 'failed'
|
||||
|
||||
@@ -558,3 +558,116 @@ test "#2617: implicit call before unrelated implicit object", ->
|
||||
result = if pass 1
|
||||
one: 1
|
||||
eq result.one, 1
|
||||
|
||||
test "#2292, b: f (z),(x)", ->
|
||||
f = (x, y) -> y
|
||||
one = 1
|
||||
two = 2
|
||||
o = b: f (one),(two)
|
||||
eq o.b, 2
|
||||
|
||||
test "#2297, Different behaviors on interpreting literal", ->
|
||||
foo = (x, y) -> y
|
||||
bar =
|
||||
baz: foo 100, on
|
||||
|
||||
eq bar.baz, on
|
||||
|
||||
qux = (x) -> x
|
||||
quux = qux
|
||||
corge: foo 100, true
|
||||
|
||||
eq quux.corge, on
|
||||
|
||||
xyzzy =
|
||||
e: 1
|
||||
f: foo
|
||||
a: 1
|
||||
b: 2
|
||||
,
|
||||
one: 1
|
||||
two: 2
|
||||
three: 3
|
||||
g:
|
||||
a: 1
|
||||
b: 2
|
||||
c: foo 2,
|
||||
one: 1
|
||||
two: 2
|
||||
three: 3
|
||||
d: 3
|
||||
four: 4
|
||||
h: foo one: 1, two: 2, three: three: three: 3,
|
||||
2
|
||||
|
||||
eq xyzzy.f.two, 2
|
||||
eq xyzzy.g.c.three, 3
|
||||
eq xyzzy.four, 4
|
||||
eq xyzzy.h, 2
|
||||
|
||||
thud = foo
|
||||
1
|
||||
one: 1
|
||||
two: 2
|
||||
three: 3
|
||||
2
|
||||
3
|
||||
eq thud.two, 2
|
||||
|
||||
test "#2715, Chained implicit calls", ->
|
||||
first = (x) -> x
|
||||
second = (x, y) -> y
|
||||
|
||||
foo = first first
|
||||
one: 1
|
||||
eq foo.one, 1
|
||||
|
||||
bar = first second
|
||||
one: 1, 2
|
||||
eq bar, 2
|
||||
|
||||
baz = first second
|
||||
one: 1,
|
||||
2
|
||||
eq baz, 2
|
||||
|
||||
qux = first second
|
||||
1
|
||||
2
|
||||
3
|
||||
4
|
||||
eq qux, 2
|
||||
|
||||
|
||||
test "More implicit calls", ->
|
||||
first = (x) -> x
|
||||
second = (x, y) -> y
|
||||
|
||||
foo = no
|
||||
if (first
|
||||
yes)
|
||||
foo = yes
|
||||
eq foo, yes
|
||||
|
||||
foo = no
|
||||
if not first
|
||||
no
|
||||
foo = yes
|
||||
eq foo, no
|
||||
|
||||
test "Implicit calls and new", ->
|
||||
first = (x) -> x
|
||||
foo = (@x) ->
|
||||
bar = first new foo first 1
|
||||
eq bar.x, 1
|
||||
|
||||
third = (x, y, z) -> z
|
||||
baz = first new foo
|
||||
new
|
||||
foo third
|
||||
one: 1
|
||||
two: 2
|
||||
1
|
||||
three: 3
|
||||
2
|
||||
eq baz.x.x.three, 3
|
||||
@@ -222,6 +222,118 @@ test "#1436: `for` etc. work as normal property names", ->
|
||||
obj.for = 'foo' of obj
|
||||
eq yes, obj.hasOwnProperty 'for'
|
||||
|
||||
test "#2706, Un-bracketed object as argument causes inconsistent behavior", ->
|
||||
foo = (x, y) -> y
|
||||
bar = baz: yes
|
||||
|
||||
eq yes, foo x: 1, bar.baz
|
||||
|
||||
test "#2608, Allow inline objects in arguments to be followed by more arguments", ->
|
||||
foo = (x, y) -> y
|
||||
|
||||
eq yes, foo x: 1, y: 2, yes
|
||||
|
||||
test "#2308, a: b = c:1", ->
|
||||
foo = a: b = c: yes
|
||||
eq b.c, yes
|
||||
eq foo.a.c, yes
|
||||
|
||||
test "#2317, a: b c: 1", ->
|
||||
foo = (x) -> x
|
||||
bar = a: foo c: yes
|
||||
eq bar.a.c, yes
|
||||
|
||||
test "#1896, a: func b, {c: d}", ->
|
||||
first = (x) -> x
|
||||
second = (x, y) -> y
|
||||
third = (x, y, z) -> z
|
||||
|
||||
one = 1
|
||||
two = 2
|
||||
three = 3
|
||||
four = 4
|
||||
|
||||
foo = a: second one, {c: two}
|
||||
eq foo.a.c, two
|
||||
|
||||
bar = a: second one, c: two
|
||||
eq bar.a.c, two
|
||||
|
||||
baz = a: second one, {c: two}, e: first first h: three
|
||||
eq baz.a.c, two
|
||||
|
||||
qux = a: third one, {c: two}, e: first first h: three
|
||||
eq qux.a.e.h, three
|
||||
|
||||
quux = a: third one, {c: two}, e: first(three), h: four
|
||||
eq quux.a.e, three
|
||||
eq quux.a.h, four
|
||||
|
||||
corge = a: third one, {c: two}, e: second three, h: four
|
||||
eq corge.a.e.h, four
|
||||
|
||||
test "Implicit objects, functions and arrays", ->
|
||||
first = (x) -> x
|
||||
second = (x, y) -> y
|
||||
|
||||
foo = [
|
||||
1
|
||||
one: 1
|
||||
two: 2
|
||||
three: 3
|
||||
more:
|
||||
four: 4
|
||||
five: 5, six: 6
|
||||
2, 3, 4
|
||||
5]
|
||||
eq foo[2], 2
|
||||
eq foo[1].more.six, 6
|
||||
|
||||
bar = [
|
||||
1
|
||||
first first first second 1,
|
||||
one: 1, twoandthree: twoandthree: two: 2, three: 3
|
||||
2,
|
||||
2
|
||||
one: 1
|
||||
two: 2
|
||||
three: first second ->
|
||||
no
|
||||
, ->
|
||||
3
|
||||
3
|
||||
4]
|
||||
eq bar[2], 2
|
||||
eq bar[1].twoandthree.twoandthree.two, 2
|
||||
eq bar[3].three(), 3
|
||||
eq bar[4], 3
|
||||
|
||||
test "#2549, Brace-less Object Literal as a Second Operand on a New Line", ->
|
||||
foo = no or
|
||||
one: 1
|
||||
two: 2
|
||||
three: 3
|
||||
eq foo.one, 1
|
||||
|
||||
bar = yes and one: 1
|
||||
eq bar.one, 1
|
||||
|
||||
baz = null ?
|
||||
one: 1
|
||||
two: 2
|
||||
eq baz.two, 2
|
||||
|
||||
test "#1865, syntax regression 1.1.3", ->
|
||||
foo = (x, y) -> y
|
||||
|
||||
bar = a: foo (->),
|
||||
c: yes
|
||||
eq bar.a.c, yes
|
||||
|
||||
baz = a: foo (->), c: yes
|
||||
eq baz.a.c, yes
|
||||
|
||||
|
||||
test "#1322: implicit call against implicit object with block comments", ->
|
||||
((obj, arg) ->
|
||||
eq obj.x * obj.y, 6
|
||||
@@ -273,7 +385,5 @@ test "#1961, #1974, regression with compound assigning to an implicit object", -
|
||||
test "#2207: Immediate implicit closes don't close implicit objects", ->
|
||||
func = ->
|
||||
key: for i in [1, 2, 3] then i
|
||||
|
||||
|
||||
eq func().key.join(' '), '1 2 3'
|
||||
|
||||
|
||||
Reference in New Issue
Block a user