You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@couchdb.apache.org by da...@apache.org on 2019/12/18 17:56:37 UTC

[couchdb-escodegen] 06/11: Update gh-pages

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 <ut...@gmail.com>
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 <es...@michael.ficarra.me>
+  Copyright (C) 2012 Robert Gust-Bardon <do...@robert.gust-bardon.org>
   Copyright (C) 2012 John Freeman <jf...@gmail.com>
-  Copyright (C) 2012 Ariya Hidayat <ar...@gmail.com>
+  Copyright (C) 2011-2012 Ariya Hidayat <ar...@gmail.com>
   Copyright (C) 2012 Mathias Bynens <ma...@qiwi.be>
   Copyright (C) 2012 Joost-Wim Boekesteijn <jo...@boekesteijn.nl>
   Copyright (C) 2012 Kris Kowal <kr...@cixar.com>
   Copyright (C) 2012 Yusuke Suzuki <ut...@gmail.com>
   Copyright (C) 2012 Arpad Borsos <ar...@googlemail.com>
-  Copyright (C) 2011 Ariya Hidayat <ar...@gmail.com>
 
   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 : */