mirror of
https://github.com/jashkenas/coffeescript.git
synced 2026-05-03 03:00:14 -04:00
[CS2] Output ES2015 arrow functions, default parameters, rest parameters (#4311)
* Eliminate wrapper around “bound” (arrow) functions; output `=>` for such functions * Remove irrelevant (and breaking) tests * Minor cleanup * When a function parameter is a splat (i.e., it uses the ES2015 rest parameter syntax) output that parameter as ES2015 * Rearrange function parameters when one of the parameters is a splat and isn’t the last parameter (very WIP) * Handle params like `@param`, adding assignment expressions for them when they appear; ensure splat parameter is last * Add parameter names (not a text like `'\nValue IdentifierLiteral: a'`) to the scope, so that parameters can’t be deleted; move body-related lines together; more explanation of what’s going on * For parameters with a default value, correctly add the parameter name to the function scope * Handle expansions in function parameters: when an expansion is found, set the parameters to only be the original parameters left of the expansion, then an `...args` parameter; and in the function body define variables for the parameters to the right of the expansion, including setting default values * Handle splat parameters the same way we handle expansions: if a splat parameter is found, it becomes the last parameter in the function definition, and all following parameters get declared in the function body. Fix the splat/rest parameter values after the post-splat parameters have been extracted from it. Clean up `Code.compileNode` so that we loop through the parameters only once, and we create all expressions using calls like `new IdentifierLiteral` rather than `@makeCode`. * Fix parameter name when a parameter is a splat attached to `this` (e.g. `@param...`) * Rather than assigning post-splat parameters based on index, use slice; passes test “Functions with splats being called with too few arguments” * Dial back our w00t indentation * Better parsing of parameter names (WIP) * Refactor processing of splat/expansion parameters * Fix assignment of default parameters for parameters that come after a splat * Better check for whether a param is attached to `this` * More understandable variable names * For parameters after a splat or expansion, assign them similar to the 1.x destructuring method of using `arguments`, except only concern ourselves with the post-splat parameters instead of all parameters; and use the splat/expansion parameter name, since `arguments` in ES fat arrow functions refers to the parent function’s `arguments` rather than the fat arrow function’s arguments/parameters * Don’t add unnamed parameters (like `[]` as a parameter) to the function scope * Disallow multiple splat/expansion parameters in function definitions; disallow lone expansion parameters * Fix `this` params not getting assigned if the parameter is after a splat parameter * Allow names of function parameters attached to `this` to be reserved words * Always add a statement to the function body defining a variable with its default value, if it has one, if the variable `== null`; this covers the case when ES doesn’t apply the default value when `null` is passed in as a value, but CoffeeScript expects `null` and `undefined` to act interchangeably * Aftermath of having both `undefined` and `null` trigger the use of default values for parameters with default values * More careful parsing of destructured parameters * Fall back to processing destructured parameters in the function body, to account for `this` or default values within destructured objects * Clean up comments * Restore new bare function test, minus the arrow function part of it * Test that bound/arrow functions aren’t overwriting the `arguments` object, which should refer to the parent scope’s `arguments` (like `this`) * Follow ES2015 spec for parameter default values: `null` gets assigned as as `null`, not the default value * Mimic ES default parameters behavior for parameters after a splat or expansion parameter * Bound functions cannot be generators: remove no-longer-relevant test, add check to throw error if `yield` appears inside a bound (arrow) function * Error for bound generator functions should underline the `yield`
This commit is contained in:
@@ -9,20 +9,14 @@
|
||||
|
||||
compile = CoffeeScript.compile;
|
||||
|
||||
CoffeeScript["eval"] = function(code, options) {
|
||||
if (options == null) {
|
||||
options = {};
|
||||
}
|
||||
CoffeeScript["eval"] = function(code, options = {}) {
|
||||
if (options.bare == null) {
|
||||
options.bare = true;
|
||||
}
|
||||
return eval(compile(code, options));
|
||||
};
|
||||
|
||||
CoffeeScript.run = function(code, options) {
|
||||
if (options == null) {
|
||||
options = {};
|
||||
}
|
||||
CoffeeScript.run = function(code, options = {}) {
|
||||
options.bare = true;
|
||||
options.shiftLine = true;
|
||||
return Function(compile(code, options))();
|
||||
@@ -33,23 +27,14 @@
|
||||
}
|
||||
|
||||
if ((typeof btoa !== "undefined" && btoa !== null) && (typeof JSON !== "undefined" && JSON !== null)) {
|
||||
compile = function(code, options) {
|
||||
if (options == null) {
|
||||
options = {};
|
||||
}
|
||||
compile = function(code, options = {}) {
|
||||
options.inlineMap = true;
|
||||
return CoffeeScript.compile(code, options);
|
||||
};
|
||||
}
|
||||
|
||||
CoffeeScript.load = function(url, callback, options, hold) {
|
||||
CoffeeScript.load = function(url, callback, options = {}, hold = false) {
|
||||
var xhr;
|
||||
if (options == null) {
|
||||
options = {};
|
||||
}
|
||||
if (hold == null) {
|
||||
hold = false;
|
||||
}
|
||||
options.sourceFiles = [url];
|
||||
xhr = window.ActiveXObject ? new window.ActiveXObject('Microsoft.XMLHTTP') : new window.XMLHttpRequest();
|
||||
xhr.open('GET', url, true);
|
||||
|
||||
@@ -35,11 +35,8 @@
|
||||
};
|
||||
|
||||
withPrettyErrors = function(fn) {
|
||||
return function(code, options) {
|
||||
return function(code, options = {}) {
|
||||
var err;
|
||||
if (options == null) {
|
||||
options = {};
|
||||
}
|
||||
try {
|
||||
return fn.call(this, code, options);
|
||||
} catch (error) {
|
||||
@@ -145,11 +142,8 @@
|
||||
}
|
||||
});
|
||||
|
||||
exports.run = function(code, options) {
|
||||
exports.run = function(code, options = {}) {
|
||||
var answer, dir, mainModule, ref;
|
||||
if (options == null) {
|
||||
options = {};
|
||||
}
|
||||
mainModule = require.main;
|
||||
mainModule.filename = process.argv[1] = options.filename ? fs.realpathSync(options.filename) : '.';
|
||||
mainModule.moduleCache && (mainModule.moduleCache = {});
|
||||
@@ -162,11 +156,8 @@
|
||||
return mainModule._compile(code, mainModule.filename);
|
||||
};
|
||||
|
||||
exports["eval"] = function(code, options) {
|
||||
exports["eval"] = function(code, options = {}) {
|
||||
var Module, _module, _require, createContext, i, isContext, js, k, len, o, r, ref, ref1, ref2, ref3, sandbox, v;
|
||||
if (options == null) {
|
||||
options = {};
|
||||
}
|
||||
if (!(code = code.trim())) {
|
||||
return;
|
||||
}
|
||||
@@ -246,14 +237,8 @@
|
||||
}
|
||||
}
|
||||
|
||||
exports._compileFile = function(filename, sourceMap, inlineMap) {
|
||||
exports._compileFile = function(filename, sourceMap = false, inlineMap = false) {
|
||||
var answer, err, raw, stripped;
|
||||
if (sourceMap == null) {
|
||||
sourceMap = false;
|
||||
}
|
||||
if (inlineMap == null) {
|
||||
inlineMap = false;
|
||||
}
|
||||
raw = fs.readFileSync(filename, 'utf8');
|
||||
stripped = raw.charCodeAt(0) === 0xFEFF ? raw.substring(1) : raw;
|
||||
try {
|
||||
|
||||
@@ -198,11 +198,8 @@
|
||||
return process.exit(1);
|
||||
};
|
||||
|
||||
compileScript = function(file, input, base) {
|
||||
compileScript = function(file, input, base = null) {
|
||||
var compiled, err, message, o, options, t, task;
|
||||
if (base == null) {
|
||||
base = null;
|
||||
}
|
||||
o = opts;
|
||||
options = compileOptions(file, base);
|
||||
try {
|
||||
@@ -439,11 +436,8 @@
|
||||
}
|
||||
};
|
||||
|
||||
outputPath = function(source, base, extension) {
|
||||
outputPath = function(source, base, extension = ".js") {
|
||||
var basename, dir, srcDir;
|
||||
if (extension == null) {
|
||||
extension = ".js";
|
||||
}
|
||||
basename = helpers.baseFileName(source, true, useWinPathSep);
|
||||
srcDir = path.dirname(source);
|
||||
if (!opts.output) {
|
||||
@@ -477,11 +471,8 @@
|
||||
})(dir, fn);
|
||||
};
|
||||
|
||||
writeJs = function(base, sourcePath, js, jsPath, generatedSourceMap) {
|
||||
writeJs = function(base, sourcePath, js, jsPath, generatedSourceMap = null) {
|
||||
var compile, jsDir, sourceMapPath;
|
||||
if (generatedSourceMap == null) {
|
||||
generatedSourceMap = null;
|
||||
}
|
||||
sourceMapPath = outputPath(sourcePath, base, ".js.map");
|
||||
jsDir = path.dirname(jsPath);
|
||||
compile = function() {
|
||||
|
||||
@@ -154,14 +154,8 @@
|
||||
}
|
||||
};
|
||||
|
||||
exports.baseFileName = function(file, stripExt, useWinPathSep) {
|
||||
exports.baseFileName = function(file, stripExt = false, useWinPathSep = false) {
|
||||
var parts, pathSep;
|
||||
if (stripExt == null) {
|
||||
stripExt = false;
|
||||
}
|
||||
if (useWinPathSep == null) {
|
||||
useWinPathSep = false;
|
||||
}
|
||||
pathSep = useWinPathSep ? /\\|\// : /\//;
|
||||
parts = file.split(pathSep);
|
||||
file = parts[parts.length - 1];
|
||||
|
||||
@@ -11,11 +11,8 @@
|
||||
exports.Lexer = Lexer = (function() {
|
||||
function Lexer() {}
|
||||
|
||||
Lexer.prototype.tokenize = function(code, opts) {
|
||||
Lexer.prototype.tokenize = function(code, opts = {}) {
|
||||
var consumed, end, i, ref2;
|
||||
if (opts == null) {
|
||||
opts = {};
|
||||
}
|
||||
this.literate = opts.literate;
|
||||
this.indent = 0;
|
||||
this.baseIndent = 0;
|
||||
@@ -269,37 +266,33 @@
|
||||
}
|
||||
this.mergeInterpolationTokens(tokens, {
|
||||
delimiter: delimiter
|
||||
}, (function(_this) {
|
||||
return function(value, i) {
|
||||
value = _this.formatString(value);
|
||||
if (indentRegex) {
|
||||
value = value.replace(indentRegex, '\n');
|
||||
}
|
||||
if (i === 0) {
|
||||
value = value.replace(LEADING_BLANK_LINE, '');
|
||||
}
|
||||
if (i === $) {
|
||||
value = value.replace(TRAILING_BLANK_LINE, '');
|
||||
}
|
||||
return value;
|
||||
};
|
||||
})(this));
|
||||
}, (value, i) => {
|
||||
value = this.formatString(value);
|
||||
if (indentRegex) {
|
||||
value = value.replace(indentRegex, '\n');
|
||||
}
|
||||
if (i === 0) {
|
||||
value = value.replace(LEADING_BLANK_LINE, '');
|
||||
}
|
||||
if (i === $) {
|
||||
value = value.replace(TRAILING_BLANK_LINE, '');
|
||||
}
|
||||
return value;
|
||||
});
|
||||
} else {
|
||||
this.mergeInterpolationTokens(tokens, {
|
||||
delimiter: delimiter
|
||||
}, (function(_this) {
|
||||
return function(value, i) {
|
||||
value = _this.formatString(value);
|
||||
value = value.replace(SIMPLE_STRING_OMIT, function(match, offset) {
|
||||
if ((i === 0 && offset === 0) || (i === $ && offset + match.length === value.length)) {
|
||||
return '';
|
||||
} else {
|
||||
return ' ';
|
||||
}
|
||||
});
|
||||
return value;
|
||||
};
|
||||
})(this));
|
||||
}, (value, i) => {
|
||||
value = this.formatString(value);
|
||||
value = value.replace(SIMPLE_STRING_OMIT, function(match, offset) {
|
||||
if ((i === 0 && offset === 0) || (i === $ && offset + match.length === value.length)) {
|
||||
return '';
|
||||
} else {
|
||||
return ' ';
|
||||
}
|
||||
});
|
||||
return value;
|
||||
});
|
||||
}
|
||||
return end;
|
||||
};
|
||||
@@ -815,14 +808,8 @@
|
||||
return [this.chunkLine + lineCount, column];
|
||||
};
|
||||
|
||||
Lexer.prototype.makeToken = function(tag, value, offsetInChunk, length) {
|
||||
Lexer.prototype.makeToken = function(tag, value, offsetInChunk = 0, length = value.length) {
|
||||
var lastCharacter, locationData, ref2, ref3, token;
|
||||
if (offsetInChunk == null) {
|
||||
offsetInChunk = 0;
|
||||
}
|
||||
if (length == null) {
|
||||
length = value.length;
|
||||
}
|
||||
locationData = {};
|
||||
ref2 = this.getLineAndColumnFromChunk(offsetInChunk), locationData.first_line = ref2[0], locationData.first_column = ref2[1];
|
||||
lastCharacter = length > 0 ? length - 1 : 0;
|
||||
@@ -866,11 +853,8 @@
|
||||
return str.replace(HEREGEX_OMIT, '$1$2');
|
||||
};
|
||||
|
||||
Lexer.prototype.validateEscapes = function(str, options) {
|
||||
Lexer.prototype.validateEscapes = function(str, options = {}) {
|
||||
var before, hex, invalidEscape, match, message, octal, ref2, unicode;
|
||||
if (options == null) {
|
||||
options = {};
|
||||
}
|
||||
match = INVALID_ESCAPE.exec(str);
|
||||
if (!match) {
|
||||
return;
|
||||
@@ -887,11 +871,8 @@
|
||||
});
|
||||
};
|
||||
|
||||
Lexer.prototype.makeDelimitedLiteral = function(body, options) {
|
||||
Lexer.prototype.makeDelimitedLiteral = function(body, options = {}) {
|
||||
var regex;
|
||||
if (options == null) {
|
||||
options = {};
|
||||
}
|
||||
if (body === '' && options.delimiter === '/') {
|
||||
body = '(?:)';
|
||||
}
|
||||
@@ -927,11 +908,8 @@
|
||||
return "" + options.delimiter + body + options.delimiter;
|
||||
};
|
||||
|
||||
Lexer.prototype.error = function(message, options) {
|
||||
Lexer.prototype.error = function(message, options = {}) {
|
||||
var first_column, first_line, location, ref2, ref3, ref4;
|
||||
if (options == null) {
|
||||
options = {};
|
||||
}
|
||||
location = 'first_line' in options ? options : ((ref3 = this.getLineAndColumnFromChunk((ref2 = options.offset) != null ? ref2 : 0), first_line = ref3[0], first_column = ref3[1], ref3), {
|
||||
first_line: first_line,
|
||||
first_column: first_column,
|
||||
@@ -944,10 +922,7 @@
|
||||
|
||||
})();
|
||||
|
||||
isUnassignable = function(name, displayName) {
|
||||
if (displayName == null) {
|
||||
displayName = name;
|
||||
}
|
||||
isUnassignable = function(name, displayName = name) {
|
||||
switch (false) {
|
||||
case indexOf.call(slice.call(JS_KEYWORDS).concat(slice.call(COFFEE_KEYWORDS)), name) < 0:
|
||||
return "keyword '" + displayName + "' can't be assigned";
|
||||
|
||||
@@ -166,14 +166,8 @@
|
||||
return null;
|
||||
};
|
||||
|
||||
Base.prototype.toString = function(idt, name) {
|
||||
Base.prototype.toString = function(idt = '', name = this.constructor.name) {
|
||||
var tree;
|
||||
if (idt == null) {
|
||||
idt = '';
|
||||
}
|
||||
if (name == null) {
|
||||
name = this.constructor.name;
|
||||
}
|
||||
tree = '\n' + idt + name;
|
||||
if (this.soak) {
|
||||
tree += '?';
|
||||
@@ -361,10 +355,7 @@
|
||||
return this;
|
||||
};
|
||||
|
||||
Block.prototype.compileToFragments = function(o, level) {
|
||||
if (o == null) {
|
||||
o = {};
|
||||
}
|
||||
Block.prototype.compileToFragments = function(o = {}, level) {
|
||||
if (o.scope) {
|
||||
return Block.__super__.compileToFragments.call(this, o, level);
|
||||
} else {
|
||||
@@ -950,34 +941,32 @@
|
||||
};
|
||||
|
||||
Value.prototype.unfoldSoak = function(o) {
|
||||
return this.unfoldedSoak != null ? this.unfoldedSoak : this.unfoldedSoak = (function(_this) {
|
||||
return function() {
|
||||
var fst, i, ifn, j, len1, prop, ref, ref3, ref4, snd;
|
||||
if (ifn = _this.base.unfoldSoak(o)) {
|
||||
(ref3 = ifn.body.properties).push.apply(ref3, _this.properties);
|
||||
return ifn;
|
||||
return this.unfoldedSoak != null ? this.unfoldedSoak : this.unfoldedSoak = (() => {
|
||||
var fst, i, ifn, j, len1, prop, ref, ref3, ref4, snd;
|
||||
if (ifn = this.base.unfoldSoak(o)) {
|
||||
(ref3 = ifn.body.properties).push.apply(ref3, this.properties);
|
||||
return ifn;
|
||||
}
|
||||
ref4 = this.properties;
|
||||
for (i = j = 0, len1 = ref4.length; j < len1; i = ++j) {
|
||||
prop = ref4[i];
|
||||
if (!prop.soak) {
|
||||
continue;
|
||||
}
|
||||
ref4 = _this.properties;
|
||||
for (i = j = 0, len1 = ref4.length; j < len1; i = ++j) {
|
||||
prop = ref4[i];
|
||||
if (!prop.soak) {
|
||||
continue;
|
||||
}
|
||||
prop.soak = false;
|
||||
fst = new Value(_this.base, _this.properties.slice(0, i));
|
||||
snd = new Value(_this.base, _this.properties.slice(i));
|
||||
if (fst.isComplex()) {
|
||||
ref = new IdentifierLiteral(o.scope.freeVariable('ref'));
|
||||
fst = new Parens(new Assign(ref, fst));
|
||||
snd.base = ref;
|
||||
}
|
||||
return new If(new Existence(fst), snd, {
|
||||
soak: true
|
||||
});
|
||||
prop.soak = false;
|
||||
fst = new Value(this.base, this.properties.slice(0, i));
|
||||
snd = new Value(this.base, this.properties.slice(i));
|
||||
if (fst.isComplex()) {
|
||||
ref = new IdentifierLiteral(o.scope.freeVariable('ref'));
|
||||
fst = new Parens(new Assign(ref, fst));
|
||||
snd.base = ref;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
})(this)();
|
||||
return new If(new Existence(fst), snd, {
|
||||
soak: true
|
||||
});
|
||||
}
|
||||
return false;
|
||||
})();
|
||||
};
|
||||
|
||||
return Value;
|
||||
@@ -1013,6 +1002,7 @@
|
||||
extend1(Call, superClass1);
|
||||
|
||||
function Call(variable1, args1, soak) {
|
||||
var args1;
|
||||
this.variable = variable1;
|
||||
this.args = args1 != null ? args1 : [];
|
||||
this.soak = soak;
|
||||
@@ -1207,10 +1197,7 @@
|
||||
exports.RegexWithInterpolations = RegexWithInterpolations = (function(superClass1) {
|
||||
extend1(RegexWithInterpolations, superClass1);
|
||||
|
||||
function RegexWithInterpolations(args) {
|
||||
if (args == null) {
|
||||
args = [];
|
||||
}
|
||||
function RegexWithInterpolations(args = []) {
|
||||
RegexWithInterpolations.__super__.constructor.call(this, new Value(new IdentifierLiteral('RegExp')), args, false);
|
||||
}
|
||||
|
||||
@@ -1421,6 +1408,7 @@
|
||||
extend1(Obj, superClass1);
|
||||
|
||||
function Obj(props, generated) {
|
||||
var generated;
|
||||
this.generated = generated != null ? generated : false;
|
||||
this.objects = this.properties = props || [];
|
||||
}
|
||||
@@ -1569,6 +1557,7 @@
|
||||
extend1(Class, superClass1);
|
||||
|
||||
function Class(variable1, parent1, body1) {
|
||||
var body1;
|
||||
this.variable = variable1;
|
||||
this.parent = parent1;
|
||||
this.body = body1 != null ? body1 : new Block;
|
||||
@@ -1674,29 +1663,27 @@
|
||||
};
|
||||
|
||||
Class.prototype.walkBody = function(name, o) {
|
||||
return this.traverseChildren(false, (function(_this) {
|
||||
return function(child) {
|
||||
var cont, exps, i, j, len1, node, ref3;
|
||||
cont = true;
|
||||
if (child instanceof Class) {
|
||||
return false;
|
||||
}
|
||||
if (child instanceof Block) {
|
||||
ref3 = exps = child.expressions;
|
||||
for (i = j = 0, len1 = ref3.length; j < len1; i = ++j) {
|
||||
node = ref3[i];
|
||||
if (node instanceof Assign && node.variable.looksStatic(name)) {
|
||||
node.value["static"] = true;
|
||||
} else if (node instanceof Value && node.isObject(true)) {
|
||||
cont = false;
|
||||
exps[i] = _this.addProperties(node, name, o);
|
||||
}
|
||||
return this.traverseChildren(false, (child) => {
|
||||
var cont, exps, i, j, len1, node, ref3;
|
||||
cont = true;
|
||||
if (child instanceof Class) {
|
||||
return false;
|
||||
}
|
||||
if (child instanceof Block) {
|
||||
ref3 = exps = child.expressions;
|
||||
for (i = j = 0, len1 = ref3.length; j < len1; i = ++j) {
|
||||
node = ref3[i];
|
||||
if (node instanceof Assign && node.variable.looksStatic(name)) {
|
||||
node.value["static"] = true;
|
||||
} else if (node instanceof Value && node.isObject(true)) {
|
||||
cont = false;
|
||||
exps[i] = this.addProperties(node, name, o);
|
||||
}
|
||||
child.expressions = exps = flatten(exps);
|
||||
}
|
||||
return cont && !(child instanceof Class);
|
||||
};
|
||||
})(this));
|
||||
child.expressions = exps = flatten(exps);
|
||||
}
|
||||
return cont && !(child instanceof Class);
|
||||
});
|
||||
};
|
||||
|
||||
Class.prototype.hoistDirectivePrologue = function() {
|
||||
@@ -2079,13 +2066,10 @@
|
||||
exports.Assign = Assign = (function(superClass1) {
|
||||
extend1(Assign, superClass1);
|
||||
|
||||
function Assign(variable1, value1, context, options) {
|
||||
function Assign(variable1, value1, context, options = {}) {
|
||||
this.variable = variable1;
|
||||
this.value = value1;
|
||||
this.context = context;
|
||||
if (options == null) {
|
||||
options = {};
|
||||
}
|
||||
this.param = options.param, this.subpattern = options.subpattern, this.operatorToken = options.operatorToken, this.moduleDeclaration = options.moduleDeclaration;
|
||||
}
|
||||
|
||||
@@ -2402,16 +2386,14 @@
|
||||
};
|
||||
|
||||
Code.prototype.compileNode = function(o) {
|
||||
var answer, boundfunc, code, exprs, i, j, k, l, len1, len2, len3, len4, len5, len6, lit, m, p, param, params, q, r, ref, ref3, ref4, ref5, ref6, ref7, ref8, splats, uniqs, val, wasEmpty, wrapper;
|
||||
if (this.bound && ((ref3 = o.scope.method) != null ? ref3.bound : void 0)) {
|
||||
this.context = o.scope.method.context;
|
||||
}
|
||||
if (this.bound && !this.context) {
|
||||
this.context = '_this';
|
||||
wrapper = new Code([new Param(new IdentifierLiteral(this.context))], new Block([this]));
|
||||
boundfunc = new Call(wrapper, [new ThisLiteral]);
|
||||
boundfunc.updateLocationDataIfMissing(this.locationData);
|
||||
return boundfunc.compileNode(o);
|
||||
var answer, code, condition, exprs, haveSplatParam, i, ifTrue, j, k, len1, len2, param, paramNames, params, paramsAfterSplat, ref, ref3, ref4, ref5, ref6, splatParamName, val, wasEmpty;
|
||||
if (this.bound) {
|
||||
if ((ref3 = o.scope.method) != null ? ref3.bound : void 0) {
|
||||
this.context = o.scope.method.context;
|
||||
}
|
||||
if (!this.context) {
|
||||
this.context = 'this';
|
||||
}
|
||||
}
|
||||
o.scope = del(o, 'classScope') || this.makeScope(o.scope);
|
||||
o.scope.shared = del(o, 'sharedScope');
|
||||
@@ -2420,102 +2402,108 @@
|
||||
delete o.isExistentialEquals;
|
||||
params = [];
|
||||
exprs = [];
|
||||
paramsAfterSplat = [];
|
||||
haveSplatParam = false;
|
||||
paramNames = [];
|
||||
this.eachParamName((name, node) => {
|
||||
if (indexOf.call(paramNames, name) >= 0) {
|
||||
node.error("multiple parameters named '" + name + "'");
|
||||
}
|
||||
return paramNames.push(name);
|
||||
});
|
||||
ref4 = this.params;
|
||||
for (j = 0, len1 = ref4.length; j < len1; j++) {
|
||||
param = ref4[j];
|
||||
if (!(param instanceof Expansion)) {
|
||||
o.scope.parameter(param.asReference(o));
|
||||
}
|
||||
}
|
||||
ref5 = this.params;
|
||||
for (k = 0, len2 = ref5.length; k < len2; k++) {
|
||||
param = ref5[k];
|
||||
if (!(param.splat || param instanceof Expansion)) {
|
||||
continue;
|
||||
}
|
||||
ref6 = this.params;
|
||||
for (l = 0, len3 = ref6.length; l < len3; l++) {
|
||||
p = ref6[l];
|
||||
if (!(p instanceof Expansion) && p.name.value) {
|
||||
o.scope.add(p.name.value, 'var', true);
|
||||
for (i = j = 0, len1 = ref4.length; j < len1; i = ++j) {
|
||||
param = ref4[i];
|
||||
if (param.splat || param instanceof Expansion) {
|
||||
if (haveSplatParam) {
|
||||
param.error('only one splat or expansion parameter is allowed per function definition');
|
||||
} else if (param instanceof Expansion && this.params.length === 1) {
|
||||
param.error('an expansion parameter cannot be the only parameter in a function definition');
|
||||
}
|
||||
}
|
||||
splats = new Assign(new Value(new Arr((function() {
|
||||
var len4, m, ref7, results;
|
||||
ref7 = this.params;
|
||||
results = [];
|
||||
for (m = 0, len4 = ref7.length; m < len4; m++) {
|
||||
p = ref7[m];
|
||||
results.push(p.asReference(o));
|
||||
haveSplatParam = true;
|
||||
if (param.splat) {
|
||||
params.push(ref = param.asReference(o));
|
||||
splatParamName = fragmentsToText(ref.compileNode(o));
|
||||
if (param.isComplex()) {
|
||||
exprs.push(new Assign(new Value(param.name), ref, '=', {
|
||||
param: true
|
||||
}));
|
||||
}
|
||||
} else {
|
||||
splatParamName = o.scope.freeVariable('args');
|
||||
params.push(new Value(new IdentifierLiteral(splatParamName)));
|
||||
}
|
||||
return results;
|
||||
}).call(this))), new Value(new IdentifierLiteral('arguments')));
|
||||
break;
|
||||
}
|
||||
ref7 = this.params;
|
||||
for (m = 0, len4 = ref7.length; m < len4; m++) {
|
||||
param = ref7[m];
|
||||
if (param.isComplex()) {
|
||||
val = ref = param.asReference(o);
|
||||
if (param.value) {
|
||||
val = new Op('?', ref, param.value);
|
||||
}
|
||||
exprs.push(new Assign(new Value(param.name), val, '=', {
|
||||
param: true
|
||||
}));
|
||||
o.scope.parameter(splatParamName);
|
||||
} else {
|
||||
ref = param;
|
||||
if (param.value) {
|
||||
lit = new Literal(ref.name.value + ' == null');
|
||||
val = new Assign(new Value(param.name), param.value, '=');
|
||||
exprs.push(new If(lit, val));
|
||||
if (param.isComplex()) {
|
||||
val = ref = param.asReference(o);
|
||||
if (param.value) {
|
||||
val = new Op('?', ref, param.value);
|
||||
}
|
||||
exprs.push(new Assign(new Value(param.name), val, '=', {
|
||||
param: true
|
||||
}));
|
||||
}
|
||||
if (!haveSplatParam) {
|
||||
if (!param.isComplex()) {
|
||||
ref = param.value != null ? new Assign(new Value(param.name), param.value, '=') : param;
|
||||
}
|
||||
o.scope.parameter(fragmentsToText((param.value != null ? param : ref).compileToFragments(o)));
|
||||
params.push(ref);
|
||||
} else {
|
||||
paramsAfterSplat.push(param);
|
||||
if ((param.value != null) && !param.isComplex()) {
|
||||
condition = new Literal(param.name.value + ' === undefined');
|
||||
ifTrue = new Assign(new Value(param.name), param.value, '=');
|
||||
exprs.push(new If(condition, ifTrue));
|
||||
}
|
||||
if (((ref5 = param.name) != null ? ref5.value : void 0) != null) {
|
||||
o.scope.add(param.name.value, 'var', true);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!splats) {
|
||||
params.push(ref);
|
||||
}
|
||||
}
|
||||
if (paramsAfterSplat.length !== 0) {
|
||||
exprs.unshift(new Assign(new Value(new Arr([new Splat(new IdentifierLiteral(splatParamName))].concat(slice.call((function() {
|
||||
var k, len2, results;
|
||||
results = [];
|
||||
for (k = 0, len2 = paramsAfterSplat.length; k < len2; k++) {
|
||||
param = paramsAfterSplat[k];
|
||||
results.push(param.asReference(o));
|
||||
}
|
||||
return results;
|
||||
})())))), new Value(new IdentifierLiteral(splatParamName))));
|
||||
}
|
||||
wasEmpty = this.body.isEmpty();
|
||||
if (splats) {
|
||||
exprs.unshift(splats);
|
||||
}
|
||||
if (exprs.length) {
|
||||
(ref8 = this.body.expressions).unshift.apply(ref8, exprs);
|
||||
(ref6 = this.body.expressions).unshift.apply(ref6, exprs);
|
||||
}
|
||||
for (i = q = 0, len5 = params.length; q < len5; i = ++q) {
|
||||
p = params[i];
|
||||
params[i] = p.compileToFragments(o);
|
||||
o.scope.parameter(fragmentsToText(params[i]));
|
||||
}
|
||||
uniqs = [];
|
||||
this.eachParamName((function(_this) {
|
||||
return function(name, node) {
|
||||
if (indexOf.call(uniqs, name) >= 0) {
|
||||
node.error("multiple parameters named '" + name + "'");
|
||||
}
|
||||
return uniqs.push(name);
|
||||
};
|
||||
})(this));
|
||||
if (!(wasEmpty || this.noReturn)) {
|
||||
this.body.makeReturn();
|
||||
}
|
||||
code = 'function';
|
||||
if (this.isGenerator) {
|
||||
code += '*';
|
||||
}
|
||||
if (this.ctor) {
|
||||
code += ' ' + this.name;
|
||||
code = '';
|
||||
if (!this.bound) {
|
||||
code += 'function';
|
||||
if (this.isGenerator) {
|
||||
code += '*';
|
||||
}
|
||||
if (this.ctor) {
|
||||
code += ' ' + this.name;
|
||||
}
|
||||
}
|
||||
code += '(';
|
||||
answer = [this.makeCode(code)];
|
||||
for (i = r = 0, len6 = params.length; r < len6; i = ++r) {
|
||||
p = params[i];
|
||||
for (i = k = 0, len2 = params.length; k < len2; i = ++k) {
|
||||
param = params[i];
|
||||
if (i) {
|
||||
answer.push(this.makeCode(", "));
|
||||
answer.push(this.makeCode(', '));
|
||||
}
|
||||
answer.push.apply(answer, p);
|
||||
if (haveSplatParam && i === params.length - 1) {
|
||||
answer.push(this.makeCode('...'));
|
||||
}
|
||||
answer.push.apply(answer, param.compileToFragments(o));
|
||||
}
|
||||
answer.push(this.makeCode(') {'));
|
||||
answer.push(this.makeCode(!this.bound ? ') {' : ') => {'));
|
||||
if (!this.body.isEmpty()) {
|
||||
answer = answer.concat(this.makeCode("\n"), this.body.compileWithDeclarations(o), this.makeCode("\n" + this.tab));
|
||||
}
|
||||
@@ -2591,9 +2579,6 @@
|
||||
node = new IdentifierLiteral(o.scope.freeVariable('arg'));
|
||||
}
|
||||
node = new Value(node);
|
||||
if (this.splat) {
|
||||
node = new Splat(node);
|
||||
}
|
||||
node.updateLocationDataIfMissing(this.locationData);
|
||||
return this.reference = node;
|
||||
};
|
||||
@@ -2602,11 +2587,8 @@
|
||||
return this.name.isComplex();
|
||||
};
|
||||
|
||||
Param.prototype.eachName = function(iterator, name) {
|
||||
Param.prototype.eachName = function(iterator, name = this.name) {
|
||||
var atParam, j, len1, node, obj, ref3, ref4;
|
||||
if (name == null) {
|
||||
name = this.name;
|
||||
}
|
||||
atParam = function(obj) {
|
||||
return iterator("@" + obj.properties[0].name.value, obj);
|
||||
};
|
||||
@@ -3037,12 +3019,15 @@
|
||||
};
|
||||
|
||||
Op.prototype.compileYield = function(o) {
|
||||
var op, parts, ref3;
|
||||
var op, parts, ref3, ref4;
|
||||
parts = [];
|
||||
op = this.operator;
|
||||
if (o.scope.parent == null) {
|
||||
this.error('yield can only occur inside functions');
|
||||
}
|
||||
if (((ref3 = o.scope.method) != null ? ref3.bound : void 0) && o.scope.method.isGenerator) {
|
||||
this.error('yield cannot occur inside bound (fat arrow) functions');
|
||||
}
|
||||
if (indexOf.call(Object.keys(this.first), 'expression') >= 0 && !(this.first instanceof Throw)) {
|
||||
if (this.first.expression != null) {
|
||||
parts.push(this.first.expression.compileToFragments(o, LEVEL_OP));
|
||||
@@ -3052,7 +3037,7 @@
|
||||
parts.push([this.makeCode("(")]);
|
||||
}
|
||||
parts.push([this.makeCode(op)]);
|
||||
if (((ref3 = this.first.base) != null ? ref3.value : void 0) !== '') {
|
||||
if (((ref4 = this.first.base) != null ? ref4.value : void 0) !== '') {
|
||||
parts.push([this.makeCode(" ")]);
|
||||
}
|
||||
parts.push(this.first.compileToFragments(o, LEVEL_OP));
|
||||
@@ -3504,13 +3489,10 @@
|
||||
|
||||
Switch.prototype.isStatement = YES;
|
||||
|
||||
Switch.prototype.jumps = function(o) {
|
||||
Switch.prototype.jumps = function(o = {
|
||||
block: true
|
||||
}) {
|
||||
var block, conds, j, jumpNode, len1, ref3, ref4, ref5;
|
||||
if (o == null) {
|
||||
o = {
|
||||
block: true
|
||||
};
|
||||
}
|
||||
ref3 = this.cases;
|
||||
for (j = 0, len1 = ref3.length; j < len1; j++) {
|
||||
ref4 = ref3[j], conds = ref4[0], block = ref4[1];
|
||||
@@ -3579,11 +3561,8 @@
|
||||
exports.If = If = (function(superClass1) {
|
||||
extend1(If, superClass1);
|
||||
|
||||
function If(condition, body1, options) {
|
||||
function If(condition, body1, options = {}) {
|
||||
this.body = body1;
|
||||
if (options == null) {
|
||||
options = {};
|
||||
}
|
||||
this.condition = options.type === 'unless' ? condition.invert() : condition;
|
||||
this.elseBody = null;
|
||||
this.isChain = false;
|
||||
|
||||
@@ -100,11 +100,8 @@
|
||||
return results;
|
||||
};
|
||||
|
||||
buildRule = function(shortFlag, longFlag, description, options) {
|
||||
buildRule = function(shortFlag, longFlag, description, options = {}) {
|
||||
var match;
|
||||
if (options == null) {
|
||||
options = {};
|
||||
}
|
||||
match = longFlag.match(OPTIONAL);
|
||||
longFlag = longFlag.match(LONG_FLAG)[1];
|
||||
return {
|
||||
|
||||
@@ -166,11 +166,8 @@
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
start: function(opts) {
|
||||
start: function(opts = {}) {
|
||||
var build, major, minor, ref1, repl;
|
||||
if (opts == null) {
|
||||
opts = {};
|
||||
}
|
||||
ref1 = process.versions.node.split('.').map(function(n) {
|
||||
return parseInt(n);
|
||||
}), major = ref1[0], minor = ref1[1], build = ref1[2];
|
||||
|
||||
@@ -109,9 +109,8 @@
|
||||
});
|
||||
};
|
||||
|
||||
Rewriter.prototype.indexOfTag = function() {
|
||||
var fuzz, i, j, k, pattern, ref, ref1;
|
||||
i = arguments[0], pattern = 2 <= arguments.length ? slice.call(arguments, 1) : [];
|
||||
Rewriter.prototype.indexOfTag = function(i, ...pattern) {
|
||||
var fuzz, j, k, ref, ref1;
|
||||
fuzz = 0;
|
||||
for (j = k = 0, ref = pattern.length; 0 <= ref ? k < ref : k > ref; j = 0 <= ref ? ++k : --k) {
|
||||
while (this.tag(i + j + fuzz) === 'HERECOMMENT') {
|
||||
@@ -216,11 +215,8 @@
|
||||
tokens.splice(i, 0, generate('CALL_END', ')', ['', 'end of input', token[2]]));
|
||||
return i += 1;
|
||||
};
|
||||
startImplicitObject = function(j, startsLine) {
|
||||
startImplicitObject = function(j, startsLine = true) {
|
||||
var idx, val;
|
||||
if (startsLine == null) {
|
||||
startsLine = true;
|
||||
}
|
||||
idx = j != null ? j : i;
|
||||
stack.push([
|
||||
'{', idx, {
|
||||
|
||||
@@ -65,11 +65,8 @@
|
||||
return !!(this.type(name) || ((ref = this.parent) != null ? ref.check(name) : void 0));
|
||||
};
|
||||
|
||||
Scope.prototype.temporary = function(name, index, single) {
|
||||
Scope.prototype.temporary = function(name, index, single = false) {
|
||||
var diff, endCode, letter, newCode, num, startCode;
|
||||
if (single == null) {
|
||||
single = false;
|
||||
}
|
||||
if (single) {
|
||||
startCode = name.charCodeAt(0);
|
||||
endCode = 'z'.charCodeAt(0);
|
||||
@@ -95,11 +92,8 @@
|
||||
return null;
|
||||
};
|
||||
|
||||
Scope.prototype.freeVariable = function(name, options) {
|
||||
Scope.prototype.freeVariable = function(name, options = {}) {
|
||||
var index, ref, temp;
|
||||
if (options == null) {
|
||||
options = {};
|
||||
}
|
||||
index = 0;
|
||||
while (true) {
|
||||
temp = this.temporary(name, index, options.single);
|
||||
|
||||
@@ -8,12 +8,9 @@
|
||||
this.columns = [];
|
||||
}
|
||||
|
||||
LineMap.prototype.add = function(column, arg, options) {
|
||||
LineMap.prototype.add = function(column, arg, options = {}) {
|
||||
var sourceColumn, sourceLine;
|
||||
sourceLine = arg[0], sourceColumn = arg[1];
|
||||
if (options == null) {
|
||||
options = {};
|
||||
}
|
||||
if (this.columns[column] && options.noReplace) {
|
||||
return;
|
||||
}
|
||||
@@ -44,11 +41,8 @@
|
||||
this.lines = [];
|
||||
}
|
||||
|
||||
SourceMap.prototype.add = function(sourceLocation, generatedLocation, options) {
|
||||
SourceMap.prototype.add = function(sourceLocation, generatedLocation, options = {}) {
|
||||
var base, column, line, lineMap;
|
||||
if (options == null) {
|
||||
options = {};
|
||||
}
|
||||
line = generatedLocation[0], column = generatedLocation[1];
|
||||
lineMap = ((base = this.lines)[line] || (base[line] = new LineMap(line)));
|
||||
return lineMap.add(column, sourceLocation, options);
|
||||
@@ -63,14 +57,8 @@
|
||||
return lineMap && lineMap.sourceLocation(column);
|
||||
};
|
||||
|
||||
SourceMap.prototype.generate = function(options, code) {
|
||||
SourceMap.prototype.generate = function(options = {}, code = null) {
|
||||
var buffer, i, j, lastColumn, lastSourceColumn, lastSourceLine, len, len1, lineMap, lineNumber, mapping, needComma, ref, ref1, v3, writingline;
|
||||
if (options == null) {
|
||||
options = {};
|
||||
}
|
||||
if (code == null) {
|
||||
code = null;
|
||||
}
|
||||
writingline = 0;
|
||||
lastColumn = 0;
|
||||
lastSourceLine = 0;
|
||||
|
||||
169
src/nodes.coffee
169
src/nodes.coffee
@@ -1601,70 +1601,126 @@ exports.Code = class Code extends Base
|
||||
makeScope: (parentScope) -> new Scope parentScope, @body, this
|
||||
|
||||
# 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` object. If the function is bound with the `=>`
|
||||
# arrow, generates a wrapper that saves the current value of `this` through
|
||||
# a closure.
|
||||
# outer scope. Handles splat parameters in the parameter list by setting
|
||||
# such parameters to be the final parameter in the function definition, as
|
||||
# required per the ES2015 spec. If the CoffeeScript function definition had
|
||||
# parameters after the splat, they are declared via expressions in the
|
||||
# function body.
|
||||
compileNode: (o) ->
|
||||
|
||||
if @bound and o.scope.method?.bound
|
||||
@context = o.scope.method.context
|
||||
|
||||
# Handle bound functions early.
|
||||
if @bound and not @context
|
||||
@context = '_this'
|
||||
wrapper = new Code [new Param new IdentifierLiteral @context], new Block [this]
|
||||
boundfunc = new Call(wrapper, [new ThisLiteral])
|
||||
boundfunc.updateLocationDataIfMissing @locationData
|
||||
return boundfunc.compileNode(o)
|
||||
if @bound
|
||||
@context = o.scope.method.context if o.scope.method?.bound
|
||||
@context = 'this' unless @context
|
||||
|
||||
o.scope = del(o, 'classScope') or @makeScope o.scope
|
||||
o.scope.shared = del(o, 'sharedScope')
|
||||
o.indent += TAB
|
||||
delete o.bare
|
||||
delete o.isExistentialEquals
|
||||
params = []
|
||||
exprs = []
|
||||
for param in @params when param not instanceof Expansion
|
||||
o.scope.parameter param.asReference o
|
||||
for param in @params when param.splat or param instanceof Expansion
|
||||
for p in @params when p not instanceof Expansion and p.name.value
|
||||
o.scope.add p.name.value, 'var', yes
|
||||
splats = new Assign new Value(new Arr(p.asReference o for p in @params)),
|
||||
new Value new IdentifierLiteral 'arguments'
|
||||
break
|
||||
for param in @params
|
||||
if param.isComplex()
|
||||
val = ref = param.asReference o
|
||||
val = new Op '?', ref, param.value if param.value
|
||||
exprs.push new Assign new Value(param.name), val, '=', param: yes
|
||||
else
|
||||
ref = param
|
||||
if param.value
|
||||
lit = new Literal ref.name.value + ' == null'
|
||||
val = new Assign new Value(param.name), param.value, '='
|
||||
exprs.push new If lit, val
|
||||
params.push ref unless splats
|
||||
wasEmpty = @body.isEmpty()
|
||||
exprs.unshift splats if splats
|
||||
@body.expressions.unshift exprs... if exprs.length
|
||||
for p, i in params
|
||||
params[i] = p.compileToFragments o
|
||||
o.scope.parameter fragmentsToText params[i]
|
||||
uniqs = []
|
||||
params = []
|
||||
exprs = []
|
||||
paramsAfterSplat = []
|
||||
haveSplatParam = no
|
||||
|
||||
# Check for duplicate parameters.
|
||||
paramNames = []
|
||||
@eachParamName (name, node) =>
|
||||
node.error "multiple parameters named '#{name}'" if name in uniqs
|
||||
uniqs.push name
|
||||
node.error "multiple parameters named '#{name}'" if name in paramNames
|
||||
paramNames.push name
|
||||
|
||||
# Parse the parameters, adding them to the list of parameters to put in the
|
||||
# function definition; and dealing with splats or expansions, including
|
||||
# adding expressions to the function body to declare all parameter
|
||||
# variables that would have been after the splat/expansion parameter.
|
||||
for param, i in @params
|
||||
# Was `...` used with this parameter? (Only one such parameter is allowed
|
||||
# per function.) Splat/expansion parameters cannot have default values,
|
||||
# so we need not worry about that.
|
||||
if param.splat or param instanceof Expansion
|
||||
if haveSplatParam
|
||||
param.error 'only one splat or expansion parameter is allowed per function definition'
|
||||
else if param instanceof Expansion and @params.length is 1
|
||||
param.error 'an expansion parameter cannot be the only parameter in a function definition'
|
||||
|
||||
haveSplatParam = yes
|
||||
if param.splat
|
||||
params.push ref = param.asReference o
|
||||
splatParamName = fragmentsToText ref.compileNode o
|
||||
if param.isComplex() # Parameter is destructured or attached to `this`
|
||||
exprs.push new Assign new Value(param.name), ref, '=', param: yes
|
||||
# TODO: output destrucutred parameters as is, *unless* they contain
|
||||
# `this` parameters; and fix destructuring of objects with default
|
||||
# values to work in this context (see Obj.compileNode
|
||||
# `if prop.context isnt 'object'`)
|
||||
|
||||
else # `param` is an Expansion
|
||||
splatParamName = o.scope.freeVariable 'args'
|
||||
params.push new Value new IdentifierLiteral splatParamName
|
||||
|
||||
o.scope.parameter splatParamName
|
||||
|
||||
# Parse all other parameters; if a splat paramater has not yet been
|
||||
# encountered, add these other parameters to the list to be output in
|
||||
# the function definition.
|
||||
else
|
||||
if param.isComplex()
|
||||
# This parameter is attached to `this`, which ES doesn’t allow;
|
||||
# or it’s destructured. So add a statement to the function body
|
||||
# assigning it, e.g. `(a) => { this.a = a; }` or with a default
|
||||
# value if it has one.
|
||||
val = ref = param.asReference o
|
||||
val = new Op '?', ref, param.value if param.value
|
||||
exprs.push new Assign new Value(param.name), val, '=', param: yes
|
||||
|
||||
# If this parameter comes before the splat or expansion, it will go
|
||||
# in the function definition parameter list.
|
||||
unless haveSplatParam
|
||||
# If this parameter has a default value, and it hasn’t already been
|
||||
# set by the `isComplex()` block above, define it as a statement in
|
||||
# the function body. This parameter comes after the splat parameter,
|
||||
# so we can’t define its default value in the parameter list.
|
||||
unless param.isComplex()
|
||||
ref = if param.value? then new Assign new Value(param.name), param.value, '=' else param
|
||||
# Add this parameter’s reference to the function scope
|
||||
o.scope.parameter fragmentsToText (if param.value? then param else ref).compileToFragments o
|
||||
params.push ref
|
||||
else
|
||||
paramsAfterSplat.push param
|
||||
# If this parameter had a default value, since it’s no longer in the
|
||||
# function parameter list we need to assign its default value
|
||||
# (if necessary) as an expression in the body.
|
||||
if param.value? and not param.isComplex()
|
||||
condition = new Literal param.name.value + ' === undefined'
|
||||
ifTrue = new Assign new Value(param.name), param.value, '='
|
||||
exprs.push new If condition, ifTrue
|
||||
# Add this parameter to the scope, since it wouldn’t have been added yet since it was skipped earlier.
|
||||
o.scope.add param.name.value, 'var', yes if param.name?.value?
|
||||
|
||||
# If there were parameters after the splat or expansion parameter, those
|
||||
# parameters need to be assigned in the body of the function.
|
||||
if paramsAfterSplat.length isnt 0
|
||||
# Create a destructured assignment, e.g. `[a, b, c] = [args..., b, c]`
|
||||
exprs.unshift new Assign new Value(
|
||||
new Arr [new Splat(new IdentifierLiteral(splatParamName)), (param.asReference o for param in paramsAfterSplat)...]
|
||||
), new Value new IdentifierLiteral splatParamName
|
||||
|
||||
# Add new expressions to the function body
|
||||
wasEmpty = @body.isEmpty()
|
||||
@body.expressions.unshift exprs... if exprs.length
|
||||
@body.makeReturn() unless wasEmpty or @noReturn
|
||||
code = 'function'
|
||||
code += '*' if @isGenerator
|
||||
code += ' ' + @name if @ctor
|
||||
|
||||
# Assemble the output
|
||||
code = ''
|
||||
unless @bound
|
||||
code += 'function'
|
||||
code += '*' if @isGenerator # Arrow functions can’t be generators
|
||||
code += ' ' + @name if @ctor
|
||||
code += '('
|
||||
answer = [@makeCode(code)]
|
||||
for p, i in params
|
||||
if i then answer.push @makeCode ", "
|
||||
answer.push p...
|
||||
answer.push @makeCode ') {'
|
||||
for param, i in params
|
||||
answer.push @makeCode ', ' if i
|
||||
answer.push @makeCode '...' if haveSplatParam and i is params.length - 1 # Rest syntax is always on the last parameter
|
||||
answer.push param.compileToFragments(o)...
|
||||
answer.push @makeCode unless @bound then ') {' else ') => {'
|
||||
answer = answer.concat(@makeCode("\n"), @body.compileWithDeclarations(o), @makeCode("\n#{@tab}")) unless @body.isEmpty()
|
||||
answer.push @makeCode '}'
|
||||
|
||||
@@ -1674,8 +1730,8 @@ exports.Code = class Code extends Base
|
||||
eachParamName: (iterator) ->
|
||||
param.eachName iterator for param in @params
|
||||
|
||||
# Short-circuit `traverseChildren` method to prevent it from crossing scope boundaries
|
||||
# unless `crossScope` is `true`.
|
||||
# Short-circuit `traverseChildren` method to prevent it from crossing scope
|
||||
# boundaries unless `crossScope` is `true`.
|
||||
traverseChildren: (crossScope, func) ->
|
||||
super(crossScope, func) if crossScope
|
||||
|
||||
@@ -1707,7 +1763,6 @@ exports.Param = class Param extends Base
|
||||
else if node.isComplex()
|
||||
node = new IdentifierLiteral o.scope.freeVariable 'arg'
|
||||
node = new Value node
|
||||
node = new Splat node if @splat
|
||||
node.updateLocationDataIfMissing @locationData
|
||||
@reference = node
|
||||
|
||||
@@ -2039,6 +2094,8 @@ exports.Op = class Op extends Base
|
||||
op = @operator
|
||||
unless o.scope.parent?
|
||||
@error 'yield can only occur inside functions'
|
||||
if o.scope.method?.bound and o.scope.method.isGenerator
|
||||
@error 'yield cannot occur inside bound (fat arrow) functions'
|
||||
if 'expression' in Object.keys(@first) and not (@first instanceof Throw)
|
||||
parts.push @first.expression.compileToFragments o, LEVEL_OP if @first.expression?
|
||||
else
|
||||
|
||||
@@ -474,7 +474,6 @@ test "`new` shouldn't add extra parens", ->
|
||||
test "`new` works against bare function", ->
|
||||
|
||||
eq Date, new ->
|
||||
eq this, new => this
|
||||
Date
|
||||
|
||||
|
||||
|
||||
@@ -1142,3 +1142,10 @@ test "imported members cannot be reassigned", ->
|
||||
export foo = 'bar'
|
||||
^^^
|
||||
'''
|
||||
|
||||
test "bound functions cannot be generators", ->
|
||||
assertErrorFormat 'f = => yield this', '''
|
||||
[stdin]:1:8: error: yield cannot occur inside bound (fat arrow) functions
|
||||
f = => yield this
|
||||
^^^^^^^^^^
|
||||
'''
|
||||
|
||||
@@ -79,6 +79,21 @@ test "even more fancy bound functions", ->
|
||||
eq obj.one(), 3
|
||||
|
||||
|
||||
test "arguments in bound functions inherit from parent function", ->
|
||||
# The `arguments` object in an ES arrow function refers to the `arguments`
|
||||
# of the parent scope, just like `this`. In the CoffeeScript 1.x
|
||||
# implementation of `=>`, the `arguments` object referred to the arguments
|
||||
# of the arrow function; but per the ES2015 spec, `arguments` should refer
|
||||
# to the parent.
|
||||
arrayEq ((a...) -> a)([1, 2, 3]), ((a...) => a)([1, 2, 3])
|
||||
|
||||
parent = (a, b, c) ->
|
||||
(bound = =>
|
||||
[arguments[0], arguments[1], arguments[2]]
|
||||
)()
|
||||
arrayEq [1, 2, 3], parent(1, 2, 3)
|
||||
|
||||
|
||||
test "self-referencing functions", ->
|
||||
changeMe = ->
|
||||
changeMe = 2
|
||||
@@ -86,15 +101,6 @@ test "self-referencing functions", ->
|
||||
changeMe()
|
||||
eq changeMe, 2
|
||||
|
||||
test "#2009: don't touch `` `this` ``", ->
|
||||
nonceA = {}
|
||||
nonceB = {}
|
||||
fn = null
|
||||
(->
|
||||
fn = => this is nonceA and `this` is nonceB
|
||||
).call nonceA
|
||||
ok fn.call nonceB
|
||||
|
||||
|
||||
# Parameter List Features
|
||||
|
||||
@@ -189,7 +195,7 @@ test "default values", ->
|
||||
eq nonceA, a(0)
|
||||
eq nonceB, a(0,0,nonceB)
|
||||
eq nonceA, a(0,0,undefined)
|
||||
eq nonceA, a(0,0,null)
|
||||
eq null, a(0,0,null) # Per ES2015, `null` doesn’t trigger a parameter default value
|
||||
eq false , a(0,0,false)
|
||||
eq nonceB, a(undefined,undefined,nonceB,undefined)
|
||||
b = (_,arg=nonceA,_1,_2) -> arg
|
||||
@@ -197,7 +203,7 @@ test "default values", ->
|
||||
eq nonceA, b(0)
|
||||
eq nonceB, b(0,nonceB)
|
||||
eq nonceA, b(0,undefined)
|
||||
eq nonceA, b(0,null)
|
||||
eq null, b(0,null)
|
||||
eq false , b(0,false)
|
||||
eq nonceB, b(undefined,nonceB,undefined)
|
||||
c = (arg=nonceA,_,_1) -> arg
|
||||
@@ -205,7 +211,7 @@ test "default values", ->
|
||||
eq 0, c(0)
|
||||
eq nonceB, c(nonceB)
|
||||
eq nonceA, c(undefined)
|
||||
eq nonceA, c(null)
|
||||
eq null, c(null)
|
||||
eq false , c(false)
|
||||
eq nonceB, c(nonceB,undefined,undefined)
|
||||
|
||||
|
||||
@@ -52,24 +52,6 @@ test "yield return can be used anywhere in the function body", ->
|
||||
y = x.next 2
|
||||
ok y.value is 42 and y.done is true
|
||||
|
||||
test "bound generator", ->
|
||||
obj =
|
||||
bound: ->
|
||||
do =>
|
||||
yield this
|
||||
unbound: ->
|
||||
do ->
|
||||
yield this
|
||||
nested: ->
|
||||
do =>
|
||||
yield do =>
|
||||
yield do =>
|
||||
yield this
|
||||
|
||||
eq obj, obj.bound().next().value
|
||||
ok obj isnt obj.unbound().next().value
|
||||
eq obj, obj.nested().next().value.next().value.next().value
|
||||
|
||||
test "`yield from` support", ->
|
||||
x = do ->
|
||||
yield from do ->
|
||||
|
||||
Reference in New Issue
Block a user