From b192e215a5737c0b41659827c4277b25a1104879 Mon Sep 17 00:00:00 2001 From: Geoffrey Booth Date: Thu, 6 Apr 2017 10:06:45 -0700 Subject: [PATCH] [CS2] Destructuring (#4478) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Output simple array destructuring assignments to ES2015 * Output simple object destructured assignments to ES2015 * Compile shorthand object properties to ES2015 shorthand properties This dramatically improves the appearance of destructured imports. * Compile default values in destructured assignment to ES2015 * Rename `wrapInBraces` to `wrapInParentheses`, and `compilePatternMatch` to `compileDestructuring`, for clarity; style improvements (no `==` or `!=`, etc.) * Don’t confuse the syntax highlighter * Comment Assign::compilePatternMatch a bit * Assignment expressions in conditionals are a bad practice * 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 * Add comments; remove unnecessary array splats in function tests * The fallback destructuring code should apply default values only if `undefined`, not falsy, to follow ES spec * Support destructuring in function parameters (first pass); catch destructured reserved words * Destructured variables in function parameter lists shouldn’t be added to the function body with `var` declarations; treat splat array function parameters the legacy way to avoid rethinking #4005 * Remove redundancy in undefined-only check for existence; fix passing option to check * Fix undefined redundancy * Simplify getting the variable name * Reimplement “check for existence if not undefined” without creating a new operator * `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 * Revert changes to tests * Restore revised test for empty destructuring assignment --- lib/coffeescript/cake.js | 9 +- lib/coffeescript/coffeescript.js | 25 +- lib/coffeescript/command.js | 38 +- lib/coffeescript/grammar.js | 2 +- lib/coffeescript/helpers.js | 8 +- lib/coffeescript/lexer.js | 156 +++--- lib/coffeescript/nodes.js | 892 ++++++++++++++++++------------- lib/coffeescript/optparse.js | 2 +- lib/coffeescript/register.js | 2 +- lib/coffeescript/repl.js | 22 +- lib/coffeescript/rewriter.js | 42 +- lib/coffeescript/scope.js | 7 +- lib/coffeescript/sourcemap.js | 20 +- src/nodes.coffee | 339 ++++++++---- test/assignment.coffee | 19 +- test/functions.coffee | 12 +- test/strict.coffee | 2 +- 17 files changed, 952 insertions(+), 645 deletions(-) diff --git a/lib/coffeescript/cake.js b/lib/coffeescript/cake.js index b5334b7d6c..13fb0e7e06 100644 --- a/lib/coffeescript/cake.js +++ b/lib/coffeescript/cake.js @@ -24,15 +24,10 @@ 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, - 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 66556ffd77..88214f9a9c 100644 --- a/lib/coffeescript/coffeescript.js +++ b/lib/coffeescript/coffeescript.js @@ -9,9 +9,9 @@ path = require('path'); - Lexer = require('./lexer').Lexer; + ({Lexer} = require('./lexer')); - parser = require('./parser').parser; + ({parser} = require('./parser')); helpers = require('./helpers'); @@ -59,7 +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 = helpers.merge, extend = helpers.extend; + ({merge, extend} = helpers); options = extend({}, options); generateSourceMap = options.sourceMap || options.inlineMap || (options.filename == null); filename = options.filename || ''; @@ -132,7 +132,7 @@ } if (options.sourceMap) { return { - js: js, + js, sourceMap: map, v3SourceMap: JSON.stringify(v3SourceMap, null, 2) }; @@ -254,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) }); @@ -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 { @@ -293,11 +293,10 @@ parser.yy = require('./nodes'); - parser.yy.parseError = function(message, arg) { - 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]; + parser.yy.parseError = function(message, {token}) { + var errorLoc, errorTag, errorText, errorToken, tokens; + ({errorToken, tokens} = parser); + [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..13478f4d86 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,9 @@ CoffeeScript = require('./coffeescript'); - ref = require('child_process'), spawn = ref.spawn, exec = ref.exec; + ({spawn, exec} = require('child_process')); - EventEmitter = require('events').EventEmitter; + ({EventEmitter} = require('events')); useWinPathSep = path.sep === '\\'; @@ -50,7 +50,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 +90,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)); } @@ -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}')`; @@ -178,10 +178,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()) { @@ -203,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)); @@ -425,12 +421,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; } } @@ -546,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'], @@ -559,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 d88799845f..52e1ad142e 100644 --- a/lib/coffeescript/grammar.js +++ b/lib/coffeescript/grammar.js @@ -2,7 +2,7 @@ (function() { var Parser, alt, alternatives, grammar, name, o, operators, token, tokens, unwrap; - Parser = require('jison').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 dcbb5bed37..a32d1f64e4 100644 --- a/lib/coffeescript/helpers.js +++ b/lib/coffeescript/helpers.js @@ -196,11 +196,11 @@ }; 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_column, last_line, last_column} = this.location); if (last_line == null) { last_line = first_line; } @@ -213,9 +213,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 ea1479da61..b5599b0b6c 100644 --- a/lib/coffeescript/lexer.js +++ b/lib/coffeescript/lexer.js @@ -1,15 +1,15 @@ // 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; + ({Rewriter, INVERSES} = require('./rewriter')); - ref1 = require('./helpers'), count = ref1.count, starts = ref1.starts, compact = ref1.compact, repeat = ref1.repeat, invertLiterate = ref1.invertLiterate, locationDataToString = ref1.locationDataToString, throwSyntaxError = ref1.throwSyntaxError; + ({count, starts, compact, repeat, invertLiterate, locationDataToString, throwSyntaxError} = require('./helpers')); 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, ref8, ref9, tag, tagToken; + var alias, colon, colonOffset, id, idLength, input, match, poppedToken, prev, ref, ref1, 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') { @@ -82,27 +82,27 @@ 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; } } - if (id === 'as' && this.seenExport && ((ref4 = this.tag()) === 'IDENTIFIER' || ref4 === 'DEFAULT')) { + if (id === 'as' && this.seenExport && ((ref2 = this.tag()) === 'IDENTIFIER' || ref2 === 'DEFAULT')) { this.token('AS', id); return id.length; } - if (id === 'default' && this.seenExport && ((ref5 = this.tag()) === 'EXPORT' || ref5 === 'AS')) { + if (id === 'default' && this.seenExport && ((ref3 = this.tag()) === 'EXPORT' || ref3 === 'AS')) { this.token('DEFAULT', id); return id.length; } - ref6 = this.tokens, prev = ref6[ref6.length - 1]; - tag = colon || (prev != null) && (((ref7 = prev[0]) === '.' || ref7 === '?.' || ref7 === '::' || ref7 === '?::') || !prev.spaced && prev[0] === '@') ? 'PROPERTY' : 'IDENTIFIER'; + ref4 = this.tokens, prev = ref4[ref4.length - 1]; + tag = colon || (prev != null) && (((ref5 = prev[0]) === '.' || ref5 === '?.' || ref5 === '::' || ref5 === '?::') || !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' && (ref8 = this.tag(), indexOf.call(LINE_BREAK, ref8) >= 0)) { + if (tag === 'WHEN' && (ref6 = this.tag(), indexOf.call(LINE_BREAK, ref6) >= 0)) { tag = 'LEADING_WHEN'; } else if (tag === 'FOR') { this.seenFor = true; @@ -167,7 +167,7 @@ tagToken.origin = [tag, alias, tagToken[2]]; } if (poppedToken) { - ref9 = [poppedToken[2].first_line, poppedToken[2].first_column], tagToken[2].first_line = ref9[0], tagToken[2].first_column = ref9[1]; + [tagToken[2].first_line, tagToken[2].first_column] = [poppedToken[2].first_line, poppedToken[2].first_column]; } if (colon) { colonOffset = input.lastIndexOf(':'); @@ -223,8 +223,8 @@ } stringToken() { - var $, attempt, delimiter, doc, end, heredoc, i, indent, indentRegex, match, quote, ref2, ref3, regex, token, tokens; - quote = (STRING_START.exec(this.chunk) || [])[0]; + 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 +244,10 @@ } })(); heredoc = quote.length === 3; - ref2 = this.matchWithInterpolations(regex, quote), tokens = ref2.tokens, end = ref2.index; + ({ + tokens, + index: end + } = this.matchWithInterpolations(regex, quote)); $ = tokens.length - 1; delimiter = quote.charAt(0); if (heredoc) { @@ -262,16 +265,14 @@ })()).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; } } 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'); @@ -285,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)) { @@ -307,7 +306,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]}`, { @@ -336,7 +335,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,22 +343,22 @@ }); break; case !(match = this.matchWithInterpolations(HEREGEX, '///')): - tokens = match.tokens, index = match.index; + ({tokens, index} = match); 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 }); 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; } } @@ -370,7 +369,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) { @@ -469,7 +468,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 +482,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 +507,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,9 +540,9 @@ } 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[0]; + [value] = match; if (CODE.test(value)) { this.tagParameters(); } @@ -551,17 +550,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 +595,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 +633,7 @@ return this; } stack = []; - tokens = this.tokens; + ({tokens} = this); i = tokens.length; tokens[--i][0] = 'PARAM_END'; while (tok = tokens[--i]) { @@ -662,7 +661,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, ref, str, strPart, tokens; tokens = []; offsetInChunk = delimiter.length; if (this.chunk.slice(0, offsetInChunk) !== delimiter) { @@ -670,10 +669,10 @@ } 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 + offsetInChunk }); tokens.push(this.makeToken('NEOSTRING', strPart, offsetInChunk)); str = str.slice(strPart.length); @@ -681,18 +680,21 @@ 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); + ({ + tokens: nested, + index + } = new Lexer().tokenize(str.slice(1), { line: line, column: column, untilBalanced: true - }), nested = ref3.tokens, index = ref3.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 (((ref = nested[1]) != null ? ref[0] : void 0) === 'TERMINATOR') { nested.splice(1, 1); } tokens.push(['TOKENS', nested]); @@ -716,7 +718,7 @@ lastToken[2].last_column -= 1; } return { - tokens: tokens, + tokens, index: offsetInChunk + delimiter.length }; } @@ -729,7 +731,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) { @@ -787,13 +789,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 +803,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 +815,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; @@ -822,11 +824,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; } @@ -842,20 +844,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 +869,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 +881,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 +924,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), { - first_line: first_line, - first_column: first_column, - last_column: first_column + ((ref4 = options.length) != null ? ref4 : 1) - 1 + 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_column, + last_column: first_column + ((ref1 = options.length) != null ? ref1 : 1) - 1 }); return throwSyntaxError(message, location); } @@ -949,7 +951,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 +960,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 9162cb7958..614e21bcec 100644 --- a/lib/coffeescript/nodes.js +++ b/lib/coffeescript/nodes.js @@ -1,16 +1,16 @@ // 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; + ({Scope} = require('./scope')); - ref1 = require('./lexer'), isUnassignable = ref1.isUnassignable, JS_FORBIDDEN = ref1.JS_FORBIDDEN; + ({isUnassignable, JS_FORBIDDEN} = require('./lexer')); - 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; + ({compact, flatten, extend, merge, del, starts, ends, some, addLocationDataFn, locationDataToString, throwSyntaxError} = require('./helpers')); exports.extend = extend; @@ -35,10 +35,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 +82,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 +105,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 +198,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 +229,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)) { @@ -292,7 +292,7 @@ return new CodeFragment(this, code); } - wrapInBraces(fragments) { + wrapInParentheses(fragments) { return [].concat(this.makeCode('('), fragments, this.makeCode(')')); } @@ -337,11 +337,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 +414,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 +426,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 +461,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) { @@ -499,31 +499,31 @@ answer = [this.makeCode("void 0")]; } if (compiledNodes.length > 1 && o.level >= LEVEL_LIST) { - return this.wrapInBraces(answer); + return this.wrapInParentheses(answer); } else { return answer; } } 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 +550,12 @@ } 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, 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; @@ -566,12 +566,12 @@ }); 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); - scope = o.scope; + ({scope} = o); if (scope.expressions === this) { declars = o.scope.hasDeclarations(); assigns = scope.hasAssignments; @@ -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; } @@ -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; @@ -720,8 +725,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)]; } @@ -755,8 +760,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 { @@ -811,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; } @@ -821,6 +826,7 @@ if (tag) { this[tag] = true; } + this.isDefaultValue = isDefaultValue; return this; } @@ -878,10 +884,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; } @@ -913,14 +919,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() { @@ -932,8 +938,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]; } @@ -970,14 +976,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; } @@ -997,6 +1003,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']; @@ -1046,11 +1062,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; @@ -1062,8 +1078,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 { @@ -1074,7 +1090,7 @@ } unfoldSoak(o) { - var call, ifn, j, left, len1, list, ref3, ref4, 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)); @@ -1086,7 +1102,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 +1127,9 @@ break; } } - ref4 = list.reverse(); - for (j = 0, len1 = ref4.length; j < len1; j++) { - call = ref4[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; @@ -1127,14 +1143,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(", ")); } @@ -1163,19 +1179,19 @@ 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, ref4, 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))); 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); @@ -1205,7 +1221,7 @@ } this.inCtor = !!method.ctor; if (!(this.inCtor || (this.accessor != null))) { - name = method.name, variable = method.variable; + ({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); @@ -1273,11 +1289,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]; @@ -1331,15 +1347,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 +1363,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 +1381,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}`; @@ -1378,12 +1394,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) { @@ -1431,8 +1447,8 @@ } 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, from} = this.range); fromCompiled = from && from.compileToFragments(o, LEVEL_PAREN) || [this.makeCode('0')]; if (to) { compiled = to.compileToFragments(o, LEVEL_PAREN); @@ -1454,14 +1470,38 @@ 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; + 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; + } + if (!prop.isAssignable()) { + return false; + } + } + return true; + } + + shouldCache() { + return !this.isAssignable(); + } + compileNode(o) { - var answer, i, idt, indent, j, join, k, key, lastNoncom, len1, len2, node, prop, props, ref3, 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++) { @@ -1473,31 +1513,36 @@ } 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 Comment || (prop instanceof Assign && prop.context === 'object')) { + 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; - if (prop instanceof Assign) { - if (prop.context !== 'object') { - prop.operatorToken.error(`unexpected ${prop.operatorToken.value}`); - } - if (prop.variable instanceof Value && prop.variable.hasProperties()) { - prop.variable.error('invalid object key'); + join = i === props.length - 1 ? '' : isCompact ? ', ' : prop === lastNoncom || prop instanceof Comment ? '\n' : ',\n'; + indent = isCompact || prop instanceof Comment ? '' : idt; + 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'); } + key = key.properties[0].name; + prop = new Assign(key, prop, 'object'); } - if (prop instanceof Value && prop["this"]) { - prop = new Assign(prop.properties[0].name, prop, 'object'); - } - if (!(prop instanceof Comment) && !(prop instanceof Assign)) { + if (key === prop) { 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); } prop = new Assign(key, value, 'object'); - } else { + } else if (!(typeof prop.bareLiteral === "function" ? prop.bareLiteral(IdentifierLiteral) : void 0)) { prop = new Assign(prop, prop, 'object'); } } @@ -1509,21 +1554,19 @@ 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); + return this.wrapInParentheses(answer); } else { return answer; } } 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; } @@ -1531,6 +1574,25 @@ 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.context === 'object') { + prop = prop.value; + } + prop = prop.unwrapAll(); + if (prop.eachName != null) { + results.push(prop.eachName(iterator)); + } else { + results.push(void 0); + } + } + return results; + } + }; Obj.prototype.children = ['properties']; @@ -1541,29 +1603,62 @@ exports.Arr = Arr = (function() { class Arr extends Base { - constructor(objs) { + constructor(objs, lhs1 = false) { super(); + this.lhs = lhs1; this.objects = objs || []; } + isAssignable() { + var i, j, len1, obj, ref1; + if (!this.objects.length) { + return false; + } + 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; + } + if (!(obj.isAssignable() && (!obj.isAtomic || obj.isAtomic()))) { + return false; + } + } + return true; + } + + shouldCache() { + return !this.isAssignable(); + } + 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 = []; + if (this.lhs) { + ref1 = this.objects; + 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 j, len1, ref3, results; - ref3 = this.objects; + var k, len2, ref2, results; + ref2 = this.objects; results = []; - for (j = 0, len1 = ref3.length; j < len1; j++) { - obj = ref3[j]; + 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(", ")); @@ -1581,10 +1676,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; } @@ -1592,6 +1687,18 @@ return false; } + eachName(iterator) { + var j, len1, obj, ref1, results; + ref1 = this.objects; + results = []; + for (j = 0, len1 = ref1.length; j < len1; j++) { + obj = ref1[j]; + obj = obj.unwrapAll(); + results.push(obj.eachName(iterator)); + } + return results; + } + }; Arr.prototype.children = ['objects']; @@ -1624,13 +1731,11 @@ } else { result = this.compileClassDeclaration(o); if ((this.name == null) && o.level === LEVEL_TOP) { - result = this.wrapInBraces(result); + result = this.wrapInParentheses(result); } } 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; @@ -1638,14 +1743,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); @@ -1671,11 +1776,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; @@ -1695,18 +1800,18 @@ } 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} = 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} = expression.base); exprs = []; end = 0; start = 0; @@ -1794,7 +1899,10 @@ addInitializerMethod(assign) { var method, methodName, variable; - variable = assign.variable, method = assign.value; + ({ + variable, + value: method + } = assign); method.isMethod = true; method.isStatic = variable.looksStatic(this.name); if (method.isStatic) { @@ -1832,11 +1940,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)`)); } @@ -1862,14 +1970,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); @@ -1917,15 +2025,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); @@ -2030,7 +2138,7 @@ exports.ImportDeclaration = ImportDeclaration = class ImportDeclaration extends ModuleDeclaration { compileNode(o) { - var code, ref3; + var code, ref1; this.checkScope(o, 'import'); o.importedSymbols = []; code = []; @@ -2038,7 +2146,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 ')); } @@ -2083,7 +2191,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 `)); @@ -2102,7 +2210,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(';')); @@ -2129,11 +2237,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; @@ -2201,8 +2309,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); @@ -2230,7 +2338,7 @@ 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) { @@ -2252,93 +2360,106 @@ } compileNode(o) { - var answer, compiledName, isValue, j, name, properties, prototype, ref3, ref4, ref5, ref6, ref7, ref8, val, varBase; - if (isValue = this.variable instanceof Value) { + var answer, compiledName, isValue, j, name, properties, prototype, ref1, ref2, ref3, ref4, ref5, ref6, val, varBase; + isValue = this.variable instanceof Value; + if (isValue) { + this.variable.param = this.param; if (this.variable.isArray() || this.variable.isObject()) { - return this.compilePatternMatch(o); + this.variable.base.lhs = true; + if (!this.variable.isAssignable()) { + return this.compileDestructuring(o); + } } 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); } } - 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; + } + message = isUnassignable(name.value); + if (message) { + name.error(message); + } + this.checkAssignability(o, name); if (this.moduleDeclaration) { - this.checkAssignability(o, varBase); - o.scope.add(varBase.value, this.moduleDeclaration); - } else if (this.param) { - o.scope.add(varBase.value, 'var'); + return o.scope.add(name.value, this.moduleDeclaration); } else { - this.checkAssignability(o, varBase); - o.scope.find(varBase.value); + return o.scope.find(name.value); + } + }); + } + if (this.value instanceof Code) { + if (this.value.isStatic) { + this.value.name = this.variable.properties[0]; + } 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; + if (o.level > LEVEL_LIST || (isValue && this.variable.base instanceof Obj && !this.param)) { + return this.wrapInParentheses(answer); } else { - return this.wrapInBraces(answer); + 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; + 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.value; - objects = this.variable.base.objects; - if (!(olen = objects.length)) { + ({value} = this); + ({objects} = this.variable.base); + olen = objects.length; + if (olen === 0) { code = value.compileToFragments(o); if (o.level >= LEVEL_OP) { - return this.wrapInBraces(code); + return this.wrapInParentheses(code); } else { return code; } } - obj = objects[0]; + [obj] = objects; if (olen === 1 && obj instanceof Expansion) { obj.error('Destructuring assignment has no target'); } isObject = this.variable.isObject(); if (top && olen === 1 && !(obj instanceof Splat)) { - defaultValue = null; + defaultValue = void 0; 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; @@ -2358,6 +2479,7 @@ obj.error(message); } if (defaultValue) { + defaultValue.isDefaultValue = true; value = new Op('?', value, defaultValue); } return new Assign(obj, value, null, { @@ -2369,7 +2491,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; } @@ -2380,7 +2503,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 }); @@ -2391,7 +2515,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 { @@ -2408,9 +2533,14 @@ 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') { - 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; @@ -2426,6 +2556,7 @@ acc = idx.unwrap() instanceof PropertyName; val = new Value(new Literal(vvarText), [new (acc ? Access : Index)(idx)]); if (defaultValue) { + defaultValue.isDefaultValue = true; val = new Op('?', val, defaultValue); } } @@ -2447,13 +2578,13 @@ if (o.level < LEVEL_LIST) { return fragments; } else { - return this.wrapInBraces(fragments); + return this.wrapInParentheses(fragments); } } 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`); } @@ -2467,23 +2598,25 @@ if (o.level <= LEVEL_LIST) { return fragments; } else { - return this.wrapInBraces(fragments); + return this.wrapInParentheses(fragments); } } } 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; - 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, to, exclusive} + } = this.variable.properties.pop()); 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,19 +2635,25 @@ } 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); + return this.wrapInParentheses(answer); } else { return answer; } } + eachName(iterator) { + return this.variable.unwrapAll().eachName(iterator); + } + }; Assign.prototype.children = ['variable', 'value']; + Assign.prototype.isAssignable = YES; + return Assign; })(); @@ -2551,7 +2690,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, ref1, ref2, ref3, ref4, ref5, signature, splatParamName, thisAssignments, wasEmpty; if (this.ctor) { if (this.isAsync) { this.name.error('Class constructor may not be async'); @@ -2561,7 +2700,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) { @@ -2575,7 +2714,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; @@ -2596,9 +2735,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'); @@ -2607,10 +2746,18 @@ } 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, '=', { + exprs.push(new Assign(new Value(param.name), ref, null, { param: true })); } @@ -2624,13 +2771,13 @@ param.assignedInBody = true; haveBodyParam = true; if (param.value != null) { - condition = new Op('==', param, new UndefinedLiteral); - ifTrue = new Assign(new Value(param.name), param.value, '=', { + condition = new Op('===', param, new UndefinedLiteral); + 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,21 +2787,30 @@ 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.lhs = true; + param.name.eachName(function(prop) { + return o.scope.parameter(prop.value); + }); + } else { + o.scope.parameter(fragmentsToText((param.value != null ? param : ref).compileToFragments(o))); + } params.push(ref); } else { paramsAfterSplat.push(param); if ((param.value != null) && !param.shouldCache()) { - condition = new Op('==', param, new UndefinedLiteral); - ifTrue = new Assign(new Value(param.name), param.value, '='); + condition = new Op('===', param, new UndefinedLiteral); + 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); } } @@ -2709,7 +2865,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(); @@ -2744,18 +2900,18 @@ return [this.makeCode(this.tab), ...answer]; } if (this.front || (o.level >= LEVEL_ACCESS)) { - return this.wrapInBraces(answer); + return this.wrapInParentheses(answer); } else { return answer; } } 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; @@ -2776,7 +2932,7 @@ } expandCtorSuper(thisAssignments) { - var haveThisParam, param, ref3, seenSuper; + var haveThisParam, param, ref1, seenSuper; if (!this.ctor) { return false; } @@ -2789,7 +2945,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"); @@ -2869,7 +3025,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); }; @@ -2879,9 +3035,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; } @@ -2937,6 +3093,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 +3118,6 @@ Splat.prototype.children = ['name']; - Splat.prototype.isAssignable = YES; - return Splat; })(); @@ -3010,7 +3168,7 @@ jumps() { var expressions, j, jumpNode, len1, node; - expressions = this.body.expressions; + ({expressions} = this.body); if (!expressions.length) { return false; } @@ -3029,7 +3187,7 @@ var answer, body, rvar, set; o.indent += TAB; set = ''; - body = this.body; + ({body} = this); if (body.isEmpty()) { body = this.makeCode(''); } else { @@ -3093,8 +3251,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() { @@ -3102,8 +3260,8 @@ } isYield() { - var ref3; - return (ref3 = this.operator) === 'yield' || ref3 === 'yield*'; + var ref1; + return (ref1 = this.operator) === 'yield' || ref1 === 'yield*'; } isUnary() { @@ -3115,12 +3273,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; @@ -3146,7 +3304,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); @@ -3154,17 +3312,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; @@ -3178,7 +3336,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; @@ -3186,7 +3344,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); @@ -3203,7 +3361,7 @@ } switch (this.operator) { case '?': - return this.compileExistence(o); + return this.compileExistence(o, this.second.isDefaultValue); case '**': return this.compilePower(o); case '//': @@ -3217,20 +3375,20 @@ if (o.level <= LEVEL_OP) { return answer; } else { - return this.wrapInBraces(answer); + return this.wrapInParentheses(answer); } } } 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); + return this.wrapInParentheses(fragments); } - compileExistence(o) { + compileExistence(o, checkOnlyUndefined) { var fst, ref; if (this.first.shouldCache()) { ref = new IdentifierLiteral(o.scope.freeVariable('ref')); @@ -3239,7 +3397,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); } @@ -3271,13 +3429,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)) { @@ -3289,7 +3447,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)); @@ -3353,11 +3511,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; } @@ -3372,13 +3530,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, ref1, 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]; + 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)); } @@ -3387,13 +3545,13 @@ if (o.level < LEVEL_OP) { return tests; } else { - return this.wrapInBraces(tests); + return this.wrapInParentheses(tests); } } 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; @@ -3402,7 +3560,7 @@ if (o.level < LEVEL_LIST) { return fragments; } else { - return this.wrapInBraces(fragments); + return this.wrapInParentheses(fragments); } } @@ -3431,8 +3589,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) { @@ -3495,20 +3653,22 @@ 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) { - 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]; - code = `typeof ${code} ${cmp} \"undefined\" ${cnj} ${code} ${cmp} null`; + [cmp, cnj] = this.negated ? ['===', '||'] : ['!==', '&&']; + code = `typeof ${code} ${cmp} \"undefined\"` + (this.comparisonTarget !== '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})`)]; } @@ -3550,7 +3710,7 @@ if (bare) { return fragments; } else { - return this.wrapInBraces(fragments); + return this.wrapInParentheses(fragments); } } @@ -3626,9 +3786,9 @@ exports.For = For = (function() { class For extends While { constructor(body, source) { - var ref3; + 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; @@ -3640,9 +3800,9 @@ 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()) { + 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; @@ -3657,9 +3817,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, 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, 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; } @@ -3692,7 +3852,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); } @@ -3707,7 +3867,7 @@ if (this.range) { forPartFragments = source.compileToFragments(merge(o, { index: ivar, - name: name, + name, step: this.step, shouldCache: shouldCacheOrIsAssignable })); @@ -3788,24 +3948,24 @@ } 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, 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) { - 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,44 +3993,44 @@ jumps(o = { block: true }) { - var block, conds, j, jumpNode, len1, ref3, ref4, ref5; - ref3 = this.cases; - for (j = 0, len1 = ref3.length; j < len1; j++) { - ref4 = ref3[j], conds = ref4[0], block = ref4[1]; + 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 (ref5 = this.otherwise) != null ? ref5.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, ref5; + 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) { - ref4 = ref3[i], conditions = ref4[0], block = ref4[1]; - ref5 = flatten([conditions]); - for (k = 0, len2 = ref5.length; k < len2; k++) { - cond = ref5[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(); } @@ -3913,17 +4073,17 @@ 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) { @@ -3938,13 +4098,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) { @@ -3983,9 +4143,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)); @@ -3998,9 +4156,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; } @@ -4012,7 +4168,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; } @@ -4069,7 +4225,7 @@ utility = function(name, o) { var ref, root; - root = o.scope.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..dbd707953f 100644 --- a/lib/coffeescript/optparse.js +++ b/lib/coffeescript/optparse.js @@ -2,7 +2,7 @@ (function() { var LONG_FLAG, MULTI_FLAG, OPTIONAL, OptionParser, SHORT_FLAG, buildRule, buildRules, normalizeArguments, repeat; - repeat = require('./helpers').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..bc759db22a 100644 --- a/lib/coffeescript/register.js +++ b/lib/coffeescript/register.js @@ -48,7 +48,7 @@ } if (child_process) { - fork = child_process.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 a277a44101..0c299bedf0 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,18 @@ CoffeeScript = require('./coffeescript'); - ref = require('./helpers'), merge = ref.merge, updateSyntaxError = ref.updateSyntaxError; + ({merge, 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, Assign, Value, Literal} = require('./nodes')); try { tokens = CoffeeScript.tokens(input); referencedVars = (function() { @@ -42,7 +42,7 @@ js = ast.compile({ bare: true, locals: Object.keys(context), - referencedVars: referencedVars + referencedVars }); return cb(null, runInContext(js, context, filename)); } catch (error) { @@ -62,9 +62,9 @@ }; 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, inputStream, outputStream} = repl); + origPrompt = (ref = repl._prompt) != null ? ref : repl.prompt; multiline = { enabled: false, initialPrompt: origPrompt.replace(/^[^> ]*/, function(x) { @@ -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..938da10920 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) { @@ -30,7 +30,7 @@ scanTokens(block) { var i, token, tokens; - tokens = this.tokens; + ({tokens} = this); i = 0; while (token = tokens[i]) { i += block.call(this, token, i, tokens); @@ -40,7 +40,7 @@ detectEnd(i, condition, action) { var levels, ref, ref1, token, tokens; - tokens = this.tokens; + ({tokens} = this); levels = 0; while (token = tokens[i]) { if (levels === 0 && condition.call(this, token, i)) { @@ -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, 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); + [stackTag, stackIdx, {sameLine, startsLine}] = stackTop(); if (inImplicitCall() && prevTag !== ',') { endImplicitCall(); } else if (inImplicitObject() && !this.insideForDeclaration && sameLine && tag !== 'TERMINATOR' && prevTag !== ':') { @@ -351,9 +351,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; } @@ -395,8 +401,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 +424,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 +444,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 +498,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/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 60d5fe2c68..be75931b9c 100644 --- a/lib/coffeescript/sourcemap.js +++ b/lib/coffeescript/sourcemap.js @@ -8,20 +8,15 @@ this.columns = []; } - add(column, arg, options) { - var sourceColumn, sourceLine; - sourceLine = arg[0], sourceColumn = arg[1]; - if (options === void 0) { - options = {}; - } + add(column, [sourceLine, sourceColumn], options = {}) { if (this.columns[column] && options.noReplace) { return; } return this.columns[column] = { line: this.line, - column: column, - sourceLine: sourceLine, - sourceColumn: sourceColumn + column, + sourceLine, + sourceColumn }; } @@ -45,14 +40,13 @@ 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]; + sourceLocation([line, column]) { + var lineMap; while (!((lineMap = this.lines[line]) || (line <= 0))) { line--; } diff --git a/src/nodes.coffee b/src/nodes.coffee index cc2a6dabc7..4dd66e5b17 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 @@ -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 @@ -581,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() @@ -627,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'] @@ -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 @@ -1097,13 +1108,26 @@ 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, @lhs = no) -> super() @objects = @properties = props or [] children: ['properties'] + isAssignable: -> + 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 @@ -1111,56 +1135,95 @@ 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 = yes + for prop in @properties + if prop instanceof Comment or (prop instanceof Assign and prop.context is 'object') + isCompact = no + 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 - 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 + indent = if isCompact or prop instanceof Comment then '' else idt + + 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 is 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 + 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 - if @front then @wrapInBraces answer else answer + answer.push @makeCode "#{if isCompact then '' else "\n#{@tab}"}}" + if @front then @wrapInParentheses answer else answer assigns: (name) -> 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 and prop.context is 'object' + prop = prop.unwrapAll() + prop.eachName iterator if prop.eachName? + #### Arr # An array literal. exports.Arr = class Arr extends Base - constructor: (objs) -> + constructor: (objs, @lhs = no) -> super() @objects = objs or [] children: ['objects'] + isAssignable: -> + return no unless @objects.length + + for obj, i in @objects + 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 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 @@ -1178,6 +1241,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. @@ -1195,7 +1263,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 @@ -1205,7 +1273,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 } @@ -1304,7 +1372,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 @@ -1405,7 +1473,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 @@ -1640,6 +1708,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) @@ -1654,39 +1724,57 @@ 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 - return @compilePatternMatch o if @variable.isArray() or @variable.isObject() + isValue = @variable instanceof Value + if isValue + # 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() + # 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() 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?() + + message = isUnassignable name.value + name.error message if message + # `moduleDeclaration` can be `'import'` or `'export'` + @checkAssignability o, name if @moduleDeclaration - @checkAssignability o, varBase - o.scope.add varBase.value, @moduleDeclaration - else if @param - o.scope.add varBase.value, 'var' + o.scope.add name.value, @moduleDeclaration else - @checkAssignability o, varBase - o.scope.find varBase.value + 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 compiledName = @variable.compileToFragments o, LEVEL_LIST if @context is 'object' @@ -1699,25 +1787,40 @@ 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 + # 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 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. - compilePatternMatch: (o) -> + compileDestructuring: (o) -> top = o.level is LEVEL_TOP {value} = this {objects} = @variable.base - unless olen = objects.length + 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 @wrapInBraces code else code + 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). - defaultValue = null + defaultValue = undefined if obj instanceof Assign and obj.context is 'object' # A regular object pattern-match. {variable: {base: idx}, value: obj} = obj @@ -1742,24 +1845,43 @@ 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 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) - assigns.push [@makeCode("#{ ref = o.scope.freeVariable 'ref' } = "), vvar...] + 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 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 @@ -1767,7 +1889,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 @@ -1779,7 +1902,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 @@ -1802,14 +1925,17 @@ 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 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 @@ -1825,7 +1951,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. @@ -1853,7 +1979,10 @@ 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 #### Code @@ -1947,13 +2076,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, '=', 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'`). + exprs.push new Assign new Value(param.name), ref, null, param: yes else # `param` is an Expansion splatParamName = o.scope.freeVariable 'args' params.push new Value new IdentifierLiteral splatParamName @@ -1972,11 +2106,11 @@ 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 - ifTrue = new Assign new Value(param.name), param.value, '=', param: yes + 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 - 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,11 +2123,17 @@ 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.lhs = yes + param.name.eachName (prop) -> + o.scope.parameter prop.value + else + o.scope.parameter fragmentsToText (if param.value? then param else ref).compileToFragments o params.push ref else paramsAfterSplat.push param @@ -2001,8 +2141,8 @@ 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 - ifTrue = new Assign new Value(param.name), param.value, '=' + 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. o.scope.add param.name.value, 'var', yes if param.name?.value? @@ -2056,7 +2196,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 @@ -2084,7 +2224,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" @@ -2205,7 +2345,8 @@ exports.Splat = class Splat extends Base children: ['name'] - isAssignable: YES + isAssignable: -> + @name.isAssignable() and (not @name.isAtomic or @name.isAtomic()) constructor: (name) -> super() @@ -2299,7 +2440,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 @@ -2410,7 +2551,7 @@ 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, @second.isDefaultValue when '**' then @compilePower o when '//' then @compileFloorDivision o when '%%' then @compileModulo o @@ -2418,7 +2559,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: @@ -2430,17 +2571,17 @@ 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) -> + 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) -> @@ -2521,7 +2662,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 @@ -2529,7 +2670,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 '' @@ -2600,12 +2741,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'] @@ -2616,10 +2758,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\"" + if @comparisonTarget isnt 'undefined' then " #{cnj} #{code} #{cmp} #{@comparisonTarget}" else '' 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 @@ -2647,7 +2798,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 @@ -2719,7 +2870,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 @@ -2954,7 +3105,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 diff --git a/test/assignment.coffee b/test/assignment.coffee index aff5371a1d..c02e9ab804 100644 --- a/test/assignment.coffee +++ b/test/assignment.coffee @@ -142,7 +142,8 @@ test "#1192: assignment starting with object literals", -> # Destructuring Assignment test "empty destructuring assignment", -> - {} = [] = undefined + {} = {} + [] = [] test "chained destructuring assignments", -> [a] = {0: b} = {'0': c} = [nonce={}] @@ -305,7 +306,7 @@ test "simple array destructuring defaults", -> [a = 2] = [undefined] eq 2, a [a = 3] = [null] - eq 3, 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] @@ -318,7 +319,7 @@ test "simple object destructuring defaults", -> {b = 2} = {b: undefined} eq b, 2 {b = 3} = {b: null} - eq b, 3 + 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 @@ -327,17 +328,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 # Breaking change in CS2: per ES2015, default values are applied for `undefined` but not for `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] @@ -368,7 +369,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() @@ -387,7 +388,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..cfe29c9c0f 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: {} } @@ -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