From 59959a6269287a8b4d94576b5e1f60b0d4c9861d Mon Sep 17 00:00:00 2001 From: Chris Connelly Date: Thu, 30 Mar 2017 21:19:06 +0100 Subject: [PATCH 01/20] Output simple array destructuring assignments to ES2015 --- lib/coffeescript/cake.js | 3 +- lib/coffeescript/coffeescript.js | 4 +- lib/coffeescript/command.js | 2 +- lib/coffeescript/lexer.js | 46 +++---- lib/coffeescript/nodes.js | 215 +++++++++++++++++++------------ lib/coffeescript/repl.js | 6 +- lib/coffeescript/rewriter.js | 28 ++-- lib/coffeescript/sourcemap.js | 8 +- src/nodes.coffee | 72 ++++++++--- 9 files changed, 236 insertions(+), 148 deletions(-) diff --git a/lib/coffeescript/cake.js b/lib/coffeescript/cake.js index b5334b7d6c..710c5437d9 100644 --- a/lib/coffeescript/cake.js +++ b/lib/coffeescript/cake.js @@ -24,9 +24,8 @@ helpers.extend(global, { task: function(name, description, action) { - var ref; if (!action) { - ref = [description, action], action = ref[0], description = ref[1]; + [action, description] = [description, action]; } return tasks[name] = { name: name, diff --git a/lib/coffeescript/coffeescript.js b/lib/coffeescript/coffeescript.js index 66556ffd77..767efd35e9 100644 --- a/lib/coffeescript/coffeescript.js +++ b/lib/coffeescript/coffeescript.js @@ -274,7 +274,7 @@ var tag, token; token = parser.tokens[this.pos++]; if (token) { - tag = token[0], this.yytext = token[1], this.yylloc = token[2]; + [tag, this.yytext, this.yylloc] = token; parser.errorToken = token.origin || token; this.yylineno = this.yylloc.first_line; } else { @@ -297,7 +297,7 @@ var errorLoc, errorTag, errorText, errorToken, token, tokens; token = arg.token; errorToken = parser.errorToken, tokens = parser.tokens; - errorTag = errorToken[0], errorText = errorToken[1], errorLoc = errorToken[2]; + [errorTag, errorText, errorLoc] = errorToken; errorText = (function() { switch (false) { case errorToken !== tokens[tokens.length - 1]: diff --git a/lib/coffeescript/command.js b/lib/coffeescript/command.js index 3bd6487dde..314197f59e 100644 --- a/lib/coffeescript/command.js +++ b/lib/coffeescript/command.js @@ -104,7 +104,7 @@ return requires.map(function(module) { var _, match, name; if (match = module.match(/^(.*)=(.*)$/)) { - _ = match[0], name = match[1], module = match[2]; + [_, name, module] = match; } name || (name = helpers.baseFileName(module, true, useWinPathSep)); return `${name} = require('${module}')`; diff --git a/lib/coffeescript/lexer.js b/lib/coffeescript/lexer.js index cd97d9f9bf..bfb64a0792 100644 --- a/lib/coffeescript/lexer.js +++ b/lib/coffeescript/lexer.js @@ -9,7 +9,7 @@ exports.Lexer = Lexer = class Lexer { tokenize(code, opts = {}) { - var consumed, end, i, ref2; + var consumed, end, i; this.literate = opts.literate; this.indent = 0; this.baseIndent = 0; @@ -29,7 +29,7 @@ i = 0; while (this.chunk = code.slice(i)) { consumed = this.identifierToken() || this.commentToken() || this.whitespaceToken() || this.lineToken() || this.stringToken() || this.numberToken() || this.regexToken() || this.jsToken() || this.literalToken(); - ref2 = this.getLineAndColumnFromChunk(consumed), this.chunkLine = ref2[0], this.chunkColumn = ref2[1]; + [this.chunkLine, this.chunkColumn] = this.getLineAndColumnFromChunk(consumed); i += consumed; if (opts.untilBalanced && this.ends.length === 0) { return { @@ -64,11 +64,11 @@ } identifierToken() { - var alias, colon, colonOffset, id, idLength, input, match, poppedToken, prev, ref2, ref3, ref4, ref5, ref6, ref7, tag, tagToken; + var alias, colon, colonOffset, id, idLength, input, match, poppedToken, prev, ref2, ref3, ref4, ref5, ref6, tag, tagToken; if (!(match = IDENTIFIER.exec(this.chunk))) { return 0; } - input = match[0], id = match[1], colon = match[2]; + [input, id, colon] = match; idLength = id.length; poppedToken = void 0; if (id === 'own' && this.tag() === 'FOR') { @@ -167,7 +167,7 @@ tagToken.origin = [tag, alias, tagToken[2]]; } if (poppedToken) { - ref7 = [poppedToken[2].first_line, poppedToken[2].first_column], tagToken[2].first_line = ref7[0], tagToken[2].first_column = ref7[1]; + [tagToken[2].first_line, tagToken[2].first_column] = [poppedToken[2].first_line, poppedToken[2].first_column]; } if (colon) { colonOffset = input.lastIndexOf(':'); @@ -224,7 +224,7 @@ stringToken() { var $, attempt, delimiter, doc, end, heredoc, i, indent, indentRegex, match, quote, ref2, ref3, regex, token, tokens; - quote = (STRING_START.exec(this.chunk) || [])[0]; + [quote] = STRING_START.exec(this.chunk) || []; if (!quote) { return 0; } @@ -307,7 +307,7 @@ if (!(match = this.chunk.match(COMMENT))) { return 0; } - comment = match[0], here = match[1]; + [comment, here] = match; if (here) { if (match = HERECOMMENT_ILLEGAL.exec(comment)) { this.error(`block comments cannot contain ${match[0]}`, { @@ -347,7 +347,7 @@ tokens = match.tokens, index = match.index; break; case !(match = REGEX.exec(this.chunk)): - regex = match[0], body = match[1], closed = match[2]; + [regex, body, closed] = match; this.validateEscapes(body, { isRegex: true, offsetInChunk: 1 @@ -370,7 +370,7 @@ default: return 0; } - flags = REGEX_FLAGS.exec(this.chunk.slice(index))[0]; + [flags] = REGEX_FLAGS.exec(this.chunk.slice(index)); end = index + flags.length; origin = this.makeToken('REGEX', null, 0, end); switch (false) { @@ -543,7 +543,7 @@ literalToken() { var match, message, origin, prev, ref2, ref3, ref4, ref5, ref6, skipToken, tag, token, value; if (match = OPERATOR.exec(this.chunk)) { - value = match[0]; + [value] = match; if (CODE.test(value)) { this.tagParameters(); } @@ -662,7 +662,7 @@ } matchWithInterpolations(regex, delimiter) { - var close, column, firstToken, index, lastToken, line, nested, offsetInChunk, open, ref2, ref3, ref4, str, strPart, tokens; + var close, column, firstToken, index, lastToken, line, nested, offsetInChunk, open, ref2, ref3, str, strPart, tokens; tokens = []; offsetInChunk = delimiter.length; if (this.chunk.slice(0, offsetInChunk) !== delimiter) { @@ -670,7 +670,7 @@ } str = this.chunk.slice(offsetInChunk); while (true) { - strPart = regex.exec(str)[0]; + [strPart] = regex.exec(str); this.validateEscapes(strPart, { isRegex: delimiter.charAt(0) === '/', offsetInChunk: offsetInChunk @@ -681,18 +681,18 @@ if (str.slice(0, 2) !== '#{') { break; } - ref2 = this.getLineAndColumnFromChunk(offsetInChunk + 1), line = ref2[0], column = ref2[1]; - ref3 = new Lexer().tokenize(str.slice(1), { + [line, column] = this.getLineAndColumnFromChunk(offsetInChunk + 1); + ref2 = new Lexer().tokenize(str.slice(1), { line: line, column: column, untilBalanced: true - }), nested = ref3.tokens, index = ref3.index; + }), nested = ref2.tokens, index = ref2.index; index += 1; open = nested[0], close = nested[nested.length - 1]; open[0] = open[1] = '('; close[0] = close[1] = ')'; close.origin = ['', 'end of interpolation', close[2]]; - if (((ref4 = nested[1]) != null ? ref4[0] : void 0) === 'TERMINATOR') { + if (((ref3 = nested[1]) != null ? ref3[0] : void 0) === 'TERMINATOR') { nested.splice(1, 1); } tokens.push(['TOKENS', nested]); @@ -729,7 +729,7 @@ firstIndex = this.tokens.length; for (i = j = 0, len = tokens.length; j < len; i = ++j) { token = tokens[i]; - tag = token[0], value = token[1]; + [tag, value] = token; switch (tag) { case 'TOKENS': if (value.length === 2) { @@ -822,11 +822,11 @@ } makeToken(tag, value, offsetInChunk = 0, length = value.length) { - var lastCharacter, locationData, ref2, ref3, token; + var lastCharacter, locationData, token; locationData = {}; - ref2 = this.getLineAndColumnFromChunk(offsetInChunk), locationData.first_line = ref2[0], locationData.first_column = ref2[1]; + [locationData.first_line, locationData.first_column] = this.getLineAndColumnFromChunk(offsetInChunk); lastCharacter = length > 0 ? length - 1 : 0; - ref3 = this.getLineAndColumnFromChunk(offsetInChunk + lastCharacter), locationData.last_line = ref3[0], locationData.last_column = ref3[1]; + [locationData.last_line, locationData.last_column] = this.getLineAndColumnFromChunk(offsetInChunk + lastCharacter); token = [tag, value, locationData]; return token; } @@ -922,11 +922,11 @@ } error(message, options = {}) { - var first_column, first_line, location, ref2, ref3, ref4; - location = 'first_line' in options ? options : ((ref3 = this.getLineAndColumnFromChunk((ref2 = options.offset) != null ? ref2 : 0), first_line = ref3[0], first_column = ref3[1], ref3), { + var first_column, first_line, location, ref2, ref3; + location = 'first_line' in options ? options : ([first_line, first_column] = this.getLineAndColumnFromChunk((ref2 = options.offset) != null ? ref2 : 0), { first_line: first_line, first_column: first_column, - last_column: first_column + ((ref4 = options.length) != null ? ref4 : 1) - 1 + last_column: first_column + ((ref3 = options.length) != null ? ref3 : 1) - 1 }); return throwSyntaxError(message, location); } diff --git a/lib/coffeescript/nodes.js b/lib/coffeescript/nodes.js index 9162cb7958..8fddf304f3 100644 --- a/lib/coffeescript/nodes.js +++ b/lib/coffeescript/nodes.js @@ -550,7 +550,7 @@ } compileWithDeclarations(o) { - var assigns, declars, exp, fragments, i, j, len1, post, ref3, ref4, ref5, rest, scope, spaced; + var assigns, declars, exp, fragments, i, j, len1, post, ref3, rest, scope, spaced; fragments = []; post = []; ref3 = this.expressions; @@ -566,8 +566,8 @@ }); if (i) { rest = this.expressions.splice(i, 9e9); - ref4 = [this.spaced, false], spaced = ref4[0], this.spaced = ref4[1]; - ref5 = [this.compileNode(o), spaced], fragments = ref5[0], this.spaced = ref5[1]; + [spaced, this.spaced] = [this.spaced, false]; + [fragments, this.spaced] = [this.compileNode(o), spaced]; this.expressions = rest; } post = this.compileNode(o); @@ -672,7 +672,12 @@ exports.PassthroughLiteral = PassthroughLiteral = class PassthroughLiteral extends Literal {}; exports.IdentifierLiteral = IdentifierLiteral = (function() { - class IdentifierLiteral extends Literal {}; + class IdentifierLiteral extends Literal { + eachName(iterator) { + return iterator(this); + } + + }; IdentifierLiteral.prototype.isAssignable = YES; @@ -997,6 +1002,16 @@ })(); } + eachName(iterator) { + if (this.hasProperties()) { + return iterator(this); + } else if (this.base.isAssignable()) { + return this.base.eachName(iterator); + } else { + return this.error('tried to assign to unassignable value'); + } + } + }; Value.prototype.children = ['base', 'properties']; @@ -1074,7 +1089,7 @@ } unfoldSoak(o) { - var call, ifn, j, left, len1, list, ref3, ref4, rite; + var call, ifn, j, left, len1, list, ref3, rite; if (this.soak) { if (this.variable instanceof Super) { left = new Literal(this.variable.compile(o)); @@ -1086,7 +1101,7 @@ if (ifn = unfoldSoak(o, this, 'variable')) { return ifn; } - ref3 = new Value(this.variable).cacheReference(o), left = ref3[0], rite = ref3[1]; + [left, rite] = new Value(this.variable).cacheReference(o); } rite = new Call(rite, this.args); rite.isNew = this.isNew; @@ -1111,9 +1126,9 @@ break; } } - ref4 = list.reverse(); - for (j = 0, len1 = ref4.length; j < len1; j++) { - call = ref4[j]; + ref3 = list.reverse(); + for (j = 0, len1 = ref3.length; j < len1; j++) { + call = ref3[j]; if (ifn) { if (call.variable instanceof Call) { call.variable = ifn; @@ -1168,14 +1183,14 @@ } compileNode(o) { - var ref, ref3, ref4, replacement, superCall; + var ref, ref3, replacement, superCall; if (!((ref3 = this.expressions) != null ? ref3.length : void 0)) { return super.compileNode(o); } superCall = new Literal(fragmentsToText(super.compileNode(o))); replacement = new Block(this.expressions.slice()); if (o.level > LEVEL_TOP) { - ref4 = superCall.cache(o, null, YES), superCall = ref4[0], ref = ref4[1]; + [superCall, ref] = superCall.cache(o, null, YES); replacement.push(ref); } replacement.unshift(superCall); @@ -1331,15 +1346,15 @@ } compileVariables(o) { - var ref3, ref4, ref5, shouldCache, step; + var shouldCache, step; o = merge(o, { top: true }); shouldCache = del(o, 'shouldCache'); - ref3 = this.cacheToCodeFragments(this.from.cache(o, LEVEL_LIST, shouldCache)), this.fromC = ref3[0], this.fromVar = ref3[1]; - ref4 = this.cacheToCodeFragments(this.to.cache(o, LEVEL_LIST, shouldCache)), this.toC = ref4[0], this.toVar = ref4[1]; + [this.fromC, this.fromVar] = this.cacheToCodeFragments(this.from.cache(o, LEVEL_LIST, shouldCache)); + [this.toC, this.toVar] = this.cacheToCodeFragments(this.to.cache(o, LEVEL_LIST, shouldCache)); if (step = del(o, 'step')) { - ref5 = this.cacheToCodeFragments(step.cache(o, LEVEL_LIST, shouldCache)), this.step = ref5[0], this.stepVar = ref5[1]; + [this.step, this.stepVar] = this.cacheToCodeFragments(step.cache(o, LEVEL_LIST, shouldCache)); } this.fromNum = this.from.isNumber() ? Number(this.fromVar) : null; this.toNum = this.to.isNumber() ? Number(this.toVar) : null; @@ -1347,7 +1362,7 @@ } compileNode(o) { - var cond, condPart, from, gt, idx, idxName, known, lt, namedIndex, ref3, ref4, stepPart, to, varPart; + var cond, condPart, from, gt, idx, idxName, known, lt, namedIndex, stepPart, to, varPart; if (!this.fromVar) { this.compileVariables(o); } @@ -1365,8 +1380,8 @@ if (this.step !== this.stepVar) { varPart += `, ${this.step}`; } - ref3 = [`${idx} <${this.equals}`, `${idx} >${this.equals}`], lt = ref3[0], gt = ref3[1]; - condPart = this.stepNum != null ? this.stepNum > 0 ? `${lt} ${this.toVar}` : `${gt} ${this.toVar}` : known ? ((ref4 = [this.fromNum, this.toNum], from = ref4[0], to = ref4[1], ref4), from <= to ? `${lt} ${to}` : `${gt} ${to}`) : (cond = this.stepVar ? `${this.stepVar} > 0` : `${this.fromVar} <= ${this.toVar}`, `${cond} ? ${lt} ${this.toVar} : ${gt} ${this.toVar}`); + [lt, gt] = [`${idx} <${this.equals}`, `${idx} >${this.equals}`]; + condPart = this.stepNum != null ? this.stepNum > 0 ? `${lt} ${this.toVar}` : `${gt} ${this.toVar}` : known ? ([from, to] = [this.fromNum, this.toNum], from <= to ? `${lt} ${to}` : `${gt} ${to}`) : (cond = this.stepVar ? `${this.stepVar} > 0` : `${this.fromVar} <= ${this.toVar}`, `${cond} ? ${lt} ${this.toVar} : ${gt} ${this.toVar}`); stepPart = this.stepVar ? `${idx} += ${this.stepVar}` : known ? namedIndex ? from <= to ? `++${idx}` : `--${idx}` : from <= to ? `${idx}++` : `${idx}--` : namedIndex ? `${cond} ? ++${idx} : --${idx}` : `${cond} ? ${idx}++ : ${idx}--`; if (namedIndex) { varPart = `${idxName} = ${varPart}`; @@ -1461,7 +1476,7 @@ } compileNode(o) { - var answer, i, idt, indent, j, join, k, key, lastNoncom, len1, len2, node, prop, props, ref3, value; + var answer, i, idt, indent, j, join, k, key, lastNoncom, len1, len2, node, prop, props, value; props = this.properties; if (this.generated) { for (j = 0, len1 = props.length; j < len1; j++) { @@ -1492,7 +1507,7 @@ } if (!(prop instanceof Comment) && !(prop instanceof Assign)) { if (prop.shouldCache()) { - ref3 = prop.base.cache(o), key = ref3[0], value = ref3[1]; + [key, value] = prop.base.cache(o); if (key instanceof IdentifierLiteral) { key = new PropertyName(key.value); } @@ -1546,6 +1561,24 @@ this.objects = objs || []; } + isAssignable() { + var i, j, len1, obj, ref3; + if (!this.objects.length) { + return false; + } + ref3 = this.objects; + for (i = j = 0, len1 = ref3.length; j < len1; i = ++j) { + obj = ref3[i]; + if (obj instanceof Splat && i + 1 !== this.objects.length) { + return false; + } + if (!(obj.isAssignable() && (!obj.isAtomic || obj.isAtomic()))) { + return false; + } + } + return true; + } + compileNode(o) { var answer, compiledObjs, fragments, index, j, len1, obj; if (!this.objects.length) { @@ -1592,6 +1625,18 @@ return false; } + eachName(iterator) { + var j, len1, obj, ref3, results; + ref3 = this.objects; + results = []; + for (j = 0, len1 = ref3.length; j < len1; j++) { + obj = ref3[j]; + obj = obj.unwrapAll(); + results.push(obj.eachName(iterator)); + } + return results; + } + }; Arr.prototype.children = ['objects']; @@ -2255,7 +2300,9 @@ var answer, compiledName, isValue, j, name, properties, prototype, ref3, ref4, ref5, ref6, ref7, ref8, val, varBase; if (isValue = this.variable instanceof Value) { if (this.variable.isArray() || this.variable.isObject()) { - return this.compilePatternMatch(o); + if (!this.variable.isAssignable()) { + return this.compilePatternMatch(o); + } } if (this.variable.isSplice()) { return this.compileSplice(o); @@ -2267,30 +2314,37 @@ return this.compileSpecialMath(o); } } - if (this.value instanceof Code) { - if (this.value.isStatic) { - this.value.name = this.variable.properties[0]; - } else if (((ref5 = this.variable.properties) != null ? ref5.length : void 0) >= 2) { - ref6 = this.variable.properties, properties = 3 <= ref6.length ? slice.call(ref6, 0, j = ref6.length - 2) : (j = 0, []), prototype = ref6[j++], name = ref6[j++]; - if (((ref7 = prototype.name) != null ? ref7.value : void 0) === 'prototype') { - this.value.name = name; - } - } - } if (!this.context) { varBase = this.variable.unwrapAll(); if (!varBase.isAssignable()) { this.variable.error(`'${this.variable.compile(o)}' can't be assigned`); } - if (!(typeof varBase.hasProperties === "function" ? varBase.hasProperties() : void 0)) { + varBase.eachName((name) => { + var message; + if (typeof name.hasProperties === "function" ? name.hasProperties() : void 0) { + return; + } + if (message = isUnassignable(name.value)) { + name.error(message); + } if (this.moduleDeclaration) { - this.checkAssignability(o, varBase); - o.scope.add(varBase.value, this.moduleDeclaration); + this.checkAssignability(o, name); + return o.scope.add(name.value, this.moduleDeclaration); } else if (this.param) { - o.scope.add(varBase.value, 'var'); + return o.scope.add(name.value, 'var'); } else { - this.checkAssignability(o, varBase); - o.scope.find(varBase.value); + this.checkAssignability(o, name); + return o.scope.find(name.value); + } + }); + } + if (this.value instanceof Code) { + if (this.value.isStatic) { + this.value.name = this.variable.properties[0]; + } else if (((ref5 = this.variable.properties) != null ? ref5.length : void 0) >= 2) { + ref6 = this.variable.properties, properties = 3 <= ref6.length ? slice.call(ref6, 0, j = ref6.length - 2) : (j = 0, []), prototype = ref6[j++], name = ref6[j++]; + if (((ref7 = prototype.name) != null ? ref7.value : void 0) === 'prototype') { + this.value.name = name; } } } @@ -2330,7 +2384,7 @@ return code; } } - obj = objects[0]; + [obj] = objects; if (olen === 1 && obj instanceof Expansion) { obj.error('Destructuring assignment has no target'); } @@ -2452,8 +2506,8 @@ } compileConditional(o) { - var fragments, left, ref3, right; - ref3 = this.variable.cacheReference(o), left = ref3[0], right = ref3[1]; + var fragments, left, right; + [left, right] = this.variable.cacheReference(o); if (!left.properties.length && left.base instanceof Literal && !(left.base instanceof ThisLiteral) && !o.scope.check(left.base.value)) { this.variable.error(`the variable \"${left.base.value}\" can't be assigned with ${this.context} because it has not been declared before`); } @@ -2473,17 +2527,17 @@ } compileSpecialMath(o) { - var left, ref3, right; - ref3 = this.variable.cacheReference(o), left = ref3[0], right = ref3[1]; + var left, right; + [left, right] = this.variable.cacheReference(o); return new Assign(left, new Op(this.context.slice(0, -1), right, this.value)).compileToFragments(o); } compileSplice(o) { - var answer, exclusive, from, fromDecl, fromRef, name, ref3, ref4, ref5, to, valDef, valRef; + var answer, exclusive, from, fromDecl, fromRef, name, ref3, to, valDef, valRef; ref3 = this.variable.properties.pop().range, from = ref3.from, to = ref3.to, exclusive = ref3.exclusive; name = this.variable.compile(o); if (from) { - ref4 = this.cacheToCodeFragments(from.cache(o, LEVEL_OP)), fromDecl = ref4[0], fromRef = ref4[1]; + [fromDecl, fromRef] = this.cacheToCodeFragments(from.cache(o, LEVEL_OP)); } else { fromDecl = fromRef = '0'; } @@ -2502,7 +2556,7 @@ } else { to = "9e9"; } - ref5 = this.value.cache(o, LEVEL_LIST), valDef = ref5[0], valRef = ref5[1]; + [valDef, valRef] = this.value.cache(o, LEVEL_LIST); answer = [].concat(this.makeCode(`[].splice.apply(${name}, [${fromDecl}, ${to}].concat(`), valDef, this.makeCode(")), "), valRef); if (o.level > LEVEL_TOP) { return this.wrapInBraces(answer); @@ -2551,7 +2605,7 @@ } compileNode(o) { - var answer, body, condition, exprs, haveBodyParam, haveSplatParam, i, ifTrue, j, k, len1, len2, m, methodScope, modifiers, name, param, paramNames, params, paramsAfterSplat, ref, ref3, ref4, ref5, ref6, ref7, ref8, signature, splatParamName, thisAssignments, wasEmpty; + var answer, body, condition, exprs, haveBodyParam, haveSplatParam, i, ifTrue, j, k, len1, len2, m, methodScope, modifiers, name, param, paramNames, params, paramsAfterSplat, ref, ref3, ref4, ref5, ref6, ref7, signature, splatParamName, thisAssignments, wasEmpty; if (this.ctor) { if (this.isAsync) { this.name.error('Class constructor may not be async'); @@ -2610,7 +2664,7 @@ params.push(ref = param.asReference(o)); splatParamName = fragmentsToText(ref.compileNode(o)); if (param.shouldCache()) { - exprs.push(new Assign(new Value(param.name), ref, '=', { + exprs.push(new Assign(new Value(param.name), ref, null, { param: true })); } @@ -2625,12 +2679,12 @@ haveBodyParam = true; if (param.value != null) { condition = new Op('==', param, new UndefinedLiteral); - ifTrue = new Assign(new Value(param.name), param.value, '=', { + ifTrue = new Assign(new Value(param.name), param.value, null, { param: true }); exprs.push(new If(condition, ifTrue)); } else { - exprs.push(new Assign(new Value(param.name), param.asReference(o), '=', { + exprs.push(new Assign(new Value(param.name), param.asReference(o), null, { param: true })); } @@ -2640,7 +2694,7 @@ ref = param.asReference(o); } else { if ((param.value != null) && !param.assignedInBody) { - ref = new Assign(new Value(param.name), param.value, '='); + ref = new Assign(new Value(param.name), param.value); } else { ref = param; } @@ -2651,7 +2705,7 @@ paramsAfterSplat.push(param); if ((param.value != null) && !param.shouldCache()) { condition = new Op('==', param, new UndefinedLiteral); - ifTrue = new Assign(new Value(param.name), param.value, '='); + ifTrue = new Assign(new Value(param.name), param.value); exprs.push(new If(condition, ifTrue)); } if (((ref7 = param.name) != null ? ref7.value : void 0) != null) { @@ -2709,7 +2763,7 @@ body = this.body.compileWithDeclarations(o); } if (this.isMethod) { - ref8 = [o.scope, o.scope.parent], methodScope = ref8[0], o.scope = ref8[1]; + [methodScope, o.scope] = [o.scope, o.scope.parent]; name = this.name.compileToFragments(o); if (name[0].code === '.') { name.shift(); @@ -2937,6 +2991,10 @@ exports.Splat = Splat = (function() { class Splat extends Base { + isAssignable() { + return this.name.isAssignable() && (!this.name.isAtomic || this.name.isAtomic()); + } + constructor(name) { super(); this.name = name.compile ? name : new Literal(name); @@ -2958,8 +3016,6 @@ Splat.prototype.children = ['name']; - Splat.prototype.isAssignable = YES; - return Splat; })(); @@ -3223,8 +3279,8 @@ } compileChain(o) { - var fragments, fst, ref3, shared; - ref3 = this.first.second.cache(o), this.first.second = ref3[0], shared = ref3[1]; + var fragments, fst, shared; + [this.first.second, shared] = this.first.second.cache(o); fst = this.first.compileToFragments(o, LEVEL_OP); fragments = fst.concat(this.makeCode(` ${(this.invert ? '&&' : '||')} `), shared.compileToFragments(o), this.makeCode(` ${this.operator} `), this.second.compileToFragments(o, LEVEL_OP)); return this.wrapInBraces(fragments); @@ -3372,13 +3428,13 @@ } compileOrTest(o) { - var cmp, cnj, i, item, j, len1, ref, ref3, ref4, ref5, sub, tests; - ref3 = this.object.cache(o, LEVEL_OP), sub = ref3[0], ref = ref3[1]; - ref4 = this.negated ? [' !== ', ' && '] : [' === ', ' || '], cmp = ref4[0], cnj = ref4[1]; + var cmp, cnj, i, item, j, len1, ref, ref3, sub, tests; + [sub, ref] = this.object.cache(o, LEVEL_OP); + [cmp, cnj] = this.negated ? [' !== ', ' && '] : [' === ', ' || ']; tests = []; - ref5 = this.array.base.objects; - for (i = j = 0, len1 = ref5.length; j < len1; i = ++j) { - item = ref5[i]; + ref3 = this.array.base.objects; + for (i = j = 0, len1 = ref3.length; j < len1; i = ++j) { + item = ref3[i]; if (i) { tests.push(this.makeCode(cnj)); } @@ -3392,8 +3448,8 @@ } compileLoopTest(o) { - var fragments, ref, ref3, sub; - ref3 = this.object.cache(o, LEVEL_LIST), sub = ref3[0], ref = ref3[1]; + var fragments, ref, sub; + [sub, ref] = this.object.cache(o, LEVEL_LIST); fragments = [].concat(this.makeCode(utility('indexOf', o) + ".call("), this.array.compileToFragments(o, LEVEL_LIST), this.makeCode(", "), ref, this.makeCode(") " + (this.negated ? '< 0' : '>= 0'))); if (fragmentsToText(sub) === fragmentsToText(ref)) { return fragments; @@ -3501,11 +3557,11 @@ } compileNode(o) { - var cmp, cnj, code, ref3; + var cmp, cnj, code; this.expression.front = this.front; code = this.expression.compile(o, LEVEL_OP); if (this.expression.unwrap() instanceof IdentifierLiteral && !o.scope.check(code)) { - ref3 = this.negated ? ['===', '||'] : ['!==', '&&'], cmp = ref3[0], cnj = ref3[1]; + [cmp, cnj] = this.negated ? ['===', '||'] : ['!==', '&&']; code = `typeof ${code} ${cmp} \"undefined\" ${cnj} ${code} ${cmp} null`; } else { code = `${code} ${(this.negated ? '==' : '!=')} null`; @@ -3626,7 +3682,6 @@ exports.For = For = (function() { class For extends While { constructor(body, source) { - var ref3; super(); this.source = source.source, this.guard = source.guard, this.step = source.step, this.name = source.name, this.index = source.index; this.body = Block.wrap([body]); @@ -3640,7 +3695,7 @@ source.ownTag.error(`cannot use own with for-${(this.from ? 'from' : 'in')}`); } if (this.object) { - ref3 = [this.index, this.name], this.name = ref3[0], this.index = ref3[1]; + [this.name, this.index] = [this.index, this.name]; } if (this.index instanceof Value && !this.index.isAssignable()) { this.index.error('index cannot be a pattern matching expression'); @@ -3657,7 +3712,7 @@ } compileNode(o) { - var body, bodyFragments, compare, compareDown, declare, declareDown, defPart, defPartFragments, down, forPartFragments, guardPart, idt1, increment, index, ivar, kvar, kvarAssign, last, lvar, name, namePart, ref, ref3, ref4, resultPart, returnResult, rvar, scope, source, step, stepNum, stepVar, svar, varPart; + var body, bodyFragments, compare, compareDown, declare, declareDown, defPart, defPartFragments, down, forPartFragments, guardPart, idt1, increment, index, ivar, kvar, kvarAssign, last, lvar, name, namePart, ref, ref3, resultPart, returnResult, rvar, scope, source, step, stepNum, stepVar, svar, varPart; body = Block.wrap([this.body]); ref3 = body.expressions, last = ref3[ref3.length - 1]; if ((last != null ? last.jumps() : void 0) instanceof Return) { @@ -3692,7 +3747,7 @@ kvar = ((this.range || this.from) && name) || index || ivar; kvarAssign = kvar !== ivar ? `${kvar} = ` : ""; if (this.step && !this.range) { - ref4 = this.cacheToCodeFragments(this.step.cache(o, LEVEL_LIST, shouldCacheOrIsAssignable)), step = ref4[0], stepVar = ref4[1]; + [step, stepVar] = this.cacheToCodeFragments(this.step.cache(o, LEVEL_LIST, shouldCacheOrIsAssignable)); if (this.step.isNumber()) { stepNum = Number(stepVar); } @@ -3788,7 +3843,7 @@ } pluckDirectCall(o, body) { - var base, defs, expr, fn, idx, j, len1, ref, ref3, ref4, ref5, ref6, ref7, ref8, ref9, val; + var base, defs, expr, fn, idx, j, len1, ref, ref3, ref4, ref5, ref6, ref7, ref8, val; defs = []; ref3 = body.expressions; for (idx = j = 0, len1 = ref3.length; j < len1; idx = ++j) { @@ -3805,7 +3860,7 @@ ref = new IdentifierLiteral(o.scope.freeVariable('fn')); base = new Value(ref); if (val.base) { - ref9 = [base, val], val.base = ref9[0], base = ref9[1]; + [val.base, base] = [base, val]; } body.expressions[idx] = new Call(base, expr.args); defs = defs.concat(this.makeCode(this.tab), new Assign(ref, fn).compileToFragments(o, LEVEL_TOP), this.makeCode(';\n')); @@ -3833,15 +3888,15 @@ jumps(o = { block: true }) { - var block, conds, j, jumpNode, len1, ref3, ref4, ref5; + var block, conds, j, jumpNode, len1, ref3, ref4; ref3 = this.cases; for (j = 0, len1 = ref3.length; j < len1; j++) { - ref4 = ref3[j], conds = ref4[0], block = ref4[1]; + [conds, block] = ref3[j]; if (jumpNode = block.jumps(o)) { return jumpNode; } } - return (ref5 = this.otherwise) != null ? ref5.jumps(o) : void 0; + return (ref4 = this.otherwise) != null ? ref4.jumps(o) : void 0; } makeReturn(res) { @@ -3861,16 +3916,16 @@ } compileNode(o) { - var block, body, cond, conditions, expr, fragments, i, idt1, idt2, j, k, len1, len2, ref3, ref4, ref5; + var block, body, cond, conditions, expr, fragments, i, idt1, idt2, j, k, len1, len2, ref3, ref4; idt1 = o.indent + TAB; idt2 = o.indent = idt1 + TAB; fragments = [].concat(this.makeCode(this.tab + "switch ("), (this.subject ? this.subject.compileToFragments(o, LEVEL_PAREN) : this.makeCode("false")), this.makeCode(") {\n")); ref3 = this.cases; for (i = j = 0, len1 = ref3.length; j < len1; i = ++j) { - ref4 = ref3[i], conditions = ref4[0], block = ref4[1]; - ref5 = flatten([conditions]); - for (k = 0, len2 = ref5.length; k < len2; k++) { - cond = ref5[k]; + [conditions, block] = ref3[i]; + ref4 = flatten([conditions]); + for (k = 0, len2 = ref4.length; k < len2; k++) { + cond = ref4[k]; if (!this.subject) { cond = cond.invert(); } diff --git a/lib/coffeescript/repl.js b/lib/coffeescript/repl.js index a277a44101..84c47c7c9c 100644 --- a/lib/coffeescript/repl.js +++ b/lib/coffeescript/repl.js @@ -168,10 +168,10 @@ module.exports = { start: function(opts = {}) { - var build, major, minor, ref1, repl; - ref1 = process.versions.node.split('.').map(function(n) { + var build, major, minor, repl; + [major, minor, build] = process.versions.node.split('.').map(function(n) { return parseInt(n); - }), major = ref1[0], minor = ref1[1], build = ref1[2]; + }); if (major < 6) { console.warn("Node 6+ required for CoffeeScript REPL"); process.exit(1); diff --git a/lib/coffeescript/rewriter.js b/lib/coffeescript/rewriter.js index c3d7384d41..c8ef515ec4 100644 --- a/lib/coffeescript/rewriter.js +++ b/lib/coffeescript/rewriter.js @@ -1,6 +1,6 @@ // Generated by CoffeeScript 2.0.0-alpha1 (function() { - var BALANCED_PAIRS, CALL_CLOSERS, EXPRESSION_CLOSE, EXPRESSION_END, EXPRESSION_START, IMPLICIT_CALL, IMPLICIT_END, IMPLICIT_FUNC, IMPLICIT_UNSPACED_CALL, INVERSES, LINEBREAKS, SINGLE_CLOSERS, SINGLE_LINERS, generate, k, left, len, ref, rite, + var BALANCED_PAIRS, CALL_CLOSERS, EXPRESSION_CLOSE, EXPRESSION_END, EXPRESSION_START, IMPLICIT_CALL, IMPLICIT_END, IMPLICIT_FUNC, IMPLICIT_UNSPACED_CALL, INVERSES, LINEBREAKS, SINGLE_CLOSERS, SINGLE_LINERS, generate, k, left, len, rite, 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; }; generate = function(tag, value, origin) { @@ -63,7 +63,7 @@ var i, k, len, ref, tag; ref = this.tokens; for (i = k = 0, len = ref.length; k < len; i = ++k) { - tag = ref[i][0]; + [tag] = ref[i]; if (tag !== 'TERMINATOR') { break; } @@ -168,10 +168,10 @@ stack = []; start = null; return this.scanTokens(function(token, i, tokens) { - var endImplicitCall, endImplicitObject, forward, inImplicit, inImplicitCall, inImplicitControl, inImplicitObject, newLine, nextTag, offset, prevTag, prevToken, ref, ref1, ref2, ref3, ref4, ref5, s, sameLine, stackIdx, stackTag, stackTop, startIdx, startImplicitCall, startImplicitObject, startsLine, tag; - tag = token[0]; - prevTag = (prevToken = i > 0 ? tokens[i - 1] : [])[0]; - nextTag = (i < tokens.length - 1 ? tokens[i + 1] : [])[0]; + var endImplicitCall, endImplicitObject, forward, inImplicit, inImplicitCall, inImplicitControl, inImplicitObject, newLine, nextTag, offset, prevTag, prevToken, ref, ref1, ref2, ref3, ref4, s, sameLine, stackIdx, stackTag, stackTop, startIdx, startImplicitCall, startImplicitObject, startsLine, tag; + [tag] = token; + [prevTag] = prevToken = i > 0 ? tokens[i - 1] : []; + [nextTag] = i < tokens.length - 1 ? tokens[i + 1] : []; stackTop = function() { return stack[stack.length - 1]; }; @@ -302,7 +302,7 @@ this.insideForDeclaration = nextTag === 'FOR'; startsLine = s === 0 || (ref2 = this.tag(s - 1), indexOf.call(LINEBREAKS, ref2) >= 0) || tokens[s - 1].newLine; if (stackTop()) { - ref3 = stackTop(), stackTag = ref3[0], stackIdx = ref3[1]; + [stackTag, stackIdx] = stackTop(); if ((stackTag === '{' || stackTag === 'INDENT' && this.tag(stackIdx - 1) === '{') && (startsLine || this.tag(s - 1) === ',' || this.tag(s - 1) === '{')) { return forward(1); } @@ -316,7 +316,7 @@ newLine = prevTag === 'OUTDENT' || prevToken.newLine; if (indexOf.call(IMPLICIT_END, tag) >= 0 || indexOf.call(CALL_CLOSERS, tag) >= 0 && newLine) { while (inImplicit()) { - ref4 = stackTop(), stackTag = ref4[0], stackIdx = ref4[1], (ref5 = ref4[2], sameLine = ref5.sameLine, startsLine = ref5.startsLine); + ref3 = stackTop(), stackTag = ref3[0], stackIdx = ref3[1], (ref4 = ref3[2], sameLine = ref4.sameLine, startsLine = ref4.startsLine); if (inImplicitCall() && prevTag !== ',') { endImplicitCall(); } else if (inImplicitObject() && !this.insideForDeclaration && sameLine && tag !== 'TERMINATOR' && prevTag !== ':') { @@ -395,8 +395,8 @@ return this.tokens.splice((this.tag(i - 1) === ',' ? i - 1 : i), 0, outdent); }; return this.scanTokens(function(token, i, tokens) { - var j, k, ref, ref1, ref2, tag; - tag = token[0]; + var j, k, ref, ref1, tag; + [tag] = token; if (tag === 'TERMINATOR') { if (this.tag(i + 1) === 'ELSE' && this.tag(i - 1) !== 'OUTDENT') { tokens.splice(i, 1, ...this.indentation()); @@ -418,7 +418,7 @@ } if (indexOf.call(SINGLE_LINERS, tag) >= 0 && this.tag(i + 1) !== 'INDENT' && !(tag === 'ELSE' && this.tag(i + 1) === 'IF')) { starter = tag; - ref2 = this.indentation(tokens[i]), indent = ref2[0], outdent = ref2[1]; + [indent, outdent] = this.indentation(tokens[i]); if (starter === 'THEN') { indent.fromThen = true; } @@ -438,8 +438,8 @@ original = null; condition = function(token, i) { var prevTag, tag; - tag = token[0]; - prevTag = this.tokens[i - 1][0]; + [tag] = token; + [prevTag] = this.tokens[i - 1]; return tag === 'TERMINATOR' || (tag === 'INDENT' && indexOf.call(SINGLE_LINERS, prevTag) < 0); }; action = function(token, i) { @@ -492,7 +492,7 @@ EXPRESSION_END = []; for (k = 0, len = BALANCED_PAIRS.length; k < len; k++) { - ref = BALANCED_PAIRS[k], left = ref[0], rite = ref[1]; + [left, rite] = BALANCED_PAIRS[k]; EXPRESSION_START.push(INVERSES[rite] = left); EXPRESSION_END.push(INVERSES[left] = rite); } diff --git a/lib/coffeescript/sourcemap.js b/lib/coffeescript/sourcemap.js index 60d5fe2c68..98c898d56f 100644 --- a/lib/coffeescript/sourcemap.js +++ b/lib/coffeescript/sourcemap.js @@ -9,8 +9,8 @@ } add(column, arg, options) { - var sourceColumn, sourceLine; - sourceLine = arg[0], sourceColumn = arg[1]; + var options, sourceColumn, sourceLine; + [sourceLine, sourceColumn] = arg; if (options === void 0) { options = {}; } @@ -45,14 +45,14 @@ add(sourceLocation, generatedLocation, options = {}) { var base, column, line, lineMap; - line = generatedLocation[0], column = generatedLocation[1]; + [line, column] = generatedLocation; lineMap = ((base = this.lines)[line] || (base[line] = new LineMap(line))); return lineMap.add(column, sourceLocation, options); } sourceLocation(arg) { var column, line, lineMap; - line = arg[0], column = arg[1]; + [line, column] = arg; while (!((lineMap = this.lines[line]) || (line <= 0))) { line--; } diff --git a/src/nodes.coffee b/src/nodes.coffee index cc2a6dabc7..bec76f24d6 100644 --- a/src/nodes.coffee +++ b/src/nodes.coffee @@ -543,6 +543,9 @@ exports.PassthroughLiteral = class PassthroughLiteral extends Literal exports.IdentifierLiteral = class IdentifierLiteral extends Literal isAssignable: YES + eachName: (iterator) -> + iterator @ + exports.PropertyName = class PropertyName extends Literal isAssignable: YES @@ -740,6 +743,14 @@ exports.Value = class Value extends Base return new If new Existence(fst), snd, soak: on no + eachName: (iterator) -> + if @hasProperties() + iterator @ + else if @base.isAssignable() + @base.eachName iterator + else + @error 'tried to assign to unassignable value' + #### Comment # CoffeeScript passes through block comments as JavaScript block comments @@ -1156,6 +1167,14 @@ exports.Arr = class Arr extends Base children: ['objects'] + isAssignable: -> + return false unless @objects.length + + for obj, i in @objects + return false if obj instanceof Splat and i + 1 != @objects.length + return false unless obj.isAssignable() and (not obj.isAtomic or obj.isAtomic()) + true + compileNode: (o) -> return [@makeCode '[]'] unless @objects.length o.indent += TAB @@ -1178,6 +1197,11 @@ exports.Arr = class Arr extends Base for obj in @objects when obj.assigns name then return yes no + eachName: (iterator) -> + for obj in @objects + obj = obj.unwrapAll() + obj.eachName iterator + #### Class # The CoffeeScript class definition. @@ -1660,30 +1684,39 @@ exports.Assign = class Assign extends Base # has not been seen yet within the current scope, declare it. compileNode: (o) -> if isValue = @variable instanceof Value - return @compilePatternMatch o if @variable.isArray() or @variable.isObject() + if @variable.isArray() or @variable.isObject() + return @compilePatternMatch o unless @variable.isAssignable() + return @compileSplice o if @variable.isSplice() return @compileConditional o if @context in ['||=', '&&=', '?='] return @compileSpecialMath o if @context in ['**=', '//=', '%%='] - if @value instanceof Code - if @value.isStatic - @value.name = @variable.properties[0] - else if @variable.properties?.length >= 2 - [properties..., prototype, name] = @variable.properties - @value.name = name if prototype.name?.value is 'prototype' + unless @context varBase = @variable.unwrapAll() unless varBase.isAssignable() @variable.error "'#{@variable.compile o}' can't be assigned" - unless varBase.hasProperties?() + + varBase.eachName (name) => + return if name.hasProperties?() + + name.error message if message = isUnassignable name.value + # `moduleDeclaration` can be `'import'` or `'export'` if @moduleDeclaration - @checkAssignability o, varBase - o.scope.add varBase.value, @moduleDeclaration + @checkAssignability o, name + o.scope.add name.value, @moduleDeclaration else if @param - o.scope.add varBase.value, 'var' + o.scope.add name.value, 'var' else - @checkAssignability o, varBase - o.scope.find varBase.value + @checkAssignability o, name + o.scope.find name.value + + if @value instanceof Code + if @value.isStatic + @value.name = @variable.properties[0] + else if @variable.properties?.length >= 2 + [properties..., prototype, name] = @variable.properties + @value.name = name if prototype.name?.value is 'prototype' val = @value.compileToFragments o, LEVEL_LIST @variable.front = true if isValue and @variable.base instanceof Obj @@ -1950,7 +1983,7 @@ exports.Code = class Code extends Base params.push ref = param.asReference o splatParamName = fragmentsToText ref.compileNode o if param.shouldCache() - exprs.push new Assign new Value(param.name), ref, '=', param: yes + exprs.push new Assign new Value(param.name), ref, null, param: yes # TODO: output destructured parameters as is, and fix destructuring # of objects with default values to work in this context (see # Obj.compileNode `if prop.context isnt 'object'`). @@ -1973,10 +2006,10 @@ exports.Code = class Code extends Base # `(arg) => { var a = arg.a; }`, with a default value if it has one. if param.value? condition = new Op '==', param, new UndefinedLiteral - ifTrue = new Assign new Value(param.name), param.value, '=', param: yes + ifTrue = new Assign new Value(param.name), param.value, null, param: yes exprs.push new If condition, ifTrue else - exprs.push new Assign new Value(param.name), param.asReference(o), '=', param: yes + exprs.push new Assign new Value(param.name), param.asReference(o), null, param: yes # If this parameter comes before the splat or expansion, it will go # in the function definition parameter list. @@ -1989,7 +2022,7 @@ exports.Code = class Code extends Base ref = param.asReference o else if param.value? and not param.assignedInBody - ref = new Assign new Value(param.name), param.value, '=' + ref = new Assign new Value(param.name), param.value else ref = param # Add this parameter’s reference to the function scope @@ -2002,7 +2035,7 @@ exports.Code = class Code extends Base # (if necessary) as an expression in the body. if param.value? and not param.shouldCache() condition = new Op '==', param, new UndefinedLiteral - ifTrue = new Assign new Value(param.name), param.value, '=' + 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? @@ -2205,7 +2238,8 @@ exports.Splat = class Splat extends Base children: ['name'] - isAssignable: YES + isAssignable: -> + @name.isAssignable() and (not @name.isAtomic or @name.isAtomic()) constructor: (name) -> super() From b9f8f5d88aa00530253bf1244a10cae7e3bcaa08 Mon Sep 17 00:00:00 2001 From: Chris Connelly Date: Thu, 30 Mar 2017 21:52:01 +0100 Subject: [PATCH 02/20] Output simple object destructured assignments to ES2015 --- lib/coffeescript/coffeescript.js | 22 +- lib/coffeescript/command.js | 31 +- lib/coffeescript/grammar.js | 4 +- lib/coffeescript/helpers.js | 13 +- lib/coffeescript/lexer.js | 124 ++++--- lib/coffeescript/nodes.js | 607 ++++++++++++++++++------------- lib/coffeescript/optparse.js | 4 +- lib/coffeescript/register.js | 4 +- lib/coffeescript/repl.js | 26 +- lib/coffeescript/rewriter.js | 27 +- src/nodes.coffee | 17 +- test/assignment.coffee | 3 - 12 files changed, 533 insertions(+), 349 deletions(-) diff --git a/lib/coffeescript/coffeescript.js b/lib/coffeescript/coffeescript.js index 767efd35e9..9d5be3fcc8 100644 --- a/lib/coffeescript/coffeescript.js +++ b/lib/coffeescript/coffeescript.js @@ -9,9 +9,13 @@ path = require('path'); - Lexer = require('./lexer').Lexer; + ({ + Lexer: Lexer + } = require('./lexer')); - parser = require('./parser').parser; + ({ + parser: parser + } = require('./parser')); helpers = require('./helpers'); @@ -59,7 +63,10 @@ exports.compile = compile = withPrettyErrors(function(code, options) { var currentColumn, currentLine, encoded, extend, filename, fragment, fragments, generateSourceMap, header, i, j, js, len, len1, map, merge, newLines, ref, ref1, sourceMapDataURI, sourceURL, token, tokens, v3SourceMap; - merge = helpers.merge, extend = helpers.extend; + ({ + merge: merge, + extend: extend + } = helpers); options = extend({}, options); generateSourceMap = options.sourceMap || options.inlineMap || (options.filename == null); filename = options.filename || ''; @@ -295,8 +302,13 @@ parser.yy.parseError = function(message, arg) { var errorLoc, errorTag, errorText, errorToken, token, tokens; - token = arg.token; - errorToken = parser.errorToken, tokens = parser.tokens; + ({ + token: token + } = arg); + ({ + errorToken: errorToken, + tokens: tokens + } = parser); [errorTag, errorText, errorLoc] = errorToken; errorText = (function() { switch (false) { diff --git a/lib/coffeescript/command.js b/lib/coffeescript/command.js index 314197f59e..95558397dc 100644 --- a/lib/coffeescript/command.js +++ b/lib/coffeescript/command.js @@ -1,6 +1,6 @@ // Generated by CoffeeScript 2.0.0-alpha1 (function() { - var BANNER, CoffeeScript, EventEmitter, SWITCHES, compileJoin, compileOptions, compilePath, compileScript, compileStdio, exec, findDirectoryIndex, forkNode, fs, helpers, hidden, joinTimeout, makePrelude, mkdirp, notSources, optionParser, optparse, opts, outputPath, parseOptions, path, printLine, printTokens, printWarn, ref, removeSource, removeSourceDir, silentUnlink, sourceCode, sources, spawn, timeLog, usage, useWinPathSep, version, wait, watch, watchDir, watchedDirs, writeJs, + var BANNER, CoffeeScript, EventEmitter, SWITCHES, compileJoin, compileOptions, compilePath, compileScript, compileStdio, exec, findDirectoryIndex, forkNode, fs, helpers, hidden, joinTimeout, makePrelude, mkdirp, notSources, optionParser, optparse, opts, outputPath, parseOptions, path, printLine, printTokens, printWarn, removeSource, removeSourceDir, silentUnlink, sourceCode, sources, spawn, timeLog, usage, useWinPathSep, version, wait, watch, watchDir, watchedDirs, writeJs, 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; }; fs = require('fs'); @@ -13,9 +13,14 @@ CoffeeScript = require('./coffeescript'); - ref = require('child_process'), spawn = ref.spawn, exec = ref.exec; + ({ + spawn: spawn, + exec: exec + } = require('child_process')); - EventEmitter = require('events').EventEmitter; + ({ + EventEmitter: EventEmitter + } = require('events')); useWinPathSep = path.sep === '\\'; @@ -50,7 +55,7 @@ optionParser = null; exports.run = function() { - var i, len, literals, ref1, replCliOpts, results, source; + var i, len, literals, ref, replCliOpts, results, source; parseOptions(); replCliOpts = { useGlobal: true @@ -90,10 +95,10 @@ opts.join = path.resolve(opts.join); console.error('\nThe --join option is deprecated and will be removed in a future version.\n\nIf for some reason it\'s necessary to share local variables between files,\nreplace...\n\n $ coffee --compile --join bundle.js -- a.coffee b.coffee c.coffee\n\nwith...\n\n $ cat a.coffee b.coffee c.coffee | coffee --compile --stdio > bundle.js\n'); } - ref1 = opts["arguments"]; + ref = opts["arguments"]; results = []; - for (i = 0, len = ref1.length; i < len; i++) { - source = ref1[i]; + for (i = 0, len = ref.length; i < len; i++) { + source = ref[i]; source = path.resolve(source); results.push(compilePath(source, true, source)); } @@ -178,10 +183,10 @@ }; findDirectoryIndex = function(source) { - var err, ext, i, index, len, ref1; - ref1 = CoffeeScript.FILE_EXTENSIONS; - for (i = 0, len = ref1.length; i < len; i++) { - ext = ref1[i]; + var err, ext, i, index, len, ref; + ref = CoffeeScript.FILE_EXTENSIONS; + for (i = 0, len = ref.length; i < len; i++) { + ext = ref[i]; index = path.join(source, `index${ext}`); try { if ((fs.statSync(index)).isFile()) { @@ -425,12 +430,12 @@ }; silentUnlink = function(path) { - var err, ref1; + var err, ref; try { return fs.unlinkSync(path); } catch (error) { err = error; - if ((ref1 = err.code) !== 'ENOENT' && ref1 !== 'EPERM') { + if ((ref = err.code) !== 'ENOENT' && ref !== 'EPERM') { throw err; } } diff --git a/lib/coffeescript/grammar.js b/lib/coffeescript/grammar.js index c47943faac..8e7eee0d8e 100644 --- a/lib/coffeescript/grammar.js +++ b/lib/coffeescript/grammar.js @@ -2,7 +2,9 @@ (function() { var Parser, alt, alternatives, grammar, name, o, operators, token, tokens, unwrap; - Parser = require('jison').Parser; + ({ + Parser: Parser + } = require('jison')); unwrap = /^function\s*\(\)\s*\{\s*return\s*([\s\S]*);\s*\}/; diff --git a/lib/coffeescript/helpers.js b/lib/coffeescript/helpers.js index 7027a43f99..39c2da690e 100644 --- a/lib/coffeescript/helpers.js +++ b/lib/coffeescript/helpers.js @@ -197,11 +197,16 @@ }; syntaxErrorToString = function() { - var codeLine, colorize, colorsEnabled, end, filename, first_column, first_line, last_column, last_line, marker, ref1, ref2, ref3, ref4, start; + var codeLine, colorize, colorsEnabled, end, filename, first_column, first_line, last_column, last_line, marker, ref1, ref2, ref3, start; if (!(this.code && this.location)) { return Error.prototype.toString.call(this); } - ref1 = this.location, first_line = ref1.first_line, first_column = ref1.first_column, last_line = ref1.last_line, last_column = ref1.last_column; + ({ + first_line: first_line, + first_column: first_column, + last_line: last_line, + last_column: last_column + } = this.location); if (last_line == null) { last_line = first_line; } @@ -214,9 +219,9 @@ end = first_line === last_line ? last_column + 1 : codeLine.length; marker = codeLine.slice(0, start).replace(/[^\s]/g, ' ') + repeat('^', end - start); if (typeof process !== "undefined" && process !== null) { - colorsEnabled = ((ref2 = process.stdout) != null ? ref2.isTTY : void 0) && !((ref3 = process.env) != null ? ref3.NODE_DISABLE_COLORS : void 0); + colorsEnabled = ((ref1 = process.stdout) != null ? ref1.isTTY : void 0) && !((ref2 = process.env) != null ? ref2.NODE_DISABLE_COLORS : void 0); } - if ((ref4 = this.colorful) != null ? ref4 : colorsEnabled) { + if ((ref3 = this.colorful) != null ? ref3 : colorsEnabled) { colorize = function(str) { return `\x1B[1;31m${str}\x1B[0m`; }; diff --git a/lib/coffeescript/lexer.js b/lib/coffeescript/lexer.js index bfb64a0792..99f2d2a696 100644 --- a/lib/coffeescript/lexer.js +++ b/lib/coffeescript/lexer.js @@ -1,11 +1,22 @@ // Generated by CoffeeScript 2.0.0-alpha1 (function() { - var BOM, BOOL, CALLABLE, CODE, COFFEE_ALIASES, COFFEE_ALIAS_MAP, COFFEE_KEYWORDS, COMMENT, COMPARE, COMPOUND_ASSIGN, HERECOMMENT_ILLEGAL, HEREDOC_DOUBLE, HEREDOC_INDENT, HEREDOC_SINGLE, HEREGEX, HEREGEX_OMIT, HERE_JSTOKEN, IDENTIFIER, INDENTABLE_CLOSERS, INDEXABLE, INVALID_ESCAPE, INVERSES, JSTOKEN, JS_KEYWORDS, LEADING_BLANK_LINE, LINE_BREAK, LINE_CONTINUER, Lexer, MATH, MULTI_DENT, NOT_REGEX, NUMBER, OPERATOR, POSSIBLY_DIVISION, REGEX, REGEX_FLAGS, REGEX_ILLEGAL, RELATION, RESERVED, Rewriter, SHIFT, SIMPLE_STRING_OMIT, STRICT_PROSCRIBED, STRING_DOUBLE, STRING_OMIT, STRING_SINGLE, STRING_START, TRAILING_BLANK_LINE, TRAILING_SPACES, UNARY, UNARY_MATH, VALID_FLAGS, WHITESPACE, compact, count, invertLiterate, isForFrom, isUnassignable, key, locationDataToString, ref, ref1, repeat, starts, throwSyntaxError, + var BOM, BOOL, CALLABLE, CODE, COFFEE_ALIASES, COFFEE_ALIAS_MAP, COFFEE_KEYWORDS, COMMENT, COMPARE, COMPOUND_ASSIGN, HERECOMMENT_ILLEGAL, HEREDOC_DOUBLE, HEREDOC_INDENT, HEREDOC_SINGLE, HEREGEX, HEREGEX_OMIT, HERE_JSTOKEN, IDENTIFIER, INDENTABLE_CLOSERS, INDEXABLE, INVALID_ESCAPE, INVERSES, JSTOKEN, JS_KEYWORDS, LEADING_BLANK_LINE, LINE_BREAK, LINE_CONTINUER, Lexer, MATH, MULTI_DENT, NOT_REGEX, NUMBER, OPERATOR, POSSIBLY_DIVISION, REGEX, REGEX_FLAGS, REGEX_ILLEGAL, RELATION, RESERVED, Rewriter, SHIFT, SIMPLE_STRING_OMIT, STRICT_PROSCRIBED, STRING_DOUBLE, STRING_OMIT, STRING_SINGLE, STRING_START, TRAILING_BLANK_LINE, TRAILING_SPACES, UNARY, UNARY_MATH, VALID_FLAGS, WHITESPACE, compact, count, invertLiterate, isForFrom, isUnassignable, key, locationDataToString, repeat, starts, throwSyntaxError, 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; }; - ref = require('./rewriter'), Rewriter = ref.Rewriter, INVERSES = ref.INVERSES; - - ref1 = require('./helpers'), count = ref1.count, starts = ref1.starts, compact = ref1.compact, repeat = ref1.repeat, invertLiterate = ref1.invertLiterate, locationDataToString = ref1.locationDataToString, throwSyntaxError = ref1.throwSyntaxError; + ({ + Rewriter: Rewriter, + INVERSES: INVERSES + } = require('./rewriter')); + + ({ + count: count, + starts: starts, + compact: compact, + repeat: repeat, + invertLiterate: invertLiterate, + locationDataToString: locationDataToString, + throwSyntaxError: throwSyntaxError + } = require('./helpers')); exports.Lexer = Lexer = class Lexer { tokenize(code, opts = {}) { @@ -64,7 +75,7 @@ } identifierToken() { - var alias, colon, colonOffset, id, idLength, input, match, poppedToken, prev, ref2, ref3, ref4, ref5, ref6, tag, tagToken; + var alias, colon, colonOffset, id, idLength, input, match, poppedToken, prev, ref, ref1, ref2, ref3, ref4, tag, tagToken; if (!(match = IDENTIFIER.exec(this.chunk))) { return 0; } @@ -82,10 +93,10 @@ if (id === 'as' && this.seenImport) { if (this.value() === '*') { this.tokens[this.tokens.length - 1][0] = 'IMPORT_ALL'; - } else if (ref2 = this.value(), indexOf.call(COFFEE_KEYWORDS, ref2) >= 0) { + } else if (ref = this.value(), indexOf.call(COFFEE_KEYWORDS, ref) >= 0) { this.tokens[this.tokens.length - 1][0] = 'IDENTIFIER'; } - if ((ref3 = this.tag()) === 'DEFAULT' || ref3 === 'IMPORT_ALL' || ref3 === 'IDENTIFIER') { + if ((ref1 = this.tag()) === 'DEFAULT' || ref1 === 'IMPORT_ALL' || ref1 === 'IDENTIFIER') { this.token('AS', id); return id.length; } @@ -98,11 +109,11 @@ this.token('DEFAULT', id); return id.length; } - ref4 = this.tokens, prev = ref4[ref4.length - 1]; - tag = colon || (prev != null) && (((ref5 = prev[0]) === '.' || ref5 === '?.' || ref5 === '::' || ref5 === '?::') || !prev.spaced && prev[0] === '@') ? 'PROPERTY' : 'IDENTIFIER'; + ref2 = this.tokens, prev = ref2[ref2.length - 1]; + tag = colon || (prev != null) && (((ref3 = prev[0]) === '.' || ref3 === '?.' || ref3 === '::' || ref3 === '?::') || !prev.spaced && prev[0] === '@') ? 'PROPERTY' : 'IDENTIFIER'; if (tag === 'IDENTIFIER' && (indexOf.call(JS_KEYWORDS, id) >= 0 || indexOf.call(COFFEE_KEYWORDS, id) >= 0) && !(this.exportSpecifierList && indexOf.call(COFFEE_KEYWORDS, id) >= 0)) { tag = id.toUpperCase(); - if (tag === 'WHEN' && (ref6 = this.tag(), indexOf.call(LINE_BREAK, ref6) >= 0)) { + if (tag === 'WHEN' && (ref4 = this.tag(), indexOf.call(LINE_BREAK, ref4) >= 0)) { tag = 'LEADING_WHEN'; } else if (tag === 'FOR') { this.seenFor = true; @@ -223,7 +234,7 @@ } stringToken() { - var $, attempt, delimiter, doc, end, heredoc, i, indent, indentRegex, match, quote, ref2, ref3, regex, token, tokens; + var $, attempt, delimiter, doc, end, heredoc, i, indent, indentRegex, match, quote, ref, regex, token, tokens; [quote] = STRING_START.exec(this.chunk) || []; if (!quote) { return 0; @@ -244,7 +255,10 @@ } })(); heredoc = quote.length === 3; - ref2 = this.matchWithInterpolations(regex, quote), tokens = ref2.tokens, end = ref2.index; + ({ + tokens: tokens, + index: end + } = this.matchWithInterpolations(regex, quote)); $ = tokens.length - 1; delimiter = quote.charAt(0); if (heredoc) { @@ -262,7 +276,7 @@ })()).join('#{}'); while (match = HEREDOC_INDENT.exec(doc)) { attempt = match[1]; - if (indent === null || (0 < (ref3 = attempt.length) && ref3 < indent.length)) { + if (indent === null || (0 < (ref = attempt.length) && ref < indent.length)) { indent = attempt; } } @@ -336,7 +350,7 @@ } regexToken() { - var body, closed, end, flags, index, match, origin, prev, ref2, ref3, ref4, regex, tokens; + var body, closed, end, flags, index, match, origin, prev, ref, ref1, ref2, regex, tokens; switch (false) { case !(match = REGEX_ILLEGAL.exec(this.chunk)): this.error(`regular expressions cannot begin with ${match[2]}`, { @@ -344,7 +358,10 @@ }); break; case !(match = this.matchWithInterpolations(HEREGEX, '///')): - tokens = match.tokens, index = match.index; + ({ + tokens: tokens, + index: index + } = match); break; case !(match = REGEX.exec(this.chunk)): [regex, body, closed] = match; @@ -353,13 +370,13 @@ offsetInChunk: 1 }); index = regex.length; - ref2 = this.tokens, prev = ref2[ref2.length - 1]; + ref = this.tokens, prev = ref[ref.length - 1]; if (prev) { - if (prev.spaced && (ref3 = prev[0], indexOf.call(CALLABLE, ref3) >= 0)) { + if (prev.spaced && (ref1 = prev[0], indexOf.call(CALLABLE, ref1) >= 0)) { if (!closed || POSSIBLY_DIVISION.test(regex)) { return 0; } - } else if (ref4 = prev[0], indexOf.call(NOT_REGEX, ref4) >= 0) { + } else if (ref2 = prev[0], indexOf.call(NOT_REGEX, ref2) >= 0) { return 0; } } @@ -469,7 +486,7 @@ } outdentToken(moveOut, noNewlines, outdentLength) { - var decreasedIndent, dent, lastIndent, ref2; + var decreasedIndent, dent, lastIndent, ref; decreasedIndent = this.indent - moveOut; while (moveOut > 0) { lastIndent = this.indents[this.indents.length - 1]; @@ -483,7 +500,7 @@ moveOut -= lastIndent; } else { dent = this.indents.pop() + this.outdebt; - if (outdentLength && (ref2 = this.chunk[outdentLength], indexOf.call(INDENTABLE_CLOSERS, ref2) >= 0)) { + if (outdentLength && (ref = this.chunk[outdentLength], indexOf.call(INDENTABLE_CLOSERS, ref) >= 0)) { decreasedIndent -= dent - moveOut; moveOut = dent; } @@ -508,11 +525,11 @@ } whitespaceToken() { - var match, nline, prev, ref2; + var match, nline, prev, ref; if (!((match = WHITESPACE.exec(this.chunk)) || (nline = this.chunk.charAt(0) === '\n'))) { return 0; } - ref2 = this.tokens, prev = ref2[ref2.length - 1]; + ref = this.tokens, prev = ref[ref.length - 1]; if (prev) { prev[match ? 'spaced' : 'newLine'] = true; } @@ -541,7 +558,7 @@ } literalToken() { - var match, message, origin, prev, ref2, ref3, ref4, ref5, ref6, skipToken, tag, token, value; + var match, message, origin, prev, ref, ref1, ref2, ref3, ref4, skipToken, tag, token, value; if (match = OPERATOR.exec(this.chunk)) { [value] = match; if (CODE.test(value)) { @@ -551,17 +568,17 @@ value = this.chunk.charAt(0); } tag = value; - ref2 = this.tokens, prev = ref2[ref2.length - 1]; + ref = this.tokens, prev = ref[ref.length - 1]; if (prev && indexOf.call(['=', ...COMPOUND_ASSIGN], value) >= 0) { skipToken = false; - if (value === '=' && ((ref3 = prev[1]) === '||' || ref3 === '&&') && !prev.spaced) { + if (value === '=' && ((ref1 = prev[1]) === '||' || ref1 === '&&') && !prev.spaced) { prev[0] = 'COMPOUND_ASSIGN'; prev[1] += '='; prev = this.tokens[this.tokens.length - 2]; skipToken = true; } if (prev && prev[0] !== 'PROPERTY') { - origin = (ref4 = prev.origin) != null ? ref4 : prev; + origin = (ref2 = prev.origin) != null ? ref2 : prev; message = isUnassignable(prev[1], origin[1]); if (message) { this.error(message, origin[2]); @@ -596,12 +613,12 @@ } else if (value === '?' && (prev != null ? prev.spaced : void 0)) { tag = 'BIN?'; } else if (prev && !prev.spaced) { - if (value === '(' && (ref5 = prev[0], indexOf.call(CALLABLE, ref5) >= 0)) { + if (value === '(' && (ref3 = prev[0], indexOf.call(CALLABLE, ref3) >= 0)) { if (prev[0] === '?') { prev[0] = 'FUNC_EXIST'; } tag = 'CALL_START'; - } else if (value === '[' && (ref6 = prev[0], indexOf.call(INDEXABLE, ref6) >= 0)) { + } else if (value === '[' && (ref4 = prev[0], indexOf.call(INDEXABLE, ref4) >= 0)) { tag = 'INDEX_START'; switch (prev[0]) { case '?': @@ -634,7 +651,9 @@ return this; } stack = []; - tokens = this.tokens; + ({ + tokens: tokens + } = this); i = tokens.length; tokens[--i][0] = 'PARAM_END'; while (tok = tokens[--i]) { @@ -662,7 +681,7 @@ } matchWithInterpolations(regex, delimiter) { - var close, column, firstToken, index, lastToken, line, nested, offsetInChunk, open, ref2, ref3, str, strPart, tokens; + var close, column, firstToken, index, lastToken, line, nested, offsetInChunk, open, ref, str, strPart, tokens; tokens = []; offsetInChunk = delimiter.length; if (this.chunk.slice(0, offsetInChunk) !== delimiter) { @@ -682,17 +701,20 @@ break; } [line, column] = this.getLineAndColumnFromChunk(offsetInChunk + 1); - ref2 = new Lexer().tokenize(str.slice(1), { + ({ + tokens: nested, + index: index + } = new Lexer().tokenize(str.slice(1), { line: line, column: column, untilBalanced: true - }), nested = ref2.tokens, index = ref2.index; + })); index += 1; open = nested[0], close = nested[nested.length - 1]; open[0] = open[1] = '('; close[0] = close[1] = ')'; close.origin = ['', 'end of interpolation', close[2]]; - if (((ref3 = nested[1]) != null ? ref3[0] : void 0) === 'TERMINATOR') { + if (((ref = nested[1]) != null ? ref[0] : void 0) === 'TERMINATOR') { nested.splice(1, 1); } tokens.push(['TOKENS', nested]); @@ -787,13 +809,13 @@ } pair(tag) { - var lastIndent, prev, ref2, ref3, wanted; - ref2 = this.ends, prev = ref2[ref2.length - 1]; + var lastIndent, prev, ref, ref1, wanted; + ref = this.ends, prev = ref[ref.length - 1]; if (tag !== (wanted = prev != null ? prev.tag : void 0)) { if ('OUTDENT' !== wanted) { this.error(`unmatched ${tag}`); } - ref3 = this.indents, lastIndent = ref3[ref3.length - 1]; + ref1 = this.indents, lastIndent = ref1[ref1.length - 1]; this.outdentToken(lastIndent, true); return this.pair(tag); } @@ -801,7 +823,7 @@ } getLineAndColumnFromChunk(offset) { - var column, lastLine, lineCount, ref2, string; + var column, lastLine, lineCount, ref, string; if (offset === 0) { return [this.chunkLine, this.chunkColumn]; } @@ -813,7 +835,7 @@ lineCount = count(string, '\n'); column = this.chunkColumn; if (lineCount > 0) { - ref2 = string.split('\n'), lastLine = ref2[ref2.length - 1]; + ref = string.split('\n'), lastLine = ref[ref.length - 1]; column = lastLine.length; } else { column += string.length; @@ -842,20 +864,20 @@ } tag() { - var ref2, token; - ref2 = this.tokens, token = ref2[ref2.length - 1]; + var ref, token; + ref = this.tokens, token = ref[ref.length - 1]; return token != null ? token[0] : void 0; } value() { - var ref2, token; - ref2 = this.tokens, token = ref2[ref2.length - 1]; + var ref, token; + ref = this.tokens, token = ref[ref.length - 1]; return token != null ? token[1] : void 0; } unfinished() { - var ref2; - return LINE_CONTINUER.test(this.chunk) || ((ref2 = this.tag()) === '\\' || ref2 === '.' || ref2 === '?.' || ref2 === '?::' || ref2 === 'UNARY' || ref2 === 'MATH' || ref2 === 'UNARY_MATH' || ref2 === '+' || ref2 === '-' || ref2 === '**' || ref2 === 'SHIFT' || ref2 === 'RELATION' || ref2 === 'COMPARE' || ref2 === '&' || ref2 === '^' || ref2 === '|' || ref2 === '&&' || ref2 === '||' || ref2 === 'BIN?' || ref2 === 'THROW' || ref2 === 'EXTENDS'); + var ref; + return LINE_CONTINUER.test(this.chunk) || ((ref = this.tag()) === '\\' || ref === '.' || ref === '?.' || ref === '?::' || ref === 'UNARY' || ref === 'MATH' || ref === 'UNARY_MATH' || ref === '+' || ref === '-' || ref === '**' || ref === 'SHIFT' || ref === 'RELATION' || ref === 'COMPARE' || ref === '&' || ref === '^' || ref === '|' || ref === '&&' || ref === '||' || ref === 'BIN?' || ref === 'THROW' || ref === 'EXTENDS'); } formatString(str) { @@ -867,7 +889,7 @@ } validateEscapes(str, options = {}) { - var before, hex, invalidEscape, match, message, octal, ref2, unicode; + var before, hex, invalidEscape, match, message, octal, ref, unicode; match = INVALID_ESCAPE.exec(str); if (!match) { return; @@ -879,7 +901,7 @@ message = octal ? "octal escape sequences are not allowed" : "invalid escape sequence"; invalidEscape = `\\${octal || hex || unicode}`; return this.error(`${message} ${invalidEscape}`, { - offset: ((ref2 = options.offsetInChunk) != null ? ref2 : 0) + match.index + before.length, + offset: ((ref = options.offsetInChunk) != null ? ref : 0) + match.index + before.length, length: invalidEscape.length }); } @@ -922,11 +944,11 @@ } error(message, options = {}) { - var first_column, first_line, location, ref2, ref3; - location = 'first_line' in options ? options : ([first_line, first_column] = this.getLineAndColumnFromChunk((ref2 = options.offset) != null ? ref2 : 0), { + var first_column, first_line, location, ref, ref1; + location = 'first_line' in options ? options : ([first_line, first_column] = this.getLineAndColumnFromChunk((ref = options.offset) != null ? ref : 0), { first_line: first_line, first_column: first_column, - last_column: first_column + ((ref3 = options.length) != null ? ref3 : 1) - 1 + last_column: first_column + ((ref1 = options.length) != null ? ref1 : 1) - 1 }); return throwSyntaxError(message, location); } @@ -949,7 +971,7 @@ exports.isUnassignable = isUnassignable; isForFrom = function(prev) { - var ref2; + var ref; if (prev[0] === 'IDENTIFIER') { if (prev[1] === 'from') { prev[1][0] = 'IDENTIFIER'; @@ -958,7 +980,7 @@ return true; } else if (prev[0] === 'FOR') { return false; - } else if ((ref2 = prev[1]) === '{' || ref2 === '[' || ref2 === ',' || ref2 === ':') { + } else if ((ref = prev[1]) === '{' || ref === '[' || ref === ',' || ref === ':') { return false; } else { return true; diff --git a/lib/coffeescript/nodes.js b/lib/coffeescript/nodes.js index 8fddf304f3..05f3efc4cd 100644 --- a/lib/coffeescript/nodes.js +++ b/lib/coffeescript/nodes.js @@ -1,16 +1,33 @@ // Generated by CoffeeScript 2.0.0-alpha1 (function() { - var Access, Arr, Assign, AwaitReturn, Base, Block, BooleanLiteral, Call, Class, Code, CodeFragment, Comment, ExecutableClassBody, Existence, Expansion, ExportAllDeclaration, ExportDeclaration, ExportDefaultDeclaration, ExportNamedDeclaration, ExportSpecifier, ExportSpecifierList, Extends, For, HoistTarget, IdentifierLiteral, If, ImportClause, ImportDeclaration, ImportDefaultSpecifier, ImportNamespaceSpecifier, ImportSpecifier, ImportSpecifierList, In, Index, InfinityLiteral, JS_FORBIDDEN, LEVEL_ACCESS, LEVEL_COND, LEVEL_LIST, LEVEL_OP, LEVEL_PAREN, LEVEL_TOP, Literal, ModuleDeclaration, ModuleSpecifier, ModuleSpecifierList, NEGATE, NO, NaNLiteral, NullLiteral, NumberLiteral, Obj, Op, Param, Parens, PassthroughLiteral, PropertyName, Range, RegexLiteral, RegexWithInterpolations, Return, SIMPLENUM, Scope, Slice, Splat, StatementLiteral, StringLiteral, StringWithInterpolations, Super, SuperCall, Switch, TAB, THIS, TaggedTemplateCall, ThisLiteral, Throw, Try, UTILITIES, UndefinedLiteral, Value, While, YES, YieldReturn, addLocationDataFn, compact, del, ends, extend, flatten, fragmentsToText, isLiteralArguments, isLiteralThis, isUnassignable, locationDataToString, merge, multident, ref1, ref2, shouldCacheOrIsAssignable, some, starts, throwSyntaxError, unfoldSoak, utility, + var Access, Arr, Assign, AwaitReturn, Base, Block, BooleanLiteral, Call, Class, Code, CodeFragment, Comment, ExecutableClassBody, Existence, Expansion, ExportAllDeclaration, ExportDeclaration, ExportDefaultDeclaration, ExportNamedDeclaration, ExportSpecifier, ExportSpecifierList, Extends, For, HoistTarget, IdentifierLiteral, If, ImportClause, ImportDeclaration, ImportDefaultSpecifier, ImportNamespaceSpecifier, ImportSpecifier, ImportSpecifierList, In, Index, InfinityLiteral, JS_FORBIDDEN, LEVEL_ACCESS, LEVEL_COND, LEVEL_LIST, LEVEL_OP, LEVEL_PAREN, LEVEL_TOP, Literal, ModuleDeclaration, ModuleSpecifier, ModuleSpecifierList, NEGATE, NO, NaNLiteral, NullLiteral, NumberLiteral, Obj, Op, Param, Parens, PassthroughLiteral, PropertyName, Range, RegexLiteral, RegexWithInterpolations, Return, SIMPLENUM, Scope, Slice, Splat, StatementLiteral, StringLiteral, StringWithInterpolations, Super, SuperCall, Switch, TAB, THIS, TaggedTemplateCall, ThisLiteral, Throw, Try, UTILITIES, UndefinedLiteral, Value, While, YES, YieldReturn, addLocationDataFn, compact, del, ends, extend, flatten, fragmentsToText, isLiteralArguments, isLiteralThis, isUnassignable, locationDataToString, merge, multident, shouldCacheOrIsAssignable, some, starts, throwSyntaxError, unfoldSoak, utility, 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; Error.stackTraceLimit = 2e308; - Scope = require('./scope').Scope; - - ref1 = require('./lexer'), isUnassignable = ref1.isUnassignable, JS_FORBIDDEN = ref1.JS_FORBIDDEN; - - ref2 = require('./helpers'), compact = ref2.compact, flatten = ref2.flatten, extend = ref2.extend, merge = ref2.merge, del = ref2.del, starts = ref2.starts, ends = ref2.ends, some = ref2.some, addLocationDataFn = ref2.addLocationDataFn, locationDataToString = ref2.locationDataToString, throwSyntaxError = ref2.throwSyntaxError; + ({ + Scope: Scope + } = require('./scope')); + + ({ + isUnassignable: isUnassignable, + JS_FORBIDDEN: JS_FORBIDDEN + } = require('./lexer')); + + ({ + compact: compact, + flatten: flatten, + extend: extend, + merge: merge, + del: del, + starts: starts, + ends: ends, + some: some, + addLocationDataFn: addLocationDataFn, + locationDataToString: locationDataToString, + throwSyntaxError: throwSyntaxError + } = require('./helpers')); exports.extend = extend; @@ -35,10 +52,10 @@ exports.CodeFragment = CodeFragment = class CodeFragment { constructor(parent, code) { - var ref3; + var ref1; this.code = `${code}`; this.locationData = parent != null ? parent.locationData : void 0; - this.type = (parent != null ? (ref3 = parent.constructor) != null ? ref3.name : void 0 : void 0) || 'unknown'; + this.type = (parent != null ? (ref1 = parent.constructor) != null ? ref1.name : void 0 : void 0) || 'unknown'; } toString() { @@ -82,7 +99,7 @@ } compileClosure(o) { - var args, argumentsNode, func, jumpNode, meth, parts, ref3, ref4; + var args, argumentsNode, func, jumpNode, meth, parts, ref1, ref2; if (jumpNode = this.jumps()) { jumpNode.error('cannot use a pure statement in an expression'); } @@ -105,11 +122,11 @@ } parts = (new Call(func, args)).compileNode(o); switch (false) { - case !(func.isGenerator || ((ref3 = func.base) != null ? ref3.isGenerator : void 0)): + case !(func.isGenerator || ((ref1 = func.base) != null ? ref1.isGenerator : void 0)): parts.unshift(this.makeCode("(yield* ")); parts.push(this.makeCode(")")); break; - case !(func.isAsync || ((ref4 = func.base) != null ? ref4.isAsync : void 0)): + case !(func.isAsync || ((ref2 = func.base) != null ? ref2.isAsync : void 0)): parts.unshift(this.makeCode("(await ")); parts.push(this.makeCode(")")); } @@ -198,17 +215,17 @@ } eachChild(func) { - var attr, child, j, k, len1, len2, ref3, ref4; + var attr, child, j, k, len1, len2, ref1, ref2; if (!this.children) { return this; } - ref3 = this.children; - for (j = 0, len1 = ref3.length; j < len1; j++) { - attr = ref3[j]; + ref1 = this.children; + for (j = 0, len1 = ref1.length; j < len1; j++) { + attr = ref1[j]; if (this[attr]) { - ref4 = flatten([this[attr]]); - for (k = 0, len2 = ref4.length; k < len2; k++) { - child = ref4[k]; + ref2 = flatten([this[attr]]); + for (k = 0, len2 = ref2.length; k < len2; k++) { + child = ref2[k]; if (func(child) === false) { return this; } @@ -229,19 +246,19 @@ } replaceInContext(match, replacement) { - var attr, child, children, i, j, k, len1, len2, ref3, ref4; + var attr, child, children, i, j, k, len1, len2, ref1, ref2; if (!this.children) { return false; } - ref3 = this.children; - for (j = 0, len1 = ref3.length; j < len1; j++) { - attr = ref3[j]; + ref1 = this.children; + for (j = 0, len1 = ref1.length; j < len1; j++) { + attr = ref1[j]; if (children = this[attr]) { if (Array.isArray(children)) { for (i = k = 0, len2 = children.length; k < len2; i = ++k) { child = children[i]; if (match(child)) { - [].splice.apply(children, [i, i - i + 1].concat(ref4 = replacement(child, this))), ref4; + [].splice.apply(children, [i, i - i + 1].concat(ref2 = replacement(child, this))), ref2; return true; } else { if (child.replaceInContext(match, replacement)) { @@ -337,11 +354,11 @@ exports.HoistTarget = HoistTarget = class HoistTarget extends Base { static expand(fragments) { - var fragment, i, j, ref3; + var fragment, i, j, ref1; for (i = j = fragments.length - 1; j >= 0; i = j += -1) { fragment = fragments[i]; if (fragment.fragments) { - [].splice.apply(fragments, [i, i - i + 1].concat(ref3 = this.expand(fragment.fragments))), ref3; + [].splice.apply(fragments, [i, i - i + 1].concat(ref1 = this.expand(fragment.fragments))), ref1; } } return fragments; @@ -414,10 +431,10 @@ } isStatement(o) { - var exp, j, len1, ref3; - ref3 = this.expressions; - for (j = 0, len1 = ref3.length; j < len1; j++) { - exp = ref3[j]; + var exp, j, len1, ref1; + ref1 = this.expressions; + for (j = 0, len1 = ref1.length; j < len1; j++) { + exp = ref1[j]; if (exp.isStatement(o)) { return true; } @@ -426,10 +443,10 @@ } jumps(o) { - var exp, j, jumpNode, len1, ref3; - ref3 = this.expressions; - for (j = 0, len1 = ref3.length; j < len1; j++) { - exp = ref3[j]; + var exp, j, jumpNode, len1, ref1; + ref1 = this.expressions; + for (j = 0, len1 = ref1.length; j < len1; j++) { + exp = ref1[j]; if (jumpNode = exp.jumps(o)) { return jumpNode; } @@ -461,13 +478,13 @@ } compileNode(o) { - var answer, compiledNodes, fragments, index, j, len1, node, ref3, top; + var answer, compiledNodes, fragments, index, j, len1, node, ref1, top; this.tab = o.indent; top = o.level === LEVEL_TOP; compiledNodes = []; - ref3 = this.expressions; - for (index = j = 0, len1 = ref3.length; j < len1; index = ++j) { - node = ref3[index]; + ref1 = this.expressions; + for (index = j = 0, len1 = ref1.length; j < len1; index = ++j) { + node = ref1[index]; node = node.unwrapAll(); node = node.unfoldSoak(o) || node; if (node instanceof Block) { @@ -506,24 +523,24 @@ } compileRoot(o) { - var exp, fragments, i, j, len1, name, prelude, preludeExps, ref3, ref4, rest; + var exp, fragments, i, j, len1, name, prelude, preludeExps, ref1, ref2, rest; o.indent = o.bare ? '' : TAB; o.level = LEVEL_TOP; this.spaced = true; - o.scope = new Scope(null, this, null, (ref3 = o.referencedVars) != null ? ref3 : []); - ref4 = o.locals || []; - for (j = 0, len1 = ref4.length; j < len1; j++) { - name = ref4[j]; + o.scope = new Scope(null, this, null, (ref1 = o.referencedVars) != null ? ref1 : []); + ref2 = o.locals || []; + for (j = 0, len1 = ref2.length; j < len1; j++) { + name = ref2[j]; o.scope.parameter(name); } prelude = []; if (!o.bare) { preludeExps = (function() { - var k, len2, ref5, results; - ref5 = this.expressions; + var k, len2, ref3, results; + ref3 = this.expressions; results = []; - for (i = k = 0, len2 = ref5.length; k < len2; i = ++k) { - exp = ref5[i]; + for (i = k = 0, len2 = ref3.length; k < len2; i = ++k) { + exp = ref3[i]; if (!(exp.unwrap() instanceof Comment)) { break; } @@ -550,12 +567,12 @@ } compileWithDeclarations(o) { - var assigns, declars, exp, fragments, i, j, len1, post, ref3, rest, scope, spaced; + var assigns, declars, exp, fragments, i, j, len1, post, ref1, rest, scope, spaced; fragments = []; post = []; - ref3 = this.expressions; - for (i = j = 0, len1 = ref3.length; j < len1; i = ++j) { - exp = ref3[i]; + ref1 = this.expressions; + for (i = j = 0, len1 = ref1.length; j < len1; i = ++j) { + exp = ref1[i]; exp = exp.unwrap(); if (!(exp instanceof Comment || exp instanceof Literal)) { break; @@ -571,7 +588,9 @@ this.expressions = rest; } post = this.compileNode(o); - scope = o.scope; + ({ + scope: scope + } = o); if (scope.expressions === this) { declars = o.scope.hasDeclarations(); assigns = scope.hasAssignments; @@ -725,8 +744,8 @@ } compileNode(o) { - var code, ref3; - code = ((ref3 = o.scope.method) != null ? ref3.bound : void 0) ? o.scope.method.context : this.value; + var code, ref1; + code = ((ref1 = o.scope.method) != null ? ref1.bound : void 0) ? o.scope.method.context : this.value; return [this.makeCode(code)]; } @@ -760,8 +779,8 @@ } compileToFragments(o, level) { - var expr, ref3; - expr = (ref3 = this.expression) != null ? ref3.makeReturn() : void 0; + var expr, ref1; + expr = (ref1 = this.expression) != null ? ref1.makeReturn() : void 0; if (expr && !(expr instanceof Return)) { return expr.compileToFragments(o, level); } else { @@ -883,10 +902,10 @@ } isAtomic() { - var j, len1, node, ref3; - ref3 = this.properties.concat(this.base); - for (j = 0, len1 = ref3.length; j < len1; j++) { - node = ref3[j]; + var j, len1, node, ref1; + ref1 = this.properties.concat(this.base); + for (j = 0, len1 = ref1.length; j < len1; j++) { + node = ref1[j]; if (node.soak || node instanceof Call) { return false; } @@ -918,14 +937,14 @@ } isSplice() { - var lastProp, ref3; - ref3 = this.properties, lastProp = ref3[ref3.length - 1]; + var lastProp, ref1; + ref1 = this.properties, lastProp = ref1[ref1.length - 1]; return lastProp instanceof Slice; } looksStatic(className) { - var ref3; - return (this["this"] || this.base instanceof ThisLiteral || this.base.value === className) && this.properties.length === 1 && ((ref3 = this.properties[0].name) != null ? ref3.value : void 0) !== 'prototype'; + var ref1; + return (this["this"] || this.base instanceof ThisLiteral || this.base.value === className) && this.properties.length === 1 && ((ref1 = this.properties[0].name) != null ? ref1.value : void 0) !== 'prototype'; } unwrap() { @@ -937,8 +956,8 @@ } cacheReference(o) { - var base, bref, name, nref, ref3; - ref3 = this.properties, name = ref3[ref3.length - 1]; + var base, bref, name, nref, ref1; + ref1 = this.properties, name = ref1[ref1.length - 1]; if (this.properties.length < 2 && !this.base.shouldCache() && !(name != null ? name.shouldCache() : void 0)) { return [this, this]; } @@ -975,14 +994,14 @@ unfoldSoak(o) { return this.unfoldedSoak != null ? this.unfoldedSoak : this.unfoldedSoak = (() => { - var fst, i, ifn, j, len1, prop, ref, ref3, snd; + var fst, i, ifn, j, len1, prop, ref, ref1, snd; if (ifn = this.base.unfoldSoak(o)) { ifn.body.properties.push(...this.properties); return ifn; } - ref3 = this.properties; - for (i = j = 0, len1 = ref3.length; j < len1; i = ++j) { - prop = ref3[i]; + ref1 = this.properties; + for (i = j = 0, len1 = ref1.length; j < len1; i = ++j) { + prop = ref1[i]; if (!prop.soak) { continue; } @@ -1061,11 +1080,11 @@ } updateLocationDataIfMissing(locationData) { - var base, ref3; + var base, ref1; if (this.locationData && this.needsUpdatedStartLocation) { this.locationData.first_line = locationData.first_line; this.locationData.first_column = locationData.first_column; - base = ((ref3 = this.variable) != null ? ref3.base : void 0) || this.variable; + base = ((ref1 = this.variable) != null ? ref1.base : void 0) || this.variable; if (base.needsUpdatedStartLocation) { this.variable.locationData.first_line = locationData.first_line; this.variable.locationData.first_column = locationData.first_column; @@ -1077,8 +1096,8 @@ } newInstance() { - var base, ref3; - base = ((ref3 = this.variable) != null ? ref3.base : void 0) || this.variable; + var base, ref1; + base = ((ref1 = this.variable) != null ? ref1.base : void 0) || this.variable; if (base instanceof Call && !base.isNew) { base.newInstance(); } else { @@ -1089,7 +1108,7 @@ } unfoldSoak(o) { - var call, ifn, j, left, len1, list, ref3, rite; + var call, ifn, j, left, len1, list, ref1, rite; if (this.soak) { if (this.variable instanceof Super) { left = new Literal(this.variable.compile(o)); @@ -1126,9 +1145,9 @@ break; } } - ref3 = list.reverse(); - for (j = 0, len1 = ref3.length; j < len1; j++) { - call = ref3[j]; + ref1 = list.reverse(); + for (j = 0, len1 = ref1.length; j < len1; j++) { + call = ref1[j]; if (ifn) { if (call.variable instanceof Call) { call.variable = ifn; @@ -1142,14 +1161,14 @@ } compileNode(o) { - var arg, argIndex, compiledArgs, fragments, j, len1, ref3, ref4; - if ((ref3 = this.variable) != null) { - ref3.front = this.front; + var arg, argIndex, compiledArgs, fragments, j, len1, ref1, ref2; + if ((ref1 = this.variable) != null) { + ref1.front = this.front; } compiledArgs = []; - ref4 = this.args; - for (argIndex = j = 0, len1 = ref4.length; j < len1; argIndex = ++j) { - arg = ref4[argIndex]; + ref2 = this.args; + for (argIndex = j = 0, len1 = ref2.length; j < len1; argIndex = ++j) { + arg = ref2[argIndex]; if (argIndex) { compiledArgs.push(this.makeCode(", ")); } @@ -1178,13 +1197,13 @@ exports.SuperCall = SuperCall = (function() { class SuperCall extends Call { isStatement(o) { - var ref3; - return ((ref3 = this.expressions) != null ? ref3.length : void 0) && o.level === LEVEL_TOP; + var ref1; + return ((ref1 = this.expressions) != null ? ref1.length : void 0) && o.level === LEVEL_TOP; } compileNode(o) { - var ref, ref3, replacement, superCall; - if (!((ref3 = this.expressions) != null ? ref3.length : void 0)) { + var ref, ref1, replacement, superCall; + if (!((ref1 = this.expressions) != null ? ref1.length : void 0)) { return super.compileNode(o); } superCall = new Literal(fragmentsToText(super.compileNode(o))); @@ -1220,7 +1239,10 @@ } this.inCtor = !!method.ctor; if (!(this.inCtor || (this.accessor != null))) { - name = method.name, variable = method.variable; + ({ + name: name, + variable: variable + } = method); if (name.shouldCache() || (name instanceof Index && name.index.isAssignable())) { nref = new IdentifierLiteral(o.scope.parent.freeVariable('name')); name.index = new Assign(nref, name.index); @@ -1288,11 +1310,11 @@ } compileToFragments(o) { - var name, node, ref3; + var name, node, ref1; name = this.name.compileToFragments(o); node = this.name.unwrap(); if (node instanceof PropertyName) { - if (ref3 = node.value, indexOf.call(JS_FORBIDDEN, ref3) >= 0) { + if (ref1 = node.value, indexOf.call(JS_FORBIDDEN, ref1) >= 0) { return [this.makeCode('["'), ...name, this.makeCode('"]')]; } else { return [this.makeCode('.'), ...name]; @@ -1393,12 +1415,12 @@ } compileArray(o) { - var args, body, cond, hasArgs, i, idt, j, known, post, pre, range, ref3, ref4, result, results, vars; + var args, body, cond, hasArgs, i, idt, j, known, post, pre, range, ref1, ref2, result, results, vars; known = (this.fromNum != null) && (this.toNum != null); if (known && Math.abs(this.fromNum - this.toNum) <= 20) { range = (function() { results = []; - for (var j = ref3 = this.fromNum, ref4 = this.toNum; ref3 <= ref4 ? j <= ref4 : j >= ref4; ref3 <= ref4 ? j++ : j--){ results.push(j); } + for (var j = ref1 = this.fromNum, ref2 = this.toNum; ref1 <= ref2 ? j <= ref2 : j >= ref2; ref1 <= ref2 ? j++ : j--){ results.push(j); } return results; }).apply(this); if (this.exclusive) { @@ -1446,8 +1468,11 @@ } compileNode(o) { - var compiled, compiledText, from, fromCompiled, ref3, to, toStr; - ref3 = this.range, to = ref3.to, from = ref3.from; + var compiled, compiledText, from, fromCompiled, to, toStr; + ({ + to: to, + from: from + } = this.range); fromCompiled = from && from.compileToFragments(o, LEVEL_PAREN) || [this.makeCode('0')]; if (to) { compiled = to.compileToFragments(o, LEVEL_PAREN); @@ -1475,6 +1500,21 @@ this.objects = this.properties = props || []; } + isAssignable() { + var j, len1, prop, ref1; + ref1 = this.properties; + for (j = 0, len1 = ref1.length; j < len1; j++) { + prop = ref1[j]; + if (prop instanceof Assign) { + prop = prop.value; + } + if (!prop.isAssignable()) { + return false; + } + } + return true; + } + compileNode(o) { var answer, i, idt, indent, j, join, k, key, lastNoncom, len1, len2, node, prop, props, value; props = this.properties; @@ -1535,10 +1575,10 @@ } assigns(name) { - var j, len1, prop, ref3; - ref3 = this.properties; - for (j = 0, len1 = ref3.length; j < len1; j++) { - prop = ref3[j]; + var j, len1, prop, ref1; + ref1 = this.properties; + for (j = 0, len1 = ref1.length; j < len1; j++) { + prop = ref1[j]; if (prop.assigns(name)) { return true; } @@ -1546,6 +1586,21 @@ return false; } + eachName(iterator) { + var j, len1, prop, ref1, results; + ref1 = this.properties; + results = []; + for (j = 0, len1 = ref1.length; j < len1; j++) { + prop = ref1[j]; + if (prop instanceof Assign) { + prop = prop.value; + } + prop = prop.unwrapAll(); + results.push(prop.eachName(iterator)); + } + return results; + } + }; Obj.prototype.children = ['properties']; @@ -1562,13 +1617,13 @@ } isAssignable() { - var i, j, len1, obj, ref3; + var i, j, len1, obj, ref1; if (!this.objects.length) { return false; } - ref3 = this.objects; - for (i = j = 0, len1 = ref3.length; j < len1; i = ++j) { - obj = ref3[i]; + ref1 = this.objects; + for (i = j = 0, len1 = ref1.length; j < len1; i = ++j) { + obj = ref1[i]; if (obj instanceof Splat && i + 1 !== this.objects.length) { return false; } @@ -1587,11 +1642,11 @@ o.indent += TAB; answer = []; compiledObjs = (function() { - var j, len1, ref3, results; - ref3 = this.objects; + var j, len1, ref1, results; + ref1 = this.objects; results = []; - for (j = 0, len1 = ref3.length; j < len1; j++) { - obj = ref3[j]; + for (j = 0, len1 = ref1.length; j < len1; j++) { + obj = ref1[j]; results.push(obj.compileToFragments(o, LEVEL_LIST)); } return results; @@ -1614,10 +1669,10 @@ } assigns(name) { - var j, len1, obj, ref3; - ref3 = this.objects; - for (j = 0, len1 = ref3.length; j < len1; j++) { - obj = ref3[j]; + var j, len1, obj, ref1; + ref1 = this.objects; + for (j = 0, len1 = ref1.length; j < len1; j++) { + obj = ref1[j]; if (obj.assigns(name)) { return true; } @@ -1626,11 +1681,11 @@ } eachName(iterator) { - var j, len1, obj, ref3, results; - ref3 = this.objects; + var j, len1, obj, ref1, results; + ref1 = this.objects; results = []; - for (j = 0, len1 = ref3.length; j < len1; j++) { - obj = ref3[j]; + for (j = 0, len1 = ref1.length; j < len1; j++) { + obj = ref1[j]; obj = obj.unwrapAll(); results.push(obj.eachName(iterator)); } @@ -1683,14 +1738,14 @@ } compileClassDeclaration(o) { - var ref3, result; + var ref1, result; if (this.externalCtor || this.boundMethods.length) { if (this.ctor == null) { this.ctor = this.makeDefaultConstructor(); } } - if ((ref3 = this.ctor) != null) { - ref3.noReturn = true; + if ((ref1 = this.ctor) != null) { + ref1.noReturn = true; } if (this.boundMethods.length) { this.proxyBoundMethods(o); @@ -1716,11 +1771,11 @@ } determineName() { - var message, name, node, ref3, tail; + var message, name, node, ref1, tail; if (!this.variable) { return null; } - ref3 = this.variable.properties, tail = ref3[ref3.length - 1]; + ref1 = this.variable.properties, tail = ref1[ref1.length - 1]; node = tail ? tail instanceof Access && tail.name : this.variable.base; if (!(node instanceof IdentifierLiteral || node instanceof PropertyName)) { return null; @@ -1740,18 +1795,22 @@ } walkBody() { - var assign, end, executableBody, expression, expressions, exprs, i, initializer, initializerExpression, j, k, len1, len2, method, properties, pushSlice, ref3, start; + var assign, end, executableBody, expression, expressions, exprs, i, initializer, initializerExpression, j, k, len1, len2, method, properties, pushSlice, ref1, start; this.ctor = null; this.boundMethods = []; executableBody = null; initializer = []; - expressions = this.body.expressions; + ({ + expressions: expressions + } = this.body); i = 0; - ref3 = expressions.slice(); - for (j = 0, len1 = ref3.length; j < len1; j++) { - expression = ref3[j]; + ref1 = expressions.slice(); + for (j = 0, len1 = ref1.length; j < len1; j++) { + expression = ref1[j]; if (expression instanceof Value && expression.isObject(true)) { - properties = expression.base.properties; + ({ + properties: properties + } = expression.base); exprs = []; end = 0; start = 0; @@ -1839,7 +1898,10 @@ addInitializerMethod(assign) { var method, methodName, variable; - variable = assign.variable, method = assign.value; + ({ + variable: variable, + value: method + } = assign); method.isMethod = true; method.isStatic = variable.looksStatic(this.name); if (method.isStatic) { @@ -1877,11 +1939,11 @@ proxyBoundMethods(o) { var name; this.ctor.thisAssignments = (function() { - var j, ref3, results; - ref3 = this.boundMethods; + var j, ref1, results; + ref1 = this.boundMethods; results = []; - for (j = ref3.length - 1; j >= 0; j += -1) { - name = ref3[j]; + for (j = ref1.length - 1; j >= 0; j += -1) { + name = ref1[j]; name = new Value(new ThisLiteral, [name]).compile(o); results.push(new Literal(`${name} = ${utility('bind', o)}(${name}, this)`)); } @@ -1907,14 +1969,14 @@ } compileNode(o) { - var args, argumentsNode, directives, externalCtor, ident, jumpNode, klass, params, parent, ref3, wrapper; + var args, argumentsNode, directives, externalCtor, ident, jumpNode, klass, params, parent, ref1, wrapper; if (jumpNode = this.body.jumps()) { jumpNode.error('Class bodies cannot contain pure statements'); } if (argumentsNode = this.body.contains(isLiteralArguments)) { argumentsNode.error("Class bodies shouldn't reference arguments"); } - this.name = (ref3 = this["class"].name) != null ? ref3 : this.defaultClassVariableName; + this.name = (ref1 = this["class"].name) != null ? ref1 : this.defaultClassVariableName; directives = this.walkBody(); this.setContext(); ident = new IdentifierLiteral(this.name); @@ -1962,15 +2024,15 @@ } } this.traverseChildren(false, (child) => { - var cont, i, j, len1, node, ref3; + var cont, i, j, len1, node, ref1; if (child instanceof Class || child instanceof HoistTarget) { return false; } cont = true; if (child instanceof Block) { - ref3 = child.expressions; - for (i = j = 0, len1 = ref3.length; j < len1; i = ++j) { - node = ref3[i]; + ref1 = child.expressions; + for (i = j = 0, len1 = ref1.length; j < len1; i = ++j) { + node = ref1[i]; if (node instanceof Value && node.isObject(true)) { cont = false; child.expressions[i] = this.addProperties(node.base.properties); @@ -2075,7 +2137,7 @@ exports.ImportDeclaration = ImportDeclaration = class ImportDeclaration extends ModuleDeclaration { compileNode(o) { - var code, ref3; + var code, ref1; this.checkScope(o, 'import'); o.importedSymbols = []; code = []; @@ -2083,7 +2145,7 @@ if (this.clause != null) { code.push(...this.clause.compileNode(o)); } - if (((ref3 = this.source) != null ? ref3.value : void 0) != null) { + if (((ref1 = this.source) != null ? ref1.value : void 0) != null) { if (this.clause !== null) { code.push(this.makeCode(' from ')); } @@ -2128,7 +2190,7 @@ exports.ExportDeclaration = ExportDeclaration = class ExportDeclaration extends ModuleDeclaration { compileNode(o) { - var code, ref3; + var code, ref1; this.checkScope(o, 'export'); code = []; code.push(this.makeCode(`${this.tab}export `)); @@ -2147,7 +2209,7 @@ } else { code = code.concat(this.clause.compileNode(o)); } - if (((ref3 = this.source) != null ? ref3.value : void 0) != null) { + if (((ref1 = this.source) != null ? ref1.value : void 0) != null) { code.push(this.makeCode(` from ${this.source.value}`)); } code.push(this.makeCode(';')); @@ -2174,11 +2236,11 @@ code = []; o.indent += TAB; compiledList = (function() { - var j, len1, ref3, results; - ref3 = this.specifiers; + var j, len1, ref1, results; + ref1 = this.specifiers; results = []; - for (j = 0, len1 = ref3.length; j < len1; j++) { - specifier = ref3[j]; + for (j = 0, len1 = ref1.length; j < len1; j++) { + specifier = ref1[j]; results.push(specifier.compileToFragments(o, LEVEL_LIST)); } return results; @@ -2246,8 +2308,8 @@ } compileNode(o) { - var ref3; - if ((ref3 = this.identifier, indexOf.call(o.importedSymbols, ref3) >= 0) || o.scope.check(this.identifier)) { + var ref1; + if ((ref1 = this.identifier, indexOf.call(o.importedSymbols, ref1) >= 0) || o.scope.check(this.identifier)) { this.error(`'${this.identifier}' has already been declared`); } else { o.importedSymbols.push(this.identifier); @@ -2275,7 +2337,12 @@ this.variable = variable1; this.value = value1; this.context = context1; - this.param = options.param, this.subpattern = options.subpattern, this.operatorToken = options.operatorToken, this.moduleDeclaration = options.moduleDeclaration; + ({ + param: this.param, + subpattern: this.subpattern, + operatorToken: this.operatorToken, + moduleDeclaration: this.moduleDeclaration + } = options); } isStatement(o) { @@ -2297,7 +2364,7 @@ } compileNode(o) { - var answer, compiledName, isValue, j, name, properties, prototype, ref3, ref4, ref5, ref6, ref7, ref8, val, varBase; + var answer, compiledName, isValue, j, name, properties, prototype, ref1, ref2, ref3, ref4, ref5, ref6, val, varBase; if (isValue = this.variable instanceof Value) { if (this.variable.isArray() || this.variable.isObject()) { if (!this.variable.isAssignable()) { @@ -2307,10 +2374,10 @@ if (this.variable.isSplice()) { return this.compileSplice(o); } - if ((ref3 = this.context) === '||=' || ref3 === '&&=' || ref3 === '?=') { + if ((ref1 = this.context) === '||=' || ref1 === '&&=' || ref1 === '?=') { return this.compileConditional(o); } - if ((ref4 = this.context) === '**=' || ref4 === '//=' || ref4 === '%%=') { + if ((ref2 = this.context) === '**=' || ref2 === '//=' || ref2 === '%%=') { return this.compileSpecialMath(o); } } @@ -2341,41 +2408,42 @@ if (this.value instanceof Code) { if (this.value.isStatic) { this.value.name = this.variable.properties[0]; - } else if (((ref5 = this.variable.properties) != null ? ref5.length : void 0) >= 2) { - ref6 = this.variable.properties, properties = 3 <= ref6.length ? slice.call(ref6, 0, j = ref6.length - 2) : (j = 0, []), prototype = ref6[j++], name = ref6[j++]; - if (((ref7 = prototype.name) != null ? ref7.value : void 0) === 'prototype') { + } else if (((ref3 = this.variable.properties) != null ? ref3.length : void 0) >= 2) { + ref4 = this.variable.properties, properties = 3 <= ref4.length ? slice.call(ref4, 0, j = ref4.length - 2) : (j = 0, []), prototype = ref4[j++], name = ref4[j++]; + if (((ref5 = prototype.name) != null ? ref5.value : void 0) === 'prototype') { this.value.name = name; } } } val = this.value.compileToFragments(o, LEVEL_LIST); - if (isValue && this.variable.base instanceof Obj) { - this.variable.front = true; - } compiledName = this.variable.compileToFragments(o, LEVEL_LIST); if (this.context === 'object') { if (this.variable.shouldCache()) { compiledName.unshift(this.makeCode('[')); compiledName.push(this.makeCode(']')); - } else if (ref8 = fragmentsToText(compiledName), indexOf.call(JS_FORBIDDEN, ref8) >= 0) { + } else if (ref6 = fragmentsToText(compiledName), indexOf.call(JS_FORBIDDEN, ref6) >= 0) { compiledName.unshift(this.makeCode('"')); compiledName.push(this.makeCode('"')); } return compiledName.concat(this.makeCode(": "), val); } answer = compiledName.concat(this.makeCode(` ${this.context || '='} `), val); - if (o.level <= LEVEL_LIST) { - return answer; - } else { + if (o.level > LEVEL_LIST || (isValue && this.variable.base instanceof Obj)) { return this.wrapInBraces(answer); + } else { + return answer; } } compilePatternMatch(o) { - var acc, assigns, code, defaultValue, expandedIdx, fragments, i, idx, isObject, ivar, j, len1, message, name, obj, objects, olen, ref, ref3, ref4, ref5, ref6, rest, top, val, value, vvar, vvarText; + var acc, assigns, code, defaultValue, expandedIdx, fragments, i, idx, isObject, ivar, j, len1, message, name, obj, objects, olen, ref, rest, top, val, value, vvar, vvarText; top = o.level === LEVEL_TOP; - value = this.value; - objects = this.variable.base.objects; + ({ + value: value + } = this); + ({ + objects: objects + } = this.variable.base); if (!(olen = objects.length)) { code = value.compileToFragments(o); if (o.level >= LEVEL_OP) { @@ -2392,7 +2460,12 @@ if (top && olen === 1 && !(obj instanceof Splat)) { defaultValue = null; if (obj instanceof Assign && obj.context === 'object') { - ref3 = obj, (ref4 = ref3.variable, idx = ref4.base), obj = ref3.value; + ({ + variable: { + base: idx + }, + value: obj + } = obj); if (obj instanceof Assign) { defaultValue = obj.value; obj = obj.variable; @@ -2464,7 +2537,12 @@ } defaultValue = null; if (obj instanceof Assign && obj.context === 'object') { - ref5 = obj, (ref6 = ref5.variable, idx = ref6.base), obj = ref5.value; + ({ + variable: { + base: idx + }, + value: obj + } = obj); if (obj instanceof Assign) { defaultValue = obj.value; obj = obj.variable; @@ -2533,8 +2611,14 @@ } compileSplice(o) { - var answer, exclusive, from, fromDecl, fromRef, name, ref3, to, valDef, valRef; - ref3 = this.variable.properties.pop().range, from = ref3.from, to = ref3.to, exclusive = ref3.exclusive; + var answer, exclusive, from, fromDecl, fromRef, name, to, valDef, valRef; + ({ + range: { + from: from, + to: to, + exclusive: exclusive + } + } = this.variable.properties.pop()); name = this.variable.compile(o); if (from) { [fromDecl, fromRef] = this.cacheToCodeFragments(from.cache(o, LEVEL_OP)); @@ -2605,7 +2689,7 @@ } compileNode(o) { - var answer, body, condition, exprs, haveBodyParam, haveSplatParam, i, ifTrue, j, k, len1, len2, m, methodScope, modifiers, name, param, paramNames, params, paramsAfterSplat, ref, ref3, ref4, ref5, ref6, ref7, signature, splatParamName, thisAssignments, wasEmpty; + var answer, body, condition, exprs, haveBodyParam, haveSplatParam, i, ifTrue, j, k, len1, len2, m, methodScope, modifiers, name, param, paramNames, params, paramsAfterSplat, ref, ref1, ref2, ref3, ref4, ref5, signature, splatParamName, thisAssignments, wasEmpty; if (this.ctor) { if (this.isAsync) { this.name.error('Class constructor may not be async'); @@ -2615,7 +2699,7 @@ } } if (this.bound) { - if ((ref3 = o.scope.method) != null ? ref3.bound : void 0) { + if ((ref1 = o.scope.method) != null ? ref1.bound : void 0) { this.context = o.scope.method.context; } if (!this.context) { @@ -2629,7 +2713,7 @@ delete o.isExistentialEquals; params = []; exprs = []; - thisAssignments = (ref4 = (ref5 = this.thisAssignments) != null ? ref5.slice() : void 0) != null ? ref4 : []; + thisAssignments = (ref2 = (ref3 = this.thisAssignments) != null ? ref3.slice() : void 0) != null ? ref2 : []; paramsAfterSplat = []; haveSplatParam = false; haveBodyParam = false; @@ -2650,9 +2734,9 @@ return thisAssignments.push(new Assign(node, target)); } }); - ref6 = this.params; - for (i = j = 0, len1 = ref6.length; j < len1; i = ++j) { - param = ref6[i]; + ref4 = this.params; + 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'); @@ -2708,7 +2792,7 @@ ifTrue = new Assign(new Value(param.name), param.value); exprs.push(new If(condition, ifTrue)); } - if (((ref7 = param.name) != null ? ref7.value : void 0) != null) { + if (((ref5 = param.name) != null ? ref5.value : void 0) != null) { o.scope.add(param.name.value, 'var', true); } } @@ -2805,11 +2889,11 @@ } eachParamName(iterator) { - var j, len1, param, ref3, results; - ref3 = this.params; + var j, len1, param, ref1, results; + ref1 = this.params; results = []; - for (j = 0, len1 = ref3.length; j < len1; j++) { - param = ref3[j]; + for (j = 0, len1 = ref1.length; j < len1; j++) { + param = ref1[j]; results.push(param.eachName(iterator)); } return results; @@ -2830,7 +2914,7 @@ } expandCtorSuper(thisAssignments) { - var haveThisParam, param, ref3, seenSuper; + var haveThisParam, param, ref1, seenSuper; if (!this.ctor) { return false; } @@ -2843,7 +2927,7 @@ } return superCall.expressions = thisAssignments; }); - haveThisParam = thisAssignments.length && thisAssignments.length !== ((ref3 = this.thisAssignments) != null ? ref3.length : void 0); + haveThisParam = thisAssignments.length && thisAssignments.length !== ((ref1 = this.thisAssignments) != null ? ref1.length : void 0); if (this.ctor === 'derived' && !seenSuper && haveThisParam) { param = thisAssignments[0].variable; param.error("Can't use @params in derived class constructors without calling super"); @@ -2923,7 +3007,7 @@ } eachName(iterator, name = this.name) { - var atParam, j, len1, node, obj, ref3, ref4; + var atParam, j, len1, node, obj, ref1, ref2; atParam = (obj) => { return iterator(`@${obj.properties[0].name.value}`, obj, this); }; @@ -2933,9 +3017,9 @@ if (name instanceof Value) { return atParam(name); } - ref4 = (ref3 = name.objects) != null ? ref3 : []; - for (j = 0, len1 = ref4.length; j < len1; j++) { - obj = ref4[j]; + ref2 = (ref1 = name.objects) != null ? ref1 : []; + for (j = 0, len1 = ref2.length; j < len1; j++) { + obj = ref2[j]; if (obj instanceof Assign && (obj.context == null)) { obj = obj.variable; } @@ -3066,7 +3150,9 @@ jumps() { var expressions, j, jumpNode, len1, node; - expressions = this.body.expressions; + ({ + expressions: expressions + } = this.body); if (!expressions.length) { return false; } @@ -3085,7 +3171,9 @@ var answer, body, rvar, set; o.indent += TAB; set = ''; - body = this.body; + ({ + body: body + } = this); if (body.isEmpty()) { body = this.makeCode(''); } else { @@ -3149,8 +3237,8 @@ } isNumber() { - var ref3; - return this.isUnary() && ((ref3 = this.operator) === '+' || ref3 === '-') && this.first instanceof Value && this.first.isNumber(); + var ref1; + return this.isUnary() && ((ref1 = this.operator) === '+' || ref1 === '-') && this.first instanceof Value && this.first.isNumber(); } isAwait() { @@ -3158,8 +3246,8 @@ } isYield() { - var ref3; - return (ref3 = this.operator) === 'yield' || ref3 === 'yield*'; + var ref1; + return (ref1 = this.operator) === 'yield' || ref1 === 'yield*'; } isUnary() { @@ -3171,12 +3259,12 @@ } isChainable() { - var ref3; - return (ref3 = this.operator) === '<' || ref3 === '>' || ref3 === '>=' || ref3 === '<=' || ref3 === '===' || ref3 === '!=='; + var ref1; + return (ref1 = this.operator) === '<' || ref1 === '>' || ref1 === '>=' || ref1 === '<=' || ref1 === '===' || ref1 === '!=='; } invert() { - var allInvertable, curr, fst, op, ref3; + var allInvertable, curr, fst, op, ref1; if (this.isChainable() && this.first.isChainable()) { allInvertable = true; curr = this; @@ -3202,7 +3290,7 @@ return this; } else if (this.second) { return new Parens(this).invert(); - } else if (this.operator === '!' && (fst = this.first.unwrap()) instanceof Op && ((ref3 = fst.operator) === '!' || ref3 === 'in' || ref3 === 'instanceof')) { + } else if (this.operator === '!' && (fst = this.first.unwrap()) instanceof Op && ((ref1 = fst.operator) === '!' || ref1 === 'in' || ref1 === 'instanceof')) { return fst; } else { return new Op('!', this); @@ -3210,17 +3298,17 @@ } unfoldSoak(o) { - var ref3; - return ((ref3 = this.operator) === '++' || ref3 === '--' || ref3 === 'delete') && unfoldSoak(o, this, 'first'); + var ref1; + return ((ref1 = this.operator) === '++' || ref1 === '--' || ref1 === 'delete') && unfoldSoak(o, this, 'first'); } generateDo(exp) { - var call, func, j, len1, param, passedParams, ref, ref3; + var call, func, j, len1, param, passedParams, ref, ref1; passedParams = []; func = exp instanceof Assign && (ref = exp.value.unwrap()) instanceof Code ? ref : exp; - ref3 = func.params || []; - for (j = 0, len1 = ref3.length; j < len1; j++) { - param = ref3[j]; + ref1 = func.params || []; + for (j = 0, len1 = ref1.length; j < len1; j++) { + param = ref1[j]; if (param.value) { passedParams.push(param.value); delete param.value; @@ -3234,7 +3322,7 @@ } compileNode(o) { - var answer, isChain, lhs, message, ref3, rhs; + var answer, isChain, lhs, message, ref1, rhs; isChain = this.isChainable() && this.first.isChainable(); if (!isChain) { this.first.front = this.front; @@ -3242,7 +3330,7 @@ if (this.operator === 'delete' && o.scope.check(this.first.unwrapAll().value)) { this.error('delete operand may not be argument or var'); } - if ((ref3 = this.operator) === '--' || ref3 === '++') { + if ((ref1 = this.operator) === '--' || ref1 === '++') { message = isUnassignable(this.first.unwrapAll().value); if (message) { this.first.error(message); @@ -3327,13 +3415,13 @@ } compileContinuation(o) { - var op, parts, ref3, ref4; + var op, parts, ref1, ref2; parts = []; op = this.operator; if (o.scope.parent == null) { this.error(`${this.operator} can only occur inside functions`); } - if (((ref3 = o.scope.method) != null ? ref3.bound : void 0) && o.scope.method.isGenerator) { + if (((ref1 = o.scope.method) != null ? ref1.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)) { @@ -3345,7 +3433,7 @@ parts.push([this.makeCode("(")]); } parts.push([this.makeCode(op)]); - if (((ref4 = this.first.base) != null ? ref4.value : void 0) !== '') { + if (((ref2 = this.first.base) != null ? ref2.value : void 0) !== '') { parts.push([this.makeCode(" ")]); } parts.push(this.first.compileToFragments(o, LEVEL_OP)); @@ -3409,11 +3497,11 @@ } compileNode(o) { - var hasSplat, j, len1, obj, ref3; + var hasSplat, j, len1, obj, ref1; if (this.array instanceof Value && this.array.isArray() && this.array.base.objects.length) { - ref3 = this.array.base.objects; - for (j = 0, len1 = ref3.length; j < len1; j++) { - obj = ref3[j]; + ref1 = this.array.base.objects; + for (j = 0, len1 = ref1.length; j < len1; j++) { + obj = ref1[j]; if (!(obj instanceof Splat)) { continue; } @@ -3428,13 +3516,13 @@ } compileOrTest(o) { - var cmp, cnj, i, item, j, len1, ref, ref3, sub, tests; + var cmp, cnj, i, item, j, len1, ref, ref1, sub, tests; [sub, ref] = this.object.cache(o, LEVEL_OP); [cmp, cnj] = this.negated ? [' !== ', ' && '] : [' === ', ' || ']; tests = []; - ref3 = this.array.base.objects; - for (i = j = 0, len1 = ref3.length; j < len1; i = ++j) { - item = ref3[i]; + ref1 = this.array.base.objects; + for (i = j = 0, len1 = ref1.length; j < len1; i = ++j) { + item = ref1[i]; if (i) { tests.push(this.makeCode(cnj)); } @@ -3487,8 +3575,8 @@ } jumps(o) { - var ref3; - return this.attempt.jumps(o) || ((ref3 = this.recovery) != null ? ref3.jumps(o) : void 0); + var ref1; + return this.attempt.jumps(o) || ((ref1 = this.recovery) != null ? ref1.jumps(o) : void 0); } makeReturn(res) { @@ -3682,8 +3770,15 @@ exports.For = For = (function() { class For extends While { constructor(body, source) { + var ref1, ref2; super(); - this.source = source.source, this.guard = source.guard, this.step = source.step, this.name = source.name, this.index = source.index; + ({ + source: this.source, + guard: this.guard, + step: this.step, + name: this.name, + index: this.index + } = source); this.body = Block.wrap([body]); this.own = !!source.own; this.object = !!source.object; @@ -3697,7 +3792,7 @@ if (this.object) { [this.name, this.index] = [this.index, this.name]; } - if (this.index instanceof Value && !this.index.isAssignable()) { + if (((ref1 = this.index) != null ? typeof ref1.isArray === "function" ? ref1.isArray() : void 0 : void 0) || ((ref2 = this.index) != null ? typeof ref2.isObject === "function" ? ref2.isObject() : void 0 : void 0)) { this.index.error('index cannot be a pattern matching expression'); } this.range = this.source instanceof Value && this.source.base instanceof Range && !this.source.properties.length && !this.from; @@ -3712,9 +3807,9 @@ } compileNode(o) { - var body, bodyFragments, compare, compareDown, declare, declareDown, defPart, defPartFragments, down, forPartFragments, guardPart, idt1, increment, index, ivar, kvar, kvarAssign, last, lvar, name, namePart, ref, ref3, resultPart, returnResult, rvar, scope, source, step, stepNum, stepVar, svar, varPart; + var body, bodyFragments, compare, compareDown, declare, declareDown, defPart, defPartFragments, down, forPartFragments, guardPart, idt1, increment, index, ivar, kvar, kvarAssign, last, lvar, name, namePart, ref, ref1, resultPart, returnResult, rvar, scope, source, step, stepNum, stepVar, svar, varPart; body = Block.wrap([this.body]); - ref3 = body.expressions, last = ref3[ref3.length - 1]; + ref1 = body.expressions, last = ref1[ref1.length - 1]; if ((last != null ? last.jumps() : void 0) instanceof Return) { this.returns = false; } @@ -3843,20 +3938,20 @@ } pluckDirectCall(o, body) { - var base, defs, expr, fn, idx, j, len1, ref, ref3, ref4, ref5, ref6, ref7, ref8, val; + var base, defs, expr, fn, idx, j, len1, ref, ref1, ref2, ref3, ref4, ref5, ref6, val; defs = []; - ref3 = body.expressions; - for (idx = j = 0, len1 = ref3.length; j < len1; idx = ++j) { - expr = ref3[idx]; + ref1 = body.expressions; + for (idx = j = 0, len1 = ref1.length; j < len1; idx = ++j) { + expr = ref1[idx]; expr = expr.unwrapAll(); if (!(expr instanceof Call)) { continue; } - val = (ref4 = expr.variable) != null ? ref4.unwrapAll() : void 0; - if (!((val instanceof Code) || (val instanceof Value && ((ref5 = val.base) != null ? ref5.unwrapAll() : void 0) instanceof Code && val.properties.length === 1 && ((ref6 = (ref7 = val.properties[0].name) != null ? ref7.value : void 0) === 'call' || ref6 === 'apply')))) { + val = (ref2 = expr.variable) != null ? ref2.unwrapAll() : void 0; + if (!((val instanceof Code) || (val instanceof Value && ((ref3 = val.base) != null ? ref3.unwrapAll() : void 0) instanceof Code && val.properties.length === 1 && ((ref4 = (ref5 = val.properties[0].name) != null ? ref5.value : void 0) === 'call' || ref4 === 'apply')))) { continue; } - fn = ((ref8 = val.base) != null ? ref8.unwrapAll() : void 0) || val; + fn = ((ref6 = val.base) != null ? ref6.unwrapAll() : void 0) || val; ref = new IdentifierLiteral(o.scope.freeVariable('fn')); base = new Value(ref); if (val.base) { @@ -3888,44 +3983,44 @@ jumps(o = { block: true }) { - var block, conds, j, jumpNode, len1, ref3, ref4; - ref3 = this.cases; - for (j = 0, len1 = ref3.length; j < len1; j++) { - [conds, block] = ref3[j]; + var block, conds, j, jumpNode, len1, ref1, ref2; + ref1 = this.cases; + for (j = 0, len1 = ref1.length; j < len1; j++) { + [conds, block] = ref1[j]; if (jumpNode = block.jumps(o)) { return jumpNode; } } - return (ref4 = this.otherwise) != null ? ref4.jumps(o) : void 0; + return (ref2 = this.otherwise) != null ? ref2.jumps(o) : void 0; } makeReturn(res) { - var j, len1, pair, ref3, ref4; - ref3 = this.cases; - for (j = 0, len1 = ref3.length; j < len1; j++) { - pair = ref3[j]; + var j, len1, pair, ref1, ref2; + ref1 = this.cases; + for (j = 0, len1 = ref1.length; j < len1; j++) { + pair = ref1[j]; pair[1].makeReturn(res); } if (res) { this.otherwise || (this.otherwise = new Block([new Literal('void 0')])); } - if ((ref4 = this.otherwise) != null) { - ref4.makeReturn(res); + if ((ref2 = this.otherwise) != null) { + ref2.makeReturn(res); } return this; } compileNode(o) { - var block, body, cond, conditions, expr, fragments, i, idt1, idt2, j, k, len1, len2, ref3, ref4; + var block, body, cond, conditions, expr, fragments, i, idt1, idt2, j, k, len1, len2, ref1, ref2; idt1 = o.indent + TAB; idt2 = o.indent = idt1 + TAB; fragments = [].concat(this.makeCode(this.tab + "switch ("), (this.subject ? this.subject.compileToFragments(o, LEVEL_PAREN) : this.makeCode("false")), this.makeCode(") {\n")); - ref3 = this.cases; - for (i = j = 0, len1 = ref3.length; j < len1; i = ++j) { - [conditions, block] = ref3[i]; - ref4 = flatten([conditions]); - for (k = 0, len2 = ref4.length; k < len2; k++) { - cond = ref4[k]; + ref1 = this.cases; + for (i = j = 0, len1 = ref1.length; j < len1; i = ++j) { + [conditions, block] = ref1[i]; + ref2 = flatten([conditions]); + for (k = 0, len2 = ref2.length; k < len2; k++) { + cond = ref2[k]; if (!this.subject) { cond = cond.invert(); } @@ -3968,17 +4063,19 @@ this.condition = options.type === 'unless' ? condition.invert() : condition; this.elseBody = null; this.isChain = false; - this.soak = options.soak; + ({ + soak: this.soak + } = options); } bodyNode() { - var ref3; - return (ref3 = this.body) != null ? ref3.unwrap() : void 0; + var ref1; + return (ref1 = this.body) != null ? ref1.unwrap() : void 0; } elseBodyNode() { - var ref3; - return (ref3 = this.elseBody) != null ? ref3.unwrap() : void 0; + var ref1; + return (ref1 = this.elseBody) != null ? ref1.unwrap() : void 0; } addElse(elseBody) { @@ -3993,13 +4090,13 @@ } isStatement(o) { - var ref3; - return (o != null ? o.level : void 0) === LEVEL_TOP || this.bodyNode().isStatement(o) || ((ref3 = this.elseBodyNode()) != null ? ref3.isStatement(o) : void 0); + var ref1; + return (o != null ? o.level : void 0) === LEVEL_TOP || this.bodyNode().isStatement(o) || ((ref1 = this.elseBodyNode()) != null ? ref1.isStatement(o) : void 0); } jumps(o) { - var ref3; - return this.body.jumps(o) || ((ref3 = this.elseBody) != null ? ref3.jumps(o) : void 0); + var ref1; + return this.body.jumps(o) || ((ref1 = this.elseBody) != null ? ref1.jumps(o) : void 0); } compileNode(o) { @@ -4124,7 +4221,9 @@ utility = function(name, o) { var ref, root; - root = o.scope.root; + ({ + root: root + } = o.scope); if (name in root.utilities) { return root.utilities[name]; } else { diff --git a/lib/coffeescript/optparse.js b/lib/coffeescript/optparse.js index 79cb367ddf..fb4998c4a6 100644 --- a/lib/coffeescript/optparse.js +++ b/lib/coffeescript/optparse.js @@ -2,7 +2,9 @@ (function() { var LONG_FLAG, MULTI_FLAG, OPTIONAL, OptionParser, SHORT_FLAG, buildRule, buildRules, normalizeArguments, repeat; - repeat = require('./helpers').repeat; + ({ + repeat: repeat + } = require('./helpers')); exports.OptionParser = OptionParser = class OptionParser { constructor(rules, banner) { diff --git a/lib/coffeescript/register.js b/lib/coffeescript/register.js index 4338790fe2..7b0a2f9930 100644 --- a/lib/coffeescript/register.js +++ b/lib/coffeescript/register.js @@ -48,7 +48,9 @@ } if (child_process) { - fork = child_process.fork; + ({ + fork: fork + } = child_process); binary = require.resolve('../../bin/coffee'); child_process.fork = function(path, args, options) { if (helpers.isCoffee(path)) { diff --git a/lib/coffeescript/repl.js b/lib/coffeescript/repl.js index 84c47c7c9c..dfbbdaeff9 100644 --- a/lib/coffeescript/repl.js +++ b/lib/coffeescript/repl.js @@ -1,6 +1,6 @@ // Generated by CoffeeScript 2.0.0-alpha1 (function() { - var CoffeeScript, addHistory, addMultilineHandler, fs, getCommandId, merge, nodeREPL, path, ref, replDefaults, runInContext, updateSyntaxError, vm; + var CoffeeScript, addHistory, addMultilineHandler, fs, getCommandId, merge, nodeREPL, path, replDefaults, runInContext, updateSyntaxError, vm; fs = require('fs'); @@ -12,18 +12,26 @@ CoffeeScript = require('./coffeescript'); - ref = require('./helpers'), merge = ref.merge, updateSyntaxError = ref.updateSyntaxError; + ({ + merge: merge, + updateSyntaxError: updateSyntaxError + } = require('./helpers')); replDefaults = { prompt: 'coffee> ', historyFile: process.env.HOME ? path.join(process.env.HOME, '.coffee_history') : void 0, historyMaxInputSize: 10240, "eval": function(input, context, filename, cb) { - var Assign, Block, Literal, Value, ast, err, js, ref1, referencedVars, token, tokens; + var Assign, Block, Literal, Value, ast, err, js, referencedVars, token, tokens; input = input.replace(/\uFF00/g, '\n'); input = input.replace(/^\(([\s\S]*)\n\)$/m, '$1'); input = input.replace(/^\s*try\s*{([\s\S]*)}\s*catch.*$/m, '$1'); - ref1 = require('./nodes'), Block = ref1.Block, Assign = ref1.Assign, Value = ref1.Value, Literal = ref1.Literal; + ({ + Block: Block, + Assign: Assign, + Value: Value, + Literal: Literal + } = require('./nodes')); try { tokens = CoffeeScript.tokens(input); referencedVars = (function() { @@ -62,9 +70,13 @@ }; addMultilineHandler = function(repl) { - var inputStream, multiline, nodeLineListener, origPrompt, outputStream, ref1, rli; - rli = repl.rli, inputStream = repl.inputStream, outputStream = repl.outputStream; - origPrompt = (ref1 = repl._prompt) != null ? ref1 : repl.prompt; + var inputStream, multiline, nodeLineListener, origPrompt, outputStream, ref, rli; + ({ + rli: rli, + inputStream: inputStream, + outputStream: outputStream + } = repl); + origPrompt = (ref = repl._prompt) != null ? ref : repl.prompt; multiline = { enabled: false, initialPrompt: origPrompt.replace(/^[^> ]*/, function(x) { diff --git a/lib/coffeescript/rewriter.js b/lib/coffeescript/rewriter.js index c8ef515ec4..f629ece855 100644 --- a/lib/coffeescript/rewriter.js +++ b/lib/coffeescript/rewriter.js @@ -30,7 +30,9 @@ scanTokens(block) { var i, token, tokens; - tokens = this.tokens; + ({ + tokens: tokens + } = this); i = 0; while (token = tokens[i]) { i += block.call(this, token, i, tokens); @@ -40,7 +42,9 @@ detectEnd(i, condition, action) { var levels, ref, ref1, token, tokens; - tokens = this.tokens; + ({ + tokens: tokens + } = this); levels = 0; while (token = tokens[i]) { if (levels === 0 && condition.call(this, token, i)) { @@ -168,7 +172,7 @@ stack = []; start = null; return this.scanTokens(function(token, i, tokens) { - var endImplicitCall, endImplicitObject, forward, inImplicit, inImplicitCall, inImplicitControl, inImplicitObject, newLine, nextTag, offset, prevTag, prevToken, ref, ref1, ref2, ref3, ref4, s, sameLine, stackIdx, stackTag, stackTop, startIdx, startImplicitCall, startImplicitObject, startsLine, tag; + var endImplicitCall, endImplicitObject, forward, inImplicit, inImplicitCall, inImplicitControl, inImplicitObject, newLine, nextTag, offset, prevTag, prevToken, ref, ref1, ref2, s, sameLine, stackIdx, stackTag, stackTop, startIdx, startImplicitCall, startImplicitObject, startsLine, tag; [tag] = token; [prevTag] = prevToken = i > 0 ? tokens[i - 1] : []; [nextTag] = i < tokens.length - 1 ? tokens[i + 1] : []; @@ -316,7 +320,12 @@ newLine = prevTag === 'OUTDENT' || prevToken.newLine; if (indexOf.call(IMPLICIT_END, tag) >= 0 || indexOf.call(CALL_CLOSERS, tag) >= 0 && newLine) { while (inImplicit()) { - ref3 = stackTop(), stackTag = ref3[0], stackIdx = ref3[1], (ref4 = ref3[2], sameLine = ref4.sameLine, startsLine = ref4.startsLine); + [ + stackTag, stackIdx, { + sameLine: sameLine, + startsLine: startsLine + } + ] = stackTop(); if (inImplicitCall() && prevTag !== ',') { endImplicitCall(); } else if (inImplicitObject() && !this.insideForDeclaration && sameLine && tag !== 'TERMINATOR' && prevTag !== ':') { @@ -351,9 +360,15 @@ return 1; } if (token[0] === '{' && (nextLocation = (ref = tokens[i + 1]) != null ? ref[2] : void 0)) { - line = nextLocation.first_line, column = nextLocation.first_column; + ({ + first_line: line, + first_column: column + } = nextLocation); } else if (prevLocation = (ref1 = tokens[i - 1]) != null ? ref1[2] : void 0) { - line = prevLocation.last_line, column = prevLocation.last_column; + ({ + last_line: line, + last_column: column + } = prevLocation); } else { line = column = 0; } diff --git a/src/nodes.coffee b/src/nodes.coffee index bec76f24d6..4c5ab47584 100644 --- a/src/nodes.coffee +++ b/src/nodes.coffee @@ -1115,6 +1115,12 @@ exports.Obj = class Obj extends Base children: ['properties'] + isAssignable: -> + for prop in @properties + prop = prop.value if prop instanceof Assign + return false unless prop.isAssignable() + true + compileNode: (o) -> props = @properties if @generated @@ -1156,6 +1162,12 @@ exports.Obj = class Obj extends Base for prop in @properties when prop.assigns name then return yes no + eachName: (iterator) -> + for prop in @properties + prop = prop.value if prop instanceof Assign + prop = prop.unwrapAll() + prop.eachName iterator + #### Arr # An array literal. @@ -1719,7 +1731,6 @@ exports.Assign = class Assign extends Base @value.name = name if prototype.name?.value is 'prototype' val = @value.compileToFragments o, LEVEL_LIST - @variable.front = true if isValue and @variable.base instanceof Obj compiledName = @variable.compileToFragments o, LEVEL_LIST if @context is 'object' @@ -1732,7 +1743,7 @@ exports.Assign = class Assign extends Base return compiledName.concat @makeCode(": "), val answer = compiledName.concat @makeCode(" #{ @context or '=' } "), val - if o.level <= LEVEL_LIST then answer else @wrapInBraces answer + if o.level > LEVEL_LIST or (isValue and @variable.base instanceof Obj) then @wrapInBraces answer else answer # Brief implementation of recursive pattern matching, when assigning array or # object literals to a value. Peeks at their properties to assign inner names. @@ -2753,7 +2764,7 @@ exports.For = class For extends While @index.error 'cannot use index with for-from' if @from and @index source.ownTag.error "cannot use own with for-#{if @from then 'from' else 'in'}" if @own and not @object [@name, @index] = [@index, @name] if @object - @index.error 'index cannot be a pattern matching expression' if @index instanceof Value and not @index.isAssignable() + @index.error 'index cannot be a pattern matching expression' if @index?.isArray?() or @index?.isObject?() @range = @source instanceof Value and @source.base instanceof Range and not @source.properties.length and not @from @pattern = @name instanceof Value @index.error 'indexes do not apply to range loops' if @range and @index diff --git a/test/assignment.coffee b/test/assignment.coffee index aff5371a1d..20c940d700 100644 --- a/test/assignment.coffee +++ b/test/assignment.coffee @@ -141,9 +141,6 @@ test "#1192: assignment starting with object literals", -> # Destructuring Assignment -test "empty destructuring assignment", -> - {} = [] = undefined - test "chained destructuring assignments", -> [a] = {0: b} = {'0': c} = [nonce={}] eq nonce, a From 066071f06080b3a4c23c116004e56dd6deb3439f Mon Sep 17 00:00:00 2001 From: Chris Connelly Date: Thu, 30 Mar 2017 22:18:13 +0100 Subject: [PATCH 03/20] Compile shorthand object properties to ES2015 shorthand properties This dramatically improves the appearance of destructured imports. --- lib/coffeescript/cake.js | 6 +- lib/coffeescript/coffeescript.js | 30 +++---- lib/coffeescript/command.js | 19 ++--- lib/coffeescript/grammar.js | 4 +- lib/coffeescript/helpers.js | 7 +- lib/coffeescript/lexer.js | 46 +++-------- lib/coffeescript/nodes.js | 134 +++++++++---------------------- lib/coffeescript/optparse.js | 4 +- lib/coffeescript/register.js | 4 +- lib/coffeescript/repl.js | 20 +---- lib/coffeescript/rewriter.js | 15 +--- lib/coffeescript/scope.js | 7 +- lib/coffeescript/sourcemap.js | 6 +- src/nodes.coffee | 14 +++- 14 files changed, 93 insertions(+), 223 deletions(-) diff --git a/lib/coffeescript/cake.js b/lib/coffeescript/cake.js index 710c5437d9..13fb0e7e06 100644 --- a/lib/coffeescript/cake.js +++ b/lib/coffeescript/cake.js @@ -27,11 +27,7 @@ if (!action) { [action, description] = [description, action]; } - return tasks[name] = { - name: name, - description: description, - action: action - }; + return tasks[name] = {name, description, action}; }, option: function(letter, flag, description) { return switches.push([letter, flag, description]); diff --git a/lib/coffeescript/coffeescript.js b/lib/coffeescript/coffeescript.js index 9d5be3fcc8..35f38ac13c 100644 --- a/lib/coffeescript/coffeescript.js +++ b/lib/coffeescript/coffeescript.js @@ -9,13 +9,9 @@ path = require('path'); - ({ - Lexer: Lexer - } = require('./lexer')); + ({Lexer} = require('./lexer')); - ({ - parser: parser - } = require('./parser')); + ({parser} = require('./parser')); helpers = require('./helpers'); @@ -63,10 +59,7 @@ exports.compile = compile = withPrettyErrors(function(code, options) { var currentColumn, currentLine, encoded, extend, filename, fragment, fragments, generateSourceMap, header, i, j, js, len, len1, map, merge, newLines, ref, ref1, sourceMapDataURI, sourceURL, token, tokens, v3SourceMap; - ({ - merge: merge, - extend: extend - } = helpers); + ({merge, extend} = helpers); options = extend({}, options); generateSourceMap = options.sourceMap || options.inlineMap || (options.filename == null); filename = options.filename || ''; @@ -139,7 +132,7 @@ } if (options.sourceMap) { return { - js: js, + js, sourceMap: map, v3SourceMap: JSON.stringify(v3SourceMap, null, 2) }; @@ -261,9 +254,9 @@ stripped = raw.charCodeAt(0) === 0xFEFF ? raw.substring(1) : raw; try { answer = compile(stripped, { - filename: filename, - sourceMap: sourceMap, - inlineMap: inlineMap, + filename, + sourceMap, + inlineMap, sourceFiles: [filename], literate: helpers.isLiterate(filename) }); @@ -302,13 +295,8 @@ parser.yy.parseError = function(message, arg) { var errorLoc, errorTag, errorText, errorToken, token, tokens; - ({ - token: token - } = arg); - ({ - errorToken: errorToken, - tokens: tokens - } = parser); + ({token} = arg); + ({errorToken, tokens} = parser); [errorTag, errorText, errorLoc] = errorToken; errorText = (function() { switch (false) { diff --git a/lib/coffeescript/command.js b/lib/coffeescript/command.js index 95558397dc..13478f4d86 100644 --- a/lib/coffeescript/command.js +++ b/lib/coffeescript/command.js @@ -13,14 +13,9 @@ CoffeeScript = require('./coffeescript'); - ({ - spawn: spawn, - exec: exec - } = require('child_process')); + ({spawn, exec} = require('child_process')); - ({ - EventEmitter: EventEmitter - } = require('events')); + ({EventEmitter} = require('events')); useWinPathSep = path.sep === '\\'; @@ -208,11 +203,7 @@ o = opts; options = compileOptions(file, base); try { - t = task = { - file: file, - input: input, - options: options - }; + t = task = {file, input, options}; CoffeeScript.emit('compile', task); if (o.tokens) { return printTokens(CoffeeScript.tokens(t.input, t.options)); @@ -551,7 +542,7 @@ compileOptions = function(filename, base) { var answer, cwd, jsDir, jsPath; answer = { - filename: filename, + filename, literate: opts.literate || helpers.isLiterate(filename), bare: opts.bare, header: opts.compile && !opts['no-header'], @@ -564,7 +555,7 @@ jsPath = outputPath(filename, base); jsDir = path.dirname(jsPath); answer = helpers.merge(answer, { - jsPath: jsPath, + jsPath, sourceRoot: path.relative(jsDir, cwd), sourceFiles: [path.relative(cwd, filename)], generatedFile: helpers.baseFileName(jsPath, false, useWinPathSep) diff --git a/lib/coffeescript/grammar.js b/lib/coffeescript/grammar.js index 8e7eee0d8e..f778dbb5b3 100644 --- a/lib/coffeescript/grammar.js +++ b/lib/coffeescript/grammar.js @@ -2,9 +2,7 @@ (function() { var Parser, alt, alternatives, grammar, name, o, operators, token, tokens, unwrap; - ({ - Parser: Parser - } = require('jison')); + ({Parser} = require('jison')); unwrap = /^function\s*\(\)\s*\{\s*return\s*([\s\S]*);\s*\}/; diff --git a/lib/coffeescript/helpers.js b/lib/coffeescript/helpers.js index 39c2da690e..134a74dbcb 100644 --- a/lib/coffeescript/helpers.js +++ b/lib/coffeescript/helpers.js @@ -201,12 +201,7 @@ if (!(this.code && this.location)) { return Error.prototype.toString.call(this); } - ({ - first_line: first_line, - first_column: first_column, - last_line: last_line, - last_column: last_column - } = this.location); + ({first_line, first_column, last_line, last_column} = this.location); if (last_line == null) { last_line = first_line; } diff --git a/lib/coffeescript/lexer.js b/lib/coffeescript/lexer.js index 99f2d2a696..b91efc1c1b 100644 --- a/lib/coffeescript/lexer.js +++ b/lib/coffeescript/lexer.js @@ -3,20 +3,9 @@ var BOM, BOOL, CALLABLE, CODE, COFFEE_ALIASES, COFFEE_ALIAS_MAP, COFFEE_KEYWORDS, COMMENT, COMPARE, COMPOUND_ASSIGN, HERECOMMENT_ILLEGAL, HEREDOC_DOUBLE, HEREDOC_INDENT, HEREDOC_SINGLE, HEREGEX, HEREGEX_OMIT, HERE_JSTOKEN, IDENTIFIER, INDENTABLE_CLOSERS, INDEXABLE, INVALID_ESCAPE, INVERSES, JSTOKEN, JS_KEYWORDS, LEADING_BLANK_LINE, LINE_BREAK, LINE_CONTINUER, Lexer, MATH, MULTI_DENT, NOT_REGEX, NUMBER, OPERATOR, POSSIBLY_DIVISION, REGEX, REGEX_FLAGS, REGEX_ILLEGAL, RELATION, RESERVED, Rewriter, SHIFT, SIMPLE_STRING_OMIT, STRICT_PROSCRIBED, STRING_DOUBLE, STRING_OMIT, STRING_SINGLE, STRING_START, TRAILING_BLANK_LINE, TRAILING_SPACES, UNARY, UNARY_MATH, VALID_FLAGS, WHITESPACE, compact, count, invertLiterate, isForFrom, isUnassignable, key, locationDataToString, repeat, starts, throwSyntaxError, 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; }; - ({ - Rewriter: Rewriter, - INVERSES: INVERSES - } = require('./rewriter')); - - ({ - count: count, - starts: starts, - compact: compact, - repeat: repeat, - invertLiterate: invertLiterate, - locationDataToString: locationDataToString, - throwSyntaxError: throwSyntaxError - } = require('./helpers')); + ({Rewriter, INVERSES} = require('./rewriter')); + + ({count, starts, compact, repeat, invertLiterate, locationDataToString, throwSyntaxError} = require('./helpers')); exports.Lexer = Lexer = class Lexer { tokenize(code, opts = {}) { @@ -256,7 +245,7 @@ })(); heredoc = quote.length === 3; ({ - tokens: tokens, + tokens, index: end } = this.matchWithInterpolations(regex, quote)); $ = tokens.length - 1; @@ -283,9 +272,7 @@ if (indent) { indentRegex = RegExp(`\\n${indent}`, "g"); } - this.mergeInterpolationTokens(tokens, { - delimiter: delimiter - }, (value, i) => { + this.mergeInterpolationTokens(tokens, {delimiter}, (value, i) => { value = this.formatString(value); if (indentRegex) { value = value.replace(indentRegex, '\n'); @@ -299,9 +286,7 @@ return value; }); } else { - this.mergeInterpolationTokens(tokens, { - delimiter: delimiter - }, (value, i) => { + this.mergeInterpolationTokens(tokens, {delimiter}, (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)) { @@ -358,10 +343,7 @@ }); break; case !(match = this.matchWithInterpolations(HEREGEX, '///')): - ({ - tokens: tokens, - index: index - } = match); + ({tokens, index} = match); break; case !(match = REGEX.exec(this.chunk)): [regex, body, closed] = match; @@ -651,9 +633,7 @@ return this; } stack = []; - ({ - tokens: tokens - } = this); + ({tokens} = this); i = tokens.length; tokens[--i][0] = 'PARAM_END'; while (tok = tokens[--i]) { @@ -692,7 +672,7 @@ [strPart] = regex.exec(str); this.validateEscapes(strPart, { isRegex: delimiter.charAt(0) === '/', - offsetInChunk: offsetInChunk + offsetInChunk }); tokens.push(this.makeToken('NEOSTRING', strPart, offsetInChunk)); str = str.slice(strPart.length); @@ -703,7 +683,7 @@ [line, column] = this.getLineAndColumnFromChunk(offsetInChunk + 1); ({ tokens: nested, - index: index + index } = new Lexer().tokenize(str.slice(1), { line: line, column: column, @@ -738,7 +718,7 @@ lastToken[2].last_column -= 1; } return { - tokens: tokens, + tokens, index: offsetInChunk + delimiter.length }; } @@ -946,8 +926,8 @@ error(message, options = {}) { var first_column, first_line, location, ref, ref1; location = 'first_line' in options ? options : ([first_line, first_column] = this.getLineAndColumnFromChunk((ref = options.offset) != null ? ref : 0), { - first_line: first_line, - first_column: first_column, + first_line, + first_column, last_column: first_column + ((ref1 = options.length) != null ? ref1 : 1) - 1 }); return throwSyntaxError(message, location); diff --git a/lib/coffeescript/nodes.js b/lib/coffeescript/nodes.js index 05f3efc4cd..c076004cb2 100644 --- a/lib/coffeescript/nodes.js +++ b/lib/coffeescript/nodes.js @@ -6,28 +6,11 @@ Error.stackTraceLimit = 2e308; - ({ - Scope: Scope - } = require('./scope')); - - ({ - isUnassignable: isUnassignable, - JS_FORBIDDEN: JS_FORBIDDEN - } = require('./lexer')); - - ({ - compact: compact, - flatten: flatten, - extend: extend, - merge: merge, - del: del, - starts: starts, - ends: ends, - some: some, - addLocationDataFn: addLocationDataFn, - locationDataToString: locationDataToString, - throwSyntaxError: throwSyntaxError - } = require('./helpers')); + ({Scope} = require('./scope')); + + ({isUnassignable, JS_FORBIDDEN} = require('./lexer')); + + ({compact, flatten, extend, merge, del, starts, ends, some, addLocationDataFn, locationDataToString, throwSyntaxError} = require('./helpers')); exports.extend = extend; @@ -588,9 +571,7 @@ this.expressions = rest; } post = this.compileNode(o); - ({ - scope: scope - } = o); + ({scope} = o); if (scope.expressions === this) { declars = o.scope.hasDeclarations(); assigns = scope.hasAssignments; @@ -1239,10 +1220,7 @@ } this.inCtor = !!method.ctor; if (!(this.inCtor || (this.accessor != null))) { - ({ - name: name, - variable: variable - } = method); + ({name, variable} = method); if (name.shouldCache() || (name instanceof Index && name.index.isAssignable())) { nref = new IdentifierLiteral(o.scope.parent.freeVariable('name')); name.index = new Assign(nref, name.index); @@ -1469,10 +1447,7 @@ compileNode(o) { var compiled, compiledText, from, fromCompiled, to, toStr; - ({ - to: to, - from: from - } = this.range); + ({to, from} = this.range); fromCompiled = from && from.compileToFragments(o, LEVEL_PAREN) || [this.makeCode('0')]; if (to) { compiled = to.compileToFragments(o, LEVEL_PAREN); @@ -1516,7 +1491,7 @@ } compileNode(o) { - var answer, i, idt, indent, j, join, k, key, lastNoncom, len1, len2, node, prop, props, value; + var answer, i, idt, indent, isCompact, j, join, k, key, l, lastNoncom, len1, len2, len3, node, prop, props, ref1, value; props = this.properties; if (this.generated) { for (j = 0, len1 = props.length; j < len1; j++) { @@ -1528,12 +1503,20 @@ } idt = o.indent += TAB; lastNoncom = this.lastNonComment(this.properties); + isCompact = true; + ref1 = this.properties; + for (k = 0, len2 = ref1.length; k < len2; k++) { + prop = ref1[k]; + if (prop instanceof Assign || prop instanceof Comment) { + isCompact = false; + } + } answer = []; - answer.push(this.makeCode(`{${(props.length === 0 ? '}' : '\n')}`)); - for (i = k = 0, len2 = props.length; k < len2; i = ++k) { + answer.push(this.makeCode(`{${(isCompact ? '' : '\n')}`)); + for (i = l = 0, len3 = props.length; l < len3; i = ++l) { prop = props[i]; - join = i === props.length - 1 ? '' : prop === lastNoncom || prop instanceof Comment ? '\n' : ',\n'; - indent = prop instanceof Comment ? '' : idt; + join = i === props.length - 1 ? '' : isCompact ? ', ' : prop === lastNoncom || prop instanceof Comment ? '\n' : ',\n'; + indent = isCompact || prop instanceof Comment ? '' : idt; if (prop instanceof Assign) { if (prop.context !== 'object') { prop.operatorToken.error(`unexpected ${prop.operatorToken.value}`); @@ -1552,7 +1535,7 @@ key = new PropertyName(key.value); } prop = new Assign(key, value, 'object'); - } else { + } else if (!(typeof prop.bareLiteral === "function" ? prop.bareLiteral(IdentifierLiteral) : void 0)) { prop = new Assign(prop, prop, 'object'); } } @@ -1564,9 +1547,7 @@ answer.push(this.makeCode(join)); } } - if (props.length !== 0) { - answer.push(this.makeCode(`\n${this.tab}}`)); - } + answer.push(this.makeCode(`${(isCompact ? '' : `\n${this.tab}`)}}`)); if (this.front) { return this.wrapInBraces(answer); } else { @@ -1728,9 +1709,7 @@ } } if (this.variable) { - assign = new Assign(this.variable, new Literal(''), null, { - moduleDeclaration: this.moduleDeclaration - }); + assign = new Assign(this.variable, new Literal(''), null, {moduleDeclaration: this.moduleDeclaration}); return [...assign.compileToFragments(o), ...result]; } else { return result; @@ -1800,17 +1779,13 @@ this.boundMethods = []; executableBody = null; initializer = []; - ({ - expressions: expressions - } = this.body); + ({expressions} = this.body); i = 0; ref1 = expressions.slice(); for (j = 0, len1 = ref1.length; j < len1; j++) { expression = ref1[j]; if (expression instanceof Value && expression.isObject(true)) { - ({ - properties: properties - } = expression.base); + ({properties} = expression.base); exprs = []; end = 0; start = 0; @@ -1899,7 +1874,7 @@ addInitializerMethod(assign) { var method, methodName, variable; ({ - variable: variable, + variable, value: method } = assign); method.isMethod = true; @@ -2337,12 +2312,7 @@ this.variable = variable1; this.value = value1; this.context = context1; - ({ - param: this.param, - subpattern: this.subpattern, - operatorToken: this.operatorToken, - moduleDeclaration: this.moduleDeclaration - } = options); + ({param: this.param, subpattern: this.subpattern, operatorToken: this.operatorToken, moduleDeclaration: this.moduleDeclaration} = options); } isStatement(o) { @@ -2438,12 +2408,8 @@ compilePatternMatch(o) { var acc, assigns, code, defaultValue, expandedIdx, fragments, i, idx, isObject, ivar, j, len1, message, name, obj, objects, olen, ref, rest, top, val, value, vvar, vvarText; top = o.level === LEVEL_TOP; - ({ - value: value - } = this); - ({ - objects: objects - } = this.variable.base); + ({value} = this); + ({objects} = this.variable.base); if (!(olen = objects.length)) { code = value.compileToFragments(o); if (o.level >= LEVEL_OP) { @@ -2613,11 +2579,7 @@ compileSplice(o) { var answer, exclusive, from, fromDecl, fromRef, name, to, valDef, valRef; ({ - range: { - from: from, - to: to, - exclusive: exclusive - } + range: {from, to, exclusive} } = this.variable.properties.pop()); name = this.variable.compile(o); if (from) { @@ -3150,9 +3112,7 @@ jumps() { var expressions, j, jumpNode, len1, node; - ({ - expressions: expressions - } = this.body); + ({expressions} = this.body); if (!expressions.length) { return false; } @@ -3171,9 +3131,7 @@ var answer, body, rvar, set; o.indent += TAB; set = ''; - ({ - body: body - } = this); + ({body} = this); if (body.isEmpty()) { body = this.makeCode(''); } else { @@ -3772,13 +3730,7 @@ constructor(body, source) { var ref1, ref2; super(); - ({ - source: this.source, - guard: this.guard, - step: this.step, - name: this.name, - index: this.index - } = source); + ({source: this.source, guard: this.guard, step: this.step, name: this.name, index: this.index} = source); this.body = Block.wrap([body]); this.own = !!source.own; this.object = !!source.object; @@ -3857,7 +3809,7 @@ if (this.range) { forPartFragments = source.compileToFragments(merge(o, { index: ivar, - name: name, + name, step: this.step, shouldCache: shouldCacheOrIsAssignable })); @@ -4063,9 +4015,7 @@ this.condition = options.type === 'unless' ? condition.invert() : condition; this.elseBody = null; this.isChain = false; - ({ - soak: this.soak - } = options); + ({soak: this.soak} = options); } bodyNode() { @@ -4135,9 +4085,7 @@ } indent = o.indent + TAB; cond = this.condition.compileToFragments(o, LEVEL_PAREN); - body = this.ensureBlock(this.body).compileToFragments(merge(o, { - indent: indent - })); + body = this.ensureBlock(this.body).compileToFragments(merge(o, {indent})); ifPart = [].concat(this.makeCode("if ("), cond, this.makeCode(") {\n"), body, this.makeCode(`\n${this.tab}}`)); if (!child) { ifPart.unshift(this.makeCode(this.tab)); @@ -4150,9 +4098,7 @@ o.chainChild = true; answer = answer.concat(this.elseBody.unwrap().compileToFragments(o, LEVEL_TOP)); } else { - answer = answer.concat(this.makeCode("{\n"), this.elseBody.compileToFragments(merge(o, { - indent: indent - }), LEVEL_TOP), this.makeCode(`\n${this.tab}}`)); + answer = answer.concat(this.makeCode("{\n"), this.elseBody.compileToFragments(merge(o, {indent}), LEVEL_TOP), this.makeCode(`\n${this.tab}}`)); } return answer; } @@ -4221,9 +4167,7 @@ utility = function(name, o) { var ref, root; - ({ - root: root - } = o.scope); + ({root} = o.scope); if (name in root.utilities) { return root.utilities[name]; } else { diff --git a/lib/coffeescript/optparse.js b/lib/coffeescript/optparse.js index fb4998c4a6..dbd707953f 100644 --- a/lib/coffeescript/optparse.js +++ b/lib/coffeescript/optparse.js @@ -2,9 +2,7 @@ (function() { var LONG_FLAG, MULTI_FLAG, OPTIONAL, OptionParser, SHORT_FLAG, buildRule, buildRules, normalizeArguments, repeat; - ({ - repeat: repeat - } = require('./helpers')); + ({repeat} = require('./helpers')); exports.OptionParser = OptionParser = class OptionParser { constructor(rules, banner) { diff --git a/lib/coffeescript/register.js b/lib/coffeescript/register.js index 7b0a2f9930..bc759db22a 100644 --- a/lib/coffeescript/register.js +++ b/lib/coffeescript/register.js @@ -48,9 +48,7 @@ } if (child_process) { - ({ - fork: fork - } = child_process); + ({fork} = child_process); binary = require.resolve('../../bin/coffee'); child_process.fork = function(path, args, options) { if (helpers.isCoffee(path)) { diff --git a/lib/coffeescript/repl.js b/lib/coffeescript/repl.js index dfbbdaeff9..0c299bedf0 100644 --- a/lib/coffeescript/repl.js +++ b/lib/coffeescript/repl.js @@ -12,10 +12,7 @@ CoffeeScript = require('./coffeescript'); - ({ - merge: merge, - updateSyntaxError: updateSyntaxError - } = require('./helpers')); + ({merge, updateSyntaxError} = require('./helpers')); replDefaults = { prompt: 'coffee> ', @@ -26,12 +23,7 @@ input = input.replace(/\uFF00/g, '\n'); input = input.replace(/^\(([\s\S]*)\n\)$/m, '$1'); input = input.replace(/^\s*try\s*{([\s\S]*)}\s*catch.*$/m, '$1'); - ({ - Block: Block, - Assign: Assign, - Value: Value, - Literal: Literal - } = require('./nodes')); + ({Block, Assign, Value, Literal} = require('./nodes')); try { tokens = CoffeeScript.tokens(input); referencedVars = (function() { @@ -50,7 +42,7 @@ js = ast.compile({ bare: true, locals: Object.keys(context), - referencedVars: referencedVars + referencedVars }); return cb(null, runInContext(js, context, filename)); } catch (error) { @@ -71,11 +63,7 @@ addMultilineHandler = function(repl) { var inputStream, multiline, nodeLineListener, origPrompt, outputStream, ref, rli; - ({ - rli: rli, - inputStream: inputStream, - outputStream: outputStream - } = repl); + ({rli, inputStream, outputStream} = repl); origPrompt = (ref = repl._prompt) != null ? ref : repl.prompt; multiline = { enabled: false, diff --git a/lib/coffeescript/rewriter.js b/lib/coffeescript/rewriter.js index f629ece855..938da10920 100644 --- a/lib/coffeescript/rewriter.js +++ b/lib/coffeescript/rewriter.js @@ -30,9 +30,7 @@ scanTokens(block) { var i, token, tokens; - ({ - tokens: tokens - } = this); + ({tokens} = this); i = 0; while (token = tokens[i]) { i += block.call(this, token, i, tokens); @@ -42,9 +40,7 @@ detectEnd(i, condition, action) { var levels, ref, ref1, token, tokens; - ({ - tokens: tokens - } = this); + ({tokens} = this); levels = 0; while (token = tokens[i]) { if (levels === 0 && condition.call(this, token, i)) { @@ -320,12 +316,7 @@ newLine = prevTag === 'OUTDENT' || prevToken.newLine; if (indexOf.call(IMPLICIT_END, tag) >= 0 || indexOf.call(CALL_CLOSERS, tag) >= 0 && newLine) { while (inImplicit()) { - [ - stackTag, stackIdx, { - sameLine: sameLine, - startsLine: startsLine - } - ] = stackTop(); + [stackTag, stackIdx, {sameLine, startsLine}] = stackTop(); if (inImplicitCall() && prevTag !== ',') { endImplicitCall(); } else if (inImplicitObject() && !this.insideForDeclaration && sameLine && tag !== 'TERMINATOR' && prevTag !== ':') { diff --git a/lib/coffeescript/scope.js b/lib/coffeescript/scope.js index c8e823ca31..87a4938443 100644 --- a/lib/coffeescript/scope.js +++ b/lib/coffeescript/scope.js @@ -30,10 +30,7 @@ if (Object.prototype.hasOwnProperty.call(this.positions, name)) { return this.variables[this.positions[name]].type = type; } else { - return this.positions[name] = this.variables.push({ - name: name, - type: type - }) - 1; + return this.positions[name] = this.variables.push({name, type}) - 1; } } @@ -110,7 +107,7 @@ assign(name, value) { this.add(name, { - value: value, + value, assigned: true }, true); return this.hasAssignments = true; diff --git a/lib/coffeescript/sourcemap.js b/lib/coffeescript/sourcemap.js index 98c898d56f..659fb5cd20 100644 --- a/lib/coffeescript/sourcemap.js +++ b/lib/coffeescript/sourcemap.js @@ -19,9 +19,9 @@ } return this.columns[column] = { line: this.line, - column: column, - sourceLine: sourceLine, - sourceColumn: sourceColumn + column, + sourceLine, + sourceColumn }; } diff --git a/src/nodes.coffee b/src/nodes.coffee index 4c5ab47584..5f55de8d69 100644 --- a/src/nodes.coffee +++ b/src/nodes.coffee @@ -1128,16 +1128,22 @@ exports.Obj = class Obj extends Base node.error 'cannot have an implicit value in an implicit object' idt = o.indent += TAB lastNoncom = @lastNonComment @properties + + isCompact = true + isCompact = false for prop in @properties when prop instanceof Assign or prop instanceof Comment + answer = [] - answer.push @makeCode "{#{if props.length is 0 then '}' else '\n'}" + answer.push @makeCode "{#{if isCompact then '' else '\n'}" for prop, i in props join = if i is props.length - 1 '' + else if isCompact + ', ' else if prop is lastNoncom or prop instanceof Comment '\n' else ',\n' - indent = if prop instanceof Comment then '' else idt + indent = if isCompact or prop instanceof Comment then '' else idt if prop instanceof Assign if prop.context isnt 'object' prop.operatorToken.error "unexpected #{prop.operatorToken.value}" @@ -1150,12 +1156,12 @@ exports.Obj = class Obj extends Base [key, value] = prop.base.cache o key = new PropertyName key.value if key instanceof IdentifierLiteral prop = new Assign key, value, 'object' - else + else if not prop.bareLiteral?(IdentifierLiteral) prop = new Assign prop, prop, 'object' if indent then answer.push @makeCode indent answer.push prop.compileToFragments(o, LEVEL_TOP)... if join then answer.push @makeCode join - answer.push @makeCode "\n#{@tab}}" unless props.length is 0 + answer.push @makeCode "#{if isCompact then '' else "\n#{@tab}"}}" if @front then @wrapInBraces answer else answer assigns: (name) -> From 2e0dab3dad81969f5bc9a78b5782b6bd608ef215 Mon Sep 17 00:00:00 2001 From: Chris Connelly Date: Sat, 1 Apr 2017 15:24:15 +0100 Subject: [PATCH 04/20] Compile default values in destructured assignment to ES2015 --- lib/coffeescript/nodes.js | 30 +++++++++++++++++------------- src/nodes.coffee | 39 ++++++++++++++++++++++++++++----------- test/assignment.coffee | 16 ++++++++-------- test/functions.coffee | 2 +- 4 files changed, 54 insertions(+), 33 deletions(-) diff --git a/lib/coffeescript/nodes.js b/lib/coffeescript/nodes.js index c076004cb2..c6aef2601c 100644 --- a/lib/coffeescript/nodes.js +++ b/lib/coffeescript/nodes.js @@ -1477,10 +1477,11 @@ isAssignable() { var j, len1, prop, ref1; + this.lhs = true; ref1 = this.properties; for (j = 0, len1 = ref1.length; j < len1; j++) { prop = ref1[j]; - if (prop instanceof Assign) { + if (prop instanceof Assign && prop.context === 'object') { prop = prop.value; } if (!prop.isAssignable()) { @@ -1507,7 +1508,7 @@ ref1 = this.properties; for (k = 0, len2 = ref1.length; k < len2; k++) { prop = ref1[k]; - if (prop instanceof Assign || prop instanceof Comment) { + if (prop instanceof Comment || (prop instanceof Assign && prop.context === 'object')) { isCompact = false; } } @@ -1517,18 +1518,15 @@ prop = props[i]; join = i === props.length - 1 ? '' : isCompact ? ', ' : prop === lastNoncom || prop instanceof Comment ? '\n' : ',\n'; indent = isCompact || prop instanceof Comment ? '' : idt; - if (prop instanceof Assign) { - if (prop.context !== 'object') { - prop.operatorToken.error(`unexpected ${prop.operatorToken.value}`); + key = prop instanceof Assign && prop.context === 'object' ? prop.variable : prop instanceof Assign ? (!this.lhs ? prop.operatorToken.error(`unexpected ${prop.operatorToken.value}`) : void 0, prop.variable) : !(prop instanceof Comment) ? prop : void 0; + if (key instanceof Value && key.hasProperties()) { + if (prop.context === 'object' || !key["this"]) { + key.error('invalid object key'); } - if (prop.variable instanceof Value && prop.variable.hasProperties()) { - prop.variable.error('invalid object key'); - } - } - if (prop instanceof Value && prop["this"]) { - prop = new Assign(prop.properties[0].name, prop, 'object'); + key = key.properties[0].name; + prop = new Assign(key, prop, 'object'); } - if (!(prop instanceof Comment) && !(prop instanceof Assign)) { + if (key === prop) { if (prop.shouldCache()) { [key, value] = prop.base.cache(o); if (key instanceof IdentifierLiteral) { @@ -1573,7 +1571,7 @@ results = []; for (j = 0, len1 = ref1.length; j < len1; j++) { prop = ref1[j]; - if (prop instanceof Assign) { + if (prop instanceof Assign && prop.context === 'object') { prop = prop.value; } prop = prop.unwrapAll(); @@ -2611,10 +2609,16 @@ } } + eachName(iterator) { + return this.variable.unwrapAll().eachName(iterator); + } + }; Assign.prototype.children = ['variable', 'value']; + Assign.prototype.isAssignable = YES; + return Assign; })(); diff --git a/src/nodes.coffee b/src/nodes.coffee index 5f55de8d69..64df3fc7ae 100644 --- a/src/nodes.coffee +++ b/src/nodes.coffee @@ -1116,8 +1116,10 @@ exports.Obj = class Obj extends Base children: ['properties'] isAssignable: -> + @lhs = true + for prop in @properties - prop = prop.value if prop instanceof Assign + prop = prop.value if prop instanceof Assign and prop.context is 'object' return false unless prop.isAssignable() true @@ -1130,7 +1132,9 @@ exports.Obj = class Obj extends Base lastNoncom = @lastNonComment @properties isCompact = true - isCompact = false for prop in @properties when prop instanceof Assign or prop instanceof Comment + for prop in @properties + if prop instanceof Comment or (prop instanceof Assign and prop.context is 'object') + isCompact = false answer = [] answer.push @makeCode "{#{if isCompact then '' else '\n'}" @@ -1144,20 +1148,28 @@ exports.Obj = class Obj extends Base else ',\n' indent = if isCompact or prop instanceof Comment then '' else idt - if prop instanceof Assign - if prop.context isnt 'object' - prop.operatorToken.error "unexpected #{prop.operatorToken.value}" - if prop.variable instanceof Value and prop.variable.hasProperties() - prop.variable.error 'invalid object key' - if prop instanceof Value and prop.this - prop = new Assign prop.properties[0].name, prop, 'object' - if prop not instanceof Comment and prop not instanceof Assign + + key = if prop instanceof Assign and prop.context is 'object' + prop.variable + else if prop instanceof Assign + prop.operatorToken.error "unexpected #{prop.operatorToken.value}" unless @lhs + prop.variable + else if prop not instanceof Comment + prop + + if key instanceof Value and key.hasProperties() + key.error 'invalid object key' if prop.context is 'object' or not key.this + key = key.properties[0].name + prop = new Assign key, prop, 'object' + + if key == prop if prop.shouldCache() [key, value] = prop.base.cache o key = new PropertyName key.value if key instanceof IdentifierLiteral prop = new Assign key, value, 'object' else if not prop.bareLiteral?(IdentifierLiteral) prop = new Assign prop, prop, 'object' + if indent then answer.push @makeCode indent answer.push prop.compileToFragments(o, LEVEL_TOP)... if join then answer.push @makeCode join @@ -1170,7 +1182,7 @@ exports.Obj = class Obj extends Base eachName: (iterator) -> for prop in @properties - prop = prop.value if prop instanceof Assign + prop = prop.value if prop instanceof Assign and prop.context is 'object' prop = prop.unwrapAll() prop.eachName iterator @@ -1682,6 +1694,8 @@ exports.Assign = class Assign extends Base children: ['variable', 'value'] + isAssignable: YES + isStatement: (o) -> o?.level is LEVEL_TOP and @context? and (@moduleDeclaration or "?" in @context) @@ -1905,6 +1919,9 @@ exports.Assign = class Assign extends Base answer = [].concat @makeCode("[].splice.apply(#{name}, [#{fromDecl}, #{to}].concat("), valDef, @makeCode(")), "), valRef if o.level > LEVEL_TOP then @wrapInBraces answer else answer + eachName: (iterator) -> + @variable.unwrapAll().eachName iterator + #### Code # A function definition. This is the only node that creates a new Scope. diff --git a/test/assignment.coffee b/test/assignment.coffee index 20c940d700..23cea501a7 100644 --- a/test/assignment.coffee +++ b/test/assignment.coffee @@ -302,7 +302,7 @@ test "simple array destructuring defaults", -> [a = 2] = [undefined] eq 2, a [a = 3] = [null] - eq 3, a + eq null, a [a = 4] = [0] eq 0, a arr = [a = 5] @@ -315,7 +315,7 @@ test "simple object destructuring defaults", -> {b = 2} = {b: undefined} eq b, 2 {b = 3} = {b: null} - eq b, 3 + eq b, null {b = 4} = {b: 0} eq b, 0 @@ -324,17 +324,17 @@ test "simple object destructuring defaults", -> {b: c = 2} = {b: undefined} eq c, 2 {b: c = 3} = {b: null} - eq c, 3 + eq c, null {b: c = 4} = {b: 0} eq c, 0 test "multiple array destructuring defaults", -> - [a = 1, b = 2, c] = [null, 12, 13] + [a = 1, b = 2, c] = [undefined, 12, 13] eq a, 1 eq b, 12 eq c, 13 - [a, b = 2, c = 3] = [null, 12, 13] - eq a, null + [a, b = 2, c = 3] = [undefined, 12, 13] + eq a, undefined eq b, 12 eq c, 13 [a = 1, b, c = 3] = [11, 12] @@ -365,7 +365,7 @@ test "destructuring assignment with context (@) properties and defaults", -> a={}; b={}; c={}; d={}; e={} obj = fn: () -> - local = [a, {b, c: null}, d] + local = [a, {b, c: undefined}, d] [@a, {b: @b = b, @c = c}, @d, @e = e] = local eq undefined, obj[key] for key in ['a','b','c','d','e'] obj.fn() @@ -384,7 +384,7 @@ test "destructuring assignment with defaults single evaluation", -> [a = fn()] = [10] eq 10, a eq 1, callCount - {a = fn(), b: c = fn()} = {a: 20, b: null} + {a = fn(), b: c = fn()} = {a: 20, b: undefined} eq 20, a eq c, 1 eq callCount, 2 diff --git a/test/functions.coffee b/test/functions.coffee index 89cf3e3abd..b9f7198555 100644 --- a/test/functions.coffee +++ b/test/functions.coffee @@ -179,7 +179,7 @@ test "destructuring in function definition", -> {url, async, beforeSend, cache, method, data} fn = -> - deepEqual ajax('/home', beforeSend: fn, cache: null, method: 'post'), { + deepEqual ajax('/home', beforeSend: fn, method: 'post'), { url: '/home', async: true, beforeSend: fn, cache: true, method: 'post', data: {} } From a196614509f2f2c9b9041ad725edb4f248fcfbec Mon Sep 17 00:00:00 2001 From: Geoffrey Booth Date: Sat, 1 Apr 2017 20:15:30 -0700 Subject: [PATCH 05/20] Rename `wrapInBraces` to `wrapInParentheses`, and `compilePatternMatch` to `compileDestructuring`, for clarity; style improvements (no `==` or `!=`, etc.) --- lib/coffeescript/nodes.js | 38 +++++++++++----------- src/nodes.coffee | 66 ++++++++++++++++++++------------------- 2 files changed, 53 insertions(+), 51 deletions(-) diff --git a/lib/coffeescript/nodes.js b/lib/coffeescript/nodes.js index c6aef2601c..4b23cf6141 100644 --- a/lib/coffeescript/nodes.js +++ b/lib/coffeescript/nodes.js @@ -292,7 +292,7 @@ return new CodeFragment(this, code); } - wrapInBraces(fragments) { + wrapInParentheses(fragments) { return [].concat(this.makeCode('('), fragments, this.makeCode(')')); } @@ -499,7 +499,7 @@ answer = [this.makeCode("void 0")]; } if (compiledNodes.length > 1 && o.level >= LEVEL_LIST) { - return this.wrapInBraces(answer); + return this.wrapInParentheses(answer); } else { return answer; } @@ -657,7 +657,7 @@ var code; code = [this.makeCode('0/0')]; if (o.level >= LEVEL_OP) { - return this.wrapInBraces(code); + return this.wrapInParentheses(code); } else { return code; } @@ -1547,7 +1547,7 @@ } answer.push(this.makeCode(`${(isCompact ? '' : `\n${this.tab}`)}}`)); if (this.front) { - return this.wrapInBraces(answer); + return this.wrapInParentheses(answer); } else { return answer; } @@ -1703,7 +1703,7 @@ } else { result = this.compileClassDeclaration(o); if ((this.name == null) && o.level === LEVEL_TOP) { - result = this.wrapInBraces(result); + result = this.wrapInParentheses(result); } } if (this.variable) { @@ -2336,7 +2336,7 @@ if (isValue = this.variable instanceof Value) { if (this.variable.isArray() || this.variable.isObject()) { if (!this.variable.isAssignable()) { - return this.compilePatternMatch(o); + return this.compileDestructuring(o); } } if (this.variable.isSplice()) { @@ -2397,13 +2397,13 @@ } answer = compiledName.concat(this.makeCode(` ${this.context || '='} `), val); if (o.level > LEVEL_LIST || (isValue && this.variable.base instanceof Obj)) { - return this.wrapInBraces(answer); + return this.wrapInParentheses(answer); } else { return answer; } } - compilePatternMatch(o) { + compileDestructuring(o) { var acc, assigns, code, defaultValue, expandedIdx, fragments, i, idx, isObject, ivar, j, len1, message, name, obj, objects, olen, ref, rest, top, val, value, vvar, vvarText; top = o.level === LEVEL_TOP; ({value} = this); @@ -2411,7 +2411,7 @@ if (!(olen = objects.length)) { code = value.compileToFragments(o); if (o.level >= LEVEL_OP) { - return this.wrapInBraces(code); + return this.wrapInParentheses(code); } else { return code; } @@ -2543,7 +2543,7 @@ if (o.level < LEVEL_LIST) { return fragments; } else { - return this.wrapInBraces(fragments); + return this.wrapInParentheses(fragments); } } @@ -2563,7 +2563,7 @@ if (o.level <= LEVEL_LIST) { return fragments; } else { - return this.wrapInBraces(fragments); + return this.wrapInParentheses(fragments); } } } @@ -2603,7 +2603,7 @@ [valDef, valRef] = this.value.cache(o, LEVEL_LIST); answer = [].concat(this.makeCode(`[].splice.apply(${name}, [${fromDecl}, ${to}].concat(`), valDef, this.makeCode(")), "), valRef); if (o.level > LEVEL_TOP) { - return this.wrapInBraces(answer); + return this.wrapInParentheses(answer); } else { return answer; } @@ -2848,7 +2848,7 @@ return [this.makeCode(this.tab), ...answer]; } if (this.front || (o.level >= LEVEL_ACCESS)) { - return this.wrapInBraces(answer); + return this.wrapInParentheses(answer); } else { return answer; } @@ -3323,7 +3323,7 @@ if (o.level <= LEVEL_OP) { return answer; } else { - return this.wrapInBraces(answer); + return this.wrapInParentheses(answer); } } } @@ -3333,7 +3333,7 @@ [this.first.second, shared] = this.first.second.cache(o); fst = this.first.compileToFragments(o, LEVEL_OP); fragments = fst.concat(this.makeCode(` ${(this.invert ? '&&' : '||')} `), shared.compileToFragments(o), this.makeCode(` ${this.operator} `), this.second.compileToFragments(o, LEVEL_OP)); - return this.wrapInBraces(fragments); + return this.wrapInParentheses(fragments); } compileExistence(o) { @@ -3493,7 +3493,7 @@ if (o.level < LEVEL_OP) { return tests; } else { - return this.wrapInBraces(tests); + return this.wrapInParentheses(tests); } } @@ -3508,7 +3508,7 @@ if (o.level < LEVEL_LIST) { return fragments; } else { - return this.wrapInBraces(fragments); + return this.wrapInParentheses(fragments); } } @@ -3656,7 +3656,7 @@ if (bare) { return fragments; } else { - return this.wrapInBraces(fragments); + return this.wrapInParentheses(fragments); } } @@ -4114,7 +4114,7 @@ alt = this.elseBodyNode() ? this.elseBodyNode().compileToFragments(o, LEVEL_LIST) : [this.makeCode('void 0')]; fragments = cond.concat(this.makeCode(" ? "), body, this.makeCode(" : "), alt); if (o.level >= LEVEL_COND) { - return this.wrapInBraces(fragments); + return this.wrapInParentheses(fragments); } else { return fragments; } diff --git a/src/nodes.coffee b/src/nodes.coffee index 64df3fc7ae..151327a163 100644 --- a/src/nodes.coffee +++ b/src/nodes.coffee @@ -279,7 +279,7 @@ exports.Base = class Base makeCode: (code) -> new CodeFragment this, code - wrapInBraces: (fragments) -> + wrapInParentheses: (fragments) -> [].concat @makeCode('('), fragments, @makeCode(')') # `fragmentsList` is an array of arrays of fragments. Each array in fragmentsList will be @@ -432,7 +432,7 @@ exports.Block = class Block extends Base answer = @joinFragmentArrays(compiledNodes, ', ') else answer = [@makeCode "void 0"] - if compiledNodes.length > 1 and o.level >= LEVEL_LIST then @wrapInBraces answer else answer + if compiledNodes.length > 1 and o.level >= LEVEL_LIST then @wrapInParentheses answer else answer # If we happen to be the top-level **Block**, wrap everything in # a safety closure, unless requested not to. @@ -532,7 +532,7 @@ exports.NaNLiteral = class NaNLiteral extends NumberLiteral compileNode: (o) -> code = [@makeCode '0/0'] - if o.level >= LEVEL_OP then @wrapInBraces code else code + if o.level >= LEVEL_OP then @wrapInParentheses code else code exports.StringLiteral = class StringLiteral extends Literal @@ -1108,7 +1108,7 @@ exports.Slice = class Slice extends Base # An object literal, nothing fancy. exports.Obj = class Obj extends Base - constructor: (props, @generated = false) -> + constructor: (props, @generated = no) -> super() @objects = @properties = props or [] @@ -1116,12 +1116,14 @@ exports.Obj = class Obj extends Base children: ['properties'] isAssignable: -> - @lhs = true + # Is this object the left-hand side of an assignment? If it is, this object + # is part of a destructuring assignment. + @lhs = yes for prop in @properties prop = prop.value if prop instanceof Assign and prop.context is 'object' - return false unless prop.isAssignable() - true + return no unless prop.isAssignable() + yes compileNode: (o) -> props = @properties @@ -1131,10 +1133,10 @@ exports.Obj = class Obj extends Base idt = o.indent += TAB lastNoncom = @lastNonComment @properties - isCompact = true + isCompact = yes for prop in @properties if prop instanceof Comment or (prop instanceof Assign and prop.context is 'object') - isCompact = false + isCompact = no answer = [] answer.push @makeCode "{#{if isCompact then '' else '\n'}" @@ -1162,7 +1164,7 @@ exports.Obj = class Obj extends Base key = key.properties[0].name prop = new Assign key, prop, 'object' - if key == prop + if key is prop if prop.shouldCache() [key, value] = prop.base.cache o key = new PropertyName key.value if key instanceof IdentifierLiteral @@ -1174,7 +1176,7 @@ exports.Obj = class Obj extends Base answer.push prop.compileToFragments(o, LEVEL_TOP)... if join then answer.push @makeCode join answer.push @makeCode "#{if isCompact then '' else "\n#{@tab}"}}" - if @front then @wrapInBraces answer else answer + if @front then @wrapInParentheses answer else answer assigns: (name) -> for prop in @properties when prop.assigns name then return yes @@ -1201,7 +1203,7 @@ exports.Arr = class Arr extends Base return false unless @objects.length for obj, i in @objects - return false if obj instanceof Splat and i + 1 != @objects.length + return false if obj instanceof Splat and i + 1 isnt @objects.length return false unless obj.isAssignable() and (not obj.isAtomic or obj.isAtomic()) true @@ -1249,7 +1251,7 @@ exports.Class = class Class extends Base # Special handling to allow `class expr.A extends A` declarations parentName = @parent.base.value if @parent instanceof Value and not @parent.hasProperties() - @hasNameClash = @name? and @name == parentName + @hasNameClash = @name? and @name is parentName if executableBody or @hasNameClash @compileNode = @compileClassDeclaration @@ -1259,7 +1261,7 @@ exports.Class = class Class extends Base result = @compileClassDeclaration o # Anonymous classes are only valid in expressions - result = @wrapInBraces result if not @name? and o.level is LEVEL_TOP + result = @wrapInParentheses result if not @name? and o.level is LEVEL_TOP if @variable assign = new Assign @variable, new Literal(''), null, { @moduleDeclaration } @@ -1358,7 +1360,7 @@ exports.Class = class Class extends Base @boundMethods.push method.name method.bound = false - if initializer.length != expressions.length + if initializer.length isnt expressions.length @body.expressions = (expression.hoist() for expression in initializer) new Block expressions @@ -1459,7 +1461,7 @@ exports.ExecutableClassBody = class ExecutableClassBody extends Base @class.externalCtor = externalCtor @externalCtor.variable.base = externalCtor - if @name != @class.name + if @name isnt @class.name @body.expressions.unshift new Assign (new IdentifierLiteral @name), @class else @body.expressions.unshift @class @@ -1710,14 +1712,14 @@ exports.Assign = class Assign extends Base unfoldSoak: (o) -> unfoldSoak o, this, 'variable' - # Compile an assignment, delegating to `compilePatternMatch` or + # Compile an assignment, delegating to `compileDestructuring` or # `compileSplice` if appropriate. Keep track of the name of the base object # we've been assigned to, for correct internal references. If the variable # has not been seen yet within the current scope, declare it. compileNode: (o) -> if isValue = @variable instanceof Value if @variable.isArray() or @variable.isObject() - return @compilePatternMatch o unless @variable.isAssignable() + return @compileDestructuring o unless @variable.isAssignable() return @compileSplice o if @variable.isSplice() return @compileConditional o if @context in ['||=', '&&=', '?='] @@ -1763,17 +1765,17 @@ exports.Assign = class Assign extends Base return compiledName.concat @makeCode(": "), val answer = compiledName.concat @makeCode(" #{ @context or '=' } "), val - if o.level > LEVEL_LIST or (isValue and @variable.base instanceof Obj) then @wrapInBraces answer else answer + if o.level > LEVEL_LIST or (isValue and @variable.base instanceof Obj) then @wrapInParentheses answer else answer # Brief implementation of recursive pattern matching, when assigning array or # object literals to a value. Peeks at their properties to assign inner names. - compilePatternMatch: (o) -> + compileDestructuring: (o) -> top = o.level is LEVEL_TOP {value} = this {objects} = @variable.base unless olen = objects.length code = value.compileToFragments o - return if o.level >= LEVEL_OP then @wrapInBraces code else code + return if o.level >= LEVEL_OP then @wrapInParentheses code else code [obj] = objects if olen is 1 and obj instanceof Expansion obj.error 'Destructuring assignment has no target' @@ -1873,7 +1875,7 @@ exports.Assign = class Assign extends Base assigns.push new Assign(obj, val, null, param: @param, subpattern: yes).compileToFragments o, LEVEL_LIST assigns.push vvar unless top or @subpattern fragments = @joinFragmentArrays assigns, ', ' - if o.level < LEVEL_LIST then fragments else @wrapInBraces fragments + if o.level < LEVEL_LIST then fragments else @wrapInParentheses fragments # When compiling a conditional assignment, take care to ensure that the # operands are only evaluated once, even though we have to reference them @@ -1889,7 +1891,7 @@ exports.Assign = class Assign extends Base new If(new Existence(left), right, type: 'if').addElse(new Assign(right, @value, '=')).compileToFragments o else fragments = new Op(@context[...-1], left, new Assign(right, @value, '=')).compileToFragments o - if o.level <= LEVEL_LIST then fragments else @wrapInBraces fragments + if o.level <= LEVEL_LIST then fragments else @wrapInParentheses fragments # Convert special math assignment operators like `a **= b` to the equivalent # extended form `a = a ** b` and then compiles that. @@ -1917,7 +1919,7 @@ exports.Assign = class Assign extends Base to = "9e9" [valDef, valRef] = @value.cache o, LEVEL_LIST answer = [].concat @makeCode("[].splice.apply(#{name}, [#{fromDecl}, #{to}].concat("), valDef, @makeCode(")), "), valRef - if o.level > LEVEL_TOP then @wrapInBraces answer else answer + if o.level > LEVEL_TOP then @wrapInParentheses answer else answer eachName: (iterator) -> @variable.unwrapAll().eachName iterator @@ -2123,7 +2125,7 @@ exports.Code = class Code extends Base answer.push @makeCode '}' return [@makeCode(@tab), answer...] if @isMethod - if @front or (o.level >= LEVEL_ACCESS) then @wrapInBraces answer else answer + if @front or (o.level >= LEVEL_ACCESS) then @wrapInParentheses answer else answer eachParamName: (iterator) -> param.eachName iterator for param in @params @@ -2151,7 +2153,7 @@ exports.Code = class Code extends Base superCall.error "'super' is only allowed in derived class constructors" if @ctor is 'base' superCall.expressions = thisAssignments - haveThisParam = thisAssignments.length and thisAssignments.length != @thisAssignments?.length + haveThisParam = thisAssignments.length and thisAssignments.length isnt @thisAssignments?.length if @ctor is 'derived' and not seenSuper and haveThisParam param = thisAssignments[0].variable param.error "Can't use @params in derived class constructors without calling super" @@ -2486,7 +2488,7 @@ exports.Op = class Op extends Base lhs = @first.compileToFragments o, LEVEL_OP rhs = @second.compileToFragments o, LEVEL_OP answer = [].concat lhs, @makeCode(" #{@operator} "), rhs - if o.level <= LEVEL_OP then answer else @wrapInBraces answer + if o.level <= LEVEL_OP then answer else @wrapInParentheses answer # Mimic Python's chained comparisons when multiple comparison operators are # used sequentially. For example: @@ -2498,7 +2500,7 @@ exports.Op = class Op extends Base fst = @first.compileToFragments o, LEVEL_OP fragments = fst.concat @makeCode(" #{if @invert then '&&' else '||'} "), (shared.compileToFragments o), @makeCode(" #{@operator} "), (@second.compileToFragments o, LEVEL_OP) - @wrapInBraces fragments + @wrapInParentheses fragments # Keep reference to the left expression, unless this an existential assignment compileExistence: (o) -> @@ -2589,7 +2591,7 @@ exports.In = class In extends Base for item, i in @array.base.objects if i then tests.push @makeCode cnj tests = tests.concat (if i then ref else sub), @makeCode(cmp), item.compileToFragments(o, LEVEL_ACCESS) - if o.level < LEVEL_OP then tests else @wrapInBraces tests + if o.level < LEVEL_OP then tests else @wrapInParentheses tests compileLoopTest: (o) -> [sub, ref] = @object.cache o, LEVEL_LIST @@ -2597,7 +2599,7 @@ exports.In = class In extends Base @makeCode(", "), ref, @makeCode(") " + if @negated then '< 0' else '>= 0') return fragments if fragmentsToText(sub) is fragmentsToText(ref) fragments = sub.concat @makeCode(', '), fragments - if o.level < LEVEL_LIST then fragments else @wrapInBraces fragments + if o.level < LEVEL_LIST then fragments else @wrapInParentheses fragments toString: (idt) -> super idt, @constructor.name + if @negated then '!' else '' @@ -2715,7 +2717,7 @@ exports.Parens = class Parens extends Base fragments = expr.compileToFragments o, LEVEL_PAREN bare = o.level < LEVEL_OP and (expr instanceof Op or expr instanceof Call or (expr instanceof For and expr.returns)) - if bare then fragments else @wrapInBraces fragments + if bare then fragments else @wrapInParentheses fragments #### StringWithInterpolations @@ -3022,7 +3024,7 @@ exports.If = class If extends Base body = @bodyNode().compileToFragments o, LEVEL_LIST alt = if @elseBodyNode() then @elseBodyNode().compileToFragments(o, LEVEL_LIST) else [@makeCode('void 0')] fragments = cond.concat @makeCode(" ? "), body, @makeCode(" : "), alt - if o.level >= LEVEL_COND then @wrapInBraces fragments else fragments + if o.level >= LEVEL_COND then @wrapInParentheses fragments else fragments unfoldSoak: -> @soak and this From b11ba7f234e069e90b886149043bd07ecfc5ac92 Mon Sep 17 00:00:00 2001 From: Geoffrey Booth Date: Sat, 4 Feb 2017 22:26:18 -0800 Subject: [PATCH 06/20] =?UTF-8?q?Don=E2=80=99t=20confuse=20the=20syntax=20?= =?UTF-8?q?highlighter?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/coffeescript/nodes.js | 3 ++- src/nodes.coffee | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/coffeescript/nodes.js b/lib/coffeescript/nodes.js index 4b23cf6141..7b0c224b07 100644 --- a/lib/coffeescript/nodes.js +++ b/lib/coffeescript/nodes.js @@ -2460,7 +2460,8 @@ assigns = []; expandedIdx = false; if (!(value.unwrap() instanceof IdentifierLiteral) || this.variable.assigns(vvarText)) { - assigns.push([this.makeCode(`${(ref = o.scope.freeVariable('ref'))} = `), ...vvar]); + ref = o.scope.freeVariable('ref'); + assigns.push([this.makeCode(ref + ' = '), ...vvar]); vvar = [this.makeCode(ref)]; vvarText = ref; } diff --git a/src/nodes.coffee b/src/nodes.coffee index 151327a163..429c76efa4 100644 --- a/src/nodes.coffee +++ b/src/nodes.coffee @@ -1816,7 +1816,8 @@ exports.Assign = class Assign extends Base expandedIdx = false # Make vvar into a simple variable if it isn't already. if value.unwrap() not instanceof IdentifierLiteral or @variable.assigns(vvarText) - assigns.push [@makeCode("#{ ref = o.scope.freeVariable 'ref' } = "), vvar...] + ref = o.scope.freeVariable 'ref' + assigns.push [@makeCode(ref + ' = '), vvar...] vvar = [@makeCode ref] vvarText = ref for obj, i in objects From c5f72792bcf8d064e10bd7f78ba4bda10b7bbe0b Mon Sep 17 00:00:00 2001 From: Simon Lydell Date: Sun, 5 Feb 2017 11:36:01 +0100 Subject: [PATCH 07/20] Comment Assign::compilePatternMatch a bit --- src/nodes.coffee | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/src/nodes.coffee b/src/nodes.coffee index 429c76efa4..59e368dc40 100644 --- a/src/nodes.coffee +++ b/src/nodes.coffee @@ -1773,13 +1773,22 @@ exports.Assign = class Assign extends Base top = o.level is LEVEL_TOP {value} = this {objects} = @variable.base + + # Special-case for `{} = a` and `[] = a` (empty patterns). Compile to simply + # `a`. unless olen = objects.length code = value.compileToFragments o return if o.level >= LEVEL_OP then @wrapInParentheses code else code [obj] = objects + + # Disallow `[...] = a` for some reason. (Could be equivalent to `[] = a`?) if olen is 1 and obj instanceof Expansion obj.error 'Destructuring assignment has no target' + isObject = @variable.isObject() + + # Special case for when there's only one thing destructured off of + # something. `{a} = b`, `[a] = b`, `{a: b} = c` if top and olen is 1 and obj not instanceof Splat # Pick the property straight off the value when there’s just one to pick # (no need to cache the value into a variable). @@ -1810,16 +1819,31 @@ exports.Assign = class Assign extends Base obj.error message if message value = new Op '?', value, defaultValue if defaultValue return new Assign(obj, value, null, param: @param).compileToFragments o, LEVEL_TOP + vvar = value.compileToFragments o, LEVEL_LIST vvarText = fragmentsToText vvar assigns = [] expandedIdx = false - # Make vvar into a simple variable if it isn't already. + + # At this point, there are several things to destructure. So the `fn()` in + # `{a, b} = fn()` must be cached, for example. Make vvar into a simple + # variable if it isn't already. if value.unwrap() not instanceof IdentifierLiteral or @variable.assigns(vvarText) ref = o.scope.freeVariable 'ref' assigns.push [@makeCode(ref + ' = '), vvar...] vvar = [@makeCode ref] vvarText = ref + + # And here comes the big loop that handles all of these cases: + # `[a, b] = c` + # `[a..., b] = c` + # `[..., a, b] = c` + # `[@a, b] = c` + # `[a = 1, b] = c` + # `{a, b} = c` + # `{@a, b} = c` + # `{a = 1, b} = c` + # etc. for obj, i in objects idx = i if not expandedIdx and obj instanceof Splat @@ -1874,6 +1898,7 @@ exports.Assign = class Assign extends Base message = isUnassignable name obj.error message if message assigns.push new Assign(obj, val, null, param: @param, subpattern: yes).compileToFragments o, LEVEL_LIST + assigns.push vvar unless top or @subpattern fragments = @joinFragmentArrays assigns, ', ' if o.level < LEVEL_LIST then fragments else @wrapInParentheses fragments From 93c8f8ec9f6ffabf2798ca62f69295511e36e3bf Mon Sep 17 00:00:00 2001 From: Geoffrey Booth Date: Wed, 8 Feb 2017 21:52:49 -0800 Subject: [PATCH 08/20] Assignment expressions in conditionals are a bad practice --- lib/coffeescript/nodes.js | 9 ++++++--- src/nodes.coffee | 13 ++++++++----- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/lib/coffeescript/nodes.js b/lib/coffeescript/nodes.js index 7b0c224b07..1c4a8a190c 100644 --- a/lib/coffeescript/nodes.js +++ b/lib/coffeescript/nodes.js @@ -2408,7 +2408,8 @@ top = o.level === LEVEL_TOP; ({value} = this); ({objects} = this.variable.base); - if (!(olen = objects.length)) { + olen = objects.length; + if (olen === 0) { code = value.compileToFragments(o); if (o.level >= LEVEL_OP) { return this.wrapInParentheses(code); @@ -2472,7 +2473,8 @@ name = obj.name.unwrap().value; obj = obj.unwrap(); val = `${olen} <= ${vvarText}.length ? ${utility('slice', o)}.call(${vvarText}, ${i}`; - if (rest = olen - i - 1) { + rest = olen - i - 1; + if (rest !== 0) { ivar = o.scope.freeVariable('i', { single: true }); @@ -2483,7 +2485,8 @@ val = new Literal(val); expandedIdx = `${ivar}++`; } else if (!expandedIdx && obj instanceof Expansion) { - if (rest = olen - i - 1) { + rest = olen - i - 1; + if (rest !== 0) { if (rest === 1) { expandedIdx = `${vvarText}.length - 1`; } else { diff --git a/src/nodes.coffee b/src/nodes.coffee index 59e368dc40..dcd3343ad0 100644 --- a/src/nodes.coffee +++ b/src/nodes.coffee @@ -1773,10 +1773,11 @@ exports.Assign = class Assign extends Base top = o.level is LEVEL_TOP {value} = this {objects} = @variable.base + olen = objects.length - # Special-case for `{} = a` and `[] = a` (empty patterns). Compile to simply - # `a`. - unless olen = objects.length + # Special-case for `{} = a` and `[] = a` (empty patterns). + # Compile to simply `a`. + if olen is 0 code = value.compileToFragments o return if o.level >= LEVEL_OP then @wrapInParentheses code else code [obj] = objects @@ -1850,7 +1851,8 @@ exports.Assign = class Assign extends Base name = obj.name.unwrap().value obj = obj.unwrap() val = "#{olen} <= #{vvarText}.length ? #{ utility 'slice', o }.call(#{vvarText}, #{i}" - if rest = olen - i - 1 + rest = olen - i - 1 + if rest isnt 0 ivar = o.scope.freeVariable 'i', single: true val += ", #{ivar} = #{vvarText}.length - #{rest}) : (#{ivar} = #{i}, [])" else @@ -1858,7 +1860,8 @@ exports.Assign = class Assign extends Base val = new Literal val expandedIdx = "#{ivar}++" else if not expandedIdx and obj instanceof Expansion - if rest = olen - i - 1 + rest = olen - i - 1 + if rest isnt 0 if rest is 1 expandedIdx = "#{vvarText}.length - 1" else From 8840aa928669430282d6901e159bb41a6ca5bf99 Mon Sep 17 00:00:00 2001 From: Geoffrey Booth Date: Tue, 28 Mar 2017 21:20:00 -0700 Subject: [PATCH 09/20] Optional check for existence that only checks `!== undefined`, not `!= null`, to follow ES convention that default values only apply when a variable is undefined, not falsy --- lib/coffeescript/nodes.js | 10 +++++++--- src/nodes.coffee | 25 ++++++++++++++++++------- 2 files changed, 25 insertions(+), 10 deletions(-) diff --git a/lib/coffeescript/nodes.js b/lib/coffeescript/nodes.js index 1c4a8a190c..7736a108b8 100644 --- a/lib/coffeescript/nodes.js +++ b/lib/coffeescript/nodes.js @@ -3314,6 +3314,8 @@ switch (this.operator) { case '?': return this.compileExistence(o); + case '??': + return this.compileExistence(o, true); case '**': return this.compilePower(o); case '//': @@ -3605,9 +3607,10 @@ exports.Existence = Existence = (function() { class Existence extends Base { - constructor(expression1) { + constructor(expression1, onlyNotUndefined = false) { super(); this.expression = expression1; + this.comparisonTarget = onlyNotUndefined ? 'undefined' : 'null'; } compileNode(o) { @@ -3616,9 +3619,10 @@ code = this.expression.compile(o, LEVEL_OP); if (this.expression.unwrap() instanceof IdentifierLiteral && !o.scope.check(code)) { [cmp, cnj] = this.negated ? ['===', '||'] : ['!==', '&&']; - code = `typeof ${code} ${cmp} \"undefined\" ${cnj} ${code} ${cmp} null`; + code = `typeof ${code} ${cmp} \"undefined\" ${cnj} ${code} ${cmp} ${this.comparisonTarget}`; } else { - code = `${code} ${(this.negated ? '==' : '!=')} null`; + cmp = this.comparisonTarget === 'null' ? this.negated ? '==' : '!=' : this.negated ? '===' : '!=='; + code = `${code} ${cmp} ${this.comparisonTarget}`; } return [this.makeCode(o.level <= LEVEL_COND ? code : `(${code})`)]; } diff --git a/src/nodes.coffee b/src/nodes.coffee index dcd3343ad0..46ea90e6fa 100644 --- a/src/nodes.coffee +++ b/src/nodes.coffee @@ -2509,7 +2509,8 @@ exports.Op = class Op extends Base return @compileUnary o if @isUnary() return @compileChain o if isChain switch @operator - when '?' then @compileExistence o + when '?' then @compileExistence o # Check if not null and not undefined + when '??' then @compileExistence o, yes # Check only if not undefined when '**' then @compilePower o when '//' then @compileFloorDivision o when '%%' then @compileModulo o @@ -2699,12 +2700,13 @@ exports.Throw = class Throw extends Base #### Existence -# Checks a variable for existence -- not *null* and not *undefined*. This is +# Checks a variable for existence -- not `null` and not `undefined`. This is # similar to `.nil?` in Ruby, and avoids having to consult a JavaScript truth -# table. +# table. Optionally only check if a variable is not `undefined`. exports.Existence = class Existence extends Base - constructor: (@expression) -> + constructor: (@expression, onlyNotUndefined = no) -> super() + @comparisonTarget = if onlyNotUndefined then 'undefined' else 'null' children: ['expression'] @@ -2715,10 +2717,19 @@ exports.Existence = class Existence extends Base code = @expression.compile o, LEVEL_OP if @expression.unwrap() instanceof IdentifierLiteral and not o.scope.check code [cmp, cnj] = if @negated then ['===', '||'] else ['!==', '&&'] - code = "typeof #{code} #{cmp} \"undefined\" #{cnj} #{code} #{cmp} null" + code = "typeof #{code} #{cmp} \"undefined\" #{cnj} #{code} #{cmp} #{@comparisonTarget}" else - # do not use strict equality here; it will break existing code - code = "#{code} #{if @negated then '==' else '!='} null" + # We explicity want to use loose equality (`==`) when comparing against `null`, + # so that an existence check roughly corresponds to a check for truthiness. + # Do *not* change this to `===` for `null`, as this will break mountains of + # existing code. When comparing only against `undefined`, however, we want to + # use `===` because this use case is for parity with ES2015+ default values, + # which only get assigned when the variable is `undefined` (but not `null`). + cmp = if @comparisonTarget is 'null' + if @negated then '==' else '!=' + else # `undefined` + if @negated then '===' else '!==' + code = "#{code} #{cmp} #{@comparisonTarget}" [@makeCode(if o.level <= LEVEL_COND then code else "(#{code})")] #### Parens From 7786899d04af2607bfc4d7415a09e05c48dae456 Mon Sep 17 00:00:00 2001 From: Geoffrey Booth Date: Sat, 1 Apr 2017 21:12:23 -0700 Subject: [PATCH 10/20] The fallback destructuring code should apply default values only if `undefined`, not falsy, to follow ES spec --- lib/coffeescript/nodes.js | 8 ++++---- src/nodes.coffee | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/lib/coffeescript/nodes.js b/lib/coffeescript/nodes.js index 7736a108b8..9443823fa4 100644 --- a/lib/coffeescript/nodes.js +++ b/lib/coffeescript/nodes.js @@ -2423,7 +2423,7 @@ } isObject = this.variable.isObject(); if (top && olen === 1 && !(obj instanceof Splat)) { - defaultValue = null; + defaultValue = void 0; if (obj instanceof Assign && obj.context === 'object') { ({ variable: { @@ -2450,7 +2450,7 @@ obj.error(message); } if (defaultValue) { - value = new Op('?', value, defaultValue); + value = new Op('??', value, defaultValue); } return new Assign(obj, value, null, { param: this.param @@ -2503,7 +2503,7 @@ if (obj instanceof Splat || obj instanceof Expansion) { obj.error("multiple splats/expansions are disallowed in an assignment"); } - defaultValue = null; + defaultValue = void 0; if (obj instanceof Assign && obj.context === 'object') { ({ variable: { @@ -2526,7 +2526,7 @@ acc = idx.unwrap() instanceof PropertyName; val = new Value(new Literal(vvarText), [new (acc ? Access : Index)(idx)]); if (defaultValue) { - val = new Op('?', val, defaultValue); + val = new Op('??', val, defaultValue); } } if (name != null) { diff --git a/src/nodes.coffee b/src/nodes.coffee index 46ea90e6fa..5f2389b93c 100644 --- a/src/nodes.coffee +++ b/src/nodes.coffee @@ -1793,7 +1793,7 @@ exports.Assign = class Assign extends Base if top and olen is 1 and obj not instanceof Splat # Pick the property straight off the value when there’s just one to pick # (no need to cache the value into a variable). - defaultValue = null + defaultValue = undefined if obj instanceof Assign and obj.context is 'object' # A regular object pattern-match. {variable: {base: idx}, value: obj} = obj @@ -1818,7 +1818,7 @@ exports.Assign = class Assign extends Base value.properties.push new (if acc then Access else Index) idx message = isUnassignable obj.unwrap().value obj.error message if message - value = new Op '?', value, defaultValue if defaultValue + value = new Op '??', value, defaultValue if defaultValue return new Assign(obj, value, null, param: @param).compileToFragments o, LEVEL_TOP vvar = value.compileToFragments o, LEVEL_LIST @@ -1873,7 +1873,7 @@ exports.Assign = class Assign extends Base else if obj instanceof Splat or obj instanceof Expansion obj.error "multiple splats/expansions are disallowed in an assignment" - defaultValue = null + defaultValue = undefined if obj instanceof Assign and obj.context is 'object' # A regular object pattern-match. {variable: {base: idx}, value: obj} = obj @@ -1896,7 +1896,7 @@ exports.Assign = class Assign extends Base name = obj.unwrap().value acc = idx.unwrap() instanceof PropertyName val = new Value new Literal(vvarText), [new (if acc then Access else Index) idx] - val = new Op '?', val, defaultValue if defaultValue + val = new Op '??', val, defaultValue if defaultValue if name? message = isUnassignable name obj.error message if message From 1e6d910e814275e0c6bd8d781e9d216f9501aa8b Mon Sep 17 00:00:00 2001 From: Geoffrey Booth Date: Sat, 1 Apr 2017 20:39:52 -0700 Subject: [PATCH 11/20] Add comments; remove unnecessary array splats in function tests --- src/nodes.coffee | 2 ++ test/assignment.coffee | 6 +++--- test/functions.coffee | 4 ++-- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/nodes.coffee b/src/nodes.coffee index 5f2389b93c..1952e78d9c 100644 --- a/src/nodes.coffee +++ b/src/nodes.coffee @@ -1765,6 +1765,8 @@ exports.Assign = class Assign extends Base return compiledName.concat @makeCode(": "), val answer = compiledName.concat @makeCode(" #{ @context or '=' } "), val + # Per https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment#Assignment_without_declaration, + # if we’re destructuring without declaring, the destructuring assignment must be wrapped in parentheses. if o.level > LEVEL_LIST or (isValue and @variable.base instanceof Obj) then @wrapInParentheses answer else answer # Brief implementation of recursive pattern matching, when assigning array or diff --git a/test/assignment.coffee b/test/assignment.coffee index 23cea501a7..321b78e960 100644 --- a/test/assignment.coffee +++ b/test/assignment.coffee @@ -302,7 +302,7 @@ test "simple array destructuring defaults", -> [a = 2] = [undefined] eq 2, a [a = 3] = [null] - eq null, a + eq null, a # Breaking change in CS2: per ES2015, default values are applied for `undefined` but not for `null`. [a = 4] = [0] eq 0, a arr = [a = 5] @@ -315,7 +315,7 @@ test "simple object destructuring defaults", -> {b = 2} = {b: undefined} eq b, 2 {b = 3} = {b: null} - eq b, null + eq b, null # Breaking change in CS2: per ES2015, default values are applied for `undefined` but not for `null`. {b = 4} = {b: 0} eq b, 0 @@ -324,7 +324,7 @@ test "simple object destructuring defaults", -> {b: c = 2} = {b: undefined} eq c, 2 {b: c = 3} = {b: null} - eq c, null + eq c, null # Breaking change in CS2: per ES2015, default values are applied for `undefined` but not for `null`. {b: c = 4} = {b: 0} eq c, 0 diff --git a/test/functions.coffee b/test/functions.coffee index b9f7198555..57b0b6efcb 100644 --- a/test/functions.coffee +++ b/test/functions.coffee @@ -150,13 +150,13 @@ test "@-parameters and splats with constructors", -> eq b, obj.last test "destructuring in function definition", -> - (([{a: [b], c}]...) -> + (({a: [b], c}) -> eq 1, b eq 2, c ) {a: [1], c: 2} context = {} - (([{a: [b, c = 2], @d, e = 4}]...) -> + (({a: [b, c = 2], @d, e = 4}) -> eq 1, b eq 2, c eq @d, 3 From 6c814a09780c5c03c7134a7eff442c0512572090 Mon Sep 17 00:00:00 2001 From: Geoffrey Booth Date: Sun, 2 Apr 2017 01:05:46 -0700 Subject: [PATCH 12/20] Support destructuring in function parameters (first pass); catch destructured reserved words --- lib/coffeescript/nodes.js | 31 ++++++++++++++++++++++++++----- src/nodes.coffee | 35 ++++++++++++++++++++++++++++------- test/functions.coffee | 10 ++++++++++ test/strict.coffee | 2 +- 4 files changed, 65 insertions(+), 13 deletions(-) diff --git a/lib/coffeescript/nodes.js b/lib/coffeescript/nodes.js index 9443823fa4..563a8f1c41 100644 --- a/lib/coffeescript/nodes.js +++ b/lib/coffeescript/nodes.js @@ -1476,11 +1476,15 @@ } isAssignable() { - var j, len1, prop, ref1; + var j, len1, message, prop, ref1; this.lhs = true; ref1 = this.properties; for (j = 0, len1 = ref1.length; j < len1; j++) { prop = ref1[j]; + message = isUnassignable(prop.unwrapAll().value); + if (message) { + prop.error(message); + } if (prop instanceof Assign && prop.context === 'object') { prop = prop.value; } @@ -1491,6 +1495,10 @@ return true; } + shouldCache() { + return !this.isAssignable(); + } + compileNode(o) { var answer, i, idt, indent, isCompact, j, join, k, key, l, lastNoncom, len1, len2, len3, node, prop, props, ref1, value; props = this.properties; @@ -1575,7 +1583,11 @@ prop = prop.value; } prop = prop.unwrapAll(); - results.push(prop.eachName(iterator)); + if (prop.eachName != null) { + results.push(prop.eachName(iterator)); + } else { + results.push(void 0); + } } return results; } @@ -2334,6 +2346,7 @@ compileNode(o) { var answer, compiledName, isValue, j, name, properties, prototype, ref1, ref2, ref3, ref4, ref5, ref6, val, varBase; if (isValue = this.variable instanceof Value) { + this.variable.param = this.param; if (this.variable.isArray() || this.variable.isObject()) { if (!this.variable.isAssignable()) { return this.compileDestructuring(o); @@ -2396,7 +2409,7 @@ return compiledName.concat(this.makeCode(": "), val); } answer = compiledName.concat(this.makeCode(` ${this.context || '='} `), val); - if (o.level > LEVEL_LIST || (isValue && this.variable.base instanceof Obj)) { + if (o.level > LEVEL_LIST || (isValue && this.variable.base instanceof Obj && !this.param)) { return this.wrapInParentheses(answer); } else { return answer; @@ -2748,12 +2761,20 @@ ref = param.asReference(o); } else { if ((param.value != null) && !param.assignedInBody) { - ref = new Assign(new Value(param.name), param.value); + ref = new Assign(new Value(param.name), param.value, null, { + param: true + }); } else { ref = param; } } - o.scope.parameter(fragmentsToText((param.value != null ? param : ref).compileToFragments(o))); + if (param.name instanceof Arr || param.name instanceof Obj) { + param.name.eachName(function(prop) { + return o.scope.parameter(fragmentsToText(prop.compileToFragments(o))); + }); + } else { + o.scope.parameter(fragmentsToText((param.value != null ? param : ref).compileToFragments(o))); + } params.push(ref); } else { paramsAfterSplat.push(param); diff --git a/src/nodes.coffee b/src/nodes.coffee index 1952e78d9c..9192b9a362 100644 --- a/src/nodes.coffee +++ b/src/nodes.coffee @@ -584,8 +584,7 @@ exports.BooleanLiteral = class BooleanLiteral extends Literal #### Return -# A `return` is a *pureStatement* -- wrapping it in a closure wouldn't -# make sense. +# A `return` is a *pureStatement*—wrapping it in a closure wouldn’t make sense. exports.Return = class Return extends Base constructor: (@expression) -> super() @@ -1121,10 +1120,17 @@ exports.Obj = class Obj extends Base @lhs = yes for prop in @properties + # Check for reserved words. + message = isUnassignable prop.unwrapAll().value + prop.error message if message + prop = prop.value if prop instanceof Assign and prop.context is 'object' return no unless prop.isAssignable() yes + shouldCache: -> + not @isAssignable() + compileNode: (o) -> props = @properties if @generated @@ -1186,7 +1192,7 @@ exports.Obj = class Obj extends Base for prop in @properties prop = prop.value if prop instanceof Assign and prop.context is 'object' prop = prop.unwrapAll() - prop.eachName iterator + prop.eachName iterator if prop.eachName? #### Arr @@ -1718,6 +1724,13 @@ exports.Assign = class Assign extends Base # has not been seen yet within the current scope, declare it. compileNode: (o) -> if isValue = @variable instanceof Value + # When compiling `@variable`, remember if it is part of a function parameter. + @variable.param = @param + + # If `@variable` is an array or an object, we’re destructuring; + # if it’s also `isAssignable()`, the destructuring syntax is supported + # in ES and we can output it as is; otherwise we `@compileDestructuring` + # and convert this ES-unsupported destructuring into acceptable output. if @variable.isArray() or @variable.isObject() return @compileDestructuring o unless @variable.isAssignable() @@ -1767,7 +1780,10 @@ exports.Assign = class Assign extends Base answer = compiledName.concat @makeCode(" #{ @context or '=' } "), val # Per https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment#Assignment_without_declaration, # if we’re destructuring without declaring, the destructuring assignment must be wrapped in parentheses. - if o.level > LEVEL_LIST or (isValue and @variable.base instanceof Obj) then @wrapInParentheses answer else answer + if o.level > LEVEL_LIST or (isValue and @variable.base instanceof Obj and not @param) + @wrapInParentheses answer + else + answer # Brief implementation of recursive pattern matching, when assigning array or # object literals to a value. Peeks at their properties to assign inner names. @@ -2089,11 +2105,16 @@ exports.Code = class Code extends Base ref = param.asReference o else if param.value? and not param.assignedInBody - ref = new Assign new Value(param.name), param.value + ref = new Assign new Value(param.name), param.value, null, param: yes else ref = param - # Add this parameter’s reference to the function scope - o.scope.parameter fragmentsToText (if param.value? then param else ref).compileToFragments o + # Add this parameter’s reference(s) to the function scope. + if param.name instanceof Arr or param.name instanceof Obj + # This parameter is destructured. + param.name.eachName (prop) -> + o.scope.parameter fragmentsToText prop.compileToFragments o + else + o.scope.parameter fragmentsToText (if param.value? then param else ref).compileToFragments o params.push ref else paramsAfterSplat.push param diff --git a/test/functions.coffee b/test/functions.coffee index 57b0b6efcb..0c6771d83b 100644 --- a/test/functions.coffee +++ b/test/functions.coffee @@ -352,3 +352,13 @@ test "#4406 Destructured parameter default evaluation order with generator funct next = -> ++current foo = ({ a = next() }, b = next()) -> [ a, b ] arrayEq foo({}), [1, 2] + +test "Destructured parameter with default value, that itself has a default value", -> + # Adapted from https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment + draw = ({size = 'big', coords = {x: 0, y: 0}, radius = 25} = {}) -> "#{size}-#{coords.x}-#{coords.y}-#{radius}" + output = draw + coords: + x: 18 + y: 30 + radius: 30 + eq output, 'big-18-30-30' diff --git a/test/strict.coffee b/test/strict.coffee index f19e61a0bf..7a4e5b0e97 100644 --- a/test/strict.coffee +++ b/test/strict.coffee @@ -1,7 +1,7 @@ # Strict Early Errors # ------------------- -# The following are prohibited under ES5's `strict` mode +# The following are prohibited under ES5’s `strict` mode # * `Octal Integer Literals` # * `Octal Escape Sequences` # * duplicate property definitions in `Object Literal`s From 5fc4e19d9e74b1ddc58739f1bf9c3581979387d7 Mon Sep 17 00:00:00 2001 From: Geoffrey Booth Date: Sun, 2 Apr 2017 02:10:20 -0700 Subject: [PATCH 13/20] =?UTF-8?q?Destructured=20variables=20in=20function?= =?UTF-8?q?=20parameter=20lists=20shouldn=E2=80=99t=20be=20added=20to=20th?= =?UTF-8?q?e=20function=20body=20with=20`var`=20declarations;=20treat=20sp?= =?UTF-8?q?lat=20array=20function=20parameters=20the=20legacy=20way=20to?= =?UTF-8?q?=20avoid=20rethinking=20#4005?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/coffeescript/coffeescript.js | 5 ++--- lib/coffeescript/nodes.js | 22 ++++++++++++++++------ lib/coffeescript/sourcemap.js | 12 +++--------- src/nodes.coffee | 32 +++++++++++++++++++------------- 4 files changed, 40 insertions(+), 31 deletions(-) diff --git a/lib/coffeescript/coffeescript.js b/lib/coffeescript/coffeescript.js index 35f38ac13c..88214f9a9c 100644 --- a/lib/coffeescript/coffeescript.js +++ b/lib/coffeescript/coffeescript.js @@ -293,9 +293,8 @@ parser.yy = require('./nodes'); - parser.yy.parseError = function(message, arg) { - var errorLoc, errorTag, errorText, errorToken, token, tokens; - ({token} = arg); + parser.yy.parseError = function(message, {token}) { + var errorLoc, errorTag, errorText, errorToken, tokens; ({errorToken, tokens} = parser); [errorTag, errorText, errorLoc] = errorToken; errorText = (function() { diff --git a/lib/coffeescript/nodes.js b/lib/coffeescript/nodes.js index 563a8f1c41..de6ce90056 100644 --- a/lib/coffeescript/nodes.js +++ b/lib/coffeescript/nodes.js @@ -1625,6 +1625,10 @@ return true; } + shouldCache() { + return !this.isAssignable(); + } + compileNode(o) { var answer, compiledObjs, fragments, index, j, len1, obj; if (!this.objects.length) { @@ -2378,8 +2382,6 @@ if (this.moduleDeclaration) { this.checkAssignability(o, name); return o.scope.add(name.value, this.moduleDeclaration); - } else if (this.param) { - return o.scope.add(name.value, 'var'); } else { this.checkAssignability(o, name); return o.scope.find(name.value); @@ -2728,8 +2730,16 @@ } haveSplatParam = true; if (param.splat) { - params.push(ref = param.asReference(o)); - splatParamName = fragmentsToText(ref.compileNode(o)); + if (param.name instanceof Arr) { + splatParamName = o.scope.freeVariable('arg'); + params.push(ref = new Value(new IdentifierLiteral(splatParamName))); + exprs.push(new Assign(new Value(param.name), ref, null, { + param: true + })); + } else { + params.push(ref = param.asReference(o)); + splatParamName = fragmentsToText(ref.compileNode(o)); + } if (param.shouldCache()) { exprs.push(new Assign(new Value(param.name), ref, null, { param: true @@ -2745,7 +2755,7 @@ param.assignedInBody = true; haveBodyParam = true; if (param.value != null) { - condition = new Op('==', param, new UndefinedLiteral); + condition = new Op('===', param, new UndefinedLiteral); ifTrue = new Assign(new Value(param.name), param.value, null, { param: true }); @@ -2779,7 +2789,7 @@ } else { paramsAfterSplat.push(param); if ((param.value != null) && !param.shouldCache()) { - condition = new Op('==', param, new UndefinedLiteral); + condition = new Op('===', param, new UndefinedLiteral); ifTrue = new Assign(new Value(param.name), param.value); exprs.push(new If(condition, ifTrue)); } diff --git a/lib/coffeescript/sourcemap.js b/lib/coffeescript/sourcemap.js index 659fb5cd20..be75931b9c 100644 --- a/lib/coffeescript/sourcemap.js +++ b/lib/coffeescript/sourcemap.js @@ -8,12 +8,7 @@ this.columns = []; } - add(column, arg, options) { - var options, sourceColumn, sourceLine; - [sourceLine, sourceColumn] = arg; - if (options === void 0) { - options = {}; - } + add(column, [sourceLine, sourceColumn], options = {}) { if (this.columns[column] && options.noReplace) { return; } @@ -50,9 +45,8 @@ return lineMap.add(column, sourceLocation, options); } - sourceLocation(arg) { - var column, line, lineMap; - [line, column] = arg; + sourceLocation([line, column]) { + var lineMap; while (!((lineMap = this.lines[line]) || (line <= 0))) { line--; } diff --git a/src/nodes.coffee b/src/nodes.coffee index 9192b9a362..78ea2c3e92 100644 --- a/src/nodes.coffee +++ b/src/nodes.coffee @@ -1206,12 +1206,15 @@ exports.Arr = class Arr extends Base children: ['objects'] isAssignable: -> - return false unless @objects.length + return no unless @objects.length for obj, i in @objects - return false if obj instanceof Splat and i + 1 isnt @objects.length - return false unless obj.isAssignable() and (not obj.isAtomic or obj.isAtomic()) - true + return no if obj instanceof Splat and i + 1 isnt @objects.length + return no unless obj.isAssignable() and (not obj.isAtomic or obj.isAtomic()) + yes + + shouldCache: -> + not @isAssignable() compileNode: (o) -> return [@makeCode '[]'] unless @objects.length @@ -1752,8 +1755,6 @@ exports.Assign = class Assign extends Base if @moduleDeclaration @checkAssignability o, name o.scope.add name.value, @moduleDeclaration - else if @param - o.scope.add name.value, 'var' else @checkAssignability o, name o.scope.find name.value @@ -2063,13 +2064,18 @@ exports.Code = class Code extends Base haveSplatParam = yes if param.splat - params.push ref = param.asReference o - splatParamName = fragmentsToText ref.compileNode o + if param.name instanceof Arr + # Splat arrays are treated oddly by ES; deal with them the legacy + # way in the function body. TODO: Should this be handled in the + # function parameter list, and if so, how? + splatParamName = o.scope.freeVariable 'arg' + params.push ref = new Value new IdentifierLiteral splatParamName + exprs.push new Assign new Value(param.name), ref, null, param: yes + else + params.push ref = param.asReference o + splatParamName = fragmentsToText ref.compileNode o if param.shouldCache() exprs.push new Assign new Value(param.name), ref, null, param: yes - # TODO: output destructured parameters as is, 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 @@ -2088,7 +2094,7 @@ exports.Code = class Code extends Base # to the function body assigning it, e.g. # `(arg) => { var a = arg.a; }`, with a default value if it has one. if param.value? - condition = new Op '==', param, new UndefinedLiteral + condition = new Op '===', param, new UndefinedLiteral ifTrue = new Assign new Value(param.name), param.value, null, param: yes exprs.push new If condition, ifTrue else @@ -2122,7 +2128,7 @@ exports.Code = class Code extends Base # function parameter list we need to assign its default value # (if necessary) as an expression in the body. if param.value? and not param.shouldCache() - condition = new Op '==', param, new UndefinedLiteral + condition = new Op '===', param, new UndefinedLiteral 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. From 770ddcd0a409ec46177c363aa56f528b2339ba25 Mon Sep 17 00:00:00 2001 From: Geoffrey Booth Date: Sun, 2 Apr 2017 11:18:48 -0700 Subject: [PATCH 14/20] Remove redundancy in undefined-only check for existence; fix passing option to check --- lib/coffeescript/nodes.js | 9 ++++++--- src/nodes.coffee | 10 ++++++---- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/lib/coffeescript/nodes.js b/lib/coffeescript/nodes.js index de6ce90056..93d2c38da7 100644 --- a/lib/coffeescript/nodes.js +++ b/lib/coffeescript/nodes.js @@ -3373,7 +3373,7 @@ return this.wrapInParentheses(fragments); } - compileExistence(o) { + compileExistence(o, checkOnlyUndefined) { var fst, ref; if (this.first.shouldCache()) { ref = new IdentifierLiteral(o.scope.freeVariable('ref')); @@ -3382,7 +3382,7 @@ fst = this.first; ref = fst; } - return new If(new Existence(fst), ref, { + return new If(new Existence(fst, checkOnlyUndefined), ref, { type: 'if' }).addElse(this.second).compileToFragments(o); } @@ -3650,7 +3650,10 @@ code = this.expression.compile(o, LEVEL_OP); if (this.expression.unwrap() instanceof IdentifierLiteral && !o.scope.check(code)) { [cmp, cnj] = this.negated ? ['===', '||'] : ['!==', '&&']; - code = `typeof ${code} ${cmp} \"undefined\" ${cnj} ${code} ${cmp} ${this.comparisonTarget}`; + code = `typeof ${code} ${cmp} \"undefined\"`; + if (this.comparisonTarget !== 'undefined') { + code += ` ${cnj} ${code} ${cmp} ${this.comparisonTarget}`; + } } else { cmp = this.comparisonTarget === 'null' ? this.negated ? '==' : '!=' : this.negated ? '===' : '!=='; code = `${code} ${cmp} ${this.comparisonTarget}`; diff --git a/src/nodes.coffee b/src/nodes.coffee index 78ea2c3e92..0634a0479c 100644 --- a/src/nodes.coffee +++ b/src/nodes.coffee @@ -2427,7 +2427,7 @@ exports.While = class While extends Base # Simple Arithmetic and logical operations. Performs some conversion from # CoffeeScript operations into their JavaScript equivalents. exports.Op = class Op extends Base - constructor: (op, first, second, flip ) -> + constructor: (op, first, second, flip) -> return new In first, second if op is 'in' if op is 'do' return Op::generateDo first @@ -2562,14 +2562,14 @@ exports.Op = class Op extends Base @wrapInParentheses fragments # Keep reference to the left expression, unless this an existential assignment - compileExistence: (o) -> + compileExistence: (o, checkOnlyUndefined) -> if @first.shouldCache() ref = new IdentifierLiteral o.scope.freeVariable 'ref' fst = new Parens new Assign ref, @first else fst = @first ref = fst - new If(new Existence(fst), ref, type: 'if').addElse(@second).compileToFragments o + new If(new Existence(fst, checkOnlyUndefined), ref, type: 'if').addElse(@second).compileToFragments o # Compile a unary **Op**. compileUnary: (o) -> @@ -2746,7 +2746,9 @@ exports.Existence = class Existence extends Base code = @expression.compile o, LEVEL_OP if @expression.unwrap() instanceof IdentifierLiteral and not o.scope.check code [cmp, cnj] = if @negated then ['===', '||'] else ['!==', '&&'] - code = "typeof #{code} #{cmp} \"undefined\" #{cnj} #{code} #{cmp} #{@comparisonTarget}" + code = "typeof #{code} #{cmp} \"undefined\"" + if @comparisonTarget isnt 'undefined' + code += " #{cnj} #{code} #{cmp} #{@comparisonTarget}" else # We explicity want to use loose equality (`==`) when comparing against `null`, # so that an existence check roughly corresponds to a check for truthiness. From d110b9a83d56b3230c1d16265c27f48c4247a304 Mon Sep 17 00:00:00 2001 From: Geoffrey Booth Date: Sun, 2 Apr 2017 11:46:47 -0700 Subject: [PATCH 15/20] Fix undefined redundancy --- lib/coffeescript/nodes.js | 5 +---- src/nodes.coffee | 4 +--- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/lib/coffeescript/nodes.js b/lib/coffeescript/nodes.js index 93d2c38da7..71be20b0f1 100644 --- a/lib/coffeescript/nodes.js +++ b/lib/coffeescript/nodes.js @@ -3650,10 +3650,7 @@ code = this.expression.compile(o, LEVEL_OP); if (this.expression.unwrap() instanceof IdentifierLiteral && !o.scope.check(code)) { [cmp, cnj] = this.negated ? ['===', '||'] : ['!==', '&&']; - code = `typeof ${code} ${cmp} \"undefined\"`; - if (this.comparisonTarget !== 'undefined') { - code += ` ${cnj} ${code} ${cmp} ${this.comparisonTarget}`; - } + code = `typeof ${code} ${cmp} \"undefined\"` + (this.comparisonTarget !== 'undefined' ? ` ${cnj} ${code} ${cmp} ${this.comparisonTarget}` : ''); } else { cmp = this.comparisonTarget === 'null' ? this.negated ? '==' : '!=' : this.negated ? '===' : '!=='; code = `${code} ${cmp} ${this.comparisonTarget}`; diff --git a/src/nodes.coffee b/src/nodes.coffee index 0634a0479c..741f43efe2 100644 --- a/src/nodes.coffee +++ b/src/nodes.coffee @@ -2746,9 +2746,7 @@ exports.Existence = class Existence extends Base code = @expression.compile o, LEVEL_OP if @expression.unwrap() instanceof IdentifierLiteral and not o.scope.check code [cmp, cnj] = if @negated then ['===', '||'] else ['!==', '&&'] - code = "typeof #{code} #{cmp} \"undefined\"" - if @comparisonTarget isnt 'undefined' - code += " #{cnj} #{code} #{cmp} #{@comparisonTarget}" + code = "typeof #{code} #{cmp} \"undefined\"" + if @comparisonTarget isnt 'undefined' then " #{cnj} #{code} #{cmp} #{@comparisonTarget}" else '' else # We explicity want to use loose equality (`==`) when comparing against `null`, # so that an existence check roughly corresponds to a check for truthiness. From 678e93146b594ef271c455cf57655f26247922d5 Mon Sep 17 00:00:00 2001 From: Geoffrey Booth Date: Sun, 2 Apr 2017 11:47:36 -0700 Subject: [PATCH 16/20] Simplify getting the variable name --- lib/coffeescript/nodes.js | 2 +- src/nodes.coffee | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/coffeescript/nodes.js b/lib/coffeescript/nodes.js index 71be20b0f1..2d194dc398 100644 --- a/lib/coffeescript/nodes.js +++ b/lib/coffeescript/nodes.js @@ -2780,7 +2780,7 @@ } if (param.name instanceof Arr || param.name instanceof Obj) { param.name.eachName(function(prop) { - return o.scope.parameter(fragmentsToText(prop.compileToFragments(o))); + return o.scope.parameter(prop.value); }); } else { o.scope.parameter(fragmentsToText((param.value != null ? param : ref).compileToFragments(o))); diff --git a/src/nodes.coffee b/src/nodes.coffee index 741f43efe2..57d45e7d59 100644 --- a/src/nodes.coffee +++ b/src/nodes.coffee @@ -2118,7 +2118,7 @@ exports.Code = class Code extends Base if param.name instanceof Arr or param.name instanceof Obj # This parameter is destructured. param.name.eachName (prop) -> - o.scope.parameter fragmentsToText prop.compileToFragments o + o.scope.parameter prop.value else o.scope.parameter fragmentsToText (if param.value? then param else ref).compileToFragments o params.push ref From 60f046e313d0474feb5afa9b0453723c991eb793 Mon Sep 17 00:00:00 2001 From: Geoffrey Booth Date: Sun, 2 Apr 2017 13:17:48 -0700 Subject: [PATCH 17/20] =?UTF-8?q?Reimplement=20=E2=80=9Ccheck=20for=20exis?= =?UTF-8?q?tence=20if=20not=20undefined=E2=80=9D=20without=20creating=20a?= =?UTF-8?q?=20new=20operator?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/coffeescript/nodes.js | 13 +++++++------ src/nodes.coffee | 20 ++++++++++++-------- 2 files changed, 19 insertions(+), 14 deletions(-) diff --git a/lib/coffeescript/nodes.js b/lib/coffeescript/nodes.js index 2d194dc398..3c7c59d2ef 100644 --- a/lib/coffeescript/nodes.js +++ b/lib/coffeescript/nodes.js @@ -816,7 +816,7 @@ exports.Value = Value = (function() { class Value extends Base { - constructor(base, props, tag) { + constructor(base, props, tag, isDefaultValue = false) { if (!props && base instanceof Value) { return base; } @@ -826,6 +826,7 @@ if (tag) { this[tag] = true; } + this.isDefaultValue = isDefaultValue; return this; } @@ -2465,7 +2466,8 @@ obj.error(message); } if (defaultValue) { - value = new Op('??', value, defaultValue); + defaultValue.isDefaultValue = true; + value = new Op('?', value, defaultValue); } return new Assign(obj, value, null, { param: this.param @@ -2541,7 +2543,8 @@ acc = idx.unwrap() instanceof PropertyName; val = new Value(new Literal(vvarText), [new (acc ? Access : Index)(idx)]); if (defaultValue) { - val = new Op('??', val, defaultValue); + defaultValue.isDefaultValue = true; + val = new Op('?', val, defaultValue); } } if (name != null) { @@ -3344,9 +3347,7 @@ } switch (this.operator) { case '?': - return this.compileExistence(o); - case '??': - return this.compileExistence(o, true); + return this.compileExistence(o, this.second.isDefaultValue); case '**': return this.compilePower(o); case '//': diff --git a/src/nodes.coffee b/src/nodes.coffee index 57d45e7d59..918b7d9207 100644 --- a/src/nodes.coffee +++ b/src/nodes.coffee @@ -629,14 +629,15 @@ exports.AwaitReturn = class AwaitReturn extends Return # A value, variable or literal or parenthesized, indexed or dotted into, # or vanilla. exports.Value = class Value extends Base - constructor: (base, props, tag) -> + constructor: (base, props, tag, isDefaultValue = no) -> return base if not props and base instanceof Value super() - @base = base - @properties = props or [] - @[tag] = true if tag + @base = base + @properties = props or [] + @[tag] = yes if tag + @isDefaultValue = isDefaultValue return this children: ['base', 'properties'] @@ -1837,7 +1838,9 @@ exports.Assign = class Assign extends Base value.properties.push new (if acc then Access else Index) idx message = isUnassignable obj.unwrap().value obj.error message if message - value = new Op '??', value, defaultValue if defaultValue + if defaultValue + defaultValue.isDefaultValue = yes + value = new Op '?', value, defaultValue return new Assign(obj, value, null, param: @param).compileToFragments o, LEVEL_TOP vvar = value.compileToFragments o, LEVEL_LIST @@ -1915,7 +1918,9 @@ exports.Assign = class Assign extends Base name = obj.unwrap().value acc = idx.unwrap() instanceof PropertyName val = new Value new Literal(vvarText), [new (if acc then Access else Index) idx] - val = new Op '??', val, defaultValue if defaultValue + if defaultValue + defaultValue.isDefaultValue = yes + val = new Op '?', val, defaultValue if name? message = isUnassignable name obj.error message if message @@ -2538,8 +2543,7 @@ exports.Op = class Op extends Base return @compileUnary o if @isUnary() return @compileChain o if isChain switch @operator - when '?' then @compileExistence o # Check if not null and not undefined - when '??' then @compileExistence o, yes # Check only if not undefined + when '?' then @compileExistence o, @second.isDefaultValue when '**' then @compilePower o when '//' then @compileFloorDivision o when '%%' then @compileModulo o From 05c1f46a9237d6b0eb83c0cd8917f2baa8603ded Mon Sep 17 00:00:00 2001 From: Geoffrey Booth Date: Sun, 2 Apr 2017 14:09:35 -0700 Subject: [PATCH 18/20] `Obj::isAssignable` should not mutate; pass `lhs` property in from `Assign` or `Code` to child arrays and objects so that those child nodes are set as allowable for destructuring --- lib/coffeescript/nodes.js | 38 ++++++++++++++++++++++++++------------ src/nodes.coffee | 28 ++++++++++++++++++---------- 2 files changed, 44 insertions(+), 22 deletions(-) diff --git a/lib/coffeescript/nodes.js b/lib/coffeescript/nodes.js index 3c7c59d2ef..614e21bcec 100644 --- a/lib/coffeescript/nodes.js +++ b/lib/coffeescript/nodes.js @@ -1470,15 +1470,15 @@ exports.Obj = Obj = (function() { class Obj extends Base { - constructor(props, generated = false) { + constructor(props, generated = false, lhs1 = false) { super(); this.generated = generated; + this.lhs = lhs1; this.objects = this.properties = props || []; } isAssignable() { var j, len1, message, prop, ref1; - this.lhs = true; ref1 = this.properties; for (j = 0, len1 = ref1.length; j < len1; j++) { prop = ref1[j]; @@ -1603,8 +1603,9 @@ exports.Arr = Arr = (function() { class Arr extends Base { - constructor(objs) { + constructor(objs, lhs1 = false) { super(); + this.lhs = lhs1; this.objects = objs || []; } @@ -1631,23 +1632,33 @@ } compileNode(o) { - var answer, compiledObjs, fragments, index, j, len1, obj; + var answer, compiledObjs, fragments, index, j, k, len1, len2, obj, ref1, unwrappedObj; if (!this.objects.length) { return [this.makeCode('[]')]; } o.indent += TAB; answer = []; - compiledObjs = (function() { - var j, len1, ref1, results; + if (this.lhs) { ref1 = this.objects; - results = []; for (j = 0, len1 = ref1.length; j < len1; j++) { obj = ref1[j]; + unwrappedObj = obj.unwrapAll(); + if (unwrappedObj instanceof Arr || unwrappedObj instanceof Obj) { + unwrappedObj.lhs = true; + } + } + } + compiledObjs = (function() { + var k, len2, ref2, results; + ref2 = this.objects; + results = []; + for (k = 0, len2 = ref2.length; k < len2; k++) { + obj = ref2[k]; results.push(obj.compileToFragments(o, LEVEL_LIST)); } return results; }).call(this); - for (index = j = 0, len1 = compiledObjs.length; j < len1; index = ++j) { + for (index = k = 0, len2 = compiledObjs.length; k < len2; index = ++k) { fragments = compiledObjs[index]; if (index) { answer.push(this.makeCode(", ")); @@ -2350,9 +2361,11 @@ compileNode(o) { var answer, compiledName, isValue, j, name, properties, prototype, ref1, ref2, ref3, ref4, ref5, ref6, val, varBase; - if (isValue = this.variable instanceof Value) { + isValue = this.variable instanceof Value; + if (isValue) { this.variable.param = this.param; if (this.variable.isArray() || this.variable.isObject()) { + this.variable.base.lhs = true; if (!this.variable.isAssignable()) { return this.compileDestructuring(o); } @@ -2377,14 +2390,14 @@ if (typeof name.hasProperties === "function" ? name.hasProperties() : void 0) { return; } - if (message = isUnassignable(name.value)) { + message = isUnassignable(name.value); + if (message) { name.error(message); } + this.checkAssignability(o, name); if (this.moduleDeclaration) { - this.checkAssignability(o, name); return o.scope.add(name.value, this.moduleDeclaration); } else { - this.checkAssignability(o, name); return o.scope.find(name.value); } }); @@ -2782,6 +2795,7 @@ } } if (param.name instanceof Arr || param.name instanceof Obj) { + param.name.lhs = true; param.name.eachName(function(prop) { return o.scope.parameter(prop.value); }); diff --git a/src/nodes.coffee b/src/nodes.coffee index 918b7d9207..4dd66e5b17 100644 --- a/src/nodes.coffee +++ b/src/nodes.coffee @@ -1108,7 +1108,7 @@ exports.Slice = class Slice extends Base # An object literal, nothing fancy. exports.Obj = class Obj extends Base - constructor: (props, @generated = no) -> + constructor: (props, @generated = no, @lhs = no) -> super() @objects = @properties = props or [] @@ -1116,10 +1116,6 @@ exports.Obj = class Obj extends Base children: ['properties'] isAssignable: -> - # Is this object the left-hand side of an assignment? If it is, this object - # is part of a destructuring assignment. - @lhs = yes - for prop in @properties # Check for reserved words. message = isUnassignable prop.unwrapAll().value @@ -1199,7 +1195,7 @@ exports.Obj = class Obj extends Base # An array literal. exports.Arr = class Arr extends Base - constructor: (objs) -> + constructor: (objs, @lhs = no) -> super() @objects = objs or [] @@ -1222,6 +1218,12 @@ exports.Arr = class Arr extends Base o.indent += TAB answer = [] + # If this array is the left-hand side of an assignment, all its children + # are too. + if @lhs + for obj in @objects + unwrappedObj = obj.unwrapAll() + unwrappedObj.lhs = yes if unwrappedObj instanceof Arr or unwrappedObj instanceof Obj compiledObjs = (obj.compileToFragments o, LEVEL_LIST for obj in @objects) for fragments, index in compiledObjs if index @@ -1727,7 +1729,8 @@ exports.Assign = class Assign extends Base # we've been assigned to, for correct internal references. If the variable # has not been seen yet within the current scope, declare it. compileNode: (o) -> - if isValue = @variable instanceof Value + isValue = @variable instanceof Value + if isValue # When compiling `@variable`, remember if it is part of a function parameter. @variable.param = @param @@ -1736,6 +1739,10 @@ exports.Assign = class Assign extends Base # in ES and we can output it as is; otherwise we `@compileDestructuring` # and convert this ES-unsupported destructuring into acceptable output. if @variable.isArray() or @variable.isObject() + # This is the left-hand side of an assignment; let `Arr` and `Obj` + # know that, so that those nodes know that they’re assignable as + # destructured variables. + @variable.base.lhs = yes return @compileDestructuring o unless @variable.isAssignable() return @compileSplice o if @variable.isSplice() @@ -1750,14 +1757,14 @@ exports.Assign = class Assign extends Base varBase.eachName (name) => return if name.hasProperties?() - name.error message if message = isUnassignable name.value + message = isUnassignable name.value + name.error message if message # `moduleDeclaration` can be `'import'` or `'export'` + @checkAssignability o, name if @moduleDeclaration - @checkAssignability o, name o.scope.add name.value, @moduleDeclaration else - @checkAssignability o, name o.scope.find name.value if @value instanceof Code @@ -2122,6 +2129,7 @@ exports.Code = class Code extends Base # Add this parameter’s reference(s) to the function scope. if param.name instanceof Arr or param.name instanceof Obj # This parameter is destructured. + param.name.lhs = yes param.name.eachName (prop) -> o.scope.parameter prop.value else From bd8cf29f2398c2f632cf7ec4dc7ff47e538aa730 Mon Sep 17 00:00:00 2001 From: Geoffrey Booth Date: Tue, 4 Apr 2017 17:07:20 -0700 Subject: [PATCH 19/20] Revert changes to tests --- test/functions.coffee | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/functions.coffee b/test/functions.coffee index 0c6771d83b..cfe29c9c0f 100644 --- a/test/functions.coffee +++ b/test/functions.coffee @@ -150,13 +150,13 @@ test "@-parameters and splats with constructors", -> eq b, obj.last test "destructuring in function definition", -> - (({a: [b], c}) -> + (([{a: [b], c}]...) -> eq 1, b eq 2, c ) {a: [1], c: 2} context = {} - (({a: [b, c = 2], @d, e = 4}) -> + (([{a: [b, c = 2], @d, e = 4}]...) -> eq 1, b eq 2, c eq @d, 3 From 7e81fa0b71ba465eb18620078c4b9dbd7342ba5e Mon Sep 17 00:00:00 2001 From: Geoffrey Booth Date: Tue, 4 Apr 2017 17:15:55 -0700 Subject: [PATCH 20/20] Restore revised test for empty destructuring assignment --- test/assignment.coffee | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/test/assignment.coffee b/test/assignment.coffee index 321b78e960..c02e9ab804 100644 --- a/test/assignment.coffee +++ b/test/assignment.coffee @@ -141,6 +141,10 @@ test "#1192: assignment starting with object literals", -> # Destructuring Assignment +test "empty destructuring assignment", -> + {} = {} + [] = [] + test "chained destructuring assignments", -> [a] = {0: b} = {'0': c} = [nonce={}] eq nonce, a