This is an automated email from the ASF dual-hosted git repository. davisp pushed a commit to branch gh-pages in repository https://gitbox.apache.org/repos/asf/couchdb-escodegen.git
commit b660de6b026d7d2b49092f10e0f59ed1d7fd46f7 Author: Constellation <[email protected]> AuthorDate: Mon Jan 14 19:05:42 2013 +0900 Update gh-pages --- escodegen.js | 1826 ++++++++++++++++++++++++++++++++++++++++------------------ 1 file changed, 1272 insertions(+), 554 deletions(-) diff --git a/escodegen.js b/escodegen.js index 59087f6..5d8400e 100644 --- a/escodegen.js +++ b/escodegen.js @@ -1,12 +1,13 @@ /* + Copyright (C) 2012 Michael Ficarra <[email protected]> + Copyright (C) 2012 Robert Gust-Bardon <[email protected]> Copyright (C) 2012 John Freeman <[email protected]> - Copyright (C) 2012 Ariya Hidayat <[email protected]> + Copyright (C) 2011-2012 Ariya Hidayat <[email protected]> Copyright (C) 2012 Mathias Bynens <[email protected]> Copyright (C) 2012 Joost-Wim Boekesteijn <[email protected]> Copyright (C) 2012 Kris Kowal <[email protected]> Copyright (C) 2012 Yusuke Suzuki <[email protected]> Copyright (C) 2012 Arpad Borsos <[email protected]> - Copyright (C) 2011 Ariya Hidayat <[email protected]> Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: @@ -30,32 +31,64 @@ */ /*jslint bitwise:true */ -/*global escodegen:true, exports:true, generateStatement: true*/ +/*global escodegen:true, exports:true, generateStatement:true, generateExpression:true, generateFunctionBody:true, process:true, require:true, define:true*/ -(function (exports) { +(function (factory, global) { + 'use strict'; + + // Universal Module Definition (UMD) to support AMD, CommonJS/Node.js, + // and plain browser loading, + if (typeof define === 'function' && define.amd) { + define(['exports'], function (exports) { + factory(exports, global); + }); + } else if (typeof exports !== 'undefined') { + factory(exports, global); + } else { + factory((global.escodegen = {}), global); + } +}(function (exports, global) { 'use strict'; var Syntax, Precedence, BinaryPrecedence, + Regex, VisitorKeys, VisitorOption, + SourceNode, isArray, base, indent, + json, + renumber, + hexadecimal, + quotes, + escapeless, + newline, + space, + parentheses, + semicolons, + safeConcatenation, + directive, extra, - parse; + parse, + sourceMap; Syntax = { AssignmentExpression: 'AssignmentExpression', ArrayExpression: 'ArrayExpression', + ArrayPattern: 'ArrayPattern', BlockStatement: 'BlockStatement', BinaryExpression: 'BinaryExpression', BreakStatement: 'BreakStatement', CallExpression: 'CallExpression', CatchClause: 'CatchClause', + ComprehensionBlock: 'ComprehensionBlock', + ComprehensionExpression: 'ComprehensionExpression', ConditionalExpression: 'ConditionalExpression', ContinueStatement: 'ContinueStatement', + DirectiveStatement: 'DirectiveStatement', DoWhileStatement: 'DoWhileStatement', DebuggerStatement: 'DebuggerStatement', EmptyStatement: 'EmptyStatement', @@ -72,6 +105,7 @@ MemberExpression: 'MemberExpression', NewExpression: 'NewExpression', ObjectExpression: 'ObjectExpression', + ObjectPattern: 'ObjectPattern', Program: 'Program', Property: 'Property', ReturnStatement: 'ReturnStatement', @@ -86,7 +120,9 @@ VariableDeclaration: 'VariableDeclaration', VariableDeclarator: 'VariableDeclarator', WhileStatement: 'WhileStatement', - WithStatement: 'WithStatement' + WithStatement: 'WithStatement', + YieldExpression: 'YieldExpression', + }; Precedence = { @@ -95,8 +131,8 @@ Conditional: 2, LogicalOR: 3, LogicalAND: 4, - LogicalXOR: 5, - BitwiseOR: 6, + BitwiseOR: 5, + BitwiseXOR: 6, BitwiseAND: 7, Equality: 8, Relational: 9, @@ -114,13 +150,15 @@ BinaryPrecedence = { '||': Precedence.LogicalOR, '&&': Precedence.LogicalAND, - '^': Precedence.LogicalXOR, '|': Precedence.BitwiseOR, + '^': Precedence.BitwiseXOR, '&': Precedence.BitwiseAND, '==': Precedence.Equality, '!=': Precedence.Equality, '===': Precedence.Equality, '!==': Precedence.Equality, + 'is': Precedence.Equality, + 'isnt': Precedence.Equality, '<': Precedence.Relational, '>': Precedence.Relational, '<=': Precedence.Relational, @@ -137,6 +175,10 @@ '/': Precedence.Multiplicative }; + Regex = { + NonAsciiIdentifierPart: new RegExp('[\xaa\xb5\xba\xc0-\xd6\xd8-\xf6\xf8-\u02c1\u02c6-\u02d1\u02e0-\u02e4\u02ec\u02ee\u0300-\u0374\u0376\u0377\u037a-\u037d\u0386\u0388-\u038a\u038c\u038e-\u03a1\u03a3-\u03f5\u03f7-\u0481\u0483-\u0487\u048a-\u0527\u0531-\u0556\u0559\u0561-\u0587\u0591-\u05bd\u05bf\u05c1\u05c2\u05c4\u05c5\u05c7\u05d0-\u05ea\u05f0-\u05f2\u0610-\u061a\u0620-\u0669\u066e-\u06d3\u06d5-\u06dc\u06df-\u06e8\u06ea-\u06fc\u06ff\u0710-\u074a\u074d-\u07b1\u07c0-\u07f5\u07fa\u08 [...] + }; + function getDefaultOptions() { // default options return { @@ -149,20 +191,28 @@ style: ' ', base: 0, adjustMultilineComment: false - } - } + }, + json: false, + renumber: false, + hexadecimal: false, + quotes: 'single', + escapeless: false, + compact: false, + parentheses: true, + semicolons: true, + safeConcatenation: false + }, + moz: { + starlessGenerator: false, + parenthesizedComprehensionBlock: false + }, + sourceMap: null, + sourceMapWithCode: false, + directive: false, + verbatim: null }; } - function unicodeEscape(ch) { - var result, i; - result = ch.charCodeAt(0).toString(16); - for (i = result.length; i < 4; i += 1) { - result = '0' + result; - } - return '\\u' + result; - } - function stringToArray(str) { var length = str.length, result = [], @@ -192,8 +242,73 @@ }; } - function endsWithLineTerminator(value) { - return (/(?:\r\n|[\n\r])$/).test(value); + // Fallback for the non SourceMap environment + function SourceNodeMock(line, column, filename, chunk) { + var result = []; + + function flatten(input) { + var i, iz; + if (isArray(input)) { + for (i = 0, iz = input.length; i < iz; ++i) { + flatten(input[i]); + } + } else if (input instanceof SourceNodeMock) { + result.push(input); + } else if (typeof input === 'string' && input) { + result.push(input); + } + } + + flatten(chunk); + this.children = result; + } + + SourceNodeMock.prototype.toString = function toString() { + var res = '', i, iz, node; + for (i = 0, iz = this.children.length; i < iz; ++i) { + node = this.children[i]; + if (node instanceof SourceNodeMock) { + res += node.toString(); + } else { + res += node; + } + } + return res; + }; + + SourceNodeMock.prototype.replaceRight = function replaceRight(pattern, replacement) { + var last = this.children[this.children.length - 1]; + if (last instanceof SourceNodeMock) { + last.replaceRight(pattern, replacement); + } else if (typeof last === 'string') { + this.children[this.children.length - 1] = last.replace(pattern, replacement); + } else { + this.children.push(''.replace(pattern, replacement)); + } + return this; + }; + + SourceNodeMock.prototype.join = function join(sep) { + var i, iz, result; + result = []; + iz = this.children.length; + if (iz > 0) { + for (i = 0, iz -= 1; i < iz; ++i) { + result.push(this.children[i], sep); + } + result.push(this.children[iz]); + this.children = result; + } + return this; + }; + + function hasLineTerminator(str) { + return /[\r\n]/g.test(str); + } + + function endsWithLineTerminator(str) { + var ch = str.charAt(str.length - 1); + return ch === '\r' || ch === '\n'; } function shallowCopy(obj) { @@ -245,8 +360,142 @@ return target; } + function generateNumber(value) { + var result, point, temp, exponent, pos; + + if (value !== value) { + throw new Error('Numeric literal whose value is NaN'); + } + if (value < 0 || (value === 0 && 1 / value < 0)) { + throw new Error('Numeric literal whose value is negative'); + } + + if (value === 1 / 0) { + return json ? 'null' : renumber ? '1e400' : '1e+400'; + } + + result = '' + value; + if (!renumber || result.length < 3) { + return result; + } + + point = result.indexOf('.'); + if (!json && result.charAt(0) === '0' && point === 1) { + point = 0; + result = result.slice(1); + } + temp = result; + result = result.replace('e+', 'e'); + exponent = 0; + if ((pos = temp.indexOf('e')) > 0) { + exponent = +temp.slice(pos + 1); + temp = temp.slice(0, pos); + } + if (point >= 0) { + exponent -= temp.length - point - 1; + temp = +(temp.slice(0, point) + temp.slice(point + 1)) + ''; + } + pos = 0; + while (temp.charAt(temp.length + pos - 1) === '0') { + pos -= 1; + } + if (pos !== 0) { + exponent -= pos; + temp = temp.slice(0, pos); + } + if (exponent !== 0) { + temp += 'e' + exponent; + } + if ((temp.length < result.length || + (hexadecimal && value > 1e12 && Math.floor(value) === value && (temp = '0x' + value.toString(16)).length < result.length)) && + +temp === value) { + result = temp; + } + + return result; + } + + function escapeAllowedCharacter(ch, next) { + var code = ch.charCodeAt(0), hex = code.toString(16), result = '\\'; + + switch (ch) { + case '\b': + result += 'b'; + break; + case '\f': + result += 'f'; + break; + case '\t': + result += 't'; + break; + default: + if (json || code > 0xff) { + result += 'u' + '0000'.slice(hex.length) + hex; + } else if (ch === '\u0000' && '0123456789'.indexOf(next) < 0) { + result += '0'; + } else if (ch === '\v') { + result += 'v'; + } else { + result += 'x' + '00'.slice(hex.length) + hex; + } + break; + } + + return result; + } + + function escapeDisallowedCharacter(ch) { + var result = '\\'; + switch (ch) { + case '\\': + result += '\\'; + break; + case '\n': + result += 'n'; + break; + case '\r': + result += 'r'; + break; + case '\u2028': + result += 'u2028'; + break; + case '\u2029': + result += 'u2029'; + break; + default: + throw new Error('Incorrectly classified character'); + } + + return result; + } + + function escapeDirective(str) { + var i, iz, ch, single, buf, quote; + + buf = str; + if (typeof buf[0] === 'undefined') { + buf = stringToArray(buf); + } + + quote = quotes === 'double' ? '"' : '\''; + for (i = 0, iz = buf.length; i < iz; i += 1) { + ch = buf[i]; + if (ch === '\'') { + quote = '"'; + break; + } else if (ch === '"') { + quote = '\''; + break; + } else if (ch === '\\') { + i += 1; + } + } + + return quote + str + quote; + } + function escapeString(str) { - var result = '', i, len, ch; + var result = '', i, len, ch, next, singleQuotes = 0, doubleQuotes = 0, single; if (typeof str[0] === 'undefined') { str = stringToArray(str); @@ -254,57 +503,112 @@ for (i = 0, len = str.length; i < len; i += 1) { ch = str[i]; - if ('\'\\\b\f\n\r\t'.indexOf(ch) >= 0) { + if (ch === '\'') { + singleQuotes += 1; + } else if (ch === '"') { + doubleQuotes += 1; + } else if (ch === '/' && json) { + result += '\\'; + } else if ('\\\n\r\u2028\u2029'.indexOf(ch) >= 0) { + result += escapeDisallowedCharacter(ch); + continue; + } else if ((json && ch < ' ') || !(json || escapeless || (ch >= ' ' && ch <= '~'))) { + result += escapeAllowedCharacter(ch, str[i + 1]); + continue; + } + result += ch; + } + + single = !(quotes === 'double' || (quotes === 'auto' && doubleQuotes < singleQuotes)); + str = result; + result = single ? '\'' : '"'; + + if (typeof str[0] === 'undefined') { + str = stringToArray(str); + } + + for (i = 0, len = str.length; i < len; i += 1) { + ch = str[i]; + if ((ch === '\'' && single) || (ch === '"' && !single)) { result += '\\'; - switch (ch) { - case '\'': - result += '\''; - break; - case '\\': - result += '\\'; - break; - case '\b': - result += 'b'; - break; - case '\f': - result += 'f'; - break; - case '\n': - result += 'n'; - break; - case '\r': - result += 'r'; - break; - case '\t': - result += 't'; - break; - } - } else if (ch < ' ' || ch.charCodeAt(0) >= 0x80) { - result += unicodeEscape(ch); - } else { - result += ch; } + result += ch; } - return '\'' + result + '\''; + return result + (single ? '\'' : '"'); } function isWhiteSpace(ch) { - return (ch === ' ') || (ch === '\u0009') || (ch === '\u000B') || - (ch === '\u000C') || (ch === '\u00A0') || - (ch.charCodeAt(0) >= 0x1680 && - '\u1680\u180E\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200A\u202F\u205F\u3000\uFEFF'.indexOf(ch) >= 0); + return '\t\v\f \xa0'.indexOf(ch) >= 0 || (ch.charCodeAt(0) >= 0x1680 && '\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\ufeff'.indexOf(ch) >= 0); + } + + function isLineTerminator(ch) { + return '\n\r\u2028\u2029'.indexOf(ch) >= 0; + } + + function isIdentifierPart(ch) { + return (ch === '$') || (ch === '_') || (ch === '\\') || + (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || + ((ch >= '0') && (ch <= '9')) || + ((ch.charCodeAt(0) >= 0x80) && Regex.NonAsciiIdentifierPart.test(ch)); + } + + function toSourceNode(generated, node) { + if (node == null) { + if (generated instanceof SourceNode) { + return generated; + } else { + node = {}; + } + } + if (node.loc == null) { + return new SourceNode(null, null, sourceMap, generated); + } + return new SourceNode(node.loc.start.line, node.loc.start.column, (sourceMap === true ? node.loc.source || null : sourceMap), generated); + } + + function join(left, right) { + var leftSource = toSourceNode(left).toString(), + rightSource = toSourceNode(right).toString(), + leftChar = leftSource.charAt(leftSource.length - 1), + rightChar = rightSource.charAt(0); + + if (((leftChar === '+' || leftChar === '-') && leftChar === rightChar) || (isIdentifierPart(leftChar) && isIdentifierPart(rightChar))) { + return [left, ' ', right]; + } else if (isWhiteSpace(leftChar) || isLineTerminator(leftChar) || isWhiteSpace(rightChar) || isLineTerminator(rightChar)) { + return [left, right]; + } + return [left, space, right]; } function addIndent(stmt) { - return base + stmt; + return [base, stmt]; + } + + function withIndent(fn) { + var previousBase, result; + previousBase = base; + base += indent; + result = fn.call(this, base); + base = previousBase; + return result; } - function adjustMultilineComment(value) { - var array, i, len, line, j, ch, spaces; + function calculateSpaces(str) { + var i; + for (i = str.length - 1; i >= 0; i -= 1) { + if (isLineTerminator(str.charAt(i))) { + break; + } + } + return (str.length - 1) - i; + } + + function adjustMultilineComment(value, specialBase) { + var array, i, len, line, j, ch, spaces, previousBase; - spaces = Number.MAX_VALUE; array = value.split(/\r\n|[\r\n]/); + spaces = Number.MAX_VALUE; // first line doesn't have indentation for (i = 1, len = array.length; i < len; i += 1) { @@ -318,95 +622,212 @@ } } - if (spaces % 2 === 1) { - // /* - // * - // */ - // If spaces are odd number, above pattern is considered. - // We waste 1 space. - spaces -= 1; + if (typeof specialBase !== 'undefined') { + // pattern like + // { + // var t = 20; /* + // * this is comment + // */ + // } + previousBase = base; + if (array[1][spaces] === '*') { + specialBase += ' '; + } + base = specialBase; + } else { + if (spaces & 1) { + // /* + // * + // */ + // If spaces are odd number, above pattern is considered. + // We waste 1 space. + spaces -= 1; + } + previousBase = base; } + for (i = 1, len = array.length; i < len; i += 1) { - array[i] = addIndent(array[i].slice(spaces)); + array[i] = toSourceNode(addIndent(array[i].slice(spaces))).join(''); } + + base = previousBase; + return array.join('\n'); } - function generateComment(comment) { + function generateComment(comment, specialBase) { if (comment.type === 'Line') { - // Esprima always produce last line comment with LineTerminator - return '//' + comment.value; + if (endsWithLineTerminator(comment.value)) { + return '//' + comment.value; + } else { + // Always use LineTerminator + return '//' + comment.value + '\n'; + } } if (extra.format.indent.adjustMultilineComment && /[\n\r]/.test(comment.value)) { - return adjustMultilineComment('/*' + comment.value + '*/'); + return adjustMultilineComment('/*' + comment.value + '*/', specialBase); } return '/*' + comment.value + '*/'; } + function addCommentsToStatement(stmt, result) { + var i, len, comment, save, node, tailingToStatement, specialBase, fragment; + + if (stmt.leadingComments && stmt.leadingComments.length > 0) { + save = result; + + comment = stmt.leadingComments[0]; + result = []; + if (safeConcatenation && stmt.type === Syntax.Program && stmt.body.length === 0) { + result.push('\n'); + } + result.push(generateComment(comment)); + if (!endsWithLineTerminator(toSourceNode(result).toString())) { + result.push('\n'); + } + + for (i = 1, len = stmt.leadingComments.length; i < len; i += 1) { + comment = stmt.leadingComments[i]; + fragment = [generateComment(comment)]; + if (!endsWithLineTerminator(toSourceNode(fragment).toString())) { + fragment.push('\n'); + } + result.push(addIndent(fragment)); + } + + result.push(addIndent(save)); + } + + if (stmt.trailingComments) { + tailingToStatement = !endsWithLineTerminator(toSourceNode(result).toString()); + specialBase = stringRepeat(' ', calculateSpaces(toSourceNode([base, result, indent]).toString())); + for (i = 0, len = stmt.trailingComments.length; i < len; i += 1) { + comment = stmt.trailingComments[i]; + if (tailingToStatement) { + // We assume target like following script + // + // var t = 20; /** + // * This is comment of t + // */ + if (i === 0) { + // first case + result = [result, indent]; + } else { + result = [result, specialBase]; + } + result.push(generateComment(comment, specialBase)); + } else { + result = [result, addIndent(generateComment(comment))]; + } + if (i !== len - 1 && !endsWithLineTerminator(toSourceNode(result).toString())) { + result = [result, '\n']; + } + } + } + + return result; + } + function parenthesize(text, current, should) { if (current < should) { - return '(' + text + ')'; + return ['(', text, ')']; } return text; } - function maybeBlock(stmt, suffix) { - var previousBase, result; + function maybeBlock(stmt, semicolonOptional, functionBody) { + var result, noLeadingComment; - if (stmt.type === Syntax.BlockStatement && (!extra.comment || !stmt.leadingComments)) { - result = ' ' + generateStatement(stmt); - if (suffix) { - return result + ' '; - } - return result; - } + noLeadingComment = !extra.comment || !stmt.leadingComments; - if (stmt.type === Syntax.EmptyStatement && (!extra.comment || !stmt.leadingComments)) { - result = ';'; - } else { - previousBase = base; - base += indent; - result = '\n' + addIndent(generateStatement(stmt)); - base = previousBase; + if (stmt.type === Syntax.BlockStatement && noLeadingComment) { + return [space, generateStatement(stmt, { functionBody: functionBody })]; } - if (suffix) { - return result + '\n' + addIndent(''); + if (stmt.type === Syntax.EmptyStatement && noLeadingComment) { + return ';'; } + + withIndent(function () { + result = [newline, addIndent(generateStatement(stmt, { semicolonOptional: semicolonOptional, functionBody: functionBody }))]; + }); + return result; } + function maybeBlockSuffix(stmt, result) { + var ends = endsWithLineTerminator(toSourceNode(result).toString()); + if (stmt.type === Syntax.BlockStatement && (!extra.comment || !stmt.leadingComments) && !ends) { + return [result, space]; + } + if (ends) { + return [result, base]; + } + return [result, newline, base]; + } + + function generateVerbatim(expr, option) { + var i, result; + result = expr[extra.verbatim].split(/\r\n|\n/); + for (i = 1; i < result.length; i++) { + result[i] = newline + base + result[i]; + } + + result = parenthesize(result, Precedence.Sequence, option.precedence); + return toSourceNode(result, expr); + } + function generateFunctionBody(node) { - var result, i, len; - result = '('; + var result, i, len, expr; + result = ['(']; for (i = 0, len = node.params.length; i < len; i += 1) { - result += node.params[i].name; - if ((i + 1) < len) { - result += ', '; + result.push(node.params[i].name); + if (i + 1 < len) { + result.push(',' + space); } } - return result + ')' + maybeBlock(node.body); + result.push(')'); + + if (node.expression) { + result.push(space); + expr = generateExpression(node.body, { + precedence: Precedence.Assignment, + allowIn: true, + allowCall: true + }); + if (expr.toString().charAt(0) === '{') { + expr = ['(', expr, ')']; + } + result.push(expr); + } else { + result.push(maybeBlock(node.body, false, true)); + } + return result; } function generateExpression(expr, option) { - var result, precedence, currentPrecedence, previousBase, i, len, raw, allowIn, allowCall; + var result, precedence, currentPrecedence, i, len, raw, fragment, multiline, leftChar, leftSource, rightChar, rightSource, allowIn, allowCall, allowUnparenthesizedNew, property, key, value; precedence = option.precedence; allowIn = option.allowIn; allowCall = option.allowCall; + if (extra.verbatim && expr.hasOwnProperty(extra.verbatim)) { + return generateVerbatim(expr, option); + } + switch (expr.type) { case Syntax.SequenceExpression: - result = ''; + result = []; allowIn |= (Precedence.Sequence < precedence); for (i = 0, len = expr.expressions.length; i < len; i += 1) { - result += generateExpression(expr.expressions[i], { + result.push(generateExpression(expr.expressions[i], { precedence: Precedence.Assignment, allowIn: allowIn, allowCall: true - }); - if ((i + 1) < len) { - result += ', '; + })); + if (i + 1 < len) { + result.push(',' + space); } } result = parenthesize(result, Precedence.Sequence, precedence); @@ -415,16 +836,19 @@ case Syntax.AssignmentExpression: allowIn |= (Precedence.Assignment < precedence); result = parenthesize( - generateExpression(expr.left, { - precedence: Precedence.Call, - allowIn: allowIn, - allowCall: true - }) + ' ' + expr.operator + ' ' + + [ + generateExpression(expr.left, { + precedence: Precedence.Call, + allowIn: allowIn, + allowCall: true + }), + space + expr.operator + space, generateExpression(expr.right, { precedence: Precedence.Assignment, allowIn: allowIn, allowCall: true - }), + }) + ], Precedence.Assignment, precedence ); @@ -433,21 +857,25 @@ case Syntax.ConditionalExpression: allowIn |= (Precedence.Conditional < precedence); result = parenthesize( - generateExpression(expr.test, { - precedence: Precedence.LogicalOR, - allowIn: allowIn, - allowCall: true - }) + ' ? ' + + [ + generateExpression(expr.test, { + precedence: Precedence.LogicalOR, + allowIn: allowIn, + allowCall: true + }), + space + '?' + space, generateExpression(expr.consequent, { precedence: Precedence.Assignment, allowIn: allowIn, allowCall: true - }) + ' : ' + + }), + space + ':' + space, generateExpression(expr.alternate, { precedence: Precedence.Assignment, allowIn: allowIn, allowCall: true - }), + }) + ], Precedence.Conditional, precedence ); @@ -459,20 +887,30 @@ allowIn |= (currentPrecedence < precedence); - result = + result = join( generateExpression(expr.left, { precedence: currentPrecedence, allowIn: allowIn, allowCall: true - }) + ' ' + expr.operator + ' ' + - generateExpression(expr.right, { - precedence: currentPrecedence + 1, - allowIn: allowIn, - allowCall: true - }); + }), + expr.operator + ); + + fragment = generateExpression(expr.right, { + precedence: currentPrecedence + 1, + allowIn: allowIn, + allowCall: true + }); + + if (expr.operator === '/' && fragment.toString().charAt(0) === '/') { + // If '/' concats with '/', it is interpreted as comment start + result.push(' ', fragment); + } else { + result = join(result, fragment); + } if (expr.operator === 'in' && !allowIn) { - result = '(' + result + ')'; + result = ['(', result, ')']; } else { result = parenthesize(result, currentPrecedence, precedence); } @@ -480,121 +918,167 @@ break; case Syntax.CallExpression: - result = generateExpression(expr.callee, { + result = [generateExpression(expr.callee, { precedence: Precedence.Call, allowIn: true, - allowCall: true - }); + allowCall: true, + allowUnparenthesizedNew: false + })]; - result += '('; + result.push('('); for (i = 0, len = expr['arguments'].length; i < len; i += 1) { - result += generateExpression(expr['arguments'][i], { + result.push(generateExpression(expr['arguments'][i], { precedence: Precedence.Assignment, allowIn: true, allowCall: true - }); - if ((i + 1) < len) { - result += ', '; + })); + if (i + 1 < len) { + result.push(',' + space); } } - result += ')'; + result.push(')'); if (!allowCall) { - result = '(' + result + ')'; + result = ['(', result, ')']; } else { result = parenthesize(result, Precedence.Call, precedence); } break; case Syntax.NewExpression: - result = 'new ' + generateExpression(expr.callee, { - precedence: Precedence.New, - allowIn: true, - allowCall: false - }); + len = expr['arguments'].length; + allowUnparenthesizedNew = option.allowUnparenthesizedNew === undefined || option.allowUnparenthesizedNew; - result += '('; - for (i = 0, len = expr['arguments'].length; i < len; i += 1) { - result += generateExpression(expr['arguments'][i], { - precedence: Precedence.Assignment, + result = join( + 'new', + generateExpression(expr.callee, { + precedence: Precedence.New, allowIn: true, - allowCall: true - }); - if ((i + 1) < len) { - result += ', '; + allowCall: false, + allowUnparenthesizedNew: allowUnparenthesizedNew && !parentheses && len === 0 + }) + ); + + if (!allowUnparenthesizedNew || parentheses || len > 0) { + result.push('('); + for (i = 0; i < len; i += 1) { + result.push(generateExpression(expr['arguments'][i], { + precedence: Precedence.Assignment, + allowIn: true, + allowCall: true + })); + if (i + 1 < len) { + result.push(',' + space); + } } + result.push(')'); } - result += ')'; result = parenthesize(result, Precedence.New, precedence); break; case Syntax.MemberExpression: - result = generateExpression(expr.object, { + result = [generateExpression(expr.object, { precedence: Precedence.Call, allowIn: true, - allowCall: allowCall - }); + allowCall: allowCall, + allowUnparenthesizedNew: false + })]; if (expr.computed) { - result += '[' + generateExpression(expr.property, { + result.push('[', generateExpression(expr.property, { precedence: Precedence.Sequence, allowIn: true, allowCall: allowCall - }) + ']'; + }), ']'); } else { if (expr.object.type === Syntax.Literal && typeof expr.object.value === 'number') { if (result.indexOf('.') < 0) { if (!/[eExX]/.test(result) && !(result.length >= 2 && result[0] === '0')) { - result += '.'; + result.push('.'); } } } - result += '.' + expr.property.name; + result.push('.' + expr.property.name); } result = parenthesize(result, Precedence.Member, precedence); break; case Syntax.UnaryExpression: - result = expr.operator; - if (result.length > 2) { - result += ' '; + fragment = generateExpression(expr.argument, { + precedence: Precedence.Unary, + allowIn: true, + allowCall: true + }); + + if (space === '') { + result = join(expr.operator, fragment); + } else { + result = [expr.operator]; + if (expr.operator.length > 2) { + // delete, void, typeof + // get `typeof []`, not `typeof[]` + result = join(result, fragment); + } else { + // Prevent inserting spaces between operator and argument if it is unnecessary + // like, `!cond` + leftSource = toSourceNode(result).toString(); + leftChar = leftSource.charAt(leftSource.length - 1); + rightChar = fragment.toString().charAt(0); + + if (((leftChar === '+' || leftChar === '-') && leftChar === rightChar) || (isIdentifierPart(leftChar) && isIdentifierPart(rightChar))) { + result.push(' ', fragment); + } else { + result.push(fragment); + } + } + } + result = parenthesize(result, Precedence.Unary, precedence); + break; + + case Syntax.YieldExpression: + if (expr.delegate) { + result = 'yield*'; + } else { + result = 'yield'; + } + if (expr.argument) { + result = join( + result, + generateExpression(expr.argument, { + precedence: Precedence.Assignment, + allowIn: true, + allowCall: true + }) + ); } - result = parenthesize( - result + generateExpression(expr.argument, { - precedence: Precedence.Unary + ( - expr.argument.type === Syntax.UnaryExpression && - expr.operator.length < 3 && - expr.argument.operator === expr.operator ? 1 : 0 - ), - allowIn: true, - allowCall: true - }), - Precedence.Unary, - precedence - ); break; case Syntax.UpdateExpression: if (expr.prefix) { result = parenthesize( - expr.operator + + [ + expr.operator, generateExpression(expr.argument, { precedence: Precedence.Unary, allowIn: true, allowCall: true - }), + }) + ], Precedence.Unary, precedence ); } else { result = parenthesize( - generateExpression(expr.argument, { - precedence: Precedence.Postfix, - allowIn: true, - allowCall: true - }) + expr.operator, + [ + generateExpression(expr.argument, { + precedence: Precedence.Postfix, + allowIn: true, + allowCall: true + }), + expr.operator + ], Precedence.Postfix, precedence ); @@ -602,60 +1086,94 @@ break; case Syntax.FunctionExpression: - result = 'function '; + result = 'function'; if (expr.id) { - result += expr.id.name; + result += ' ' + expr.id.name; + } else { + result += space; } - result += generateFunctionBody(expr); + + result = [result, generateFunctionBody(expr)]; break; + case Syntax.ArrayPattern: case Syntax.ArrayExpression: if (!expr.elements.length) { result = '[]'; break; } - result = '[\n'; - previousBase = base; - base += indent; - for (i = 0, len = expr.elements.length; i < len; i += 1) { - if (!expr.elements[i]) { - result += addIndent(''); - if ((i + 1) === len) { - result += ','; + multiline = expr.elements.length > 1; + result = ['[', multiline ? newline : '']; + withIndent(function (indent) { + for (i = 0, len = expr.elements.length; i < len; i += 1) { + if (!expr.elements[i]) { + if (multiline) { + result.push(indent); + } + if (i + 1 === len) { + result.push(','); + } + } else { + result.push(multiline ? indent : '', generateExpression(expr.elements[i], { + precedence: Precedence.Assignment, + allowIn: true, + allowCall: true + })); + } + if (i + 1 < len) { + result.push(',' + (multiline ? newline : space)); } - } else { - result += addIndent(generateExpression(expr.elements[i], { - precedence: Precedence.Assignment, - allowIn: true, - allowCall: true - })); - } - if ((i + 1) < len) { - result += ',\n'; } + }); + if (multiline && !endsWithLineTerminator(toSourceNode(result).toString())) { + result.push(newline); } - base = previousBase; - result += '\n' + addIndent(']'); + result.push(multiline ? base : '', ']'); break; case Syntax.Property: if (expr.kind === 'get' || expr.kind === 'set') { - result = expr.kind + ' ' + generateExpression(expr.key, { - precedence: Precedence.Sequence, - allowIn: true, - allowCall: true - }) + generateFunctionBody(expr.value); - } else { - result = + result = [ + expr.kind + ' ', generateExpression(expr.key, { precedence: Precedence.Sequence, allowIn: true, allowCall: true - }) + ': ' + generateExpression(expr.value, { - precedence: Precedence.Assignment, + }), + generateFunctionBody(expr.value) + ]; + } else { + if (expr.shorthand) { + result = generateExpression(expr.key, { + precedence: Precedence.Sequence, allowIn: true, allowCall: true }); + } else if (expr.method) { + result = []; + if (expr.value.generator) { + result.push('*'); + } + result.push(generateExpression(expr.key, { + precedence: Precedence.Sequence, + allowIn: true, + allowCall: true + }), generateFunctionBody(expr.value)); + } else { + result = [ + generateExpression(expr.key, { + precedence: Precedence.Sequence, + allowIn: true, + allowCall: true + }), + ':' + space, + generateExpression(expr.value, { + precedence: Precedence.Assignment, + allowIn: true, + allowCall: true + }) + ]; + } } break; @@ -664,21 +1182,95 @@ result = '{}'; break; } - result = '{\n'; - previousBase = base; - base += indent; - for (i = 0, len = expr.properties.length; i < len; i += 1) { - result += addIndent(generateExpression(expr.properties[i], { + multiline = expr.properties.length > 1; + + withIndent(function (indent) { + fragment = generateExpression(expr.properties[0], { precedence: Precedence.Sequence, allowIn: true, allowCall: true - })); - if ((i + 1) < len) { - result += ',\n'; + }); + }); + + if (!multiline) { + // issues 4 + // Do not transform from + // dejavu.Class.declare({ + // method2: function () {} + // }); + // to + // dejavu.Class.declare({method2: function () { + // }}); + if (!hasLineTerminator(toSourceNode(fragment).toString())) { + result = [ '{', space, fragment, space, '}' ]; + break; + } + } + + withIndent(function (indent) { + result = [ '{', newline, indent, fragment ]; + + if (multiline) { + result.push(',' + newline); + for (i = 1, len = expr.properties.length; i < len; i += 1) { + result.push(indent, generateExpression(expr.properties[i], { + precedence: Precedence.Sequence, + allowIn: true, + allowCall: true + })); + if (i + 1 < len) { + result.push(',' + newline); + } + } + } + }); + + if (!endsWithLineTerminator(toSourceNode(result).toString())) { + result.push(newline); + } + result.push(base, '}'); + break; + + case Syntax.ObjectPattern: + if (!expr.properties.length) { + result = '{}'; + break; + } + + multiline = false; + if (expr.properties.length === 1) { + property = expr.properties[0]; + if (property.value.type !== Syntax.Identifier) { + multiline = true; + } + } else { + for (i = 0, len = expr.properties.length; i < len; i += 1) { + property = expr.properties[i]; + if (!property.shorthand) { + multiline = true; + break; + } + } + } + result = ['{', multiline ? newline : '' ]; + + withIndent(function (indent) { + for (i = 0, len = expr.properties.length; i < len; i += 1) { + result.push(multiline ? indent : '', generateExpression(expr.properties[i], { + precedence: Precedence.Sequence, + allowIn: true, + allowCall: true + })); + if (i + 1 < len) { + result.push(',' + (multiline ? newline : space)); + } } + }); + + if (multiline && !endsWithLineTerminator(toSourceNode(result).toString())) { + result.push(newline); } - base = previousBase; - result += '\n' + addIndent('}'); + result.push(multiline ? base : '', '}'); break; case Syntax.ThisExpression: @@ -709,90 +1301,185 @@ break; } - if (typeof expr.value === 'string') { - result = escapeString(expr.value); - break; + if (typeof expr.value === 'string') { + result = escapeString(expr.value); + break; + } + + if (typeof expr.value === 'number') { + result = generateNumber(expr.value); + break; + } + + result = expr.value.toString(); + break; + + case Syntax.ComprehensionExpression: + result = [ + '[', + generateExpression(expr.body, { + precedence: Precedence.Assignment, + allowIn: true, + allowCall: true + }) + ]; + + if (expr.blocks) { + for (i = 0, len = expr.blocks.length; i < len; i += 1) { + fragment = generateExpression(expr.blocks[i], { + precedence: Precedence.Sequence, + allowIn: true, + allowCall: true + }); + result = join(result, fragment); + } + } + + if (expr.filter) { + result = join(result, 'if' + space); + fragment = generateExpression(expr.filter, { + precedence: Precedence.Sequence, + allowIn: true, + allowCall: true + }); + if (extra.moz.parenthesizedComprehensionBlock) { + result = join(result, [ '(', fragment, ')' ]); + } else { + result = join(result, fragment); + } + } + result.push(']'); + break; + + case Syntax.ComprehensionBlock: + if (expr.left.type === Syntax.VariableDeclaration) { + fragment = [ + expr.left.kind + ' ', + generateStatement(expr.left.declarations[0], { + allowIn: false + }) + ]; + } else { + fragment = generateExpression(expr.left, { + precedence: Precedence.Call, + allowIn: true, + allowCall: true + }); } - if (typeof expr.value === 'number' && expr.value === Infinity) { - // Infinity is variable - result = '1e+1000'; - break; - } + fragment = join(fragment, expr.of ? 'of' : 'in'); + fragment = join(fragment, generateExpression(expr.right, { + precedence: Precedence.Sequence, + allowIn: true, + allowCall: true + })); - result = expr.value.toString(); + if (extra.moz.parenthesizedComprehensionBlock) { + result = [ 'for' + space + '(', fragment, ')' ]; + } else { + result = join('for' + space, fragment); + } break; default: - break; - } - - if (result === undefined) { throw new Error('Unknown expression type: ' + expr.type); } - return result; + + return toSourceNode(result, expr); } function generateStatement(stmt, option) { - var i, len, result, previousBase, comment, save, ret, node, allowIn; + var i, len, result, node, allowIn, functionBody, directiveContext, fragment, semicolon; allowIn = true; + semicolon = ';'; + functionBody = false; + directiveContext = false; if (option) { - allowIn = option.allowIn; + allowIn = option.allowIn === undefined || option.allowIn; + if (!semicolons && option.semicolonOptional === true) { + semicolon = ''; + } + functionBody = option.functionBody; + directiveContext = option.directiveContext; } switch (stmt.type) { case Syntax.BlockStatement: - result = '{\n'; + result = ['{', newline]; - previousBase = base; - base += indent; - for (i = 0, len = stmt.body.length; i < len; i += 1) { - result += addIndent(generateStatement(stmt.body[i])) + '\n'; - } - base = previousBase; + withIndent(function () { + for (i = 0, len = stmt.body.length; i < len; i += 1) { + fragment = addIndent(generateStatement(stmt.body[i], { + semicolonOptional: i === len - 1, + directiveContext: functionBody + })); + result.push(fragment); + if (!endsWithLineTerminator(toSourceNode(fragment).toString())) { + result.push(newline); + } + } + }); - result += addIndent('}'); + result.push(addIndent('}')); break; case Syntax.BreakStatement: if (stmt.label) { - result = 'break ' + stmt.label.name + ';'; + result = 'break ' + stmt.label.name + semicolon; } else { - result = 'break;'; + result = 'break' + semicolon; } break; case Syntax.ContinueStatement: if (stmt.label) { - result = 'continue ' + stmt.label.name + ';'; + result = 'continue ' + stmt.label.name + semicolon; + } else { + result = 'continue' + semicolon; + } + break; + + case Syntax.DirectiveStatement: + if (stmt.raw) { + result = stmt.raw + semicolon; } else { - result = 'continue;'; + result = escapeDirective(stmt.directive) + semicolon; } break; case Syntax.DoWhileStatement: - result = 'do' + maybeBlock(stmt.body, true) + 'while (' + generateExpression(stmt.test, { - precedence: Precedence.Sequence, - allowIn: true, - allowCall: true - }) + ');'; + // Because `do 42 while (cond)` is Syntax Error. We need semicolon. + result = join('do', maybeBlock(stmt.body)); + result = maybeBlockSuffix(stmt.body, result); + result = join(result, [ + 'while' + space + '(', + generateExpression(stmt.test, { + precedence: Precedence.Sequence, + allowIn: true, + allowCall: true + }), + ')' + semicolon + ]); break; case Syntax.CatchClause: - previousBase = base; - base += indent; - result = ' catch (' + generateExpression(stmt.param, { - precedence: Precedence.Sequence, - allowIn: true, - allowCall: true - }) + ')'; - base = previousBase; - result += maybeBlock(stmt.body); + withIndent(function () { + result = [ + 'catch' + space + '(', + generateExpression(stmt.param, { + precedence: Precedence.Sequence, + allowIn: true, + allowCall: true + }), + ')' + ]; + }); + result.push(maybeBlock(stmt.body)); break; case Syntax.DebuggerStatement: - result = 'debugger;'; + result = 'debugger' + semicolon; break; case Syntax.EmptyStatement: @@ -800,358 +1487,356 @@ break; case Syntax.ExpressionStatement: - result = generateExpression(stmt.expression, { + result = [generateExpression(stmt.expression, { precedence: Precedence.Sequence, allowIn: true, allowCall: true - }); + })]; // 12.4 '{', 'function' is not allowed in this position. - // wrap espression with parentheses - if (result[0] === '{' || result.indexOf('function ') === 0) { - result = '(' + result + ');'; + // wrap expression with parentheses + if (result.toString().charAt(0) === '{' || (result.toString().slice(0, 8) === 'function' && " (".indexOf(result.toString().charAt(8)) >= 0) || (directive && directiveContext && stmt.expression.type === Syntax.Literal && typeof stmt.expression.value === 'string')) { + result = ['(', result, ')' + semicolon]; } else { - result += ';'; + result.push(semicolon); } break; case Syntax.VariableDeclarator: if (stmt.init) { - result = stmt.id.name + ' = ' + generateExpression(stmt.init, { - precedence: Precedence.Assignment, - allowIn: allowIn, - allowCall: true - }); + result = [ + generateExpression(stmt.id, { + precedence: Precedence.Assignment, + allowIn: allowIn, + allowCall: true + }) + space + '=' + space, + generateExpression(stmt.init, { + precedence: Precedence.Assignment, + allowIn: allowIn, + allowCall: true + }) + ]; } else { result = stmt.id.name; } break; case Syntax.VariableDeclaration: - result = stmt.kind; + result = [stmt.kind]; // special path for // var x = function () { // }; if (stmt.declarations.length === 1 && stmt.declarations[0].init && stmt.declarations[0].init.type === Syntax.FunctionExpression) { - result += ' ' + generateStatement(stmt.declarations[0], { + result.push(' ', generateStatement(stmt.declarations[0], { allowIn: allowIn - }); + })); } else { // VariableDeclarator is typed as Statement, // but joined with comma (not LineTerminator). // So if comment is attached to target node, we should specialize. - previousBase = base; - base += indent; - - node = stmt.declarations[0]; - if (extra.comment && node.leadingComments) { - result += '\n' + addIndent(generateStatement(node, { - allowIn: allowIn - })); - } else { - result += ' ' + generateStatement(node, { - allowIn: allowIn - }); - } - - for (i = 1, len = stmt.declarations.length; i < len; i += 1) { - node = stmt.declarations[i]; + withIndent(function () { + node = stmt.declarations[0]; if (extra.comment && node.leadingComments) { - result += ',\n' + addIndent(generateStatement(node, { + result.push('\n', addIndent(generateStatement(node, { allowIn: allowIn - })); + }))); } else { - result += ', ' + generateStatement(node, { + result.push(' ', generateStatement(node, { allowIn: allowIn - }); + })); } - } - base = previousBase; + + for (i = 1, len = stmt.declarations.length; i < len; i += 1) { + node = stmt.declarations[i]; + if (extra.comment && node.leadingComments) { + result.push(',' + newline, addIndent(generateStatement(node, { + allowIn: allowIn + }))); + } else { + result.push(',' + space, generateStatement(node, { + allowIn: allowIn + })); + } + } + }); } - result += ';'; + result.push(semicolon); break; case Syntax.ThrowStatement: - result = 'throw ' + generateExpression(stmt.argument, { - precedence: Precedence.Sequence, - allowIn: true, - allowCall: true - }) + ';'; + result = [join( + 'throw', + generateExpression(stmt.argument, { + precedence: Precedence.Sequence, + allowIn: true, + allowCall: true + }) + ), semicolon]; break; case Syntax.TryStatement: - result = 'try' + maybeBlock(stmt.block); + result = ['try', maybeBlock(stmt.block)]; + result = maybeBlockSuffix(stmt.block, result); for (i = 0, len = stmt.handlers.length; i < len; i += 1) { - result += generateStatement(stmt.handlers[i]); + result = join(result, generateStatement(stmt.handlers[i])); + if (stmt.finalizer || i + 1 !== len) { + result = maybeBlockSuffix(stmt.handlers[i].body, result); + } } if (stmt.finalizer) { - result += ' finally' + maybeBlock(stmt.finalizer); + result = join(result, ['finally', maybeBlock(stmt.finalizer)]); } break; case Syntax.SwitchStatement: - previousBase = base; - base += indent; - result = 'switch (' + generateExpression(stmt.discriminant, { - precedence: Precedence.Sequence, - allowIn: true, - allowCall: true - }) + ') {\n'; - base = previousBase; + withIndent(function () { + result = [ + 'switch' + space + '(', + generateExpression(stmt.discriminant, { + precedence: Precedence.Sequence, + allowIn: true, + allowCall: true + }), + ')' + space + '{' + newline + ]; + }); if (stmt.cases) { for (i = 0, len = stmt.cases.length; i < len; i += 1) { - result += addIndent(generateStatement(stmt.cases[i])) + '\n'; + fragment = addIndent(generateStatement(stmt.cases[i], {semicolonOptional: i === len - 1})); + result.push(fragment); + if (!endsWithLineTerminator(toSourceNode(fragment).toString())) { + result.push(newline); + } } } - result += addIndent('}'); + result.push(addIndent('}')); break; case Syntax.SwitchCase: - previousBase = base; - base += indent; - if (stmt.test) { - result = 'case ' + generateExpression(stmt.test, { - precedence: Precedence.Sequence, - allowIn: true, - allowCall: true - }) + ':'; - } else { - result = 'default:'; - } + withIndent(function () { + if (stmt.test) { + result = [ + join('case', generateExpression(stmt.test, { + precedence: Precedence.Sequence, + allowIn: true, + allowCall: true + })), + ':' + ]; + } else { + result = ['default:']; + } - i = 0; - len = stmt.consequent.length; - if (len && stmt.consequent[0].type === Syntax.BlockStatement) { - result += maybeBlock(stmt.consequent[0]); - i = 1; - } + i = 0; + len = stmt.consequent.length; + if (len && stmt.consequent[0].type === Syntax.BlockStatement) { + fragment = maybeBlock(stmt.consequent[0]); + result.push(fragment); + i = 1; + } - for (; i < len; i += 1) { - result += '\n' + addIndent(generateStatement(stmt.consequent[i])); - } + if (i !== len && !endsWithLineTerminator(toSourceNode(result).toString())) { + result.push(newline); + } - base = previousBase; + for (; i < len; i += 1) { + fragment = addIndent(generateStatement(stmt.consequent[i], {semicolonOptional: i === len - 1 && semicolon === ''})); + result.push(fragment); + if (i + 1 !== len && !endsWithLineTerminator(toSourceNode(fragment).toString())) { + result.push(newline); + } + } + }); break; case Syntax.IfStatement: - if (stmt.alternate) { - if (stmt.alternate.type === Syntax.IfStatement) { - previousBase = base; - base += indent; - result = 'if (' + generateExpression(stmt.test, { + withIndent(function () { + result = [ + 'if' + space + '(', + generateExpression(stmt.test, { precedence: Precedence.Sequence, allowIn: true, allowCall: true - }) + ')'; - base = previousBase; - result += maybeBlock(stmt.consequent, true) + 'else ' + generateStatement(stmt.alternate); + }), + ')' + ]; + }); + if (stmt.alternate) { + result.push(maybeBlock(stmt.consequent)); + result = maybeBlockSuffix(stmt.consequent, result); + if (stmt.alternate.type === Syntax.IfStatement) { + result = join(result, ['else ', generateStatement(stmt.alternate, {semicolonOptional: semicolon === ''})]); } else { - previousBase = base; - base += indent; - result = 'if (' + generateExpression(stmt.test, { - precedence: Precedence.Sequence, - allowIn: true, - allowCall: true - }) + ')'; - base = previousBase; - result += maybeBlock(stmt.consequent, true) + 'else' + maybeBlock(stmt.alternate); + result = join(result, join('else', maybeBlock(stmt.alternate, semicolon === ''))); } } else { - previousBase = base; - base += indent; - result = 'if (' + generateExpression(stmt.test, { - precedence: Precedence.Sequence, - allowIn: true, - allowCall: true - }) + ')'; - base = previousBase; - result += maybeBlock(stmt.consequent); + result.push(maybeBlock(stmt.consequent, semicolon === '')); } break; case Syntax.ForStatement: - previousBase = base; - base += indent; - result = 'for ('; - if (stmt.init) { - if (stmt.init.type === Syntax.VariableDeclaration) { - result += generateStatement(stmt.init, { - allowIn: false - }); + withIndent(function () { + result = ['for' + space + '(']; + if (stmt.init) { + if (stmt.init.type === Syntax.VariableDeclaration) { + result.push(generateStatement(stmt.init, {allowIn: false})); + } else { + result.push(generateExpression(stmt.init, { + precedence: Precedence.Sequence, + allowIn: false, + allowCall: true + }), ';'); + } } else { - result += generateExpression(stmt.init, { + result.push(';'); + } + + if (stmt.test) { + result.push(space, generateExpression(stmt.test, { precedence: Precedence.Sequence, - allowIn: false, + allowIn: true, allowCall: true - }) + ';'; + }), ';'); + } else { + result.push(';'); } - } else { - result += ';'; - } - if (stmt.test) { - result += ' ' + generateExpression(stmt.test, { - precedence: Precedence.Sequence, - allowIn: true, - allowCall: true - }) + ';'; - } else { - result += ';'; - } - - if (stmt.update) { - result += ' ' + generateExpression(stmt.update, { - precedence: Precedence.Sequence, - allowIn: true, - allowCall: true - }) + ')'; - } else { - result += ')'; - } - base = previousBase; + if (stmt.update) { + result.push(space, generateExpression(stmt.update, { + precedence: Precedence.Sequence, + allowIn: true, + allowCall: true + }), ')'); + } else { + result.push(')'); + } + }); - result += maybeBlock(stmt.body); + result.push(maybeBlock(stmt.body, semicolon === '')); break; case Syntax.ForInStatement: - result = 'for ('; - if (stmt.left.type === Syntax.VariableDeclaration) { - previousBase = base; - base += indent + indent; - result += stmt.left.kind + ' ' + generateStatement(stmt.left.declarations[0], { - allowIn: false - }); - base = previousBase; - } else { - previousBase = base; - base += indent; - result += generateExpression(stmt.left, { - precedence: Precedence.Call, - allowIn: true, - allowCall: true - }); - base = previousBase; - } + result = ['for' + space + '(']; + withIndent(function () { + if (stmt.left.type === Syntax.VariableDeclaration) { + withIndent(function () { + result.push(stmt.left.kind + ' ', generateStatement(stmt.left.declarations[0], { + allowIn: false + })); + }); + } else { + result.push(generateExpression(stmt.left, { + precedence: Precedence.Call, + allowIn: true, + allowCall: true + })); + } - previousBase = base; - base += indent; - result += ' in ' + generateExpression(stmt.right, { - precedence: Precedence.Sequence, - allowIn: true, - allowCall: true - }) + ')'; - base = previousBase; - result += maybeBlock(stmt.body); + result = join(result, 'in'); + result = [join( + result, + generateExpression(stmt.right, { + precedence: Precedence.Sequence, + allowIn: true, + allowCall: true + }) + ), ')']; + }); + result.push(maybeBlock(stmt.body, semicolon === '')); break; case Syntax.LabeledStatement: - result = stmt.label.name + ':' + maybeBlock(stmt.body); + result = [stmt.label.name + ':', maybeBlock(stmt.body, semicolon === '')]; break; case Syntax.Program: - result = ''; - for (i = 0, len = stmt.body.length; i < len; i += 1) { - result += addIndent(generateStatement(stmt.body[i])); - if ((i + 1) < len) { - result += '\n'; + len = stmt.body.length; + result = [safeConcatenation && len > 0 ? '\n' : '']; + for (i = 0; i < len; i += 1) { + fragment = addIndent( + generateStatement(stmt.body[i], { + semicolonOptional: !safeConcatenation && i === len - 1, + directiveContext: true + }) + ); + result.push(fragment); + if (i + 1 < len && !endsWithLineTerminator(toSourceNode(fragment).toString())) { + result.push(newline); } } break; case Syntax.FunctionDeclaration: - result = 'function '; - if (stmt.id) { - result += stmt.id.name; - } - result += generateFunctionBody(stmt); + result = [(stmt.generator && !extra.moz.starlessGenerator ? 'function* ' : 'function ') + stmt.id.name, generateFunctionBody(stmt)]; break; case Syntax.ReturnStatement: if (stmt.argument) { - result = 'return ' + generateExpression(stmt.argument, { - precedence: Precedence.Sequence, - allowIn: true, - allowCall: true - }) + ';'; + result = [join( + 'return', + generateExpression(stmt.argument, { + precedence: Precedence.Sequence, + allowIn: true, + allowCall: true + }) + ), semicolon]; } else { - result = 'return;'; + result = ['return' + semicolon]; } break; case Syntax.WhileStatement: - previousBase = base; - base += indent; - result = 'while (' + generateExpression(stmt.test, { - precedence: Precedence.Sequence, - allowIn: true, - allowCall: true - }) + ')'; - base = previousBase; - result += maybeBlock(stmt.body); + withIndent(function () { + result = [ + 'while' + space + '(', + generateExpression(stmt.test, { + precedence: Precedence.Sequence, + allowIn: true, + allowCall: true + }), + ')' + ]; + }); + result.push(maybeBlock(stmt.body, semicolon === '')); break; case Syntax.WithStatement: - previousBase = base; - base += indent; - result = 'with (' + generateExpression(stmt.object, { - precedence: Precedence.Sequence, - allowIn: true, - allowCall: true - }) + ')'; - base = previousBase; - result += maybeBlock(stmt.body); + withIndent(function () { + result = [ + 'with' + space + '(', + generateExpression(stmt.object, { + precedence: Precedence.Sequence, + allowIn: true, + allowCall: true + }), + ')' + ]; + }); + result.push(maybeBlock(stmt.body, semicolon === '')); break; default: - break; - } - - if (result === undefined) { throw new Error('Unknown statement type: ' + stmt.type); } // Attach comments if (extra.comment) { - if (stmt.leadingComments) { - save = result; - - comment = stmt.leadingComments[0]; - result = generateComment(comment); - if (!endsWithLineTerminator(result)) { - result += '\n'; - } - - for (i = 1, len = stmt.leadingComments.length; i < len; i += 1) { - comment = stmt.leadingComments[i]; - ret = generateComment(comment); - if (!endsWithLineTerminator(ret)) { - ret += '\n'; - } - result += addIndent(ret); - } - - result += addIndent(save); - } + result = addCommentsToStatement(stmt, result); + } - if (stmt.trailingComments) { - for (i = 0, len = stmt.trailingComments.length; i < len; i += 1) { - comment = stmt.trailingComments[i]; - if (!endsWithLineTerminator(result)) { - result += '\n'; - } - result += addIndent(generateComment(comment)); - } - } + fragment = toSourceNode(result).toString(); + if (stmt.type === Syntax.Program && !safeConcatenation && newline === '' && fragment.charAt(fragment.length - 1) === '\n') { + result = toSourceNode(result).replaceRight(/\s+$/, ''); } - return result; + return toSourceNode(result, stmt); } function generate(node, options) { - var defaultOptions = getDefaultOptions(); + var defaultOptions = getDefaultOptions(), result, pair; - if (typeof options !== 'undefined') { + if (options != null) { // Obsolete options // // `options.indent` @@ -1161,7 +1846,9 @@ if (typeof options.indent === 'string') { defaultOptions.format.indent.style = options.indent; } - + if (typeof options.base === 'number') { + defaultOptions.format.indent.base = options.base; + } options = updateDeeply(defaultOptions, options); indent = options.format.indent.style; if (typeof options.base === 'string') { @@ -1169,20 +1856,47 @@ } else { base = stringRepeat(indent, options.format.indent.base); } - parse = options.parse; } else { options = defaultOptions; indent = options.format.indent.style; base = stringRepeat(indent, options.format.indent.base); - parse = options.parse; } + json = options.format.json; + renumber = options.format.renumber; + hexadecimal = json ? false : options.format.hexadecimal; + quotes = json ? 'double' : options.format.quotes; + escapeless = options.format.escapeless; + if (options.format.compact) { + newline = space = indent = base = ''; + } else { + newline = '\n'; + space = ' '; + } + parentheses = options.format.parentheses; + semicolons = options.format.semicolons; + safeConcatenation = options.format.safeConcatenation; + directive = options.directive; + parse = json ? null : options.parse; + sourceMap = options.sourceMap; extra = options; + if (sourceMap) { + if (typeof process !== 'undefined') { + // We assume environment is node.js + SourceNode = require('source-map').SourceNode; + } else { + SourceNode = global.sourceMap.SourceNode; + } + } else { + SourceNode = SourceNodeMock; + } + switch (node.type) { case Syntax.BlockStatement: case Syntax.BreakStatement: case Syntax.CatchClause: case Syntax.ContinueStatement: + case Syntax.DirectiveStatement: case Syntax.DoWhileStatement: case Syntax.DebuggerStatement: case Syntax.EmptyStatement: @@ -1202,10 +1916,12 @@ case Syntax.VariableDeclarator: case Syntax.WhileStatement: case Syntax.WithStatement: - return generateStatement(node); + result = generateStatement(node); + break; case Syntax.AssignmentExpression: case Syntax.ArrayExpression: + case Syntax.ArrayPattern: case Syntax.BinaryExpression: case Syntax.CallExpression: case Syntax.ConditionalExpression: @@ -1216,21 +1932,35 @@ case Syntax.MemberExpression: case Syntax.NewExpression: case Syntax.ObjectExpression: + case Syntax.ObjectPattern: case Syntax.Property: case Syntax.SequenceExpression: case Syntax.ThisExpression: case Syntax.UnaryExpression: case Syntax.UpdateExpression: - return generateExpression(node, { + case Syntax.YieldExpression: + + result = generateExpression(node, { precedence: Precedence.Sequence, allowIn: true, allowCall: true }); + break; default: - break; + throw new Error('Unknown node type: ' + node.type); + } + + if (!sourceMap) { + return result.toString(); + } + + pair = result.toStringWithSourceMap({file: options.sourceMap}); + + if (options.sourceMapWithCode) { + return pair; } - throw new Error('Unknown node type: ' + node.type); + return pair.map.toString(); } // simple visitor implementation @@ -1238,6 +1968,7 @@ VisitorKeys = { AssignmentExpression: ['left', 'right'], ArrayExpression: ['elements'], + ArrayPattern: ['elements'], BlockStatement: ['body'], BinaryExpression: ['left', 'right'], BreakStatement: ['label'], @@ -1245,6 +1976,7 @@ CatchClause: ['param', 'body'], ConditionalExpression: ['test', 'consequent', 'alternate'], ContinueStatement: ['label'], + DirectiveStatement: [], DoWhileStatement: ['body', 'test'], DebuggerStatement: [], EmptyStatement: [], @@ -1261,11 +1993,12 @@ MemberExpression: ['object', 'property'], NewExpression: ['callee', 'arguments'], ObjectExpression: ['properties'], + ObjectPattern: ['properties'], Program: ['body'], Property: ['key', 'value'], ReturnStatement: ['argument'], SequenceExpression: ['expressions'], - SwitchStatement: ['descriminant', 'cases'], + SwitchStatement: ['discriminant', 'cases'], SwitchCase: ['test', 'consequent'], ThisExpression: [], ThrowStatement: ['argument'], @@ -1275,7 +2008,8 @@ VariableDeclaration: ['declarations'], VariableDeclarator: ['id', 'init'], WhileStatement: ['test', 'body'], - WithStatement: ['object', 'body'] + WithStatement: ['object', 'body'], + YieldExpression: ['argument'] }; VisitorOption = { @@ -1284,17 +2018,27 @@ }; function traverse(top, visitor) { - var worklist, leavelist, node, ret, current, current2, candidates, candidate; + var worklist, leavelist, node, ret, current, current2, candidates, candidate, marker = {}; worklist = [ top ]; - leavelist = []; + leavelist = [ null ]; while (worklist.length) { node = worklist.pop(); - if (node) { + if (node === marker) { + node = leavelist.pop(); + if (visitor.leave) { + ret = visitor.leave(node, leavelist[leavelist.length - 1]); + } else { + ret = undefined; + } + if (ret === VisitorOption.Break) { + return; + } + } else if (node) { if (visitor.enter) { - ret = visitor.enter(node); + ret = visitor.enter(node, leavelist[leavelist.length - 1]); } else { ret = undefined; } @@ -1303,7 +2047,7 @@ return; } - worklist.push(null); + worklist.push(marker); leavelist.push(node); if (ret !== VisitorOption.Skip) { @@ -1325,21 +2069,10 @@ } } } - } else { - node = leavelist.pop(); - if (visitor.leave) { - ret = visitor.leave(node); - } else { - ret = undefined; - } - if (ret === VisitorOption.Break) { - return; - } } } } - // based on LLVM libc++ upper_bound / lower_bound // MIT License @@ -1408,45 +2141,30 @@ function attachComments(tree, providedComments, tokens) { // At first, we should calculate extended comment ranges. - var comments = [], len, i; + var comments = [], comment, len, i; if (!tree.range) { throw new Error('attachComments needs range information'); } + // tokens array is empty, we attach comments to tree as 'leadingComments' + if (!tokens.length) { + if (providedComments.length) { + for (i = 0, len = providedComments.length; i < len; i += 1) { + comment = deepCopy(providedComments[i]); + comment.extendedRange = [0, tree.range[0]]; + comments.push(comment); + } + tree.leadingComments = comments; + } + return tree; + } + for (i = 0, len = providedComments.length; i < len; i += 1) { comments.push(extendCommentRange(deepCopy(providedComments[i]), tokens)); } - // - // For example, - // - // var i1 = 10, // test - // i2 = 20; - // - // Above comment is probably trailing to i1 = 10. - // So we specialize comma token. - // - // And second, comma first style example, - // - // var i = [ - // 10 // testing - // , 20 // testing 2 - // ]; - // - // 'testing' comment is trailing to 10 is proper. - // - // So we add some specialize path. - // - // 1. check comment is'nt containing LineTerminator - // 2. check LineTerminator is not found between previous token and comment - // 3. check LineTerminator is found between comment and next token - // - // If above conditions are all true, - // we assume this comment should be attached to previous token as trailing comment. - // - - // This traverse attacher is based on John Freeman's implementation. + // This is based on John Freeman's implementation. traverse(tree, { cursor: 0, enter: function (node) { @@ -1517,11 +2235,11 @@ } // Sync with package.json. - exports.version = '0.0.4'; + exports.version = '0.0.16-dev'; exports.generate = generate; exports.traverse = traverse; exports.attachComments = attachComments; -}(typeof exports === 'undefined' ? (escodegen = {}) : exports)); +}, this)); /* vim: set sw=4 ts=4 et tw=80 : */
